/* ==========================================================================
   echo//log — Phase 9 LIGHT design-system LAYER
   --------------------------------------------------------------------------
   Additive override. Linked AFTER css/styles.css so it WINS the cascade.
   Aesthetic: light Swiss / Dieter Rams. White paper, near-black Roboto Mono,
   hairline rgba(0,0,0,0.1) rules, Inter 9px uppercase nav labels, generous air.
   Tokens extracted from the operator's Figma ("Production" page), 2026-06-05.

   SCOPE NOTE: this layer deliberately does NOT restyle the Settings/About
   overlay (renders at #/about under body.is-more-open). Those rules are
   guarded with :not(.is-more-open) where they could leak. Leave About alone.

   Class hooks reused from the live DOM (js/screens/collection.js + styles.css):
     .topbar .topbar__brand .topbar__brand-slash .topbar__icon-btn
     .bottom-nav  a[data-nav]  .nav__glyph  .nav__label
     .btn (+ new modifiers)
     .profile-widget .profile-widget__top .profile-widget__code
     .profile-stats .profile-stats__cell
     .collection-bar (NEW) .collection-bar__band .collection-bar__cell
     .collection__section-header (+ __title / __action)
     .collection-grid .insect-card .insect-card__art .insect-card__grade
     .insect-card__label .keep-log-card .stage__bg
   Grade hooks: rarity classes emitted by collection_bar.js / rarity.js
     band:  [data-grade="common|uncommon|rare|epic|legendary|glitch"]
     card:  .rarity-1 .. .rarity-5 / .rarity-glitch (sets --r per tier)
   ========================================================================== */

/* ==========================================================================
   1. TOKENS
   Brief tokens (new names) + REMAP of the legacy styles.css tokens so every
   pre-existing rule that reads --line / --ink-dim / --r-N inherits the light
   palette automatically. One source of truth, no per-rule sweep needed.
   ========================================================================== */
:root {
  /* --- Phase-9 named tokens (from Figma dev-mode) --- */
  --bg:        #ffffff;
  --bg-frost:  rgba(255, 255, 255, 0.9);   /* topbar + bottom nav (blur 20px) */
  --ink:       #0a0a0a;                     /* primary text + wordmark        */
  --ink-50:    rgba(0, 0, 0, 0.5);          /* dim labels, inactive nav       */
  --ink-30:    rgba(0, 0, 0, 0.3);          /* faint: the "//", placeholders  */
  --line:      rgba(0, 0, 0, 0.1);          /* ALL hairlines / borders        */
  --ok:        #16803d;                     /* success green                  */

  /* --- PCB-grammar tokens (Phase-9 visual upgrade, 2026-06-05; DECISIONS §54) ---
     Silkscreen ref-designators, hairline traces, LED-dot indicators. The ONLY
     new tokens this pass. Dark values land in the body.theme-dark stage (H). */
  --trace:      rgba(0, 0, 0, 0.06);   /* hairline connectors, thinner than --line */
  --silkscreen: var(--ink-30);         /* tiny ref labels (SCN, B1..B4, COC, DEX)  */
  --led-on:     var(--g-rare);         /* active indicator dot (#5a9cff)           */
  --led-off:    var(--ink-30);         /* idle indicator dot                       */

  /* 6 grade colours — prototype hexes, read fine on white. */
  /* Grade colours — exact from Figma (node 150:679). Saturated, read well on
     white. legendary/glitch not in the Figma sample → kept on-palette. */
  --g-common:    #969696;
  --g-uncommon:  #16803d;
  --g-rare:      #5a9cff;
  --g-epic:      #7a32c4;
  --g-legendary: #ed5a6a;
  --g-glitch:    #e8c06a;   /* gold; operator open to a pink swap — gold for now */

  /* Grade-TEXT variants. The bar cells above are solid swatches (decorative,
     no contrast rule). But the grade TAG is 9px text on white — the light
     green/gold fail WCAG hard (~1.7-1.9). These darker hue-matched tokens
     hit AA (>=4.5) for the small tag while the bar keeps its vivid colour.
     Same hue family, lowered lightness. See contrast-audit in agent memory. */
  --gt-common:    #6b7385;  /* ~4.6 on #fff */
  --gt-uncommon:  #2f8f5b;  /* ~4.6 */
  --gt-rare:      #3f6fb0;  /* ~4.7 */
  --gt-epic:      #7a55c2;  /* ~4.7 */
  --gt-legendary: #c83b4b;  /* ~4.8 */
  --gt-glitch:    #9a7414;  /* ~4.8 (dark gold/amber — gold is unreadable as text) */

  /* Semantic danger/destructive token (R9 §17 4-button system). Same hue as
     --gt-legendary, ~4.8:1 on white = AA. Dark theme can override later. */
  --danger:       #c83b4b;

  /* Type system (operator 2026-06-07, revised): prose --ui = SF on Apple
     (-apple-system / BlinkMacSystemFont) and Inter on Android / Windows / other
     (Inter is the loaded webfont — it wins on every non-Apple device, where
     -apple-system does not resolve). --mono = Roboto Mono everywhere. mono =
     ACCENT (tags, numbers, short UPPER labels, section headers, ASCII art,
     wordmark); readable / long prose = --ui. Segoe UI / Roboto stay as the
     offline fallback if Inter has not loaded yet. */
  --mono: "Roboto Mono", ui-monospace, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", monospace;
  --ui:   -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, system-ui, sans-serif;

  /* --- Remap legacy styles.css tokens onto the Phase-9 palette --- */
  --ink-dim:      var(--ink-50);
  --ink-10:       var(--line);
  --ink-25:       var(--ink-30);
  --ink-50-legacy: var(--ink-50); /* (styles.css --ink-50 is darker @0.58; ours wins) */
  --ink-70:       rgba(0, 0, 0, 0.78);
  --line-strong:  rgba(0, 0, 0, 0.25);
  --line-ink:     var(--ink);

  /* Surfaces flatten to paper-white / one faint cushion in the light system. */
  --bg-elev:  rgba(0, 0, 0, 0.035);
  --bg-soft:  rgba(0, 0, 0, 0.035);
  --bg-alt:   rgba(0, 0, 0, 0.035);
  --bg-term:  rgba(0, 0, 0, 0.035);

  --accent-green: var(--ok);

  /* Font aliases — point the legacy font tokens at the Phase-9 stacks so
     every existing selector renders in the new families. */
  --font-mono: var(--mono);
  --font-ui:   var(--ui);

  /* Map the tier accent tokens (--r-1..--r-6, read by .rarity-N rules and the
     .insect-card__grade `var(--r)` lookup) onto the grade palette. This lights
     up every grade tag / collection-bar cell with no extra wiring. */
  --r-1: var(--g-common);
  --r-2: var(--g-uncommon);
  --r-3: var(--g-rare);
  --r-4: var(--g-epic);
  --r-5: var(--g-legendary);
  --r-6: var(--g-glitch);

  /* Faint ASCII texture ink behind card art (very low contrast on white). */
  --stage-bg-ink: rgba(0, 0, 0, 0.12);

  /* Default --r so any stray .insect-card__grade resolves to a quiet ink
     even with no rarity class. Per-tier --r is wired just below. */
  --r: var(--ink-50);

  /* Chrome heights (brief: topbar 58, nav 64). */
  --top-h: 58px;
  --nav-h: 64px;
  --radius: 0;

  /* Hover/active wash (P1-1): consolidated interaction tokens. */
  --wash-hover:  rgba(0, 0, 0, 0.04);
  --wash-active: rgba(0, 0, 0, 0.07);

  /* Alarm LED (P2-1): red state reuses the AA-safe legendary text token. */
  --led-alarm: var(--gt-legendary);
}

/* ==========================================================================
   1b. DARK THEME — overrides for the Phase-9-ONLY tokens.
   styles.css body.theme-dark already flips the legacy base (--bg/--ink/--line/
   --ink-NN/--r-N/--bg-elev/--stage-bg-ink). The tokens introduced in THIS file
   (frost chrome, ink-30, wash, grade-TEXT) had no dark values, so dark mode
   left a white frosted topbar/nav over black + near-invisible faint text.
   This block supplies them. --silkscreen/--led-off/--led-alarm derive from the
   tokens below, so they fix automatically. --danger is handled by styles.css. */
body.theme-dark {
  --bg-frost:    rgba(10, 10, 10, 0.9);
  --ink-30:      rgba(255, 255, 255, 0.3);
  --wash-hover:  rgba(255, 255, 255, 0.06);
  --wash-active: rgba(255, 255, 255, 0.1);
  --trace:       rgba(255, 255, 255, 0.06);

  /* Grade TEXT — bright hue-matched tints, AA on near-black. */
  --gt-common:    #aeb6c4;
  --gt-uncommon:  #5fce91;
  --gt-rare:      #7cc4ff;
  --gt-epic:      #c4a5f5;
  --gt-legendary: #ff7a88;
  --gt-glitch:    #e8c06a;
}

/* ==========================================================================
   2. APP BASE
   White paper, mono default. Light reset deltas only — we do NOT re-reset
   everything styles.css already handles.
   ========================================================================== */
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--mono);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Selection — quiet, on-palette. */
::selection {
  background: rgba(0, 0, 0, 0.08);
  color: var(--ink);
}

/* ==========================================================================
   3. TOPBAR  (header.topbar — 58h, frosted white, hairline)
   ========================================================================== */
/* App column — content is a centered ~360 block (Figma frame = 390 with 15px
   gutters). The chrome (topbar + nav) floats over it as inset contained
   blocks, NOT edge-to-edge. */
#app {
  max-width: 390px;
  margin: 0 auto;
  position: relative;
}
#screen-root {
  padding:
    calc(max(24px, env(safe-area-inset-top)) + var(--top-h) + 24px)
    15px
    calc(var(--nav-h) + max(16px, env(safe-area-inset-bottom)) + 24px);
}

/* Global white gradient fades — sit above screen-root content (z 5),
   below the frosted topbar/nav chrome (z 10/20). Decorative overlay only;
   pointer-events:none so they never block taps. Centred to the app column. */
