/* =============================================================================
   styles.css

   Layout follows the Unity/C# original: the generated nested-circle diagram
   is the full-screen backdrop, and every piece of chrome -- the title, the
   number/factors readout, and the controls -- floats on top of it as a
   translucent "frosted" overlay panel. The overlays stay neutral and quiet
   so the diagram remains the visual centerpiece; one accent color is used
   sparingly (slider thumbs, checkboxes, the confirm button, focus rings).
   A monospace treatment for the number/factors display, since this is a tool
   about numbers -- a small, content-grounded typographic choice.
   ============================================================================= */

:root {
  --color-background: #f7f8fa;
  --color-surface: #ffffff;
  --color-ink: #1b1f23;
  --color-ink-muted: #5b6572;
  --color-hairline: #dde1e6;
  /* Deliberately more visible than --color-hairline -- used only for the
     background/unfilled portion of slider tracks, which needs to read
     clearly against the translucent frosted overlay panels. */
  --color-track-background: #b6bfc9;
  --color-accent: #3452d9;
  --color-accent-ink: #ffffff;
  /* Overridden live via the "Background color" picker in main.js. */
  --color-canvas-background: #002000;

  /* Overlay chrome: translucent so the diagram shows through, backed by a
     blur so text stays legible over whatever colours are behind it. */
  --color-overlay: rgba(247, 248, 250, 0.82);
  --color-overlay-hairline: rgba(27, 31, 35, 0.12);
  --shadow-overlay: 0 6px 24px rgba(27, 31, 35, 0.22);

  --font-body: system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "SFMono-Regular", ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  /* The fixed SVG backdrop fills the viewport and every control is a fixed
     overlay, so the page itself never scrolls -- only the controls panel
     scrolls internally when it is taller than the screen. */
  overflow: hidden;
  background: var(--color-canvas-background);
  color: var(--color-ink);
  font-family: var(--font-body);
}

/* -----------------------------------------------------------------------
   Full-screen diagram backdrop
   ----------------------------------------------------------------------- */

#circle-canvas {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  /* Fills the whole viewport; the square diagram is centered within it
     (default preserveAspectRatio) and this colour covers the rest. */
  background: var(--color-canvas-background);
}

#circle-canvas circle {
  transition: none; /* all motion is driven explicitly by render/lerp_animation.js */
}

/* Number labels: dark ink with a soft light halo (paint-order: stroke draws
   the outline behind the fill) so they stay legible on any circle colour or
   background. font-size and stroke-width are both set per circle in
   render/svg_circle_renderer.js -- font-size from the "Label size" slider
   (core/ has no notion of circle radius any more) and stroke-width as a
   fraction of that font-size, so the halo scales with the text instead of
   staying a fixed 2px (which, against a small label, rendered as an
   oversized blob rather than a thin outline). */
#circle-canvas text {
  fill: #10131a;
  stroke: rgba(255, 255, 255, 0.85);
  paint-order: stroke;
  stroke-linejoin: round;
  font-family: var(--font-mono);
  font-weight: 600;
  pointer-events: none;
}

/* -----------------------------------------------------------------------
   Shared frosted-overlay treatment
   ----------------------------------------------------------------------- */

.overlay-panel {
  background: var(--color-overlay);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
  backdrop-filter: blur(12px) saturate(1.4);
  border: 1px solid var(--color-overlay-hairline);
  border-radius: 10px;
  box-shadow: var(--shadow-overlay);
}

/* -----------------------------------------------------------------------
   Title -- also the "Number value" panel's own collapse affordance now (see
   index.html): a <details>/<summary> whose disclosed content is the number
   slider/input and the (separately, further collapsible) Info blurb, so
   collapsed shows the title alone and expanded shows everything below it.
   ----------------------------------------------------------------------- */

.app-header {
  position: fixed;
  top: 1rem;
  left: 1rem;
  z-index: 15;
  /* Wide enough for the title to stay on one line next to its own collapse
     arrow (220px, the old number-panel's width, wrapped it to two). */
  width: 300px;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.5rem 1rem 1rem;
}

.app-header:not([open]) {
  padding-bottom: 0.5rem;
}

.app-header-summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  list-style: none;
}

.app-header-summary::-webkit-details-marker {
  display: none;
}

.app-header-summary::after {
  content: "\25BE"; /* down-pointing triangle: expanded */
  font-size: 0.9em;
  margin-left: 0.5rem;
  color: var(--color-ink-muted);
  flex-shrink: 0;
}

