/* pattern-maker/styles.css */

:root {
  --black: #111;
  --accent: #E70565;
  --accent-dark: #c40454;
  --grey-mid: #767676;
  --grey-light: #e8e8e8;
  --grey-bg: #f5f5f5;
  --white: #fff;
  --border: #d4d4d4;
  --track: #bdbdbd;
  --font: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
}

html {
  font-size: 18px;
  /* Reserve the scrollbar gutter so the centred .app doesn't jump sideways
     when the page grows tall enough to show a scrollbar (e.g. a settings
     panel expanding). Keeps the full-width nav bar reaching the left edge,
     unlike the body-shift trick used for centred columns on edwardhorsford.com. */
  scrollbar-gutter: stable;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The hidden attribute always wins: controls are shown/hidden by setting it,
   and any author display rule (flex rows, grids) would otherwise defeat the
   browser's low-specificity default and leave inert controls visible. */
[hidden] {
  display: none !important;
}

body {
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--black);
  background: var(--white);
  margin: 0;
  padding: 0;
}

.app {
  max-width: 1280px;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

.app__head h1 {
  font-size: 1.75rem;
  margin: 0 0 0.25rem;
}

.description {
  color: var(--grey-mid);
  margin: 0 0 1.5rem;
  max-width: 60ch;
}

/* --- Buttons --- */

.button {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.55em 1em;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.25;
  cursor: pointer;
  border: 2px solid var(--black);
  background: var(--white);
  color: var(--black);
}

.button:hover { background: var(--grey-bg); }
.button:active { background: var(--grey-light); }
.button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Low-emphasis actions (clear all, see all, more presets): a link, not a box */
.button-link {
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  border: none;
  background: none;
  padding: 0;
  color: var(--black);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.button-link:hover { color: var(--accent); }
.button-link:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.button--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--white);
}
.button--primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
.button--primary:active { background: #a3033f; border-color: #a3033f; }

/* --- Side panel with tabs (Settings / Presets / Favourites) --- */

/* The whole panel (tab strip included) sits on the grey surface, so the
   tabs read as part of the panel rather than floating on the page */
.panel {
  background: var(--grey-bg);
  border-radius: 3px;
}

.tabs {
  display: flex;
  border-bottom: 1px solid var(--grey-light);
}
.tab {
  flex: 1;
  font: inherit;
  font-weight: 600;
  font-size: 0.95rem;
  padding: 0.6rem 0.5rem;
  border: none;
  /* The underline sits on top of the strip's hairline */
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  background: transparent;
  color: var(--grey-mid);
  cursor: pointer;
}
.tab:hover { color: var(--black); }
.tab:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
.tab[aria-selected="true"] {
  color: var(--black);
  border-bottom-color: var(--accent);
}

.tabpanel {
  padding: 0.85rem;
}
.tabpanel[hidden] { display: none; }

.panel__empty {
  color: var(--grey-mid);
  font-size: 0.9rem;
  margin: 0.25rem 0.25rem 0.75rem;
}

/* Standing small print (the favourites-join-the-gallery note) */
.panel__note {
  color: var(--grey-mid);
  font-size: 0.8rem;
  margin: 0.75rem 0.25rem 0.25rem;
}

/* "From the gallery" — a single scrollable row of other people's patterns
   in the ★ panel, sized to match the favourites cards above it */
.gallery-row { margin-top: 1rem; }
.gallery-row__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
}
.gallery-row__title { margin: 0; font-size: 1rem; }
.gallery-row__strip {
  display: flex;
  gap: 0.75rem;
  overflow-x: auto;
  padding: 0.25rem 0;
  margin-top: 0.5rem;
}
.gallery-row__strip .preset-item { flex: 0 0 96px; }

/* Gallery cards carry a where-and-when line under the pattern name */
.preset__meta {
  color: var(--grey-mid);
  font-size: 0.7rem;
  line-height: 1.2;
  text-align: center;
}

/* A button-link sitting inside running text takes the text's size */
.button-link--inline { font-size: inherit; }

/* --- Preset / favourite thumbnail grids --- */

.presets__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 0.75rem;
}

.preset {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0.5rem;
  background: var(--white);
  border: 1px solid var(--grey-light);
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(17, 17, 17, 0.05);
  cursor: pointer;
  font: inherit;
  font-size: 0.8rem;
  color: var(--black);
  text-align: center;
}
.preset:hover {
  border-color: var(--grey-mid);
  box-shadow: 0 2px 6px rgba(17, 17, 17, 0.1);
}
.preset:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.preset svg { width: 100%; height: auto; display: block; background: var(--grey-bg); }

.presets__more { margin-top: 0.75rem; }

/* --- "More presets…" modal: the full catalogue in a wider grid --- */
.preset-modal {
  border: none;
  border-radius: 3px;
  box-shadow: 0 10px 40px rgba(17, 17, 17, 0.25);
  padding: 0;
  width: min(680px, calc(100vw - 2rem));
}
.preset-modal::backdrop { background: rgba(0, 0, 0, 0.4); }
.preset-modal__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 0.5rem 0 0;
  border-bottom: 1px solid var(--grey-light);
}

/* The modal's tab strip rides the head's own hairline */
.tabs--modal {
  flex: 1;
  border-bottom: none;
}
.tabs--modal .tab { padding: 0.75rem 0.5rem; }
.preset-modal__close {
  border: none;
  background: none;
  font-size: 1.6rem;
  line-height: 1;
  color: var(--grey-mid);
  cursor: pointer;
  padding: 0 0.25rem;
}
.preset-modal__close:hover { color: var(--black); }
.preset-modal__close:focus-visible { outline: 3px solid var(--accent); }
.preset-modal__body {
  padding: 1rem;
  max-height: min(70vh, 40rem);
  overflow-y: auto;
}
.presets__grid--modal { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }

/* Favourite / history tiles: a preset button plus corner controls. The
   wrapper is stretched by the grid row; the button fills it so tiles in a
   row stay equal height even when a label wraps. */
.preset-item { position: relative; }
.preset-item .preset { width: 100%; height: 100%; }

/* Corner controls sit borderless INSIDE the card (a boxed button hanging
   over the edge visually collided with the next card's). Delete top-right;
   history tiles also get a star top-left — a saved star stays visible so
   favourited entries are recognisable without hovering. */
.preset-item__delete,
.preset-item__fav {
  position: absolute;
  top: 0.3rem;
  width: 1.5rem;
  height: 1.5rem;
  border: none;
  border-radius: 3px;
  background: var(--white);
  color: var(--grey-mid);
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 100ms ease;
}
.preset-item__delete { right: 0.3rem; font-size: 1rem; }
.preset-item__fav { left: 0.3rem; font-size: 0.85rem; }

/* Marks a saved animation (favourites and history cards) */
.preset-item__anim {
  position: absolute;
  left: 0.35rem;
  bottom: 1.8rem;
  font-size: 0.65rem;
  color: var(--grey-mid);
  pointer-events: none;
}
.preset-item:hover .preset-item__delete,
.preset-item:hover .preset-item__fav,
.preset-item__delete:focus-visible,
.preset-item__fav:focus-visible,
.preset-item__fav.is-saved { opacity: 1; }
.preset-item__delete:hover,
.preset-item__fav:hover { color: var(--accent); }
.preset-item__delete:focus-visible,
.preset-item__fav:focus-visible { outline: 3px solid var(--accent); outline-offset: -1px; }
.preset-item__fav.is-saved { color: var(--accent); }

/* --- History: automatic captures below the favourites, grouped by age --- */
.history { margin-top: 1.75rem; border-top: 1px solid var(--grey-light); padding-top: 1rem; }
.history__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
}
.history__title { margin: 0; font-size: 1rem; }
.history__group {
  margin: 1rem 0 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--grey-mid);
}
.history__more { margin-top: 0.75rem; }

/* While any modal is up the page behind must not scroll */
body:has(dialog[open]) { overflow: hidden; }

.preset__noimg {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  width: 100%;
  color: var(--grey-mid);
  background: var(--grey-bg);
  font-size: 1.5rem;
}

/* --- Accordion arrows --- */
/* One shared, properly equilateral triangle (height 12px → half-height 6px,
   width 12 × √3/2 ≈ 10.4px), rotating a quarter-turn when open. */
.group > summary::before,
.display > summary::before,
.subgroup > summary::before {
  content: '';
  display: inline-block;
  width: 0;
  height: 0;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  border-left: 10.4px solid var(--grey-mid);
  margin-right: 0.6rem;
  transition: transform 120ms ease;
}
.group[open] > summary::before,
.display[open] > summary::before,
.subgroup[open] > summary::before {
  transform: rotate(90deg);
}
.subgroup > summary::before {
  border-top-width: 5px;
  border-bottom-width: 5px;
  border-left-width: 8.7px;
}

/* --- Two-column layout --- */

.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 440px);
  gap: 2rem;
  align-items: start;
}

.preview {
  position: sticky;
  top: 1rem;
}

/* Toolbar row under the stage: zoom controls left, favourite star right —
   nothing overlaps the pattern (screenshots stay clean). */
.preview__bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 0.5rem;
}

.preview__zoom {
  display: flex;
  align-items: center;
  gap: 0.15rem;
}
.preview__zoom button {
  font: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  width: 1.7rem;
  height: 1.7rem;
  border: none;
  border-radius: 3px;
  background: transparent;
  color: var(--grey-mid);
  cursor: pointer;
}
.preview__zoom button:hover { color: var(--accent); background: var(--grey-bg); }
.preview__zoom button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.preview__zoom #zoom-reset { width: auto; padding: 0 0.5rem; font-size: 0.8rem; }
.preview__zoom #zoom-level {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--grey-mid);
  min-width: 3em;
  text-align: center;
}

.preview__stage--zoomed { cursor: grab; }
.preview__stage--zoomed:active { cursor: grabbing; }

.preview__stage {
  aspect-ratio: 1 / 1;
  background: var(--grey-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4%;
}
/* The SVG draws its own panel shape (circle/square/hexagon), so no CSS crop —
   the stage's grey shows around it. */
.preview__svg {
  width: 100%;
  height: 100%;
}

.heavy-warning {
  text-align: center;
  padding: 1.5rem;
  max-width: 22rem;
}
.heavy-warning p { margin: 0 0 0.75rem; }
.heavy-warning__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
  margin-top: 1rem;
}

.preview__info {
  color: var(--grey-mid);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
  margin: 0.75rem 0 0.25rem;
  min-height: 1.3em;
}

.preview__erase-note {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  color: var(--grey-mid);
  font-size: 0.8rem;
  margin: 0 0 0.5rem;
}

.preview__stage { cursor: crosshair; }
.preview__stage .heavy-warning { cursor: auto; }