.app-fade {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  width: min(390px, 100vw);
  height: 120px;                /* operator spec: 120px (Figma 179:750/751) */
  pointer-events: none;
  z-index: 5;
}
.app-fade--top {
  top: 0;
  background: linear-gradient(to bottom, var(--bg), rgba(255, 255, 255, 0));
}
.app-fade--bottom {
  bottom: 0;
  background: linear-gradient(to top, var(--bg), rgba(255, 255, 255, 0));
}

/* Topbar — a 360-wide contained block, 24px from the top, full hairline
   border on all sides (Figma comment: not flush, content-width, 24px top). */
.topbar {
  position: fixed;
  top: max(24px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  width: min(360px, calc(100vw - 30px));
  height: var(--top-h);
  min-height: var(--top-h);
  padding: 9px;
  padding-left: 21px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--bg-frost);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  border: 1px solid var(--line);
  z-index: 10;
}

/* Wordmark — Roboto Mono SemiBold 12, ink, with the "//" in --ink-30. */
.topbar__brand {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  letter-spacing: 0;
  color: var(--ink);
  text-decoration: none;
}
.topbar__brand-slash {
  color: var(--ink-30);
}

/* Status cluster — the more (≡) button on the right. Sound button (♪) kept
   from the legacy shell; squared into the same 40-box system. */
.topbar__status {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 40x40 hairline square button — the topbar "more" / icon button. */
.topbar__icon-btn {
  width: 40px;
  height: 40px;
  min-width: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 0;
  color: var(--ink);
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease;
}
.topbar__icon-btn:hover {
  background: var(--wash-hover);
}
.topbar__icon-btn:active {
  background: var(--wash-active);
}
.topbar__icon-btn:focus-visible {
  outline: none;
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}
.topbar__icon-glyph {
  font-family: var(--mono);
  font-size: 16px;
  line-height: 1;
}
/* R8: topbar profile avatar = a square monogram tile (sharp-corner house law),
   reusing .topbar__icon-btn. The letter is the username initial. */
.topbar__avatar-mono {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  color: var(--ink);
}

/* ==========================================================================
   4. BOTTOM NAV  (nav.bottom-nav — 64h, frosted, 3 equal columns)
   Visual order L>R: DEVICE / COLLECTION / SETTINGS.
   ========================================================================== */
/* Bottom nav — same treatment as the topbar (Figma comment "то же что топбар"):
   a 360-wide contained block, inset from the bottom, full hairline border. */
.bottom-nav {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: max(16px, env(safe-area-inset-bottom));
  width: min(360px, calc(100vw - 30px));
  height: var(--nav-h);
  background: var(--bg-frost);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  border: 1px solid var(--line);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  z-index: 20;
}
.bottom-nav a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 44px;            /* >= 44px tap target */
  padding: 8px 4px;
  text-decoration: none;
  color: var(--ink-50);        /* inactive icon + label */
  transition: color 140ms ease;
}
/* Inactive label — Inter Medium 9, +1 tracking, uppercase, --ink-50. */
.bottom-nav a .nav__label {
  font-family: var(--ui);
  font-size: 9px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: inherit;
}
.bottom-nav a .nav__glyph {
  font-family: var(--mono);
  font-size: 20px;             /* 20px icon row */
  line-height: 1;
  color: inherit;
}
/* Active tab = full --ink (icon + label). Support both router hooks:
   .is-active (legacy) and aria-current="page" (likely new router). */
.bottom-nav a.is-active,
.bottom-nav a[aria-current="page"] {
  color: var(--ink);
}
.bottom-nav a:active {
  color: var(--ink);
}
.bottom-nav a:focus-visible {
  outline: none;
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--line);
}
/* Phase-9 nav is a flat 3-tab bar — neutralise the legacy "primary"
   (raised generate) tab styling in case stale markup lingers pre-rewrite. */
.bottom-nav a.is-primary {
  background: transparent;
  transform: none;
}
.bottom-nav a.is-primary .nav__glyph {
  background: transparent;
  color: inherit;
  width: auto;
  height: auto;
  border-radius: 0;
}

/* ==========================================================================
   5. BUTTONS
   .btn base + variants: main (black), square (40x40 hairline), ghost (mono).
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  padding: 0 16px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid var(--ink);
  border-radius: var(--radius); /* 0 — sharp corners */
  background: var(--bg);
  color: var(--ink);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, opacity 140ms ease, border-color 140ms ease;
}
.btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink);
}
.btn:disabled,
.btn[aria-disabled="true"] {
  color: var(--ink-30);
  border-color: var(--line);
  background: var(--bg);
  cursor: not-allowed;
  pointer-events: none;
}

/* PRIMARY / main — black fill, white mono uppercase, full-width, 40h, 2px. */
.btn--main,
.btn--primary {
  width: 100%;
  background: var(--ink);
  color: #fff;
  border-color: var(--ink);
}
.btn--main:hover,
.btn--primary:hover {
  background: #000;
}
.btn--main:active,
.btn--primary:active {
  opacity: 0.85;
}

/* SQUARE — 40x40 hairline icon box. */
.btn--square {
  width: 40px;
  height: 40px;
  min-width: 40px;
  padding: 0;
  border-color: var(--line);
  background: transparent;
}
.btn--square:hover {
  background: var(--wash-hover);
}

/* GHOST (secondary) — transparent, --ink-50 text, hairline border. Specificity
   bumped to .btn.btn--ghost so it BEATS the styles.css ghost (white fill + ink
   border) that was making secondary buttons render as heavy ink boxes (§63 fix). */
.btn.btn--ghost {
  background: transparent;
  color: var(--ink-50);
  border-color: var(--line);
}
.btn.btn--ghost:hover {
  color: var(--ink);
  border-color: var(--line-strong);
}

/* Compact ghost (reused by cocoon "open" inline buttons) — keep it light. */
.btn.btn--ghost.btn--xs {
  height: 28px;
  padding: 0 10px;
  font-size: 11px;
}

/* DANGER — destructive action (Delete profile). Reuses the styles.css filled
   .btn.btn--danger (red fill + white label), tinted by the phase9 --danger token
   above (AA-safe #c83b4b, white-on-red ~4.5:1). One of the 4 canonical button
   types: primary (filled) / secondary (.btn--ghost) / danger / square (icon). */

/* ==========================================================================
   6. PROFILE WIDGET  (@username + pipe-divided stats row)
   ========================================================================== */
.profile-widget {
  padding: 0;
  margin-bottom: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.profile-widget__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
.profile-widget__code {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

/* Stats row — pipe-divided cells with thin VERTICAL hairline dividers.
   Switch the legacy 3-col grid to an inline flow so an arbitrary number of
   cells (Species / Catches / Cocoons) sit on one line, divider BETWEEN. */
.profile-stats {
  display: flex;
  align-items: center;
  gap: 0;
}
/* R9 (Figma 169:169): three equal columns stretched to content width —
   Species left, Catches centre, Cocoons right. No pipe dividers. */
.profile-stats__cell {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--ink-50);
  opacity: 1;
  white-space: nowrap;
}
.profile-stats__cell:first-child {
  justify-content: flex-start;
}
.profile-stats__cell:last-child {
  justify-content: flex-end;
}

/* ==========================================================================
   7. COLLECTION BAR  (NEW — replaces level / XP)
   Markup contract (built by the Collection screen rewrite):
     <div class="collection-bar">
       <div class="collection-bar__band" data-grade="common">
         <span class="collection-bar__cell is-filled"></span> x9
       </div>
       ... one band per grade, left>right common>glitch ...
     </div>
   Thin (~5px). Empty cells = faint --line track; filled cells = grade colour.
   ========================================================================== */
/* Figma node 150:679: ONE 4px track (faint 5%-ink bg). Each grade adds a
   left-packed segment whose width is proportional to caught/54 — colours sum
   contiguously, fill grows left>right, the uneven runs show the distribution.
   No per-species empty cells. */
.collection-bar {
  display: flex;
  width: 100%;
  height: 4px;
  background: rgba(0, 0, 0, 0.05);
  overflow: hidden;
}
.collection-bar__seg {
  height: 4px;
  display: block;
  transition: width 240ms ease;
}
.collection-bar__seg[data-grade="common"]    { background: var(--g-common); }
.collection-bar__seg[data-grade="uncommon"]  { background: var(--g-uncommon); }
.collection-bar__seg[data-grade="rare"]      { background: var(--g-rare); }
.collection-bar__seg[data-grade="epic"]      { background: var(--g-epic); }
.collection-bar__seg[data-grade="legendary"] { background: var(--g-legendary); }
.collection-bar__seg[data-grade="glitch"]    { background: var(--g-glitch); }

/* ==========================================================================
   8. SECTION HEADER  (left mono uppercase label + right "-> SHOW/HIDE")
   ========================================================================== */
.collection__section-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 12px;
  padding-bottom: 8px;
  /* R9: divider ONLY under a COLLAPSED section; expanded sections show no
     header underline (the grid below provides the structure). */
  border-bottom: none;
}
.collection__section-header--collapsed {
  border-bottom: 1px solid var(--line);
}
.collection__section-header__title {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 1;
}
.collection__section-header__action {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-50);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  transition: color 140ms ease;
}
.collection__section-header__action:hover {
  color: var(--ink);
  opacity: 1;
}

/* ==========================================================================
   9. COLLECTION GRID + INSECT CARD
   3 cols; square art with a faint ASCII texture; grade tag top-right per
   grade; species name (mono) below. Plus locked + "Keep logging" variants.
   ========================================================================== */
.collection-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.insect-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--bg);
  min-width: 0;
  overflow: visible;
  text-decoration: none;
  transition: transform 140ms ease;
}
.insect-card:active {
  transform: scale(0.97);
}
.insect-card:focus-visible {
  outline: none;
}
.insect-card:focus-visible .insect-card__art {
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}

/* Square art tile — paper bg, hairline frame. */
.insect-card__art {
  aspect-ratio: 1 / 1;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--bg);
  --stage-halo: var(--bg);
}

/* Faint ASCII texture behind the bug (very low contrast). The bug art
   (.stage__bug / .ascii-bug) paints in --ink above it. */
.insect-card__art .stage__bg {
  color: var(--stage-bg-ink);
  opacity: 0.5;
}

