/* ---------------------------------------------------------
   Menu button + sidebar
   --------------------------------------------------------- */

#menu-btn,
#mobile-header-edit-btn,
#mobile-find-btn {
  position: fixed;
  /* Level with the toolbar and with an open panel's top. */
  top: max(var(--panel-inset), env(safe-area-inset-top));
  left: max(16px, env(safe-area-inset-left));
  z-index: var(--z-panel);
  width: var(--chrome-button-w);
  height: var(--chrome-button-h);
  display: grid;
  align-content: center;
  justify-items: center;
  padding: 0;
  border: var(--hairline) solid var(--rule-2);
  /* --leaf: on the glass, not --radius-sm which is for inside panels. */
  border-radius: var(--leaf);
  background: var(--paper-card);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity var(--dur-base) var(--ease);
}
/* Accent border reserved for :focus-visible (keyboard); hover is fill only. */
@media (hover: hover) {
  #menu-btn:hover,
  #mobile-header-edit-btn:hover,
  #mobile-find-btn:hover { background: var(--paper-2); color: var(--accent-deep); }
}
#menu-btn:active,
#mobile-header-edit-btn:active,
#mobile-find-btn:active { background: var(--paper-3); color: var(--accent-deep); }
/* The hamburger's bars are background-painted spans, not currentColor strokes,
   so color on the button does not reach them — tint the spans explicitly. */
@media (hover: hover) {
  #menu-btn:hover span { background: var(--accent-deep); }
}
/* Outside @media (hover) — :active must tint bars on touch too. */
#menu-btn:active span { background: var(--accent-deep); }
/* Top right on Mobile (ui/mobile-header.ts paintFind). While the pen holds the
   corner, Find stands one button to its left
   (research/old/done-2026-09-15.md, R20). */
#mobile-find-btn {
  right: max(16px, env(safe-area-inset-right));
  left: auto;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity var(--dur-base) var(--ease),
              right var(--dur-base) var(--ease);
}
#mobile-header-edit-btn:not([hidden]) ~ #mobile-find-btn {
  right: calc(max(16px, env(safe-area-inset-right)) + var(--chrome-button-w) + 8px);
}
#mobile-find-btn[hidden] { display: none; }
/* Above phone width the toolbar stays at the top and carries its own Find
   (data-desktop in index.html), so the corner one was a second button for one
   command, on the Feed and on the Playlist alike. At phone width the toolbar
   drops Find (mobile.css) and the corner is the only way in. */
@media not all and (max-width: 700px) {
  #mobile-find-btn { display: none; }
}
#mobile-find-btn svg { width: 18px; height: 18px; color: var(--accent-text); }
/* Top-right corner on Mobile; fades with visibility delay (same as #toolbar).
   0.3s flat — does not follow whimsy axis. */
#mobile-header-edit-btn {
  right: max(16px, env(safe-area-inset-right));
  left: auto;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity 0.3s var(--ease),
              visibility 0s;
}
#mobile-header-edit-btn[hidden] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              opacity 0.3s var(--ease),
              visibility 0s 0.3s;
}
#mobile-header-edit-btn svg { width: 18px; height: 18px; color: var(--accent-text); }
/* 2px bars, 6px gap → pitch of 8, which lands on whole device pixels at every
   common DPR. Stack is 18×18, matching the pen icon in the opposite corner.
   Do not tighten the gap — the spacing is the whole drawing. */
#menu-btn { gap: 6px; }
#menu-btn span {
  width: 18px;
  height: 2px;
  border-radius: var(--radius-xs);
  background: var(--ink);
  /* Matches the button's own background transition. */
  transition: background var(--dur-fast) var(--ease);
}
/* 900 asks for "the heaviest weight the face has" — a variable face clamps to
   its axis maximum. ui/appearance.ts sets [data-display-sans]. */
:root[data-display-sans] .wordmark :is(h1, h2) { font-weight: 900; }

#sidebar.is-open ~ #menu-btn,
#sidebar.is-open ~ #mobile-find-btn { opacity: 0; pointer-events: none; }
/* Edit button stays reachable above the sidebar. */
#sidebar.is-open ~ #mobile-header-edit-btn { z-index: calc(var(--z-overlay) + 1); }