/* Favourite star (in the toolbar row under the stage) */
.preview__tools {
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

/* Mirrored play/pause for the animation, so running it never means
   scrolling away from the pattern */
.preview__play {
  width: 2.4rem;
  height: 2.4rem;
  border: none;
  border-radius: 3px;
  background: transparent;
  color: var(--grey-mid);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
}
.preview__play:hover { color: var(--accent); background: var(--grey-bg); }
.preview__play:active { background: var(--grey-light); }
.preview__play:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

.preview__fav {
  width: 2.4rem;
  height: 2.4rem;
  border: none;
  border-radius: 3px;
  background: transparent;
  color: var(--grey-mid);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 0 0.15rem;
}
.preview__fav:hover { color: var(--accent); background: var(--grey-bg); }
.preview__fav:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.preview__fav.is-saved { color: var(--accent); }

/* Erasable dots dim on hover so it's clear what a click will remove */
.preview__stage .dot { transition: opacity 80ms ease; }
.preview__stage .dot:hover { opacity: 0.35; }

/* Hover ring: a clone of the hovered dot appended last (so it draws on top),
   restyled as a pink outline — findable even in dense overlapping patterns.
   mix-blend-mode needs !important to beat the clone's inline style. */
.preview__stage .dot-hover-ring {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2px;
  fill-opacity: 1;
  stroke-opacity: 1;
  opacity: 1;
  mix-blend-mode: normal !important;
  pointer-events: none;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}

/* Quick actions pinned right under the image — same spot on every screen.
   Wraps to a second row on narrow screens. */
.quickbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.5rem;
}
.quickbar .button--small { margin-top: 0; }
/* Label takes its own line so the buttons all fit on one row below it */
.quickbar__label {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--grey-mid);
}
/* The mood dial: how adventurous the randomisers are */
.quickbar__mood {
  font-family: inherit;
  font-size: 0.85rem;
  color: var(--black);
  padding: 0.1rem 0.2rem;
  background: var(--white);
  border: 1px solid var(--track);
  border-radius: 3px;
}
.quickbar__mood:hover { border-color: var(--grey-mid); }
.quickbar__mood:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* --- Config panel --- */

/* Settings accordions: white cards on the panel's grey surface */
.group {
  background: var(--white);
  border: 1px solid var(--grey-light);
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(17, 17, 17, 0.05);
  margin-bottom: 0.6rem;
}

/* Section-level guidance shown at the top of a group body */
.group__hint {
  color: var(--grey-mid);
  font-size: 0.82rem;
  line-height: 1.35;
  margin: 0 0 0.85rem;
}

.group > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 1.05rem;
  padding: 0.75rem 1rem;
  list-style: none;
  border-radius: 3px;
}
.group > summary:hover { background: var(--grey-bg); }
.group > summary::-webkit-details-marker { display: none; }
.group > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }

/* A section holding a setting changed from its default gets an accent dot —
   on the summary (mobile accordion) and the rail button (desktop), so
   collapsed sections never hide active settings. On the rail the dot sits
   in the button's right padding, so long labels never wrap. */
details[data-set] > summary::after,
.rail__btn--set::after {
  content: '';
  display: inline-block;
  width: 5px;
  height: 5px;
  margin-left: 0.4rem;
  vertical-align: 2px;
  border-radius: 50%;
  background: var(--accent);
}
.rail__btn { position: relative; }
.rail__btn--set::after {
  position: absolute;
  right: 0.15rem;
  top: 50%;
  transform: translateY(-50%);
  margin-left: 0;
}

.group__body {
  padding: 0.5rem 1rem 1rem;
}

/* --- Controls --- */

.control { margin-bottom: 0.85rem; }
.control:last-child { margin-bottom: 0; }
.control[hidden] { display: none; }

/* Controls that only apply when a parent toggle is on: kept in the layout
   (no reflow when toggling), just visibly inert. */
.control--disabled { opacity: 0.4; }
.control--disabled input { cursor: default; }

.control__label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  font-weight: 600;
  margin-bottom: 0.15rem;
}

/* Editable value: type an exact number for fine adjustment */
.control__valuewrap {
  display: inline-flex;
  align-items: baseline;
  gap: 0.1em;
  font-weight: 400;
}
.control__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 400;
  color: var(--accent);
  font-size: 0.95rem;
  width: 5.5em;
  text-align: right;
  border: none;
  border-bottom: 1px dashed transparent;
  background: transparent;
  padding: 0;
}
.control__value:hover:not(:read-only) { border-bottom-color: var(--accent); cursor: text; }
.control__value:focus-visible:not(:read-only) {
  outline: none;
  border-bottom: 1px solid var(--accent);
}
.control__suffix {
  font-family: var(--font-mono);
  font-size: 0.95rem;
  color: var(--accent);
}

.control__hint {
  color: var(--grey-mid);
  font-size: 0.82rem;
  line-height: 1.35;
  margin: 0.3rem 0 0;
}

/* Quick-set chips for special slider values (e.g. the golden angle) */
.control__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.3rem;
}
.chip {
  font: inherit;
  font-size: 0.75rem;
  padding: 0.1em 0.6em;
  border: 1px solid var(--border);
  background: var(--white);
  color: var(--grey-mid);
  cursor: pointer;
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.chip--active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--white);
}
.chip--active:hover {
  background: var(--accent-dark);
  border-color: var(--accent-dark);
  color: var(--white);
}

/* Range slider */
.control__slider { position: relative; }

.control input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 5px;
  background: var(--track);
  cursor: pointer;
  margin: 0.3rem 0;
}
.control input[type="range"]:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }

.control input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--black);
  border: 3px solid var(--white);
  box-shadow: 0 0 0 1px var(--grey-mid);
  cursor: pointer;
}
.control input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--black);
  border: 3px solid var(--white);
  box-shadow: 0 0 0 1px var(--grey-mid);
  cursor: pointer;
}