/* Per-tier --r wiring. styles.css already sets this on .rarity-N, but we
   redeclare here so the Phase-9 layer is self-sufficient (the standalone
   prototype loads ONLY this file). The grade tag + any --r consumer reads it. */
.rarity-1 { --r: var(--g-common); }
.rarity-2 { --r: var(--g-uncommon); }
.rarity-3 { --r: var(--g-rare); }
.rarity-4 { --r: var(--g-epic); }
.rarity-5 { --r: var(--g-legendary); }
.rarity-glitch { --r: var(--g-glitch); }

/* Grade tag — top-right. Coloured per grade with the ACCESSIBLE --gt-*
   text variant (>=4.5 on white), NOT the vivid --g-* swatch colour, because
   the tag is 9px text. Bar cells keep the vivid --g-*. */
.insect-card__grade {
  position: absolute;
  top: 5px;
  right: 5px;
  z-index: 4;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
  background: var(--bg);
  padding: 1px 4px;
  line-height: 1.3;
}
.insect-card__grade.rarity-1 { color: var(--gt-common); }
.insect-card__grade.rarity-2 { color: var(--gt-uncommon); }
.insect-card__grade.rarity-3 { color: var(--gt-rare); }
.insect-card__grade.rarity-4 { color: var(--gt-epic); }
.insect-card__grade.rarity-5 { color: var(--gt-legendary); }
.insect-card__grade.rarity-glitch { color: var(--gt-glitch); }

/* Reveal rarity label is small TEXT — use AA-safe --gt-* (KB §E), not vivid --g-*. */
.reveal__rarity.rarity-1      { color: var(--gt-common); }
.reveal__rarity.rarity-2      { color: var(--gt-uncommon); }
.reveal__rarity.rarity-3      { color: var(--gt-rare); }
.reveal__rarity.rarity-4      { color: var(--gt-epic); }
.reveal__rarity.rarity-5      { color: var(--gt-legendary); }
.reveal__rarity.rarity-glitch { color: var(--gt-glitch); }

/* Dup counter pill (kept from legacy markup) — quiet, bottom-right. */
.insect-card__count {
  position: absolute;
  bottom: 5px;
  right: 5px;
  z-index: 4;
  font-family: var(--mono);
  font-size: 9px;
  color: var(--ink-50);
  background: var(--bg);
  padding: 1px 4px;
}

/* Species name — mono near-black, centred under the tile. */
.insect-card__label {
  padding: 0 2px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Re-quiet the whole-card text colour the legacy `.rarity-N{color}` rule
   sets — in the light system only the grade TAG carries grade colour. */
.insect-card.rarity-1,
.insect-card.rarity-2,
.insect-card.rarity-3,
.insect-card.rarity-4,
.insect-card.rarity-5,
.insect-card.rarity-glitch {
  color: var(--ink);
}

/* --- Locked / unowned card (NEW variant) ---
   A blank silhouette tile with a faint "?" and an --ink-30 label. The
   Collection rewrite can render .insect-card.is-locked for unowned species. */
.insect-card.is-locked .insect-card__art,
.insect-card--locked .insect-card__art {
  background: var(--bg-elev);
  display: grid;
  place-items: center;
}
.insect-card.is-locked .insect-card__art::after,
.insect-card--locked .insect-card__art::after {
  content: "?";
  font-family: var(--mono);
  font-size: 22px;
  color: var(--ink-30);
}
.insect-card.is-locked .insect-card__label,
.insect-card--locked .insect-card__label {
  color: var(--ink-30);
}
.insect-card.is-locked .insect-card__grade,
.insect-card--locked .insect-card__grade {
  display: none;
}

/* --- "Keep logging..." placeholder (existing .keep-log-card) ---
   No grade tag, --ink-30 label, faint "?" rain. Recolour to the light system. */
.keep-log-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: var(--bg);
  min-width: 0;
  overflow: visible;
  pointer-events: none;
}
.keep-log-card__art {
  aspect-ratio: 1 / 1;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--bg-elev);
}
.keep-log-card__col {
  color: var(--ink-30);
}
.keep-log-card__label {
  padding: 0 2px;
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-30);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- Cocoon SLOT cells (COCOONS grid — operator mockup). Reuse the
   .insect-card art square so cocoons align with the ANOMALIES/SPECIES grid;
   per-state accent (anxious = red, ready = ink). ---- */
.cocoon-slot { cursor: default; }
.cocoon-slot[role="button"] { cursor: pointer; }
.cocoon-slot[role="button"]:hover .insect-card__art { border-color: var(--line-strong); }
.cocoon-slot[role="button"]:focus-visible { outline: none; }
.cocoon-slot[role="button"]:focus-visible .insect-card__art { box-shadow: inset 0 0 0 1px var(--ink); }
.cocoon-slot__art {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.1;
  white-space: pre;
  color: var(--ink-50);
}
/* Breathing flicker — two frames cross-fade (reuses the stage bug keyframes). */
.cocoon-slot__art--a { animation: stageBugFrameA 1800ms steps(1) infinite; }
.cocoon-slot__art--b { animation: stageBugFrameB 1800ms steps(1) infinite; }
.cocoon-slot__tag { color: var(--ink-50); }
/* The QUIET status IS the live countdown the user reads — needs AA (>=4.5),
   so it sits on --ink-70, not the dim --ink-50 label tone. */
.cocoon-slot__status { color: var(--ink-70); }
.cocoon-slot--anxious .insect-card__art { border-color: var(--danger); }
.cocoon-slot--anxious .cocoon-slot__art,
.cocoon-slot--anxious .cocoon-slot__tag,
.cocoon-slot--anxious .cocoon-slot__status { color: var(--danger); }
.cocoon-slot--ready .insect-card__art { border-color: var(--ink); }
.cocoon-slot--ready .cocoon-slot__art,
.cocoon-slot--ready .cocoon-slot__tag { color: var(--ink); }
.cocoon-slot--empty .insect-card__art { background: var(--bg-elev); }
.cocoon-slot--empty .cocoon-slot__status { color: var(--ink-30); }

/* ==========================================================================
   11. ABOUT / SETTINGS — legacy overlay guard (retired, §67)
   Settings is now a normal scrolling nav-tab (#/about). The is-more-open
   body class and popup-close bar are no longer used by the new screen.
   Keep this block only to neutralise any stale body.is-more-open that
   might fire from old cached code — it is otherwise inert.
   ========================================================================== */
body.is-more-open .topbar {
  z-index: 10;
}
/* Suppress the legacy pinned close bar — Settings no longer needs it. */
body.is-more-open .popup-close {
  display: none !important;
}
/* Restore normal screen-root padding (styles.css adds 60px for old close bar). */
body.is-more-open #screen-root {
  padding-bottom: calc(var(--nav-h) + max(16px, env(safe-area-inset-bottom)) + 24px);
}

/* ==========================================================================
   12. DEVICE HUB  (#/device) — v4 redesign (2026-06-07)
   Light instrument panel, no PCB decoration, no scroll. Single screen.
   Components: profile avatar · bytes strip · bait 2×2 · cocoons row · scanner.
   ========================================================================== */

/* Hub: flex column, fills the viewport between chrome, no scroll.
   min-height mirrors the screen-root available area so .device-scan (flex:1)
   actually grows. Safe-area clamp handles notched phones. */
.device-hub {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  position: relative;
  min-height: calc(
    100dvh
    - max(24px, env(safe-area-inset-top)) - var(--top-h) - 24px
    - var(--nav-h) - max(16px, env(safe-area-inset-bottom)) - 24px
  );
}

/* ---- LED status dot — Rams-clean, no glow, 8×8 circle. ---- */
.device-led {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--led-off);
  display: inline-block;
  flex: none;
}
.device-led[data-state="on"]      { background: var(--led-on);    border-color: transparent; }
.device-led[data-state="cooking"] { background: var(--led-on);    border-color: transparent; animation: ledBlink 1.6s steps(1, end) infinite; }
.device-led[data-state="ready"]   { background: var(--ok);        border-color: transparent; }
.device-led[data-state="alarm"]   { background: var(--led-alarm); border-color: transparent; animation: ledBlink 1.6s steps(1, end) infinite; }
@keyframes ledBlink {
  0%, 55%   { opacity: 1; }
  56%, 100% { opacity: 0.3; }
}
@media (prefers-reduced-motion: reduce) {
  .device-led[data-state="cooking"],
  .device-led[data-state="alarm"] { animation: none; }
}

/* ---- BYTES strip — flat horizontal bar, full width. ---- */
.device-bytes {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  background: var(--bg);
  min-height: 46px;
  cursor: pointer;
  text-align: left;
  padding: 0;
  font: inherit;
  color: inherit;
  width: 100%;
}
.device-bytes:active { background: var(--wash-active); }
.device-bytes:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); }

.device-bytes__lbl {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  border-right: 1px solid var(--line);
  flex: none;
}

.device-bytes__cell {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink);
  border-right: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
  padding: 0 6px;
}

.device-bytes__sz  { color: var(--ink-50); font-size: 11px; }
.device-bytes__count { color: var(--ink); font-size: 12px; }

.device-bytes__merge {
  display: flex;
  align-items: center;
  padding: 0 12px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-50);
  flex: none;
  white-space: nowrap;
}

/* ---- BAIT SPOTS 2×2 grid — compact tiles. ---- */
.device-bait-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-2);
}

.device-bait {
  position: relative;
  border: 1px solid var(--line);
  background: var(--bg);
  padding: 10px 12px;
  min-height: 74px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}
.device-bait:active { transform: scale(0.985); }
.device-bait:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); }

.device-bait__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.device-bait__ref {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--ink-30);
}

/* ASCII art for active byte — shown via _updateSpotVisual when spot is active. */
.device-bait__art {
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1.1;
  white-space: pre;
  color: var(--g-rare);
  margin: 0;
  letter-spacing: 0;
  text-align: center;
  align-self: center;
}

.device-bait__state {
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.3;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
}
/* Empty hint text (bait_empty key) */
.device-bait:not(.device-bait--active) .device-bait__state {
  font-size: 10px;
  color: var(--ink-30);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.device-bait__state--ready { color: var(--ok); }

/* Active bait — byte art appears, timer/ready text is ink-weight. */
.device-bait--active .device-bait__state {
  color: var(--ink);
  font-weight: 600;
}

/* ---- COCOONS summary row — single compact bar. ---- */
.device-coc {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--line);
  background: var(--bg);
  padding: 0 14px;
  min-height: 48px;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  width: 100%;
}
.device-coc:active { background: var(--wash-active); }
.device-coc:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); }