/* How far a panel stands off the window's edges. One number, because the
   toolbar, the menu button, the foot bars, the tour bar and the Playlist all
   line up on a panel's edge (chrome.css, timeline.css, mobile.css). 12px at
   phone width, where the controls in the corners stand 12px off (mobile.css). */
:root { --panel-inset: 14px; }

/* Non-modal: the canvas stays interactive with the sidebar open. */
#sidebar {
  position: fixed;
  z-index: var(--z-overlay);
  top: max(var(--panel-inset), env(safe-area-inset-top));
  bottom: calc(max(var(--panel-inset), env(safe-area-inset-bottom)) + var(--dock));
  left: max(var(--panel-inset), env(safe-area-inset-left));
  width: min(var(--sidebar-w), calc(100vw - 28px));
  display: flex;
  flex-direction: column;
  background: var(--paper-card);
  border: var(--hairline) solid var(--rule-2);
  /* --leaf, the toolbar's corner: the sheet stands level with the toolbar and
     the zoom pill along the top, and a rounder corner beside theirs read as
     a mismatch. The header panel is the same sheet. */
  border-radius: var(--leaf);
  box-shadow: var(--shadow-2);
  transform: translateX(calc(-100% - 22px));
  opacity: 0;
  visibility: hidden;
  transition: transform var(--dur-base) var(--ease-back),
              opacity var(--dur-fast) var(--ease),
              visibility 0s linear var(--dur-base);
}
#sidebar.is-open {
  transform: none;
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

/* The shadow stops where the Timeline starts. Both sheets end a panel inset
   above the dock, and --shadow-2 falls downward, so it darkened the strip.
   The clip sits at the strip's top edge and reaches far out on the other three
   sides. With the Timeline shut, --dock is 0 and the edge is the window's. */
#sidebar,
#header-panel {
  clip-path: inset(-100vmax -100vmax
    calc(-1 * max(var(--panel-inset), env(safe-area-inset-bottom))) -100vmax);
}

/* Same sheet as #sidebar, arriving from the right (where the pen that opens it
   is). Shares .side-head, .side-body, .side-sec and all fields. */
#header-panel {
  position: fixed;
  z-index: var(--z-overlay);
  top: max(var(--panel-inset), env(safe-area-inset-top));
  bottom: calc(max(var(--panel-inset), env(safe-area-inset-bottom)) + var(--dock));
  right: max(var(--panel-inset), env(safe-area-inset-right));
  width: min(var(--sidebar-w), calc(100vw - 28px));
  display: flex;
  flex-direction: column;
  background: var(--paper-card);
  border: var(--hairline) solid var(--rule-2);
  border-radius: var(--leaf);
  box-shadow: var(--shadow-2);
  transform: translateX(calc(100% + 22px));
  opacity: 0;
  visibility: hidden;
  transition: transform var(--dur-base) var(--ease-back),
              opacity var(--dur-fast) var(--ease),
              visibility 0s linear var(--dur-base);
}
#header-panel.is-open {
  transform: none;
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}
/* The pen hides under the panel it opened, and Find beside it goes too. */
#header-panel.is-open ~ #mobile-header-edit-btn,
#header-panel.is-open ~ #mobile-find-btn { opacity: 0; pointer-events: none; }

/* No text selection in panel chrome — re-enabled on inputs below.
   Off by default because the schema grows rows this file does not know about. */
#sidebar,
#header-panel {
  -webkit-user-select: none;
  user-select: none;
}
/* Re-enable selection on editable fields — inherited `none` reaches into
   <input> in Chromium. Written wide; checkboxes/ranges are a harmless no-op. */
#sidebar :is(input, textarea, [contenteditable]),
#header-panel :is(input, textarea, [contenteditable]) {
  -webkit-user-select: text;
  user-select: text;
}

.side-head {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  padding: 16px 18px 14px;
  border-bottom: 1px solid var(--rule);
}
.wordmark { margin: 0; }
/* h1 in sidebar, h2 in header panel — same mark, different rank. */
.wordmark :is(h1, h2) {
  margin: 0;
  font-size: var(--t-display);
  /* Tier's display weight, unshifted — see the sans override below. */
  font-weight: var(--display-weight);
  letter-spacing: calc(0.14em * var(--ls-scale));
  line-height: 1;
  color: var(--ink);
}
.wordmark .sub {
  display: block;
  margin-top: 3px;
  /* Sub-tiny register, off-scale on purpose (see .ctx-item kbd in menu.css). */
  font-size: 10px;
  font-style: var(--body-italic);
  letter-spacing: calc(0.22em * var(--ls-scale));
  text-transform: uppercase;
  color: var(--ink-3);
}
/* The two panel closes. The svg fills the button, and draws both the hover
   plate and the cross about the centre of its own 28 px viewBox
   (index.html). One render places both, so they cannot come apart. A 15 px
   glyph grid-centred on a background plate sat at a half pixel, and each
   engine snapped the two differently: half a pixel off at 100%, a whole
   device pixel at 125% on Harsh (research/old/done-2026-09-15.md, R10). */