.app-header:not([open]) .app-header-summary::after {
  content: "\25B8"; /* right-pointing triangle: collapsed */
}

.app-header h1 {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}

.number-value-label {
  margin: 0 0 0.15rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-muted);
}

/* -----------------------------------------------------------------------
   Number + prime-factors readout -- horizontally centred near the top of the
   screen, in its own centre lane between the title (far left) and the
   controls toggle (far right), both at the same top offset. The number
   itself leads (larger), the factorisation ("prime" / "2²·3") sits just
   below it -- no panel background, so it reads as part of the diagram
   itself rather than a floating card.
   ----------------------------------------------------------------------- */

.number-display {
  position: fixed;
  top: 1rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 90vw;
  font-family: var(--font-mono);
  text-align: center;
}

/* No background panel any more -- both lines sit directly over the animated
   diagram, so they get a thin outline halo to stay legible against any
   circle colour or background: white fill, thin black halo (inverted from
   an earlier dark-fill/white-halo version, which read as too heavy). A
   multi-directional text-shadow, not -webkit-text-stroke: that property
   draws its stroke centred ON the glyph outline (half eating into the
   letterform), which at this font size ate most of the fill, leaving text
   that looked hollow/outline-only. The SVG circle labels avoid the same
   problem with paint-order: stroke, which paints the halo entirely BEHIND
   the fill -- text-shadow (always rendered behind an element's own text) is
   the standard-CSS way to get that same behind-fill halo for ordinary HTML
   text. Offsets are sub-pixel (0.6px, not a full 1px) to keep the halo
   reading as thin rather than a thick ring. */
.number-display-number,
.number-display-value {
  color: #ffffff;
  text-shadow:
    -0.6px -0.6px 0 rgba(0, 0, 0, 0.85),
     0.6px -0.6px 0 rgba(0, 0, 0, 0.85),
    -0.6px  0.6px 0 rgba(0, 0, 0, 0.85),
     0.6px  0.6px 0 rgba(0, 0, 0, 0.85),
     0px   -0.6px 0 rgba(0, 0, 0, 0.85),
     0px    0.6px 0 rgba(0, 0, 0, 0.85),
    -0.6px  0px   0 rgba(0, 0, 0, 0.85),
     0.6px  0px   0 rgba(0, 0, 0, 0.85);
}

.number-display-number {
  font-size: 1.35rem;
  font-weight: 700;
}

.number-display-value {
  font-size: 1.1rem;
  font-weight: 600;
}

/* Real superscript for factor powers -- normal digits raised and shrunk, so a
   two-digit power (2^10) has even spacing, unlike mixed Unicode superscript
   glyphs. line-height 0 keeps the raised digits from growing the line box. */
.number-display-value sup {
  font-size: 0.5em;
  vertical-align: 0.7em;
  line-height: 0;
  margin-left: 0.04em;
}

/* -----------------------------------------------------------------------
   Controls show/hide toggle (CSS-only -- see index.html)
   ----------------------------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.controls-toggle {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 20;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 0.8rem;
  font-size: 0.85rem;
  cursor: pointer;
  user-select: none;
}

/* This "open" button is only ever visible while the panel is collapsed (see
   the rule hiding it below), so it only ever needs the one, collapsed-state
   arrow -- the panel's own .controls-close-button is what's shown while
   open instead. */
.controls-toggle::after {
  content: "\25B8"; /* right-pointing triangle: collapsed, click to open */
  font-size: 0.7em;
  color: var(--color-ink-muted);
}

#controls-toggle:focus-visible + .controls-toggle {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* While the panel is open, the corner "Controls" button steps aside for the
   panel's own close button instead (see .controls-close-button) -- only one
   of the two is ever visible at a time. */
body:has(#controls-toggle:checked) .controls-toggle {
  display: none;
}

/* The panel's heading row: the "Controls" title on the left, the collapse
   arrow on the right, sharing one line at the very top of the panel. The
   arrow is a second label tied to the same #controls-toggle checkbox (see
   index.html), so clicking it closes the panel with no JS involved, just
   like the corner button opens it. */
.controls-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.1rem 0.3rem 0.3rem;
}

.controls-panel-title {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-muted);
}

/* Same down-pointing triangle glyph as every other collapse arrow in the app
   (.app-header-summary::after, .control-group summary::after, etc.) --
   this one used to be a plain up-arrow ("&uarr;"), a visibly different glyph
   style from the rest. */