.device-coc__lbl {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}

.device-coc__dots {
  display: flex;
  gap: 6px;
}

.device-coc__count {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
}

.device-coc__chev {
  font-family: var(--mono);
  font-size: 15px;
  color: var(--ink-30);
}

/* ---- SIGNAL SCAN instrument — light panel, grows to fill remaining space. ---- */
.device-scan {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  background: var(--bg);
}

.device-scan__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px 0;
  flex: none;
}

.device-scan__title {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}

.device-scan__active {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-50);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Waveform area — anchor wrapping the SVG, grows to fill. */
.device-scan__view {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  text-decoration: none;
}

/* Resting-panel spectrum preview — same look as the scan minigame's .spectrum
   (mono block-chars, dim ink), centred in the view. Animated by
   mountSpectrumPreview (device.js); reads as a live signal even when static. */
.device-scan__spectrum {
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.1;
  letter-spacing: 1px;
  white-space: pre;
  color: var(--ink-30);
  margin: 0;
  text-align: center;
  pointer-events: none;
}

.device-scan__foot {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-top: 1px solid var(--line);
  flex: none;
}

/* Contained filled-black SCAN CTA — 44px touch target. */
.device-scan__cta {
  margin-left: auto;          /* swap: SCAN button sits RIGHT (photo link is left) */
  height: 44px;
  padding: 0 22px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--ink);
  color: #fff;
  border: 1px solid var(--ink);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  text-decoration: none;
}
.device-scan__cta:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink); }

/* Photo-scan text link — minimal; sits LEFT now (SCAN button has the auto margin). */
.device-scan__photo {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-50);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
  cursor: pointer;
  white-space: nowrap;
}
.device-scan__photo:focus-visible { outline: none; box-shadow: 0 0 0 1px var(--ink); }

/* ---- Popup overlay — bottom-sheet modal. ---- */
.device-popup-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.25);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 200;
  padding-bottom: env(safe-area-inset-bottom);
}

/* Sheet panel — sits at the bottom edge, no bottom border. */
.device-popup {
  width: 100%;
  max-width: 520px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-bottom: none;
  padding: 10px 16px 22px;
  display: flex;
  flex-direction: column;
  gap: 0;
  font-family: var(--mono);
}

/* v4 grab handle. */
.device-popup__grab {
  width: 36px;
  height: 4px;
  background: var(--line);
  margin: 2px auto 12px;
  flex: none;
}

/* v4 header row: title left, close right, single bottom hairline. */
.device-popup__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
  flex: none;
}

.device-popup__title {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink);
}

/* Close button in sheet header — compact, ghost style. */
.device-popup__close {
  height: 40px;
  min-height: 40px;
  padding: 0 14px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-50);
  cursor: pointer;
  flex: none;
}
.device-popup__close:active { background: var(--wash-active); }
.device-popup__close:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); }

/* Body text for simple state messages (occupied bait / no bytes). */
.device-popup__body {
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
  padding: 12px 0 4px;
}

/* Profile popup content. */
.device-popup__username {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink);
  padding: 12px 0 4px;
}

.device-popup__stats {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-bottom: 8px;
}

.device-popup__stat-row {
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-50);
}

.device-popup__debug {
  font-size: 10px;
  color: var(--ink-30);
  letter-spacing: 0.04em;
  padding: 4px 0;
}

/* ---- Byte popup internals ---- */

.device-popup__byte-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 0;
}

.device-byte-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  background: none;
  font-family: var(--mono);
  cursor: pointer;
  color: var(--ink);
  text-align: left;
  min-height: 44px;
}
.device-byte-row:hover  { background: var(--wash-hover); }
.device-byte-row:active { background: var(--wash-active); }
.device-byte-row:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); }

.device-byte-art {
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1.1;
  min-width: 20px;
  text-align: center;
  color: var(--g-rare);
  white-space: pre;
  margin: 0;
}

.device-byte-art--lg {
  font-size: 11px;
  min-width: 32px;
}

/* Animated glitch flicker for byte blocks. */
@keyframes byte-flicker {
  0%   { opacity: 1;    color: var(--g-rare); }
  45%  { opacity: 0.85; color: var(--g-epic); }
  55%  { opacity: 1;    color: var(--g-rare); }
  100% { opacity: 1;    color: var(--g-rare); }
}
.device-byte-art--anim {
  animation: byte-flicker 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .device-byte-art--anim { animation: none; }
}

.device-byte-info {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
}

.device-byte-timer {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-50);
  margin-left: auto;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}

/* ---- Merge bench popup. ---- */

.device-popup--bench {
  max-height: 80vh;
  overflow-y: auto;
}

.device-bench {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 0;
}

.device-bench__row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}

.device-bench__count {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ink);
}

.device-bench__need {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-30);
  letter-spacing: 0.04em;
  margin-left: auto;
}

.device-bench__merge-btn {
  margin-left: auto;
  width: auto;            /* contain it — override .btn--main width:100% (R1c): a
                             right-aligned button, not a heavy full-width black bar */
  font-size: 10px;
  padding: 0 16px;
  letter-spacing: 0.06em;
  min-height: 44px;       /* keep the 44px touch target (§62) */
}

/* ---- §57 Stage 4: Cocoon popup slots — v4 uniform row heights, no per-slot borders. ---- */

.device-popup--cocoons {
  max-height: 80vh;
  overflow-y: auto;
}

.device-cocoon-slots {
  display: flex;
  flex-direction: column;
  /* No gap — uniform height handled by each slot's fixed height. */
}

/* One slot row — fixed 60px height so button/no-button rows are IDENTICAL height.
   No per-slot bottom borders (only the header divider above separates content). */
.device-cocoon-slot {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 60px;
  font-family: var(--mono);
  font-size: 11px;
}

.device-cocoon-slot__label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
  width: 52px;
  flex: none;
}

.device-cocoon-slot__status {
  flex: 1;
  min-width: 0;                 /* allow shrink so the action button never overflows */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
}

.device-cocoon-slot--anxious .device-cocoon-slot__status--anxious {
  color: var(--gt-legendary);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 10px;
}

.device-cocoon-slot--empty .device-cocoon-slot__status {
  color: var(--ink-30);
}

.device-cocoon-slot__status--timer {
  color: var(--ink-50);
}

/* OPEN button — primary, 36px height, auto-pushes right. Sits inside 60px row
   without expanding it (height is explicitly smaller than the row). */
.device-cocoon-slot__open-btn {
  margin-left: auto;
  flex: 0 0 auto;          /* contained — never full-width (no legacy .btn) */
  width: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  min-height: 36px;
  padding: 0 18px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: var(--ink);
  color: #fff;
  border: 1px solid var(--ink);
  cursor: pointer;
}
.device-cocoon-slot__open-btn:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink); }

/* CALM button — standalone (NOT .btn): ghost, red text, contained 36px. */
.device-cocoon-slot__calm-btn {
  margin-left: auto;
  flex: 0 0 auto;
  width: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  min-height: 36px;
  padding: 0 18px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background: transparent;
  color: var(--gt-legendary);
  border: 1px solid var(--line);
  cursor: pointer;
}
.device-cocoon-slot__calm-btn:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); }

/* ---- Scan byte drop line (used by scan.js result display). ---- */
.scan-result__byte {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--g-rare);
  letter-spacing: 0.06em;
  margin-top: 4px;
  font-weight: 600;
}

/* ---- Device toast — brief non-modal feedback. ---- */
.device-toast {
  position: fixed;
  bottom: calc(var(--nav-h) + 16px + env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  background: var(--ink);
  color: #fff;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  padding: 8px 16px;
  white-space: nowrap;
  z-index: 300;
  opacity: 1;
  transition: opacity 0.4s ease;
}

.device-toast--out {
  opacity: 0;
}

/* ==========================================================================
   12b. DEVICE SCAN TAKEOVER — .device-scan-takeover container + hub state
   When the inline scan is active: [data-scan-active] on .device-hub hides the
   resting tiles; .device-scan-takeover expands to fill the content column.
   ========================================================================== */

/* Takeover host — absolute fill over the hub content area (between chrome
   and bottom nav). Uses the same #screen-root padding zone so it slots in
   without touching the fixed chrome. */
.device-scan-takeover {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  z-index: 2;
}
.device-scan-takeover[hidden] { display: none; }

/* Hide resting hub tiles when the takeover is active.
   .device-scan stays visible (it becomes part of the scan-live header visually,
   but the takeover covers the scan section too — hiding all tiles is correct). */
.device-hub[data-scan-active] > .device-bytes,
.device-hub[data-scan-active] > .device-bait-grid,
.device-hub[data-scan-active] > .device-coc,
.device-hub[data-scan-active] > .device-scan {
  visibility: hidden;
  pointer-events: none;
}

/* ==========================================================================
   13. SIGNAL SCAN TAKEOVER  (inline inside #/device, 2026-06-07)
   Replaces the old #/scan route CSS. All classes follow the .sl__ prefix
   (scan-live) from the approved mockup design-scan-inline-2026-06-07.html.
   Visual law: same v4 device language — light/flat ink-on-paper, plain 90deg
   (no chamfer/radius), tokens only, mono. No shadow/gradient/3D except the
   sanctioned denoise/bloom motion and the existing .device-byte-art--anim.
   ========================================================================== */

/* Outer shell: fills the .device-scan-takeover host with a flex column.
   Sits between fixed topbar + bottom-nav — no scroll, EVER. */
.scan-live {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  background: var(--bg);
  overflow: hidden;
  position: relative;
}

/* ---- phase-based show/hide (set by JS as live.dataset.phase="scan"|"reveal") ----
   SCAN phase  : spectrum stage + lock bar visible (flex:1); reveal + actions hidden.
   REVEAL phase: meta band + spectrum stage + lock bar hidden; reveal (flex:1) + actions visible.
   Both phases keep the header (sl__head) and mode row (sl__modes) visible.
   Using [data-phase] selectors lets CSS be the single source of layout truth,
   avoiding the bug where JS forgot to hide .sl__meta on outcome. */

/* SCAN phase — default state when data-phase is absent or "scan". */
.scan-live[data-phase="scan"] .sl__reveal,
.scan-live[data-phase="scan"] .sl__actions {
  display: none;
}

/* REVEAL phase — stage / lock / meta disappear; reveal + actions fill the space. */
.scan-live[data-phase="reveal"] .sl__meta,
.scan-live[data-phase="reveal"] .sl__stage,
.scan-live[data-phase="reveal"] .sl__lock {
  display: none;
}
.scan-live[data-phase="reveal"] .sl__reveal {
  display: flex;
  flex: 1;
  min-height: 0;
}
.scan-live[data-phase="reveal"] .sl__actions {
  display: flex;
}

/* ---- header row: SIGNAL SCAN title + ACTIVE pill + X close ---- */
.sl__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 8px 11px 14px;
  border-bottom: 1px solid var(--line);
  flex: none;
}
.sl__title {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}
.sl__active {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--ink-50);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
}
/* X close button — 44x44 touch target, ghost. */
.sl__close {
  margin-left: auto;
  width: 44px;
  height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--ink-50);
  font-family: var(--mono);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  flex: none;
}
.sl__close:hover { color: var(--ink); background: var(--wash-hover); }
.sl__close:active { background: var(--wash-active); }
.sl__close:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); color: var(--ink); }