#side-close,
#header-close {
  --plate: transparent;
  flex: none;
  /* As far from the panel's top edge as from its side (R35). The head pads
     16 px above and 18 px beside, so the 2 px here makes up the difference.
     Centred on the head it sat lower than it sat in, and read as loose. */
  align-self: flex-start;
  margin-top: 2px;
  width: var(--icon-button); height: var(--icon-button);
  display: grid;
  padding: 0;
  border: 0;
  /* --radius-pill: follows the corner axis, not always a circle. Here for
     the focus ring. The plate takes the same radius below. */
  border-radius: var(--radius-pill);
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), transform var(--dur-base) var(--ease-back);
}
#side-close svg,
#header-close svg { width: 100%; height: 100%; display: block; }
.close-plate {
  fill: var(--plate);
  stroke: none;
  /* A circle, or a square at Harsh. An engine without rx as a property keeps
     the circle the attribute draws. */
  rx: min(var(--radius-pill), 14px);
  transition: fill var(--dur-fast) var(--ease);
}
/* --accent-deep for controls, --accent-text for words.
   The spin is quality.css's to remove at the plain tier. */
@media (hover: hover) {
  #side-close:hover,
  #header-close:hover { color: var(--accent-deep); --plate: var(--paper-2); transform: rotate(90deg); }
}
#side-close:focus-visible,
#header-close:focus-visible { color: var(--accent-deep); --plate: var(--paper-2); }
/* No spin on :active — it would outlast the panel's exit transition. */
#side-close:active,
#header-close:active { color: var(--accent-deep); --plate: var(--paper-3); }

/* Tab strip — outside .side-body so it holds still while a tab scrolls. */
.side-tabs {
  display: flex;
  flex: none;
  gap: 2px;
  padding: 0 12px;
  border-bottom: 1px solid var(--rule);
}
.side-tab {
  flex: 1;
  min-height: calc(32px * var(--density));
  padding: 0 4px;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  background: none;
  font: inherit;
  font-size: var(--t-tiny);
  font-style: var(--body-italic);
  letter-spacing: calc(0.18em * var(--ls-scale));
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
@media (hover: hover) {
  .side-tab:hover { color: var(--ink); }
}
/* Press anticipates the accent the active tab wears. Before .is-on in source. */
.side-tab:active { color: var(--accent-text); }
.side-tab.is-on { color: var(--accent-text); border-bottom-color: var(--accent-text); }
.side-tab:focus { outline: none; }
.side-tab:focus-visible {
  outline: var(--sel-line) solid var(--select);
  outline-offset: calc(var(--sel-gap) * -1);
}

.side-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 18px 16px;
}

.side-sec {
  padding: calc(15px * var(--density)) 0;
  border-top: 1px solid var(--rule);
}
/* Not :first-child — a hidden first child still draws the border. ui/panel.ts
   marks the first visible section. */
.side-sec.is-first { border-top: 0; }
.side-sec h2 {
  margin: 0 0 11px;
  font-size: var(--t-tiny);
  font-weight: 400;
  font-style: var(--body-italic);
  letter-spacing: calc(0.24em * var(--ls-scale));
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Chromeless input — affordance on hover, box on focus. */
.board-title {
  display: block;
  width: 100%;
  margin: 0 0 11px;
  padding: 3px 6px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-xs);
  background: none;
  font: inherit;
  /* Off-scale: sized for comfortable typing, not to rank against the heading. */
  font-size: 18px;
  color: inherit;
  cursor: text;
  transition: border-color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease);
}
.board-title::placeholder { color: var(--ink-3); font-style: var(--body-italic); opacity: 1; }
.board-title:hover { border-color: var(--rule); }
.board-title:focus {
  outline: none;
  border-color: var(--accent-text);
  background: var(--paper-card);
}