.controls-close-button {
  font-size: 0.7rem;
  color: var(--color-ink-muted);
  cursor: pointer;
  user-select: none;
}

.controls-close-button:hover {
  color: var(--color-ink);
}

/* -----------------------------------------------------------------------
   Number panel -- the number slider/input, merged into the title panel
   above (.app-header) as that <details>'s disclosed content; position,
   sizing and collapse behaviour all live on .app-header now, so this
   section is just the number-specific styling (the custom slider, and the
   Info blurb).
   ----------------------------------------------------------------------- */

/* Blurb below the number-value cluster, set off by a gap and hairline.
   A native <details>/<summary> disclosure -- open by default -- reusing the
   same collapsed/expanded arrow convention as .app-header-summary so the
   whole app has one consistent affordance for it. Nested inside .app-header
   itself (see index.html), so collapsing the title panel hides this too;
   its own top margin only needs to make up the gap on top of that flex
   column's existing 0.5rem row gap, not supply the full gap alone as it
   would as a plain sibling. */
.number-panel-info {
  margin: 0.45rem 0 0;
  padding-top: 0.8rem;
  border-top: 1px solid var(--color-overlay-hairline);
}

.number-panel-info summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  list-style: none;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-muted);
}

.number-panel-info summary::-webkit-details-marker {
  display: none;
}

.number-panel-info summary::after {
  content: "\25BE"; /* down-pointing triangle: section is open */
  font-size: 0.9em;
  margin-left: 0.5rem;
}

.number-panel-info:not([open]) summary::after {
  content: "\25B8"; /* right-pointing triangle: section is collapsed */
}

.number-panel-info-body {
  margin: 0.55rem 0 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--color-ink-muted);
}

.number-panel-info-credit {
  display: block;
  margin-top: 0.6rem;
}

.number-panel-info a {
  color: var(--color-accent);
  text-decoration: none;
}

.number-panel-info a:hover {
  text-decoration: underline;
}

/* Custom track/thumb so only the thumb (the "button") carries the accent
   colour -- accent-color alone would also fill the track up to the thumb,
   which is the "blue track colouring" being removed here. Needs the full
   cross-browser pseudo-element treatment since browsers only let you
   restyle the thumb/track once the native appearance is turned off. Sized
   to match the app's other custom sliders (.dual-range-slider), not
   enlarged. */

.number-panel input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  accent-color: unset;
  background: transparent;
  height: 1.5rem;
}

.number-panel input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--color-track-background);
}

.number-panel input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  margin-top: -6px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: none;
  background: var(--color-accent);
  cursor: pointer;
}

.number-panel input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--color-track-background);
}

.number-panel input[type="range"]::-moz-range-progress {
  background: transparent;
}

.number-panel input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: none;
  background: var(--color-accent);
  cursor: pointer;
}

/* -----------------------------------------------------------------------
   Controls panel
   ----------------------------------------------------------------------- */

.controls-panel {
  position: fixed;
  /* Aligned with the hidden .controls-toggle button (also top: 1rem), so the
     panel opens exactly where that button sat. */
  top: 1rem;
  right: 1rem;
  width: 288px;
  /* A cap, not a fixed height (that was "bottom: 1rem" before) -- the panel
     now shrinks to fit its content, so collapsing sections shrinks the
     panel instead of leaving empty space below the last one. Still caps at
     the same effective bottom margin, and overflow-y still scrolls the rare
     case content taller than that. */
  max-height: calc(100vh - 2rem);
  z-index: 15;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.35rem 1rem 1rem;
}

body:has(#controls-toggle:not(:checked)) .controls-panel {
  display: none;
}

.control-group {
  margin: 0;
  min-width: 0;
  border: none;
  border-top: 1px solid var(--color-overlay-hairline);
  border-radius: 0;
  /* Top padding is the breathing room between the separator border above and
     this section's own heading -- kept generous so a new section reads as a
     clean break rather than crowding the line it sits under. */
  padding: 1.35rem 0 0.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.control-group:first-of-type {
  border-top: none;
}

#hue-color-sliders-container,
#auto-rotate-scale-container,
#label-size-container,
#zoom-min-size-container,
#zoom-scale-container,
#vertical-offset-container {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* -----------------------------------------------------------------------
   Dual-range (min/max) slider -- see ui/dual_range_slider.js. Two native
   range inputs stacked exactly on top of each other and a track/fill pair
   underneath; only each input's own thumb is ever clickable (pointer-events
   re-enabled solely on ::-webkit-slider-thumb / ::-moz-range-thumb), so the
   two full-width, otherwise-transparent inputs never intercept a click
   meant for the other one's handle.
   ----------------------------------------------------------------------- */