/* ---- mode selector: segmented STABLE / UNSTABLE ---- */
.sl__modes {
  display: flex;
  flex: none;
  border-bottom: 1px solid var(--line);
}
.sl__mode {
  flex: 1;
  min-height: 44px;
  padding: 0 10px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: transparent;
  color: var(--ink-50);
  border: 0;
  border-right: 1px solid var(--line);
  cursor: pointer;
  transition: background var(--dur-1) ease, color var(--dur-1) ease;
}
.sl__mode:last-child { border-right: 0; }
.sl__mode[aria-pressed="true"] { background: var(--ink); color: #fff; }
.sl__mode:hover:not([aria-pressed="true"]) { background: var(--wash-hover); color: var(--ink); }
.sl__mode:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--ink); }

/* ---- meta band: FREQ/SIG readout + DROP odds text + monochrome ramp ---- */
.sl__meta {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  flex: none;
}
.sl__readout {
  display: flex;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink-50);
}
.sl__val {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.sl__odds-types {
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1.2;
  letter-spacing: 0.03em;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
}
.sl__odds-types .lab {
  color: var(--ink);
  letter-spacing: 0.06em;
}
/* Neutral monochrome drop ramp (op-2: bug/cocoon/fail are not rarities). */
.sl__ramp {
  display: flex;
  height: 5px;
  border: 1px solid var(--line);
  overflow: hidden;
}

/* ---- spectrum stage: the tune area, flex:1 owns slack ---- */
.sl__stage {
  position: relative;
  flex: 1;
  min-height: 0;
  background: var(--bg-elev);
  overflow: hidden;
  cursor: ew-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
  -webkit-user-select: none;
}
.sl__stage:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--ink);
}
/* ASCII spectrum bars — white-space:pre is load-bearing (column alignment). */
.spectrum {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.08;
  letter-spacing: 1px;
  white-space: pre;
  color: var(--ink-30);
  margin: 0;
  text-align: center;
  pointer-events: none;
}
.sl__stage.is-inband .spectrum { color: var(--gt-uncommon); }

/* Needle — vertical hairline bar. */
.needle {
  position: absolute;
  top: 14px;
  bottom: 34px;
  width: 2px;
  background: var(--ink);
  transform: translateX(-1px);
  opacity: 0.7;
  pointer-events: none;
}
.sl__stage.is-inband .needle { background: var(--gt-uncommon); opacity: 1; }

/* Caret triangle below the needle. */
.caret {
  position: absolute;
  bottom: 20px;
  color: var(--ink-50);
  transform: translateX(-50%);
  font-size: 10px;
  font-family: var(--mono);
  pointer-events: none;
}
.sl__stage.is-inband .caret { color: var(--gt-uncommon); }

/* Bottom-left hint text inside the stage. */
.s-hint {
  position: absolute;
  left: 14px;
  bottom: 6px;
  color: var(--ink-30);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  pointer-events: none;
}

/* ---- lock bar row ---- */
.sl__lock {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-top: 1px solid var(--line);
  flex: none;
}
.sl__lock-lbl {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-50);
  flex: none;
  text-transform: uppercase;
}
.sl__lock-bar {
  flex: 1;
  height: 8px;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  overflow: hidden;
}
.sl__lock-fill {
  height: 100%;
  width: 0%;
  background: var(--ink-30);
  transition: width 60ms linear;
}
/* In-band: lock fill turns green. JS toggles .is-active on .sl__lock. */
.sl__lock.is-active .sl__lock-fill { background: var(--gt-uncommon); }

.sl__lock-pct {
  min-width: 40px;
  text-align: right;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* ---- reveal panel: centred art well + result + outcome footer ----
   Replaces stage+lock after 100% lock (JS sets reveal.hidden=false,
   stage.hidden=true, lockRow.hidden=true). flex:1 so it owns the slack. */
.sl__reveal {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 20px 14px;
  text-align: center;
  background: var(--bg-elev);
}
/* ASCII art — white-space:pre. Grade colour applied inline by JS. */
.reveal-art {
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.18;
  white-space: pre;
  margin: 0;
  color: var(--ink);
  text-align: center;
}
.reveal-art--fail { color: var(--ink-30); }
.reveal-art--noise { color: var(--ink-30); }

/* Cool-render bloom host — renderVariantInto() needs explicit dimensions. */
.scan-art-stage:not([hidden]) {
  width: 168px;
  height: 150px;
  margin: 0 auto;
  font-size: 14px;
}

/* Species name label. */
.reveal-name {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}
/* Grade/meta line. */
.reveal-meta {
  font-family: var(--ui);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
}
.reveal-meta .grade { font-weight: 500; }

/* Byte drop line (duplicate catch). */
.reveal-byte {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--gt-rare);
}

/* Cocoon timer chip (state D). */
.coc-timer {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--ui);
  font-size: 12px;
  letter-spacing: 0;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.coc-tend {
  font-family: var(--ui);
  font-size: 11px;
  line-height: 1.45;
  letter-spacing: 0;
  color: var(--ink-50);
  max-width: 260px;
}

/* ---- actions footer — pinned at the bottom of the takeover ---- */
.sl__actions {
  display: flex;
  gap: var(--s-2);
  padding: 12px 14px;
  border-top: 1px solid var(--line);
  flex: none;
}
/* Base action button — ghost. */
.act {
  flex: 1;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 16px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-50);
}
.act:hover  { background: var(--wash-hover);  color: var(--ink); }
.act:active { background: var(--wash-active); color: var(--ink); }
.act:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink); }
/* Primary variant — filled black. */
.act--primary {
  background: var(--ink);
  color: #fff;
  border-color: var(--ink);
}
.act--primary:hover  { background: var(--ink); opacity: 0.9; color: #fff; }
.act--primary:active { opacity: 0.82; }

/* scan-result__byte: legacy class for dupe byte line (used in older scan CSS).
   Kept as an alias for .reveal-byte so any leftover reference resolves. */
.scan-result__byte {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--g-rare);
  letter-spacing: 0.06em;
  margin-top: 4px;
  font-weight: 600;
}

/* Reduced-motion guard on drift / denoise animation: the browser will still
   run the rAF loop but Math.random() glitch chars are suppressed via CSS
   (the loop checks this with matchMedia in future; for now the CSS disables
   transition-based effects). */
@media (prefers-reduced-motion: reduce) {
  .sl__lock-fill { transition: none; }
  .spectrum, .needle, .caret { transition: none; }
}

/* ==========================================================================
   14. ONBOARDING  (#/onboarding) — 4-page slideshow, v4 redesign.
   Layout: HARD height cap (no scroll); topbar (shared); .ob-topbar
   (breadcrumb dots + skip); .ob-track-wrap (flex:1, min-height:0 forces
   shrink); .ob-track > 4× .ob-slide (art 168px capped + text centered +
   margin-top:auto footer spacer); .ob-footer pinned by the slide column.
   body.is-onboarding hides the bottom nav (handled by main.js).
   NO-SCROLL GUARANTEE: .onboarding-wrap height = 100dvh − topbar (hard cap,
   not min-height). .ob-track-wrap min-height:0 lets the flex child shrink.
   Art box hard-capped at 168px so RU slide3 (longest body, ~4 lines at
   14px/1.55) still fits: topbar(54) + art(168) + gap(12) + title(28) +
   gap(8) + body(~87) + slide-padding(28) + footer(64) ≈ 449px < 786px OK.
   ========================================================================== */

/* Outer wrapper — hard height = viewport minus the shared topbar.
   overflow:hidden is load-bearing (clips the off-screen slides). */
/* Full-viewport takeover. The phase9 `#screen-root` id rule (§3) sets a big
   top/bottom padding to clear the fixed chrome; its ID specificity (1,0,0)
   beats the legacy `body.is-onboarding .screen-root` (0,2,0), so we must zero
   it at id-level here, and hide the global topbar — onboarding owns the screen
   (it has its own crumbs+skip bar). This is what kills the 152px doc-scroll. */
body.is-onboarding #screen-root { padding: 0; }
body.is-onboarding .topbar { display: none; }

.onboarding-wrap {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  background: var(--bg);
  overflow: hidden;
}

/* ---- Slideshow top bar ---- */
/* breadcrumb dots (left) + SKIP text button (right).
   Padding tightened to preserve vertical budget. SKIP is a text button
   with min-height:44px so the 10px label gets the full tap target. */
.ob-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 21px 6px;
  flex-shrink: 0;
}

/* 4 breadcrumb dots — active = --ink filled, rest = --ink-30 outline. */
.ob-crumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;           /* tap-area padding around the dots */
}
.ob-crumb {
  width: 8px;
  height: 8px;
  border-radius: 0;           /* sharp — no pill */
  border: 1px solid var(--ink-30);
  background: transparent;
  transition: background var(--dur-2) ease, border-color var(--dur-2) ease;
}
.ob-crumb.is-active {
  background: var(--ink);
  border-color: var(--ink);
}