/* Equal columns. minmax(0,1fr) prevents a long <select> value from blowing
   out its column. */
.field-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0 8px;
  /* Bottoms aligned — keeps controls level when one label wraps. */
  align-items: end;
}

/* Palette picker — a button styled to match a <select>, because it needs colour
   chips no native dropdown can paint. See buildPicker() in ui/panel.ts. */
.field-picker {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  /* The selects' and the buttons' height, so a row of mixed boxes is level. */
  min-height: calc(35px * var(--density));
  padding: 0 10px;
  color: var(--ink);
  font: inherit;
  text-align: left;
  border: var(--hairline) solid var(--rule-2);
  border-radius: var(--ctl-radius);
  background: var(--paper);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}
.field-picker:hover { border-color: var(--accent-text); background: var(--paper-2); }
.field-picker:focus { outline: none; }
.field-picker:focus-visible {
  outline: var(--sel-line) solid var(--select);
  outline-offset: var(--sel-gap);
}
.field-picker-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The chevron a <select> draws below, for a picker with no chips in its place. */
.field-picker:not(:has(.field-picker-chips))::after {
  content: '';
  flex: none;
  width: 11px;
  height: 7px;
  margin-right: 3px;
  background: var(--ink-2);
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1.5 2.4 6 6.2 10.5 2.4'/%3E%3C/svg%3E");
  -webkit-mask: var(--chevron) center / 11px 7px no-repeat;
  mask: var(--chevron) center / 11px 7px no-repeat;
  transition: background var(--dur-fast) var(--ease);
}
.field-picker:hover::after { background: var(--accent); }
.field-picker-chips { flex: none; display: flex; gap: 2px; }
.field-picker-chips > i {
  width: 11px;
  height: 11px;
  /* Capped at 2px so the 11px chip does not become a dot. */
  border-radius: min(2px, var(--radius-xs));
  /* Inset ring — a border would not show a pale chip on pale paper. */
  box-shadow: inset 0 0 0 var(--hairline) color-mix(in srgb, var(--ink) 22%, transparent);
}

/* Fold for rarely-used settings. */
.advanced { margin-top: 4px; }
.advanced > summary {
  display: flex;
  align-items: center;
  /* Centred, word and caret together: these are the one control in the panel
     that is a word rather than a box, and left-aligned they read as another
     heading. */
  justify-content: center;
  gap: 6px;
  padding: 5px 0;
  cursor: pointer;
  list-style: none;
  font-size: var(--t-tiny);
  font-style: var(--body-italic);
  letter-spacing: calc(0.24em * var(--ls-scale));
  text-transform: uppercase;
  color: var(--ink-3);
  transition: color var(--dur-fast) var(--ease);
}
.advanced > summary::-webkit-details-marker { display: none; }
.advanced > summary:hover { color: var(--accent-text); }
/* Drawn caret — rotates on open. ::after, so it follows the word it belongs to
   rather than leading it.
   It costs no width: -10px is its own 4px plus the 6px gap, handed back, so the
   flex row measures the word alone and centres that. The arrow then hangs off
   the word's right where the eye expects it, and the label sits where it would
   sit with no arrow at all. */
.advanced > summary::after {
  content: "";
  flex: none;
  margin-right: -10px;
  width: 0; height: 0;
  border-left: 4px solid currentColor;
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
  transition: transform var(--dur-fast) var(--ease);
}
/* Open, the label is at the foot of what it opened and the arrow is the way
   back, so it points up the way it came. The nudge is mirrored with it: it is
   there to keep the ink centred after a quarter turn, not to move it. The
   folds that stay on top turn the other way, below - down is where their rows
   still are. */
.advanced[open] > summary::after { transform: rotate(-90deg) translateX(-1px); }

/* ---- opening and shutting one ----
   The drawer's own movement is in ui/panel.ts (openAndShut), not here, and for
   one reason: it is a height, and a height only pushes the rest of the panel
   about if it is in pixels. Every CSS-only way to say "grow to your own
   height" fails on this box - `auto 0fr` and `auto 1fr` both resolve to the
   content in a grid whose own height is auto (measured on this fold: 453px
   either way), and interpolate-size is not in Gecko - so the number is
   measured and the movement is run from script, reading the tokens below so
   the whimsy tier and the quality dial still own the pace.

   What stays here is the part that is not the movement: the box the rows are
   measured in, and the caret's turn. */