.dual-range-slider {
  position: relative;
  height: 16px;
  /* Extra top margin (beyond .control-group's own row gap) leaves room for
     the "start:"/"end:" labels floating just above the track. */
  margin: 1rem 0 0.3rem;
}

.dual-range-track {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 4px;
  transform: translateY(-50%);
  border-radius: 2px;
  background: var(--color-track-background);
  pointer-events: none;
}

/* The accent-coloured bar between the two handles is intentionally hidden --
   only the plain grey track and the handles themselves are shown. The
   element and its JS (ui/dual_range_slider.js's updateFill) are left in
   place; this is a deliberate one-line visual change, not a structural one. */
.dual-range-fill {
  display: none;
}

.dual-range-slider input[type="range"] {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  appearance: none;
  -webkit-appearance: none;
  accent-color: unset;
  background: transparent;
  pointer-events: none;
}

.dual-range-slider input[type="range"]::-webkit-slider-runnable-track {
  -webkit-appearance: none;
  background: transparent;
  height: 100%;
}

.dual-range-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  pointer-events: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: none;
  background: var(--color-accent);
  cursor: pointer;
}

.dual-range-slider input[type="range"]::-moz-range-track {
  background: transparent;
  border: none;
  height: 100%;
}

.dual-range-slider input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: none;
  background: var(--color-accent);
  cursor: pointer;
}

/* The second (upper) input sits on top when both handles land on the same
   value, so it's always reachable rather than getting hidden under the
   first. */
.dual-range-slider input[type="range"]:last-of-type {
  z-index: 2;
}

/* When only the leaf layer is visible the "start" (outermost-layer) handle of
   a layer range does nothing -- main.js adds this class to hide it. */
.dual-range-slider.hide-start-thumb input[type="range"]:first-of-type {
  pointer-events: none;
}

.dual-range-slider.hide-start-thumb input[type="range"]:first-of-type::-webkit-slider-thumb {
  opacity: 0;
}

.dual-range-slider.hide-start-thumb input[type="range"]:first-of-type::-moz-range-thumb {
  opacity: 0;
}

.dual-range-slider.hide-start-thumb .slider-value-label:first-of-type {
  display: none;
}

/* -----------------------------------------------------------------------
   Single-thumb slider wrapper -- just a positioning context for its own
   floating value label (see ui/range_label.js's attachSingleRangeLabel).
   The slider itself keeps its normal native rendering; only the label is
   new here.
   ----------------------------------------------------------------------- */

.single-range-slider {
  position: relative;
  /* Top: clearance for the floating value label, which sits just above the
     track (see .slider-value-label) -- without this it rode up into the name
     label on the row above. Bottom: a deliberate gap before the next control
     so a slider row does not crowd whatever follows it. */
  margin: 1.1rem 0 0.9rem;
}

/* -----------------------------------------------------------------------
   Floating slider value label -- shared by both .single-range-slider and
   .dual-range-slider. Positioned via a JS-computed `left` in px (see
   ui/range_label.js), always just above the track.
   ----------------------------------------------------------------------- */

/* Redundant on the "Number value" slider -- the number input right below it
   already shows the live value -- so that one floating label is suppressed,
   and its wrapper's top clearance (reserved elsewhere for the label) shrinks
   to match. */
#number-slider-value-label {
  display: none;
}

.number-panel .single-range-slider:has(#number-slider) {
  margin-top: 0.3rem;
}

.slider-value-label {
  position: absolute;
  bottom: calc(100% + 3px);
  transform: translateX(-50%);
  font-size: 0.68rem;
  color: var(--color-ink-muted);
  white-space: nowrap;
  pointer-events: none;
}

.control-group summary {
  padding: 0;
  /* A little extra room under the heading itself, on top of the
     .control-group flex gap already between every row -- a heading reads as
     cramped at just the ordinary row-to-row gap, especially when the row
     right under it is a compact checkbox rather than a label+input pair. */
  margin-bottom: 0.15rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-muted);
}