/* Tucked control: a small link reveals an optional refinement (e.g. gradient) */
.control--tuck { margin-top: -0.35rem; }
.control__reveal {
  font: inherit;
  font-size: 0.8rem;
  border: none;
  background: none;
  padding: 0;
  color: var(--grey-mid);
  cursor: pointer;
  text-decoration: underline;
  text-decoration-style: dotted;
}
.control__reveal:hover { color: var(--accent); }
.control__reveal:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* An expanded tuck is inset behind a grey rule to show it's conditional */
.control--tuck > div {
  margin-top: 0.5rem;
  border-left: 2px solid var(--grey-light);
  padding-left: 0.75rem;
}

/* Collapsed subsection inside a settings group (opt-in effects) —
   a grey inset on the white card rather than another border */
.subgroup {
  background: var(--grey-bg);
  border-radius: 3px;
  margin: 0.85rem 0;
}
.subgroup > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.9rem;
  padding: 0.5rem 0.75rem;
  list-style: none;
  color: var(--grey-mid);
}
.subgroup > summary::-webkit-details-marker { display: none; }
.subgroup > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
.subgroup__body { padding: 0.25rem 0.75rem 0.75rem; }

/* Tick marks under a slider track for notable values (0, quarter turns) */
.control__ticks {
  position: relative;
  height: 6px;
  margin-top: 1px;
}
.control__ticks span {
  position: absolute;
  top: 0;
  width: 2px;
  height: 5px;
  background: var(--track);
  transform: translateX(-50%);
}

.control select {
  width: 100%;
  padding: 0.55em 0.6em;
  font-family: inherit;
  font-size: 1rem;
  color: var(--black);
  background: var(--white);
  border: 1px solid var(--track);
  border-radius: 3px;
}
.control select:hover { border-color: var(--grey-mid); }
.control select:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Checkbox rows */
.control--check {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
}
.control--check input[type="checkbox"] {
  width: 1.4rem;
  height: 1.4rem;
  accent-color: var(--accent);
  cursor: pointer;
  flex: none;
}
.control--check label { font-weight: 600; cursor: pointer; }
/* Hint sits on its own line under the checkbox + label */
.control--check .control__hint {
  flex-basis: 100%;
  margin: 0 0 0 2rem;
}

/* Colour picker row */
.control--colour {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
}
.control--colour label { font-weight: 600; }
.control--colour input[type="color"] {
  width: 3rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--track);
  border-radius: 3px;
  background: none;
  cursor: pointer;
}
.control--colour input[type="color"]:hover { border-color: var(--grey-mid); }
.control--colour input[type="color"]:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* Inline swap beside a colour swatch — flips it with its partner colour */
.control__swap {
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  padding: 0.35rem 0.4rem;
  border: 1px solid var(--track);
  background: none;
  color: var(--grey-mid);
  cursor: pointer;
}
.control__swap:hover { color: var(--accent); border-color: var(--grey-mid); }
.control__swap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Appearance — collapsed by default, compact controls. Same card treatment
   as the settings groups, here sitting directly on the white page. */
.display {
  background: var(--white);
  border: 1px solid var(--grey-light);
  border-radius: 3px;
  box-shadow: 0 1px 2px rgba(17, 17, 17, 0.05);
  margin-top: 1rem;
}
.display > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 1.05rem;
  padding: 0.75rem 1rem;
  list-style: none;
  border-radius: 3px;
}
.display > summary:hover { background: var(--grey-bg); }
.display > summary::-webkit-details-marker { display: none; }
.display > summary:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }

#display-controls {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 0.75rem;
  padding: 0.25rem 1rem 0.9rem;
}
/* Everything spans the panel except the paint boxes, which sit side by side,
   and the action buttons (randomise/invert), which share a row the same way */
#display-controls > * { grid-column: 1 / -1; }
#display-controls > .display__group,
#display-controls > .control--button { grid-column: auto; }
#output-controls { padding: 0.25rem 1rem 0.9rem; }
#display-controls .control--check,
#output-controls .control--check { margin-bottom: 0.5rem; }
#display-controls .control--colour,
#display-controls .control,
#output-controls .control { margin-bottom: 0.6rem; }

/* Fill / stroke paint boxes: each paint's controls grouped on a grey inset */
.display__group {
  background: var(--grey-bg);
  border-radius: 3px;
  padding: 0.6rem 0.75rem 0.1rem;
  margin-bottom: 0.75rem;
}
.display__group .control--check:first-child { margin-bottom: 0.6rem; }

/* Animate panel: the A/B snapshot slots and playback controls */
#animate-controls { padding: 0.25rem 1rem 0.9rem; }
#animate-controls .control { margin-bottom: 0.6rem; }
/* The snapshot slots share one grid — thumbs row, buttons row, sync row —
   so nothing in one column can push the others out of alignment. Thumbs
   take the odd (1fr) columns; the swap arrows between neighbours take the
   narrow even columns, which is also how sequences are reordered. */