.fold-body { min-height: 0; }

/* While a box is moving, the rows inside it do not animate anything of their
   own. A row that arrives under a pointer that never moved is hovered on the
   frame it appears, and its own 130ms border-colour transition starts from the
   resting colour - so most of that time is spent on a border somewhere between
   --rule-2 and the accent, which against the hovered fill is close to no
   border at all. It reads as a button that came up unoutlined, and it is the
   row's transition rather than the box's animation that draws it.
   Set by slideBox() in ui/panel.ts, and off again the moment the box lands. */
.is-sliding *,
.is-sliding *::before,
.is-sliding *::after { transition: none !important; }

/* The same box for a run of rows that comes and goes with a setting rather
   than with a summary - the Paper section's, behind Show paper. Everything
   about the movement is shared (slideBox, ui/panel.ts); what is not shared is
   that this one has no label of its own, so it carries no margin and the rows
   inside keep the spacing they would have had as children of the section. */
.reveal-body { min-height: 0; }
/* The caret turns back at the *start* of the close rather than snapping at the
   end of it: `open` is still on the whole way out - that is what the close is
   - so the rule that rotates it needs saying again, the other way. The
   transition on it is already there. */
.advanced[open].is-closing > summary::after { transform: none; }

/* Open, the word travels to the foot of what it opened. It is the way back,
   and the way back belongs at the end of the thing you are reading rather than
   above it - the same place a "show less" sits anywhere else. Nothing moves it
   by hand: the rows grow in the box above it, so it rides down on the same
   animation and rides back up on the close.
   `order` rather than a second element, and only while open, so the markup
   stays one summary and one body whichever way round they are drawn. */
.advanced[open] {
  display: flex;
  flex-direction: column;
}
/* `display` from an author sheet beats the UA's `[hidden]`, and paintFold()
   hides a fold whose every row is hidden. Without this, the rule above would
   put those back on screen. */
.advanced[hidden] { display: none; }
.advanced[open] > summary { order: 1; }
/* The gap between the rows and the label sits on the body, never on the
   summary. Spacing that exists only while the fold is open is still there on
   the last frame of a close and gone the moment `open` comes off - a few
   pixels snapping shut after the movement has already finished. On the body it
   is measured and animated with the height (openAndShut, ui/panel.ts), so it
   arrives at zero exactly when the rows do. */
.advanced > .fold-body { margin-bottom: 7px; }

/* A fold that IS the section heading (e.g. keyboard legend, Paper).
   ui/panel.ts marks these when the section has no h2 of its own. */
.advanced.is-head { margin-top: 0; }
/* And it sits where an h2 sits: a heading is the left edge of the column,
   whatever the toggles inside a section do. */
.advanced.is-head > summary {
  justify-content: flex-start;
  padding-top: 0;
}
.advanced.is-head[open] > summary { order: 0; }
/* Its gap is above the rows, not under them - the label stays on top - and
   eleven, the space an h2 keeps under itself. Same rule as above: on the body,
   so the close does not end on a jump. */
.advanced.is-head > .fold-body { margin: 11px 0 0; }
.advanced.is-head[open] > summary::after { transform: rotate(90deg) translateX(1px); }

/* Wraps so nowrap buttons do not push each other off-screen. */
.btn-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.btn-row:last-child { margin-bottom: 0; }
.btn-row button {
  flex: 1;
  min-height: calc(35px * var(--density));
  padding: 0 12px;
  color: var(--ink);
  border: var(--hairline) solid var(--rule-2);
  border-radius: var(--ctl-radius);
  background: var(--paper);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              transform var(--dur-base) var(--ease-back),
              color var(--dur-fast) var(--ease);
}
/* Lift/grow/press governed by whimsy-axis tokens. */
@media (hover: hover) {
  .btn-row button:hover {
    background: var(--paper-2);
    border-color: var(--accent-text);
    color: var(--accent-deep);
    transform: translateY(var(--btn-lift)) scale(var(--btn-grow));
  }
}
.btn-row button:active {
  transform: scale(var(--btn-press));
  background: var(--paper-3);
  /* Faster curve on press — the hover's spring is too slow for a click. */
  transition: transform var(--dur-fast) var(--ease-in),
              background var(--dur-fast) var(--ease);
}
.btn-row button.primary {
  background: var(--accent);
  border-color: var(--accent-deep);
  color: var(--accent-fg);
  letter-spacing: calc(0.03em * var(--ls-scale));
}
@media (hover: hover) {
  .btn-row button.primary:hover { background: var(--accent-deep); color: var(--accent-fg); }
}
/* Must restate — .btn-row button:active would override with --paper-3. */
.btn-row button.primary:active { background: var(--accent-deep); color: var(--accent-fg); }
/* At Harsh the accent fill can be close to the paper — ink border keeps an edge
   visible on every hue. */