/* Every .control-group is a <details>/<summary> disclosure (the "Number
   value" section included) -- natively collapsible/keyboard-operable with no
   JS at all. This just replaces the browser's own default disclosure marker
   with one matching the app's existing collapsed/expanded arrow convention
   (see .controls-toggle). */
.control-group summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  list-style: none;
}

.control-group summary::-webkit-details-marker {
  display: none;
}

.control-group summary::after {
  content: "\25BE"; /* down-pointing triangle: section is open */
  font-size: 0.9em;
  margin-left: 0.5rem;
}

details.control-group:not([open]) summary::after {
  content: "\25B8"; /* right-pointing triangle: section is collapsed */
}

/* A collapsed section (open attribute absent) shows only its own summary
   row -- native <details> behaviour -- so its bottom padding can shrink to
   match, rather than leaving the same gap a fully expanded section reserves
   below its last control. */
.control-group:not([open]) {
  padding-bottom: 1.35rem;
}

.control-group label:not(.toggle-row) {
  font-size: 0.8rem;
  color: var(--color-ink-muted);
  margin-top: 0.35rem;
}

.control-group label:first-of-type {
  margin-top: 0;
}

/* A bare <label> that sits directly in a section (rather than in a
   .control-row or a slider wrapper) labels the control on the row below it
   -- e.g. "Move duration (s)" above its slider. The disclosed content of a
   <details> is laid out inside an anonymous wrapper, not as flex items of
   .control-group, so without this such a label would fall back to inline
   flow and share a line with whatever follows. */
.control-group > label:not(.toggle-row) {
  display: block;
}

/* A label and its single control (a <select> or the colour swatch) sharing
   one line: label left, control right. Used in the "Numbering" and "Color"
   sections. The vertical margin and min-height keep consecutive rows (and a
   row's control against the row above/below) from crowding -- a bare select
   or colour input is short enough that at the plain flex gap they read as
   cramped, and a two-line wrapped label could visually collide. */
.control-row {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.6rem;
  margin: 0.4rem 0;
  min-height: 2rem;
}

.control-group .control-row label {
  margin-top: 0;
}

/* The select takes the rest of the row so longer option text
   ("Anti-clockwise") is not clipped inside the fixed-width panel. */
.control-row select,
.control-row input[type="number"] {
  flex: 1;
  min-width: 0;
}

/* Export: PNG / SVG side by side, each taking half the row. */
.export-button-row {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.3rem;
}

.export-button-row button {
  flex: 1;
}

.toggle-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
}

/* [hidden] alone won't win against .toggle-row's own `display: flex` --
   both are author rules of equal specificity, and hidden's `display: none`
   only lives in the browser's (lower-priority) UA stylesheet. This is what
   actually hides "Auto rotate" -- kept in the markup rather than removed --
   until it's reintroduced. */
.toggle-row[hidden] {
  display: none;
}

.number-field-row {
  display: flex;
  gap: 0.4rem;
}

.number-field-row input[type="number"] {
  flex: 1;
  min-width: 0;
}

/* -----------------------------------------------------------------------
   Form controls
   ----------------------------------------------------------------------- */

input[type="number"],
input[type="text"] {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  padding: 0.3rem 0.45rem;
  border: 1px solid var(--color-hairline);
  border-radius: 4px;
  background: var(--color-surface);
  color: var(--color-ink);
}

select {
  font-family: var(--font-body);
  font-size: 0.85rem;
  padding: 0.4rem 0.5rem;
  min-height: 2rem;
  border: 1px solid var(--color-hairline);
  border-radius: 4px;
  background: var(--color-surface);
  color: var(--color-ink);
}

/* The native colour input renders as a tiny fixed swatch by default; give it
   an explicit box matching the selects' height so it does not sit dwarfed and
   crowded next to its label. */
input[type="color"] {
  width: 3rem;
  height: 2rem;
  padding: 2px;
  border: 1px solid var(--color-hairline);
  border-radius: 4px;
  background: var(--color-surface);
  cursor: pointer;
}

button {
  font-family: var(--font-body);
  font-size: 0.9rem;
  border: 1px solid var(--color-hairline);
  border-radius: 4px;
  background: var(--color-surface);
  color: var(--color-ink);
  padding: 0.3rem 0.6rem;
  cursor: pointer;
}

button:hover {
  border-color: var(--color-accent);
}

input[type="range"] {
  accent-color: var(--color-accent);
  width: 100%;
}