.anim__slots {
  display: grid;
  gap: 0.4rem 0.35rem;
  margin-bottom: 0.4rem;
}
.anim__slots[data-count="2"] { grid-template-columns: 1fr auto 1fr; }
.anim__slots[data-count="3"] { grid-template-columns: 1fr auto 1fr auto 1fr; }
.anim__slots[data-count="4"] { grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr; }
.anim__slots[data-count="5"] { grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr auto 1fr; }
.anim__slots[data-count="6"] { grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr auto 1fr auto 1fr; }
.anim__col-1 { grid-column: 1; }
.anim__col-2 { grid-column: 2; }
.anim__col-3 { grid-column: 3; }
.anim__col-4 { grid-column: 4; }
.anim__col-5 { grid-column: 5; }
.anim__col-6 { grid-column: 6; }
.anim__col-7 { grid-column: 7; }
.anim__col-8 { grid-column: 8; }
.anim__col-9 { grid-column: 9; }
.anim__col-10 { grid-column: 10; }
.anim__col-11 { grid-column: 11; }
.anim__slots .anim__swap { grid-row: 1; align-self: center; }
/* Per-slot controls stay quiet: a link to capture, a bare × to remove */
.anim__slot-buttons {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
}
.anim__set { font-size: 0.8rem; }
.anim__remove {
  border: none;
  background: none;
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  padding: 0.1rem 0.3rem;
  border-radius: 3px;
  color: var(--grey-mid);
  cursor: pointer;
}
.anim__remove:hover { color: var(--accent); background: var(--grey-light); }
.anim__remove:focus-visible { outline: 3px solid var(--accent); }
.anim__add { margin-bottom: 0.4rem; }

/* The "what changes" table: a row per setting that differs between the
   snapshots — a column per snapshot with in-place editors, a Motion column
   for how it animates, and a delete column for removing the change. */
.anim__diff { margin: 0.75rem 0; font-size: 0.85rem; }
.anim__diff-title { font-weight: 600; margin: 0 0 0.2rem; }
.anim__diff-empty { color: var(--grey-mid); margin: 0; }
.anim__diff-scroll { overflow-x: auto; }
.anim__difftable { width: 100%; border-collapse: collapse; }
.anim__difftable th {
  text-align: left;
  font-weight: 600;
  padding: 0.25rem 0.5rem 0.25rem 0;
  border-bottom: 1px solid var(--grey-light);
}
.anim__difftable td {
  padding: 0.3rem 0.5rem 0.3rem 0;
  border-bottom: 1px solid var(--grey-light);
  vertical-align: middle;
}
.anim__diff-setting { font-weight: 600; }
.anim__diff-input {
  font: inherit;
  width: 4.5rem;
  padding: 0.15rem 0.25rem;
  border: 1px solid var(--track);
}
.anim__diff-cell, .anim__diff-config, .anim__diff-add {
  font: inherit;
  font-size: 0.8rem;
  padding: 0.15rem 0.2rem;
  border: 1px solid var(--track);
  background: var(--white);
  max-width: 10rem;
}
.anim__diff-colour {
  width: 2.2rem;
  height: 1.7rem;
  padding: 0;
  border: 1px solid var(--track);
  background: none;
  cursor: pointer;
}
.anim__diff-swatch {
  width: 0.9rem;
  height: 0.9rem;
  border: 1px solid var(--track);
  display: inline-block;
  vertical-align: -1px;
}
.anim__diff-static { color: var(--grey-mid); font-size: 0.8rem; }
.anim__diff-del { text-align: right; }
.anim__diff-x {
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  border: none;
  background: none;
  color: var(--grey-mid);
  cursor: pointer;
  padding: 0 0.3rem;
}
.anim__diff-x:hover { color: var(--accent); }
.anim__diff-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.anim__diff-picker { color: var(--grey-mid); }
.anim__diff-picker button { margin-right: 0.35rem; }
.anim__diff-keep { margin-right: 0.5rem; }
.anim__diff-add { margin-top: 0.4rem; color: var(--grey-mid); }

/* A synced slot: the loaded snapshot follows the editor until unlinked.
   Deliberately loud — silent syncing would surprise. */
.anim__thumb--editing {
  border: 2px solid var(--accent);
  outline: 1px solid var(--accent);
  outline-offset: 1px;
}
.anim__editing {
  border: none;
  background: var(--accent);
  color: var(--white);
  padding: 0.3rem 0.5rem;
  text-align: center;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
}
.anim__editing:hover { background: var(--accent-dark); }
.anim__editing:active { background: var(--black); }
.anim__editing:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* Idle sync bars keep their grid space — visibility, not display — so the
   row never reflows when an offer appears. */
.anim__editing--hidden { visibility: hidden; }

/* The pre-sync offer: quiet outline until accepted */
.anim__editing--offer {
  background: var(--white);
  color: var(--accent);
  border: 1px solid var(--accent);
  font-weight: 400;
}
.anim__editing--offer:hover { background: var(--grey-bg); }
.anim__thumb {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 0;
  border: 1px solid var(--border);
  background: var(--white);
  cursor: pointer;
}
.anim__thumb:disabled { cursor: default; }
.anim__thumb:hover:not(:disabled) { border-color: var(--accent); }
.anim__thumb:active:not(:disabled) { background: var(--grey-bg); }
.anim__thumb:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.anim__thumb svg { display: block; width: 100%; height: 100%; }
.anim__empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--grey-mid);
  font-size: 1.4rem;
}
.anim__swap {
  border: none;
  background: none;
  color: var(--grey-mid);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.25rem;
}
.anim__swap:hover { color: var(--black); }
.anim__swap:active { background: var(--grey-bg); }
.anim__swap:focus-visible { outline: 3px solid var(--accent); }
/* The gap between two thumbs: insert a snapshot here, or swap the pair */
.anim__gap {
  grid-row: 1;
  align-self: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
}
.anim__insert {
  border: none;
  background: none;
  font: inherit;
  font-size: 1.05rem;
  line-height: 1;
  padding: 0.15rem 0.3rem;
  border-radius: 3px;
  color: var(--grey-mid);
  cursor: pointer;
}
.anim__insert:hover { color: var(--accent); background: var(--grey-light); }
.anim__insert:focus-visible { outline: 3px solid var(--accent); }