:root[data-whimsy="2"] .btn-row button.primary,
:root[data-whimsy="2"] .btn-row button[aria-pressed="true"],
:root[data-whimsy="2"] .btn-row button[aria-pressed="true"]:hover {
  border-color: var(--ink);
}
/* Radio-pair (e.g. Portrait / Landscape): filled to distinguish from hover. */
.btn-row button[aria-pressed="true"],
.btn-row button[aria-pressed="true"]:hover {
  background: var(--accent);
  border-color: var(--accent-deep);
  color: var(--accent-fg);
}
/* Outlined until hover — reads as available, not as urged. */
.btn-row button.danger { border-color: var(--danger); color: var(--danger); }
@media (hover: hover) {
  .btn-row button.danger:hover {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--paper-card);
  }
}
/* Fills on press too — on touch this is the only feedback. */
.btn-row button.danger:active {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--paper-card);
}
/* Armed state (armClear() in main.ts). Stays filled — no pulse. */
.btn-row button.danger[data-arming] {
  background: var(--danger);
  border-color: var(--danger);
  color: var(--paper-card);
}

/* The gap a field carries underneath it, named because a second rule needs the
   same number: a (?) trailing a row of fields has to be lifted by exactly this
   to sit on the controls' line rather than on the floor of the box. */
:root {
  --field-gap: 11px;
  /* Every box in the panel - select, picker, button, tip - is this tall. */
  --ctl-h: calc(35px * var(--density));
  /* And this round: --leaf, but never more than half the height.
     At Softish --leaf is 20px against a 35px box, so the two corners down one
     side ask for 40px of a 35px edge and the engine has to scale both radii
     down itself. Gecko reconciles that imperfectly and leaves a pair of short
     straight tails poking out of the pill's ends - which the hover scale then
     enlarges into the lines you can see. Clamped here, the arcs meet exactly
     and there is nothing left to reconcile. Middle (10px) and Harsh (0) are
     under the cap already, which is why only Softish ever showed it.
     Exactly half was still not enough in motion: mid-hover the scale puts the
     box at a fraction of a device pixel, the two radii come out a rounding
     error over the edge, and the tails flashed back for a frame or two. Half a
     pixel short is invisible at rest and never meets the edge. */
  --ctl-radius: min(var(--leaf), calc(var(--ctl-h) / 2 - 0.5px));
}
.field { display: block; margin-bottom: var(--field-gap); }
.field > span {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: var(--t-small);
  font-style: var(--body-italic);
  color: var(--ink-2);
  margin-bottom: 5px;
}
.field output { font-family: var(--font-mono); font-style: normal; color: var(--ink); }
/* Centred head for stop-labelled dials (e.g. whimsy card). */
.field.is-dial > span:first-child { justify-content: center; }
/* Custom chevron via mask (a data URI cannot see currentColor).
   appearance:none drops the native arrow; this puts one back. */
.field:has(select) {
  position: relative;
  /* Mark inset — follows the corner radius. */
  --lane: max(13px, calc(var(--leaf) * 0.75));
}
.field:has(select)::after {
  content: '';
  position: absolute;
  right: var(--lane);
  bottom: 0;
  width: 11px;
  height: calc(35px * var(--density));   /* the select's own height, so centred */
  background: var(--ink-2);
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1.5 2.4 6 6.2 10.5 2.4'/%3E%3C/svg%3E");
  -webkit-mask: var(--chevron) center / 11px 7px no-repeat;
  mask: var(--chevron) center / 11px 7px no-repeat;
  pointer-events: none;
  transition: background var(--dur-fast) var(--ease);
}
.field:has(select):hover::after { background: var(--accent); }
.field select {
  width: 100%;
  min-height: calc(35px * var(--density));
  padding: 0 calc(var(--lane) + 19px) 0 10px;
  appearance: none;
  -webkit-appearance: none;
  border: var(--hairline) solid var(--rule-2);
  border-radius: var(--ctl-radius);
  background: var(--paper);
  cursor: pointer;
}
.field select:hover { border-color: var(--accent-text); }