/* The unfilled remainder of a native slider's track (thumb to max) is a
   fairly faint browser-default colour, easy to lose against the translucent
   frosted panels -- giving the track pseudo-elements an explicit,
   more-visible background fixes that. The number-panel, dual-range-slider and
   the fully-custom .single-range-slider sliders draw their own tracks and are
   unaffected by this. */
input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--color-track-background);
}

input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--color-track-background);
}

/* Every plain slider inside the controls panel (the .single-range-slider ones
   -- Label size, the Zoom sliders, Vertical offset, the Animate durations) is
   drawn fully custom, the same way the .number-panel slider already is: a thin
   grey track with just the round accent-coloured thumb on top, no
   browser-native fill. Leaving the native thumb while forcing an explicit 4px
   track height made Chrome align the thumb to the track's TOP edge instead of
   its centre, so it sat a half-height low; a custom thumb with an explicit
   centring margin-top removes that.

   Deliberately scoped to `.controls-panel .single-range-slider` rather than
   bare `input[type="range"]`: the earlier bare version leaked its
   `::-webkit-slider-thumb { margin-top: -6px }` onto the .dual-range-slider
   thumbs (which set no margin-top of their own), shoving those up by 6px. The
   .number-panel slider lives in a different panel and keeps its own rules. */
.controls-panel .single-range-slider input[type="range"] {
  appearance: none;
  -webkit-appearance: none;
  accent-color: unset;
  background: transparent;
  height: 1.5rem;
}

.controls-panel .single-range-slider input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: var(--color-track-background);
}

.controls-panel .single-range-slider input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  /* Half the 4px track minus half the 16px thumb: (2 - 8) = -6px, which
     centres the thumb on the track. */
  margin-top: -6px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: none;
  background: var(--color-accent);
  cursor: pointer;
}

.controls-panel .single-range-slider input[type="range"]::-moz-range-track {
  height: 4px;
  border-radius: 2px;
  background: var(--color-track-background);
}

.controls-panel .single-range-slider input[type="range"]::-moz-range-progress {
  background: transparent;
}

.controls-panel .single-range-slider input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: none;
  background: var(--color-accent);
  cursor: pointer;
}

input[type="checkbox"] {
  accent-color: var(--color-accent);
}

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* -----------------------------------------------------------------------
   Narrow screens: the controls span the width instead of a fixed column,
   and cap their height so the diagram behind stays visible.
   ----------------------------------------------------------------------- */

@media (max-width: 700px) {
  /* .app-header is also the number panel now (see index.html/the Title
     section above) -- one compact, left-anchored box rather than spanning
     full width, keeping it out of the controls panel's way below without
     needing to measure its height. */
  .app-header {
    top: 0.75rem;
    left: 0.75rem;
    width: 160px;
    padding: 0.35rem 0.75rem 0.75rem;
  }

  .controls-panel {
    left: 0.75rem;
    right: 0.75rem;
    width: auto;
    top: 8.5rem;
    bottom: auto;
    max-height: 54vh;
  }

  .number-display {
    top: 0.75rem;
  }

  .controls-toggle {
    top: 0.75rem;
    right: 0.75rem;
  }
}

/* -----------------------------------------------------------------------
   Confirm dialog (see ui/confirm_dialog.js) -- class names must match
   exactly what that file assigns.
   ----------------------------------------------------------------------- */

.confirm-dialog-overlay {
  position: fixed;
  inset: 0;
  background: rgba(27, 31, 35, 0.45);
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.confirm-dialog-box {
  background: var(--color-surface);
  border-radius: 8px;
  padding: 1.5rem;
  max-width: 320px;
  box-shadow: 0 12px 32px rgba(27, 31, 35, 0.2);
}

.confirm-dialog-message {
  margin: 0 0 1.25rem;
  font-size: 0.95rem;
  line-height: 1.4;
}

.confirm-dialog-button-row {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
}

.confirm-dialog-cancel-button {
  background: var(--color-surface);
  color: var(--color-ink);
}

.confirm-dialog-confirm-button {
  background: var(--color-accent);
  color: var(--color-accent-ink);
  border-color: var(--color-accent);
}

/* -----------------------------------------------------------------------
   Footer credit link -- small, unobtrusive, bottom-centre.
   ----------------------------------------------------------------------- */

.footer-credit-link {
  position: fixed;
  bottom: 0.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  font-size: 0.65rem;
  color: var(--color-ink-muted);
  text-decoration: none;
}

.footer-credit-link:hover {
  color: var(--color-accent);
  text-decoration: underline;
}