/* SKIP — quiet text button, mono, uppercase.
   min-height:44px satisfies HIG without visible bulk. */
.ob-skip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 4px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-50);
  background: none;
  border: none;
  cursor: pointer;
  transition: color var(--dur-1) ease;
}
.ob-skip:hover {
  color: var(--ink);
}
.ob-skip:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink);
  color: var(--ink);
}

/* ---- Slide track (horizontal carousel) ---- */
/* min-height:0 on .ob-track-wrap is critical: without it a flex child
   cannot shrink below its content height, causing the overflow. */
.ob-track-wrap {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.ob-track {
  display: flex;
  width: 100%;
  height: 100%;
  transition: transform 280ms cubic-bezier(0.42, 0, 0.28, 1);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .ob-track { transition: none; }
}

/* ---- Individual slide ---- */
/* Each slide is a flex column: art (fixed top) + text (centered middle)
   + footer spacer (margin-top:auto pin). Padding tight to maximise
   vertical room for text. */
.ob-slide {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;   /* center the art+text group as a unit */
  gap: 28px;                 /* air between art and the text block */
  padding: 16px 28px 0;
  box-sizing: border-box;
  overflow: hidden;
}

/* ---- Art well — capped at 168px to guarantee text fits at all heights.
   Square box, hairline border, pre centred inside. ---- */
.ob-art {
  width: 168px;
  height: 168px;
  flex-shrink: 0;
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.ob-art pre {
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1.25;
  white-space: pre;
  color: var(--ink-30);
  margin: 0;
  letter-spacing: 0;
  text-align: center;
}

/* ---- Text block: Inter prose (the ONE exception to the mono-only rule).
   Centred column, max-width constrains line length to ~42 chars at 14px
   (comfortable for prose; RU ~55 chars/line wraps to 3–4 lines).
   margin-top:auto on .ob-text pushes it into the vertical centre of the
   remaining space between art and footer. ---- */
.ob-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
  max-width: 300px;
}
.ob-title {
  font-family: var(--ui);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
  margin: 0;
  letter-spacing: -0.01em;
}
.ob-body {
  font-family: var(--ui);
  font-size: 14px;
  font-weight: 400;
  line-height: 1.55;
  color: var(--ink);
  margin: 0;
}

/* ---- CTA footer — pinned at the bottom of the slide column.
   margin-top:auto on .ob-footer pushes it to the bottom edge.
   Safe-area-aware bottom padding for iPhone notch/home-indicator. ---- */
.ob-footer {
  flex-shrink: 0;
  width: 100%;
  padding: 0 20px;
  padding-bottom: max(28px, env(safe-area-inset-bottom));
  padding-top: 16px;
  margin-top: auto;
}
.ob-footer .btn {
  height: var(--h-md);   /* 40px visual + tap area from parent column */
  min-height: 44px;       /* HIG hard floor */
}

/* Hide bottom nav during onboarding. */
body.is-onboarding .bottom-nav {
  display: none;
}
/* The global white fades are for scrolling content under the chrome; the
   onboarding takeover has neither, and the bottom fade was covering the CTA. */
body.is-onboarding .app-fade {
  display: none;
}

/* ==========================================================================
   15. REVEAL SCREENS  (#/reveal, #/cocoon-reveal)
   v4 redesign — light/flat ink-on-paper, plain 90°, tokens only, mono.
   Matches the scan state-C payoff pattern (design-scan-inline-2026-06-07.html):
   art → name (grade TEXT colour) → GRADE · meta line → action footer.
   Layout: flex column, full content-area height, NO scroll, art centered.
   ========================================================================== */

/* ---- Outer wrapper: fills the space between topbar + nav, no scroll. ---- */
.reveal {
  display: flex;
  flex-direction: column;
  align-items: center;
  height: calc(
    100dvh
    - max(24px, env(safe-area-inset-top)) - var(--top-h) - 24px
    - var(--nav-h) - max(16px, env(safe-area-inset-bottom)) - 24px
  );
  /* screen-root adds its own padding (15px sides), so we just add vertical. */
  padding-top: var(--s-5);
  overflow: hidden;
  background: var(--bg);
  width: 100%;
  box-sizing: border-box;
}

/* ---- Art well: faint paper tint, fixed dimensions for renderVariantInto.
   No dashed outline in v4 — structure is hairlines + whitespace only.   ---- */
.reveal__frame {
  width: 168px;
  height: 150px;
  background: var(--bg-elev);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  overflow: hidden;
  /* R9 halo fix: solid --bg behind the glyph silhouette (matches Collection
     .insect-card__art hosting), white in light / black in dark. */
  --stage-halo: var(--bg);
}

/* Inside the frame: placeholder <pre.ascii-bug> is dim before reveal. */
.reveal__frame .ascii-bug {
  font-size: 13px;
  color: var(--ink-30);
}

/* Rarity border overrides — v4 keeps hairlines, drops dashes + thick borders. */
.reveal__frame.rarity-1,
.reveal__frame.rarity-2,
.reveal__frame.rarity-3,
.reveal__frame.rarity-4,
.reveal__frame.rarity-5,
.reveal__frame.rarity-glitch {
  border-style: solid;
  border-width: 1px;
  border-color: var(--line);
}

/* On reveal the art fills the frame; keep variant-stage dimensions. */
.reveal__frame.variant-stage {
  width: 168px;
  height: 150px;
  padding: 0;
}

/* ---- Phase label: scanning… / composing… / resolving… ---- */
.reveal__label {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
  margin-top: var(--s-3);
  line-height: 1.2;
}

/* ---- Species name: mono 500 UPPER, grade TEXT colour. ---- */
.reveal__name {
  margin: var(--s-3) 0 0;
  font-family: var(--mono);
  font-size: 16px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);          /* default; overridden per rarity below */
  text-align: center;
  line-height: 1.2;
}
/* Grade TEXT colour on the name — AA-safe --gt-* (≥4.5:1 on white). */
.reveal__name.rarity-1      { color: var(--gt-common); }
.reveal__name.rarity-2      { color: var(--gt-uncommon); }
.reveal__name.rarity-3      { color: var(--gt-rare); }
.reveal__name.rarity-4      { color: var(--gt-epic); }
.reveal__name.rarity-5      { color: var(--gt-legendary); }
.reveal__name.rarity-glitch { color: var(--gt-glitch); }

/* ---- GRADE · meta line — .reveal__grade-line wraps rarity + variant. ---- */
.reveal__grade-line {
  display: flex;
  align-items: center;
  gap: 0;
  margin-top: var(--s-1);
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1.4;
  letter-spacing: 0.04em;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
  flex-wrap: wrap;
  justify-content: center;
  text-align: center;
}
/* Grade portion (RARE, EPIC, etc.) — 500 weight, grade text colour. */
.reveal__rarity {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
}
.reveal__rarity.rarity-1      { color: var(--gt-common); }
.reveal__rarity.rarity-2      { color: var(--gt-uncommon); }
.reveal__rarity.rarity-3      { color: var(--gt-rare); }
.reveal__rarity.rarity-4      { color: var(--gt-epic); }
.reveal__rarity.rarity-5      { color: var(--gt-legendary); }
.reveal__rarity.rarity-glitch { color: var(--gt-glitch); }
/* Dot separator between grade and variant label. */
/* (variant label removed — Phase 9 has one canonical variant) */
/* Variant/outcome meta — normal weight, dim. */
.reveal__variant {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: none;
  color: var(--ink-50);
}

/* ---- Flavor text (observation line). ---- */
.reveal__flavor {
  font-family: var(--ui);
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-50);
  text-align: center;
  max-width: 280px;
  margin: var(--s-2) 0 0;
}

/* ---- Tag-match block — signals panel, compact in v4. ---- */
.reveal__tags {
  margin-top: var(--s-2);
  width: 100%;
  max-width: 280px;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.reveal__tags-header {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink-50);
  text-align: center;
}
.reveal__tags-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  justify-content: center;
}
.tag-chip {
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-30);
  border: 1px solid var(--line);
  padding: 2px 6px;
  line-height: 1.3;
}
.tag-chip.is-matched {
  color: var(--ink);
  border-color: var(--line-strong);
}

/* ---- CTA footer — mirrors .sl__actions: pinned to the bottom. ---- */
.reveal__cta-row {
  display: flex;
  gap: var(--s-2);
  padding: var(--s-3) 0 0;
  width: 100%;
  margin-top: auto;    /* pushes footer to the bottom of the flex column */
  border-top: 1px solid var(--line);
  flex: none;
}
/* Override .btn sizing to match .act inside the CTA footer. */
.reveal__cta-row .btn {
  flex: 1;
  min-height: 44px;
  height: auto;
  width: auto;          /* cancel full-width from .btn--primary */
  padding: 0 16px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-50);
  text-decoration: none;
  border-radius: var(--radius);
}
.reveal__cta-row .btn:hover  { background: var(--wash-hover);  color: var(--ink); }
.reveal__cta-row .btn:active { background: var(--wash-active); color: var(--ink); }
.reveal__cta-row .btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink);
}
.reveal__cta-row .btn--primary {
  background: var(--ink);
  color: #fff;
  border-color: var(--ink);
}
.reveal__cta-row .btn--primary:hover  { background: var(--ink); opacity: 0.9; color: #fff; }
.reveal__cta-row .btn--primary:active { opacity: 0.82; }
.reveal__cta-row .btn--secondary {
  background: transparent;
  color: var(--ink-50);
  border-color: var(--line);
}

/* ---- FAIL card: keep presence (§63) — art dim, label full --ink. ---- */
/* The fail-card classes are added to the existing .reveal__frame + label. */
.reveal__frame.fail-card__frame {
  background: var(--bg-elev);
  border: 1px solid var(--line);
  color: var(--ink-30);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.18;
  white-space: pre;
  text-align: center;
}
.fail-card__label {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);           /* full ink — a miss must register (§63) */
  text-align: center;
  margin-top: var(--s-2);
}