/* Slider style shared by five hosts: .field, .np-volume, .ctx-range, .dk-dial,
   .xo-dial.
   :is() keeps them in one rule. quality.css squares the thumb at Harsh.
   Add any new slider to this list or it prints a native track. */
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"] {
  width: 100%;
  height: 22px;
  -webkit-appearance: none;
          appearance: none;
  background: transparent;
  cursor: pointer;
  /* Without this a finger drag gets reinterpreted as a panel scroll. */
  touch-action: none;
}
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"]::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--rule-2), var(--rule));
}
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"]::-moz-range-track {
  height: 3px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--rule-2), var(--rule));
}
/* background-clip: padding-box on both thumbs — without it the accent fill
   bleeds past the border as a hairline ring at sub-pixel seams. */
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 15px; height: 15px;
  margin-top: -6px;
  border: 1.5px solid var(--paper-card);
  border-radius: var(--radius-xs);
  background: var(--accent);
  background-clip: padding-box;
  transform: rotate(45deg);
}
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"]::-moz-range-thumb {
  width: 13px; height: 13px;
  border: 1.5px solid var(--paper-card);
  border-radius: var(--radius-xs);
  background: var(--accent);
  background-clip: padding-box;
}
/* Remove the browser's thumb focus ring; replace with :focus-visible on the
   track so keyboard users can still find it. */
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"]::-moz-focus-outer,
#pick-hue::-moz-focus-outer { border: 0; }
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"]:focus,
#pick-hue:focus { outline: none; }
:is(.field, .np-volume, .ctx-range, .dk-dial, .xo-dial) input[type="range"]:focus-visible,
#pick-hue:focus-visible {
  outline: var(--sel-line) solid var(--select);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
/* Pigment swatch button — opens the app's colour picker (ui/color-picker.ts).
   Fill is written inline by syncControls(). */
.field .pigment-swatch {
  -webkit-appearance: none;
          appearance: none;
  box-sizing: border-box;
  width: 100%;
  height: calc(35px * var(--density));
  padding: 0;
  border: var(--hairline) solid var(--rule-2);
  border-radius: var(--radius-sm);
  background: var(--paper);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease);
}
.field .pigment-swatch:hover { border-color: var(--accent-text); }