/* --- Animation filmstrip: the snapshots, under the pattern --- */

/* A quiet grey accordion area: the title collapses it, the switch beside */
.animstrip {
  background: var(--grey-bg);
  border-radius: 3px;
  margin-top: 0.5rem;
  padding: 0.45rem 0.75rem;
}
.animstrip__head {
  display: flex;
  align-items: center;
  gap: 1rem;
}
.animstrip__toggle {
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  border: none;
  background: none;
  padding: 0.15rem 0;
  color: var(--black);
  cursor: pointer;
}
.animstrip__toggle:hover { color: var(--accent); }
.animstrip__toggle:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.animstrip__on {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--grey-mid);
  cursor: pointer;
}
.animstrip__body { margin-top: 0.5rem; }
.animstrip__slots {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  overflow-x: auto;
}
/* The slot grid tightens from panel-filling columns to thumbnail-sized
   ones (the strip is a film row, not a sheet) */
.animstrip .anim__slots { grid-template-columns: repeat(11, max-content); }
.animstrip .anim__thumb { width: 84px; height: 84px; }
/* One sync bar shows at a time, so they can all share the full-width row
   without a long label ballooning its thumb's column */
.animstrip .anim__editing {
  grid-column: 1 / -1;
  grid-row: 3;
  justify-self: start;
}
.animstrip .anim__add {
  flex: 0 0 auto;
  align-self: center;
  white-space: nowrap;
}
.animstrip__hint { margin: 0.4rem 0 0; }
/* The position scrub, under the thumbnails it moves between */
.animstrip .control { margin: 0.65rem 0 0.25rem; }
.anim__changes { font-size: 0.85rem; }

/* --- The what-changes modal: full-width diff table + live preview --- */

.anim-modal {
  width: min(1200px, calc(100vw - 2rem));
  max-height: min(92vh, calc(100% - 2rem));
  border: none;
  border-radius: 4px;
  padding: 0;
  box-shadow: 0 12px 40px rgba(17, 17, 17, 0.3);
}
.anim-modal::backdrop { background: rgba(0, 0, 0, 0.4); }
.anim-modal__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem 1rem 0;
}
.anim-modal__body {
  display: flex;
  align-items: flex-start;
  gap: 1.25rem;
  padding: 0.75rem 1rem 1rem;
}
/* The preview earns its keep: it takes near half the dialog */
.anim-modal__side { flex: 1 1 45%; min-width: 280px; }
.anim-modal__preview {
  background: var(--grey-bg);
  border-radius: 3px;
  padding: 0.6rem;
  margin-bottom: 0.6rem;
}
.anim-modal__preview svg { display: block; width: 100%; height: auto; }
.anim-modal__dense {
  margin: 0;
  padding: 2rem 0.5rem;
  text-align: center;
  color: var(--grey-mid);
  font-size: 0.85rem;
}
.anim-modal__main { flex: 1 1 55%; min-width: 0; }
.anim-modal .anim__diff { margin: 0; }
.anim-modal__main .anim__add { margin-bottom: 0.75rem; }
.anim-modal__play { margin-top: 0.35rem; }
/* Each snapshot column headed by its thumbnail, with move/remove below */
.anim__diff-col { vertical-align: bottom; }
.anim__diff-thumb {
  display: block;
  width: 76px;
  height: 76px;
  background: var(--white);
  border: 1px solid var(--grey-light);
  border-radius: 3px;
  margin-bottom: 0.2rem;
}
.anim__diff-thumb svg { display: block; width: 100%; height: 100%; }
.anim__diff-colctl {
  display: flex;
  align-items: center;
  gap: 0.1rem;
}
.anim__diff-letter { padding: 0 0.15rem; }
.anim__colbtn {
  border: none;
  background: none;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 400;
  line-height: 1;
  padding: 0.15rem 0.25rem;
  border-radius: 3px;
  color: var(--grey-mid);
  cursor: pointer;
}
.anim__colbtn:hover { color: var(--accent); background: var(--grey-bg); }
.anim__colbtn:focus-visible { outline: 3px solid var(--accent); }

/* Mobile: preview above the table, everything full width */
@media (max-width: 860px) {
  .anim-modal__body { flex-direction: column; }
  .anim-modal__side { flex: none; width: 100%; }
}
.anim__actions {
  margin-top: 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
/* The animation downloads, sitting among the Export section's still
   exports: label on its own line, buttons in a row beneath */
.anim__downloads {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin: 0.25rem 1rem 0.75rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--grey-light);
}
.anim__downloads .anim__download-label {
  flex-basis: 100%;
  margin-left: 0;
  font-weight: 600;
  color: var(--black);
}
.anim__downloads .anim__progress { flex-basis: 100%; margin: 0; }
/* Room for the longest label (Resume), so toggling play never shifts the row */
.anim__play { min-width: 5.5rem; }
.anim__download-label {
  margin-left: 0.25rem;
  font-size: 0.85rem;
  color: var(--grey-mid);
}
.anim__clear { margin-left: auto; }
/* Reserves exactly one line (line-height and min-height agree), so text
   appearing during an export can't nudge anything below it. */
.anim__progress {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
  line-height: 1.4;
  color: var(--grey-mid);
  min-height: 1.4em;
}

/* Compact opacity slider on a colour row (outranks the generic full-width
   range rule) */