/* ---- Empty / error states inside reveal context. ---- */
.reveal .empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  text-align: center;
  padding: var(--s-5) 0;
  color: var(--ink-50);
  /* prose body = --ui (the UPPER label below stays mono). */
  font-family: var(--ui);
  font-size: 13px;
}
.reveal .empty-state strong {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}

/* Reduced-motion: suppress the fade-in on the art well. */
@media (prefers-reduced-motion: reduce) {
  .reveal__frame { transition: none; }
  .reveal__name  { transition: none; }
}

/* ==========================================================================
   16. DETAIL SCREEN  (#/detail/:id) — v4 spec sheet  2026-06-07
   A scrollable content screen: art → name + grade → stats → facts →
   notes → wiki → signals → self-gift action. Uses the global white
   top/bottom .app-fade so content fades under the fixed chrome.
   Visual law: same v4 language as Collection + Device — light/flat
   ink-on-paper, plain 90deg (no chamfer), hairlines, mono, tokens only.
   ========================================================================== */

/* ---- Outer content container ---- */
.detail-screen {
  display: flex;
  flex-direction: column;
  gap: 0;
  /* R9 relayout: padding comes from #screen-root (§3), which already clears
     the fixed topbar + nav. The screen previously re-added that clearance
     here, double-padding the top by ~100px and floating the hero too low. */
}

/* Back affordance — quiet mono link at the top of the deep detail route
   (reached from a collection card; bottom-nav is the only other way out). */
.detail-back {
  align-self: flex-start;
  margin-bottom: var(--s-3);
  padding: var(--s-2) 0;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  background: none;
  border: 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-50);
  cursor: pointer;
}
.detail-back:hover  { color: var(--ink); }
.detail-back:active { opacity: 0.6; }

/* ---- Hero art well ---- */
/* Hairline-bordered frame, faint --bg-elev fill (the habitat bg tint),
   centred content; mirrors .reveal__frame sizing (168x150). */
.detail-hero-frame {
  width: 100%;
  aspect-ratio: 4 / 3;
  max-height: 220px;
  position: relative;
  border: 1px solid var(--line);
  background: var(--bg-elev);
  overflow: hidden;
  margin-bottom: var(--s-5);
  /* R9 halo fix: host the stage EXACTLY like Collection .insect-card__art —
     NO custom .stage__bug position override. The base .stage rules (styles.css)
     centre the layers via absolute + translate(-50%), size-agnostically. With
     that hosting a SOLID halo no longer hides the glyph, so the fill behind the
     symbols is --bg (white in light, black in dark). */
  --stage-halo: var(--bg);
}
.detail-hero-frame .ascii-art,
.detail-hero-frame pre {
  white-space: pre;
}

/* font-size for the hero art (larger than card, same as .detail__hero.variant-stage) */
.detail-hero-frame.variant-stage {
  font-size: 17px;
  font-weight: 400;
}

/* ---- Identity block: name + rarity + stats ---- */
.detail-identity {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--line);
  margin-bottom: 0;
}

.detail-name-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
}

/* Species name — mono, prominent, grade-coloured via --gt-* (overridden
   by the rarityCls the JS adds: .rarity-N applies --gt-* via the rules below). */
.detail-name {
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--ink);
  margin: 0;
  flex: 1;
  min-width: 0;
}

/* Grade TEXT colour on the name — AA-safe --gt-* (>=4.5:1 on white). */
.detail-name.rarity-1      { color: var(--gt-common); }
.detail-name.rarity-2      { color: var(--gt-uncommon); }
.detail-name.rarity-3      { color: var(--gt-rare); }
.detail-name.rarity-4      { color: var(--gt-epic); }
.detail-name.rarity-5      { color: var(--gt-legendary); }
.detail-name.rarity-glitch { color: var(--gt-glitch); }

/* Rarity badge — mirrors .insect-card__grade: 9px mono UPPER, paper bg,
   no filled colour (uses --gt-* text). */
.detail-rarity-tag {
  flex-shrink: 0;
  font-family: var(--mono);
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
  background: var(--bg);
  padding: 2px 6px;
  border: 1px solid var(--line);
  line-height: 1.4;
  white-space: nowrap;
}
.detail-rarity-tag.rarity-1      { color: var(--gt-common);    border-color: var(--gt-common); }
.detail-rarity-tag.rarity-2      { color: var(--gt-uncommon);  border-color: var(--gt-uncommon); }
.detail-rarity-tag.rarity-3      { color: var(--gt-rare);      border-color: var(--gt-rare); }
.detail-rarity-tag.rarity-4      { color: var(--gt-epic);      border-color: var(--gt-epic); }
.detail-rarity-tag.rarity-5      { color: var(--gt-legendary); border-color: var(--gt-legendary); }
.detail-rarity-tag.rarity-glitch { color: var(--gt-glitch);    border-color: var(--gt-glitch); }

/* Stats row — .data-chip-row + .data-chip already in styles.css;
   override to v4 quiet palette (no bg-fill, hairline-bordered, tabular). */
.detail-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}
.detail-stats .data-chip {
  background: transparent;
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  color: var(--ink-50);
  padding: 2px 8px;
  min-height: var(--h-sm);
  font-variant-numeric: tabular-nums;
}

/* ---- Section header — mirrors .collection__section-header exactly ---- */
.detail-section-hd {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  padding: var(--s-4) 0 var(--s-2);
  border-bottom: 1px solid var(--line);
  margin-bottom: 0;
}
.detail-section-hd__title {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}

/* ---- FACTS: <dl> styled as iOS-style grouped list-rows ----
   Label (dt) left in --ink-50; value (dd) below in --ink; hairline
   between rows (dt+dd pair). */
.detail-facts {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
}
.detail-facts dt {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
  margin: 0;
  padding-top: var(--s-3);
}
.detail-facts dd {
  font-family: var(--ui);   /* readable prose → Inter (mono is accent-only) */
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink);
  margin: var(--s-1) 0 0;
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
}
/* Remove last hairline separator */
.detail-facts dd:last-of-type {
  border-bottom: none;
  padding-bottom: var(--s-2);
}

/* ---- Field notes ---- */
.detail-notes-body {
  font-family: var(--ui);   /* readable prose → Inter */
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
  margin: 0;
  padding: var(--s-3) 0 0;
}
.detail-notes-body + .detail-notes-body {
  padding-top: var(--s-2);
}

/* ---- Wikipedia link — mono underlined text, NOT a filled button ---- */
.detail-wiki-link {
  display: inline-block;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.03em;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  padding: var(--s-3) 0;
  transition: color var(--dur-1) var(--ease);
}
.detail-wiki-link:hover  { color: var(--ink-50); }
.detail-wiki-link:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink);
}

/* ---- Tag chips row — reuses .tag-chip from phase9.css §15 ---- */
.detail-tags-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  padding: var(--s-3) 0 var(--s-2);
}

/* ---- Not-found / empty state ---- */
.detail-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-12) var(--s-4);
  text-align: center;
}
.detail-empty__label {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
}
.detail-empty__body {
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 400;
  color: var(--ink-50);
  line-height: 1.5;
}
/* Back button inside empty state — ghost style, 44px height */
.detail-empty .btn {
  min-height: 44px;
  padding: 0 var(--s-5);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--dur-1) var(--ease);
}
.detail-empty .btn:hover  { background: var(--wash-hover); }
.detail-empty .btn:active { background: var(--wash-active); }
.detail-empty .btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--ink);
}

/* ---- Spacing section wrapper (notes / wiki / signals / gift) ---- */
.detail-section {
  display: flex;
  flex-direction: column;
}

/* ==========================================================================
   17. SETTINGS SCREEN  (#/about) — v4 redesign (2026-06-07, §67)
   A normal scrolling nav-tab. Matches the v4 sibling language (Collection,
   Device): light/flat ink-on-paper, plain 90° (border-radius:0), hairlines,
   mono, tokens only.
   Layout: .settings-screen scroll wrapper → sections (.settings-section)
   separated by gap. Within sections: .collection__section-header headers,
   .settings-list-row list rows, .s-seg segmented controls, palette grid.

   Cascade note: legacy .about-* / .setting-row / .seg-switch / .palette-picker
   rules live in styles.css. The new .settings-* / .s-seg classes don't collide.
   The old .popup-close guard (§11) suppresses the legacy pinned close bar.
   ========================================================================== */

/* ---- Outer wrapper — scrolling settings column ---- */
.settings-screen {
  display: flex;
  flex-direction: column;
  gap: var(--s-7);          /* 28px loose between sections — iOS Settings rhythm */
}

/* ---- Header block ---- */
.settings-head {
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--line);
}
.settings-head__title {
  font-family: var(--ui);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--ink);
  margin: 0 0 var(--s-1);
}
.settings-head__sub {
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-50);
  margin: 0;
}

/* ---- Section wrapper ---- */
.settings-section {
  display: flex;
  flex-direction: column;
  gap: 0;
}
/* Narrow gap between .collection__section-header and the first row. */
.settings-section > .collection__section-header {
  margin-bottom: 0;
}

/* ---- Section body text (Contact / Account descriptions) ---- */
.settings-section__body {
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.55;
  /* Readable multi-line prose -> --ink-70 for AA (--ink-50 is ~3.9:1, fails). */
  color: var(--ink-70);
  margin: 0;
  padding: var(--s-3) 0;
  max-width: 60ch;
}

/* ---- List row (KB LIST ROW pattern) ----
   Flex row: label left (--ink), control right; hairline bottom divider.
   ≥44px height on tappable rows; 8px vertical padding on display rows. */
.settings-list-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 44px;
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--line);
}
/* Last row within a section: no hairline below (section gap provides air). */
.settings-section > .settings-list-row:last-child,
.settings-section > .settings-palette-block:last-child {
  border-bottom: none;
}

/* Label — left side, --ink full weight, mono 12px. */
.settings-list-row__label {
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ink);
  flex: 1;
  min-width: 0;
}
/* Hint subtext (onboarding row second line) */
.settings-list-row__hint {
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 400;
  color: var(--ink-70);   /* readable hint sentence -> AA (was --ink-50 ~3.9:1) */
  line-height: 1.4;
  flex: 1;
  min-width: 0;
  /* When label + hint both exist, stack them — use column flex on parent
     for the intro row (which has both label and hint). */
}