/* Dropped font list (ui/fonts.ts). Hidden until a face is added. */
#appearance-fonts {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 6px;
}
#appearance-fonts[hidden] { display: none; }
.font-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 6px;
  border-radius: var(--leaf);
}
.font-row:hover { background: var(--paper-2); }
.font-row-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--t-small);
  color: var(--ink);
}
.font-row-remove {
  flex: none;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: var(--leaf);
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
@media (hover: hover) {
  .font-row-remove:hover { color: var(--accent-deep); background: var(--paper-2); }
}
.font-row-remove:active { background: var(--paper-3); }
.font-row-remove:focus-visible { color: var(--accent-deep); background: var(--paper-2); }
/* Smaller than --icon-button — a secondary action on a row. */
.font-row-remove svg { width: 12px; height: 12px; }

/* Checkboxes with SVG tick. */
.check {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  cursor: pointer;
}
/* A slider that belongs to the box above it, Line height under Wrap long
   names, stands as far from the box as a box stands from the slider above. */
#mobile-header-settings .check + .field { margin-top: 12px; }
.check input {
  -webkit-appearance: none;
          appearance: none;
  flex: none;
  width: 17px; height: 17px;
  margin: 0;
  border: 1.5px solid var(--rule-2);
  /* A circle on the soft tiers, a square on Harsh - a checkbox is a square with
     a tick, not a circle carrying meaning, so it rides --radius-pill (see the
     token's note in tokens.css). --radius-xs left it a rounded square at Softish. */
  border-radius: var(--radius-pill);
  background: var(--paper);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.check input:hover { border-color: var(--accent-text); }
.check input:checked {
  background-color: var(--accent);
  border-color: var(--accent-deep);
  /* SVG tick. White because a data URI cannot read --accent-fg. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.6 8.4 6.6 11.4 12.4 4.9'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

.hint {
  margin: 9px 0 0;
  font-size: var(--t-small);
  font-style: var(--body-italic);
  line-height: 1.5;
  color: var(--ink-3);
}
.hint em { font-style: normal; color: var(--accent-text); }

/* A hint folded behind a (?), and the row it explains, in one grid: the row
   takes the first cell, the marker sits at its end, and the paragraph opens
   across both underneath. Two cells apart, which is why this is a button and
   `hidden` rather than a <details> — see buildHint() in ui/panel.ts. */
.tip-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  /* Bottoms aligned, as .field-pair is: the marker lines up with the controls
     it follows, not with their labels. */
  align-items: end;
  gap: 0 8px;
}
.tip-row > .hint { grid-column: 1 / -1; }
.tip-btn {
  /* The selects' and pickers' own box, squared: same height, same hairline,
     same paper, and --leaf for the corners - so it rounds and unrounds with
     the whimsy dial instead of staying a circle on a squared-off board. */
  height: calc(35px * var(--density));
  aspect-ratio: 1;
  /* The row it trails is a stack of fields, and a field carries --field-gap
     underneath - so aligning to the bottom of that box put the marker a gap
     low. The same gap here lifts it back onto the controls' line. */
  margin-bottom: var(--field-gap);
  display: grid;
  place-content: center;
  padding: 0;
  border: var(--hairline) solid var(--rule-2);
  border-radius: var(--ctl-radius);
  background: var(--paper);
  color: var(--ink-2);
  font: inherit;
  font-size: var(--t-small);
  font-style: normal;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
@media (hover: hover) {
  .tip-btn:hover { border-color: var(--accent-text); background: var(--paper-2); }
}
.tip-btn[aria-expanded="true"] {
  border-color: var(--accent-text);
  background: var(--paper-3);
  color: var(--accent-text);
}

.keys { margin: 0; padding: 0; list-style: none; font-size: var(--t-small); color: var(--ink-2); }
.keys li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: baseline;
  padding: 4px 0;
  font-style: var(--body-italic);
}
.keys li span:not(.key-combo) { color: var(--ink-3); text-align: right; }
/* A row's keys stay together at its left, with the "+" between them. */
.key-combo { flex: none; display: inline-flex; align-items: center; gap: 4px; }
kbd {
  font-family: var(--font-mono);
  font-style: normal;
  /* Sub-tiny register (see .ctx-item kbd in menu.css). */
  font-size: 10px;
  padding: 2px 6px;
  border: 1px solid var(--rule-2);
  border-bottom-width: 2px;
  border-radius: var(--radius-xs);
  background: var(--paper);
  color: var(--ink);
  white-space: nowrap;
}
/* In the Keys list each cap is a key: a one-letter one is square, and a longer
   one is the same height and grows sideways. */
.keys kbd {
  display: inline-grid;
  place-items: center;
  min-width: 30px;
  height: 30px;
  padding: 0 6px;
}

.side-foot {
  padding: 10px 18px calc(12px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--rule);
  font-size: var(--t-tiny);
  font-style: var(--body-italic);
  letter-spacing: calc(0.1em * var(--ls-scale));
  color: var(--ink-3);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
/* Button dressed as text — inherits the row's font/size/colour. */
.side-foot .by {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease);
}
.side-foot .by:hover { color: var(--accent-text); text-decoration: underline; }
.side-foot .by:focus { outline: none; }
.side-foot .by:focus-visible {
  outline: var(--sel-line) solid var(--select);
  outline-offset: var(--sel-gap);
}

/* ---------------------------------------------------------------------------
   Greyed on /patch (no board). ui/panel.ts sets disabled; this is only the
   fade. Nested .is-inert resets to 1 so opacities do not multiply. */
#sidebar .is-inert { opacity: 0.45; }
#sidebar .is-inert .is-inert { opacity: 1; }
#sidebar .is-inert :is(button, input, select) {
  pointer-events: none;
  cursor: default;
}

/* Legacy fallback without color-mix() — see tokens.css §7. */
@supports not (color: color-mix(in srgb, red, blue)) {
  .field-picker-chips > i {
    box-shadow: inset 0 0 0 var(--hairline) rgb(var(--ink-c) / 22%);
  }
}