.control--colour input[type="range"].control__alpha {
  width: 4.5rem;
  flex: none;
  margin-left: 0.3rem;
}
.control__alphavalue {
  font-size: 0.85rem;
  color: var(--grey-mid);
  min-width: 2.2rem;
}

/* A tucked colour control stacks its reveal link above the picker row
   (the colour row itself is a flex row, which would put them side by side) */
.control--colour.control--tuck { display: block; }
.control--colour.control--tuck > div {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* Note shown when both paints are switched off */
.control--note {
  color: var(--grey-mid);
  font-size: 0.9rem;
  margin: 0.25rem 0 0.9rem;
}

/* The paint boxes stack when the panel gets too narrow for two columns */
@media (max-width: 640px) {
  #display-controls { grid-template-columns: 1fr; }
}

/* On desktop the Appearance disclosure lives at the end of the settings
   panel (main.js moves it): match the group cards' rhythm, and stack the
   paint boxes — the panel is too narrow for two columns of colour rows. */
.tabpanel .display { margin: 0 0 0.6rem; }
.tabpanel #display-controls { grid-template-columns: 1fr; }

.button--small {
  padding: 0.35em 0.75em;
  font-size: 0.85rem;
  border-width: 1px;
  border-color: var(--border);
  margin-top: 0.25rem;
}

/* The longer Appearance title only where there's room */
@media (max-width: 860px) {
  .display__summary-extra { display: none; }
}

/* --- Narrow screens: pattern on top, panels in sheets from a bottom bar --- */

/* Desktop never sees the toolbar; the sheet machinery only exists ≤860px */
.sheetbar { display: none; }

@media (max-width: 860px) {
  .layout {
    grid-template-columns: 1fr;
    gap: 1.25rem;
  }

  /* No header at all: the pattern IS the page (the title lives in the tab
     and the site nav stays for the way back). Even padding all round. */
  .app__head { display: none; }
  .app { padding: 1.25rem 1.25rem 4.5rem; }   /* bottom room for the toolbar */

  /* The pattern owns the top of the screen and never scrolls away — the
     panels come up over the lower half instead. Full gutter width, so the
     white border around the stage matches everything else; the SVG centres
     itself inside. */
  .preview__stage {
    width: 100%;
    height: 40vh;
    aspect-ratio: auto;
  }

  /* Panels wait in sheets: hidden until their toolbar button raises them.
     Sheets may cover the zoom/favourite bar and info line — the image keeps
     its full size and the pattern itself stays clear of the sheet. Animate
     lives inside the Export sheet. */
  .panel,
  .display--appearance,
  .export { display: none; }

  /* The toolbar already says which panel is up, so the panel's own tab bar
     would just repeat it. (Presets and ★ open as modals on mobile, not
     sheets, so only the settings sheet uses the panel.) */
  .app--sheet-settings .tabs { display: none; }

  .app--sheet-settings .panel,
  .app--sheet-style .display--appearance,
  .app--sheet-export .export {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 3.4rem;                  /* sits on the toolbar */
    z-index: 9;
    background: var(--white);
    border-top: 1px solid var(--border);
    /* Fixed height, not max-height: collapsing a settings group inside the
       sheet must not bounce the sheet's own height around */
    height: 42vh;
    overflow-y: auto;
    /* Sides match the page padding so sheet content lines up with the image */
    padding: 0.5rem 1.25rem 1rem;
  }

  /* The sheet draws the outer box, so the panels inside it shed their own
     borders and side padding — no boxes in boxes, no doubled-up padding.
     (The sheet's top border is re-stated where border:none would eat it.) */
  .app--sheet-style .display--appearance,
  .app--sheet-export .display--output {
    border: none;
    border-top: 1px solid var(--border);
    margin-top: 0;
  }
  .app--sheet-style .display--appearance > summary,
  .app--sheet-export .display--output > summary { padding: 0.5rem 0; }
  .app--sheet-style #display-controls,
  .app--sheet-export #output-controls {
    padding-left: 0;
    padding-right: 0;
    padding-bottom: 0;
  }
  /* The sheet rules above already repaint the panel white; the tabpanel just
     sheds its padding so sheet content lines up with the page gutter */
  .app--sheet-settings .tabpanel {
    padding: 0;
  }
  /* The sheet's own bottom padding closes the sheet — no stacked margins
     after the last group */
  .app--sheet-settings .group:last-of-type { margin-bottom: 0; }

  /* No hover on touch: favourite delete and star buttons stay visible */
  .preset-item__delete,
  .preset-item__fav { opacity: 1; }

  .sheetbar {
    display: grid;
    grid-template-columns: 3.25rem repeat(4, 1fr);  /* narrow ★ column first */
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    background: var(--white);
    border-top: 1px solid var(--border);
  }
  .sheetbar__btn {
    font: inherit;
    font-weight: 600;
    font-size: 0.9rem;
    padding: 0.85rem 0.25rem;
    border: none;
    border-top: 3px solid transparent;
    background: transparent;
    color: var(--grey-mid);
    cursor: pointer;
  }
  .sheetbar__btn:hover { color: var(--black); }
  .sheetbar__btn:active { background: var(--grey-bg); }
  .sheetbar__btn:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
  .sheetbar__btn--active {
    color: var(--black);
    border-top-color: var(--accent);
    background: var(--grey-bg);
  }

  /* The filmstrip tightens on small screens and scrolls sideways */
  .animstrip .anim__thumb { width: 64px; height: 64px; }
}

/* --- Desktop app shell: the page never scrolls, the pattern always fits --- */

/* The rail and finder only exist on desktop; mobile keeps the accordion
   and sheets. */