/* The intro row uses a stacked text group + trailing arrow. */
.settings-list-row--link {
  text-decoration: none;
  color: inherit;
  flex-wrap: nowrap;
  cursor: pointer;
  transition: background var(--dur-1) var(--ease);
  padding: var(--s-3) 0;
}
.settings-list-row--link:hover {
  background: var(--wash-hover);
}
.settings-list-row--link:active {
  background: var(--wash-active);
}
.settings-list-row--link:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--ink);
}
/* Stack label + hint vertically inside the link row. */
.settings-list-row--link .settings-list-row__label,
.settings-list-row--link .settings-list-row__hint {
  display: block;
  flex: none;
}
.settings-list-row--link {
  flex-wrap: wrap;
  align-items: center;
}
/* Group label + hint into a flex-1 column; arrow stays trailing. */
.settings-list-row--link .settings-list-row__label {
  width: 100%;
  flex: 0 0 auto;
}
.settings-list-row--link .settings-list-row__hint {
  width: 100%;
  flex: 0 0 auto;
}
/* Wrap reflow: use a text group approach — put label + hint in a div. */
/* Simpler: make the link a grid. */
.settings-list-row--link {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  align-items: center;
  column-gap: var(--s-3);
  row-gap: 2px;
}
.settings-list-row--link .settings-list-row__label {
  grid-column: 1;
  grid-row: 1;
}
.settings-list-row--link .settings-list-row__hint {
  grid-column: 1;
  grid-row: 2;
}
.settings-list-row__trail {
  grid-column: 2;
  grid-row: 1 / span 2;
  font-family: var(--mono);
  font-size: 14px;
  color: var(--ink-30);
  align-self: center;
}

/* ---- Segmented control (.s-seg) ----
   Reuses the .sl__modes molecule from §13 (STABLE/UNSTABLE) — same
   visual contract: hairline outer box, mono buttons divided by hairlines,
   active = --ink fill + white label, ≥44px height, plain 90°.
   Added: [aria-pressed] hooks for a11y. */
.s-seg {
  display: inline-flex;
  border: 1px solid var(--line);
  flex: none;
}
.s-seg__btn {
  flex: 1;
  min-height: 44px;
  min-width: 44px;
  padding: 0 var(--s-3);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: transparent;
  color: var(--ink-50);
  border: 0;
  border-right: 1px solid var(--line);
  cursor: pointer;
  transition: background var(--dur-1) ease, color var(--dur-1) ease;
  white-space: nowrap;
}
.s-seg__btn:last-child {
  border-right: 0;
}
.s-seg__btn[aria-pressed="true"] {
  background: var(--ink);
  color: #fff;
}
.s-seg__btn:hover:not([aria-pressed="true"]) {
  background: var(--wash-hover);
  color: var(--ink);
}
.s-seg__btn:active {
  opacity: 0.85;
}
.s-seg__btn:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--ink);
}

/* ---- Palette block (stacked: label above, grid below) ---- */
.settings-palette-block {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--s-3) 0 var(--s-4);
  border-bottom: 1px solid var(--line);
}
.settings-palette-block .settings-list-row__label {
  flex: none;
}

/* Palette grid — 4 col, 2 rows (8 palettes). */
.settings-palette-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-2);
}
@media (max-width: 380px) {
  .settings-palette-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Palette item button — hairline tile, swatches row + name below. */
.settings-palette-item {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--s-1);
  padding: var(--s-2);
  border: 1px solid var(--line);
  background: var(--bg);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease);
  text-align: left;
  font: inherit;
  color: inherit;
}
.settings-palette-item:hover {
  border-color: var(--line-strong);
}
.settings-palette-item:active {
  background: var(--wash-active);
}
.settings-palette-item:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--ink);
}
/* Active palette: full --ink border + hairline inset ring. */
.settings-palette-item.is-active,
.settings-palette-item[aria-pressed="true"] {
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}

/* 6 colour swatches in a row — flex:1 each, fixed height. */
.settings-palette-swatches {
  display: flex;
  gap: 2px;
  width: 100%;
}
.settings-palette-swatch {
  flex: 1;
  height: 10px;
  /* background set inline (DATA hex, not a token) */
}

/* Palette name — mono 10px, dim; full ink when active. */
.settings-palette-name {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink-50);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.settings-palette-item.is-active .settings-palette-name,
.settings-palette-item[aria-pressed="true"] .settings-palette-name {
  color: var(--ink);
}

/* ---- Action row (contact links, data buttons) ----
   Two ghost buttons side by side. */
.settings-action-row {
  display: flex;
  gap: var(--s-2);
  padding-top: var(--s-1);
  flex-wrap: wrap;
}
.settings-action-row .btn {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  height: 44px;
}

/* ---- Destructive button row (Logout + Delete) ----
   Separated from the data row by a gap; both sit above the debug id.
   R9 §17 4-button system: Logout uses the SECONDARY type (.btn--ghost),
   Delete uses the DANGER type (.btn--danger) — no bespoke per-button styles. */
.settings-danger-row {
  display: flex;
  gap: var(--s-2);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  flex-wrap: wrap;
}
.settings-danger-row .btn {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  height: 44px;
}
/* Delete = QUIET destructive: red text + red hairline, no loud fill — keeps the
   whole settings page on one calm ink/paper plane while still reading clearly as
   dangerous. 3 classes out-specify the legacy .btn.btn--danger fill AND its
   body.theme-dark colour override, so it stays a red-outline ghost in both themes. */
.settings-danger-row .btn.btn--danger {
  background: transparent;
  color: var(--danger);
  border: 1px solid var(--danger);
}
/* Dark theme: the legacy `body.theme-dark .btn.btn--danger { color:#0a0a0a }`
   (specificity 0,3,1) was forcing the ghost label to near-black -> invisible on
   the dark page. Out-specify it (0,4,1) so the label stays red in dark too. */
body.theme-dark .settings-danger-row .btn.btn--danger { color: var(--danger); }
.settings-danger-row .btn.btn--danger:hover  { background: var(--wash-hover); }
.settings-danger-row .btn.btn--danger:active { background: var(--wash-active); }

/* ---- Debug id line ---- */
.settings-debug-id {
  margin-top: var(--s-3);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink-30);
  user-select: all;
  word-break: break-all;
  line-height: 1.5;
}

/* ---- Override legacy styles.css .setting-row / .palette-picker rules when the
   new settings screen is mounted. (.seg-switch is now also restyled in §18.) ---- */

/* ==========================================================================
   18. CAPTURE SCREEN  (#/generate) — photo + audio capture, v4 restyle.
   Legacy markup (js/screens/generate.js: .capture / .capture-card / .seg-switch)
   brought into the phase9 light system: hairline cards, big Inter title, mono
   accents, Inter prose, 4-button system, segmented modes. phase9 loads after
   styles.css so these win on equal specificity.
   ========================================================================== */
.screen-head {
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s-6);
}
.screen-title {
  font-family: var(--ui);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--ink);
  margin: 0 0 var(--s-1);
}
.screen-sub {
  font-family: var(--ui);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-50);
  margin: 0;
}

.capture {
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}

.capture-card {
  border: 1px solid var(--line);
  background: var(--bg);
  padding: var(--s-4);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.capture-card__head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--line);
}
.capture-card__icon {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink);
}
.capture-card__title {
  font-family: var(--ui);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.capture-card__status {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-50);
}
.capture-card__stage {
  min-height: 132px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  padding: var(--s-3);
  position: relative;
  overflow: hidden;
}
.capture-card__placeholder {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-30);
  text-align: center;
}
.capture-card__timer {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-50);
  font-variant-numeric: tabular-nums;
}
.capture-card__actions {
  display: flex;
  gap: var(--s-2);
}
.capture-card__actions .btn { flex: 1; }
.capture-card__ready-row {
  display: flex;
  gap: var(--s-3);
  align-items: center;
  width: 100%;
}
.capture-card__preview {
  max-width: 120px;
  max-height: 120px;
  display: block;
  border: 1px solid var(--line);
}
.capture-card__video {
  width: 100%;
  max-height: 220px;
  background: #000;
  display: block;
}
.capture-card__live-actions {
  display: flex;
  gap: var(--s-2);
  margin-top: var(--s-2);
  width: 100%;
}
.capture-card__live-actions .btn { flex: 1; }
.waveform {
  width: 100%;
  height: 48px;
  display: block;
}

/* ASCII signal readouts (interpretation, not a clean preview). */
.signal-readout {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  align-items: center;
}
.signal-readout__ascii {
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1.1;
  letter-spacing: 1px;
  color: var(--ink-50);
  white-space: pre;
  margin: 0;
}
.signal-readout__inline,
.signal-merge__diag {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--ink-50);
  white-space: pre-line;
  text-align: center;
}
.signal-merge {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding: var(--s-2) 0;
}
.signal-merge__hint {
  font-family: var(--ui);
  font-size: 12px;
  line-height: 1.4;
  color: var(--ink-50);
}
.signal-merge__hint--good { color: var(--ok); }

/* Mode segmented (STABLE / UNSTABLE) — same look as the Settings .s-seg. */
.mode-switch {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.seg-switch {
  display: flex;
  border: 1px solid var(--line);
}
.seg-switch__btn {
  flex: 1;
  min-height: 44px;
  padding: 0 var(--s-3);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: transparent;
  color: var(--ink-50);
  border: 0;
  border-right: 1px solid var(--line);
  cursor: pointer;
  transition: background var(--dur-1) ease, color var(--dur-1) ease;
}
.seg-switch__btn:last-child { border-right: 0; }
.seg-switch__btn.is-active { background: var(--ink); color: #fff; }
.seg-switch__btn:hover:not(.is-active) { background: var(--wash-hover); color: var(--ink); }
.mode-switch__hint {
  font-family: var(--ui);
  font-size: 12px;
  line-height: 1.4;
  color: var(--ink-50);
  margin: 0;
}

/* Generate button — primary filled, full width. */
#generate-btn {
  width: 100%;
  background: var(--ink);
  color: #fff;
  border: 1px solid var(--ink);
}
#generate-btn:disabled {
  background: var(--bg);
  color: var(--ink-30);
  border-color: var(--line);
  cursor: not-allowed;
}