.rail { display: none; }
.finder { display: none; }

/* The finder briefly tints the control it landed on */
.control--flash { animation: control-flash 1.5s ease-out; }
@keyframes control-flash {
  0%, 45% { background: rgba(231, 5, 101, 0.12); box-shadow: 0 0 0 4px rgba(231, 5, 101, 0.12); }
  100% { background: transparent; box-shadow: none; }
}

@media (min-width: 861px) {
  /* Viewport-locked shell: the injected nav bar keeps its natural height,
     the app takes the rest, and nothing ever pushes the pattern off screen.
     No page scrollbar, so no gutter to reserve either. */
  html { scrollbar-gutter: auto; }
  body {
    height: 100dvh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  .app {
    flex: 1;
    min-height: 0;
    width: 100%;
    display: flex;
    flex-direction: column;
    padding: 0.75rem 1.25rem 1rem;
  }

  /* Slim, app-like header: title and description share one line. */
  .app__head {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    margin-bottom: 0.6rem;
  }
  .app__head h1 { font-size: 1.1rem; margin: 0; white-space: nowrap; }
  .description {
    font-size: 0.85rem;
    margin: 0;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .layout {
    flex: 1;
    min-height: 0;
    align-items: stretch;
    /* The panel gains the rail's width so the controls keep their room,
       ceding some back to the pattern on narrower desktops */
    grid-template-columns: minmax(0, 1fr) clamp(400px, 40vw, 520px);
  }

  /* Tighter chrome under the stage, so everything fits a short viewport */
  .quickbar__label { flex-basis: auto; margin-right: 0.2rem; }
  .preview__info { margin: 0.4rem 0 0.15rem; }
  .actions { margin-top: 0.5rem; }

  /* The stage takes whatever height the toolbars below it leave over; the
     SVG (viewBox only) letterboxes itself inside. No sticky needed — the
     page can't scroll. */
  .preview {
    position: static;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }
  .preview__stage {
    flex: 1;
    min-height: 13rem;
    aspect-ratio: auto;
  }

  /* The panel fills the column; each tab's content scrolls inside it. */
  .panel {
    display: flex;
    flex-direction: column;
    min-height: 0;
  }
  .tabpanel {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
  }

  /* Settings tab: the finder on top, then rail on the left with the one
     open section beside it. The rail navigates — the section summaries
     (mobile's accordion headers) disappear, and rail.js keeps exactly one
     section open. */
  .tabpanel[data-panel="settings"] {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
  }
  .railwrap {
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: stretch;
  }

  .finder {
    display: block;
    position: relative;
    padding: 0.75rem 0.85rem 0;
  }
  .finder__input {
    width: 100%;
    font: inherit;
    font-size: 0.9rem;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 3px;
    background: var(--white);
  }
  .finder__input:focus-visible { outline: 3px solid var(--accent); outline-offset: -1px; }
  .finder__list {
    position: absolute;
    left: 0.85rem;
    right: 0.85rem;
    z-index: 5;
    margin: 0.2rem 0 0;
    padding: 0.25rem;
    list-style: none;
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: 3px;
    box-shadow: 0 4px 12px rgba(17, 17, 17, 0.12);
    max-height: 16rem;
    overflow-y: auto;
  }
  .finder__item {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.4rem 0.6rem;
    border-radius: 3px;
    font-size: 0.9rem;
    cursor: pointer;
  }
  .finder__item--active { background: var(--grey-bg); }
  .finder__where {
    color: var(--grey-mid);
    font-size: 0.78rem;
    flex-shrink: 0;
  }
  .rail {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 0 0 7rem;
    padding: 0.85rem 0 0.85rem 0.4rem;
    overflow-y: auto;
  }
  .rail__btn {
    font: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    text-align: left;
    padding: 0.4rem 0.6rem;
    border: none;
    /* The tabs' accent underline, turned vertical */
    border-left: 3px solid transparent;
    background: transparent;
    color: var(--grey-mid);
    cursor: pointer;
    border-radius: 0 3px 3px 0;
  }
  .rail__btn:hover { color: var(--black); background: var(--grey-light); }
  .rail__btn:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; }
  .rail__btn--active {
    color: var(--black);
    border-left-color: var(--accent);
    background: var(--white);
  }

  .tabpanel__sections {
    flex: 1;
    min-width: 0;
    overflow-y: auto;
    padding: 0.85rem;
  }
  /* Sections the rail has switched away from */
  .group--railoff { display: none; }
  /* The rail is the section's title; body padding recovers the summary's space */
  .tabpanel__sections > details > summary { display: none; }
  .tabpanel__sections > details .group__body { padding-top: 0.85rem; }
  .tabpanel__sections .display { margin-top: 0; }
  .tabpanel__sections #display-controls,
  .tabpanel__sections #output-controls { padding-top: 0.85rem; }

  /* The panel is narrower than the sheets these controls were sized for:
     grid items may shrink, and a colour row's opacity slider gives way
     instead of forcing a sideways scroll — the swatch never does. */
  .tabpanel__sections #display-controls > * { min-width: 0; }
  .tabpanel__sections .control--colour input[type="range"].control__alpha {
    flex: 1 1 2.5rem;
    min-width: 2rem;
    max-width: 4.5rem;
  }
  .tabpanel__sections .control--colour input[type="color"] { flex-shrink: 0; }

  /* Download/copy/reset close the Export section on desktop */
  .tabpanel__sections .display .actions {
    margin-top: 0.25rem;
    padding: 0 1rem 0.9rem;
  }
}
