/* ============================================================
   site.css — the PUBLIC surface: landing, sign-in, 404.

   Apple's design language, web platform. Translucent chrome the
   content scrolls under; solid cards; a type scale whose tracking
   is size-specific; motion that belongs to springs, not to CSS.

   Nothing here animates anything a finger can grab. Keyframes
   appear exactly twice, both on ambient decoration that no gesture
   ever touches. Everything grabbable is driven from site.js.

   ponytail: the token block mirrors index.html's inline copy rather
   than being shared with it. Extracting the dashboard's <style> into
   this file is the real fix — do it when the palette next changes.
   ============================================================ */
:root {
  --bg:        #0c0d0f;
  --bg-tint:   #16181d;
  --surface:   #16181c;
  --surface-2: #1e2127;
  --surface-3: #262a31;
  --chrome:    rgba(18, 20, 24, .62);
  --chrome-hi: rgba(255, 255, 255, .08);
  --line:      rgba(255, 255, 255, .09);
  --line-2:    rgba(255, 255, 255, .16);
  --text:      #e9ecf0;
  --text-2:    #99a0aa;
  --text-3:    #878e99;
  --accent:      #f2a71b;
  --accent-2:    #ffc255;
  --accent-ink:  #131519;
  --accent-soft: rgba(242, 167, 27, .15);
  --sig-low:      #f4565c;
  --sig-low-soft: rgba(244, 86, 92, .14);
  --sig-high:      #2fc07d;
  --sig-high-soft: rgba(47, 192, 125, .14);
  /* Bigger surfaces read as thicker material: deeper shadow, stronger blur. */
  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
  --shadow-2: 0 10px 30px -8px rgba(0,0,0,.55), 0 2px 6px rgba(0,0,0,.3);
  --shadow-3: 0 32px 70px -16px rgba(0,0,0,.72), 0 6px 16px rgba(0,0,0,.4);
  --r-ctl: 11px; --r-card: 20px; --r-panel: 28px;
}
:root[data-theme="light"] {
  --bg: #f4f5f6; --bg-tint: #e9ebee;
  --surface: #ffffff; --surface-2: #f1f3f5; --surface-3: #e7eaee;
  --chrome: rgba(252, 253, 254, .68); --chrome-hi: rgba(255,255,255,.9);
  --line: rgba(16, 20, 26, .10); --line-2: rgba(16, 20, 26, .20);
  --text: #14171b; --text-2: #5c646e; --text-3: #686f79;
  --accent: #a56800; --accent-2: #8a5600; --accent-ink: #ffffff;
  --accent-soft: rgba(165, 104, 0, .12);
  --sig-low: #c3243c; --sig-low-soft: rgba(195, 36, 60, .10);
  --sig-high: #0d7a4e; --sig-high-soft: rgba(13, 122, 78, .10);
  --shadow-1: 0 1px 2px rgba(16,20,26,.07), 0 0 0 1px rgba(16,20,26,.04);
  --shadow-2: 0 10px 30px -8px rgba(16,20,26,.16), 0 2px 6px rgba(16,20,26,.07);
  --shadow-3: 0 32px 70px -16px rgba(16,20,26,.22), 0 6px 16px rgba(16,20,26,.09);
}
@media (prefers-color-scheme: light) {
  /* The system preference is a DEFAULT. An explicit dark choice outranks it. */
  :root:not([data-theme="dark"]) {
    --bg: #f4f5f6; --bg-tint: #e9ebee;
    --surface: #ffffff; --surface-2: #f1f3f5; --surface-3: #e7eaee;
    --chrome: rgba(252, 253, 254, .68); --chrome-hi: rgba(255,255,255,.9);
    --line: rgba(16, 20, 26, .10); --line-2: rgba(16, 20, 26, .20);
    --text: #14171b; --text-2: #5c646e; --text-3: #686f79;
    --accent: #a56800; --accent-2: #8a5600; --accent-ink: #ffffff;
    --accent-soft: rgba(165, 104, 0, .12);
    --sig-low: #c3243c; --sig-low-soft: rgba(195, 36, 60, .10);
    --sig-high: #0d7a4e; --sig-high-soft: rgba(13, 122, 78, .10);
    --shadow-1: 0 1px 2px rgba(16,20,26,.07), 0 0 0 1px rgba(16,20,26,.04);
    --shadow-2: 0 10px 30px -8px rgba(16,20,26,.16), 0 2px 6px rgba(16,20,26,.07);
    --shadow-3: 0 32px 70px -16px rgba(16,20,26,.22), 0 6px 16px rgba(16,20,26,.09);
  }
}

/* ============================================================
   TYPOGRAPHY
   Tracking is size-specific — negative on display, ~0 on body.
   Leading tracks size inversely. Hierarchy is weight + size +
   leading as a SET. Everything in rem, so the user's text-size
   setting scales the layout with the words.
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
html {
  font: 100%/1.5 system-ui, -apple-system, 'SF Pro Text', 'Segoe UI', sans-serif;
  font-optical-sizing: auto;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
[id] { scroll-margin-top: 5.5rem; }   /* the floating bar must not swallow the heading */
body {
  min-height: 100dvh;
  color: var(--text);
  background: var(--bg);
  /* A faint ground wash, so the translucent bar has something to blur. */
  background-image:
    radial-gradient(1200px 680px at 50% -14%, var(--bg-tint), transparent 64%),
    radial-gradient(900px 520px at 108% 6%, var(--bg-tint), transparent 58%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  overflow-wrap: break-word;
}
img, svg { display: block; max-width: 100%; }
button, input, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

.t-hero  { font-size: clamp(2.6rem, 1.55rem + 4.6vw, 5rem);      line-height: 1.02; letter-spacing: -.036em; font-weight: 660; }
.t-title { font-size: clamp(1.75rem, 1.28rem + 2.1vw, 2.75rem);  line-height: 1.08; letter-spacing: -.028em; font-weight: 640; }
.t-head  { font-size: clamp(1.0625rem, 1rem + .3vw, 1.25rem);    line-height: 1.28; letter-spacing: -.014em; font-weight: 620; }
.t-lede  { font-size: clamp(1.0625rem, .98rem + .55vw, 1.375rem); line-height: 1.45; letter-spacing: -.009em; font-weight: 400; color: var(--text-2); }
.t-body  { font-size: 1.0625rem; line-height: 1.5;  letter-spacing: -.002em; }
.t-cap   { font-size: .8125rem;  line-height: 1.5;  letter-spacing: 0; color: var(--text-2); }
.t-label { font-size: .6875rem;  line-height: 1.4;  letter-spacing: .085em; font-weight: 620; text-transform: uppercase; color: var(--text-3); }
.num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; letter-spacing: -.03em;
}
.dim { color: var(--text-2); }

/* ============================================================
   LAYOUT — one rhythm, in rem.
   ============================================================ */
.wrap { width: 100%; max-width: 68rem; margin-inline: auto; padding-inline: clamp(1.25rem, 5vw, 2.5rem); }
.wrap-narrow { max-width: 44rem; }
.section { padding-block: clamp(4rem, 10vw, 8rem); }
.stack > * + * { margin-top: var(--gap, 1rem); }
.center { text-align: center; margin-inline: auto; }
.row-actions { display: flex; flex-wrap: wrap; gap: .65rem; }
.center .row-actions { justify-content: center; }

/* ============================================================
   MATERIALS
   The bar is the only translucent layer on the page — a light
   translucent surface never sits on another one. Cards are solid.
   ============================================================ */
.bar {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  background: var(--chrome);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
}
/* Scroll edge effect, not a permanent divider: the hairline and the soft
   gradient below it fade in only once content is actually underneath.
   --scrolled is written from the real scroll position, never from a class. */
.bar { --scrolled: 0; }
.bar::before {
  content: ""; position: absolute; inset: auto 0 0 0; height: 1px;
  background: var(--line); opacity: var(--scrolled);
}
.bar::after {
  content: ""; position: absolute; inset: 100% 0 auto 0; height: 22px;
  background: linear-gradient(to bottom, var(--bg), transparent);
  opacity: calc(var(--scrolled) * .8); pointer-events: none;
}
.bar-inner { display: flex; align-items: center; gap: .35rem; height: 3.25rem; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-1);
}
.card-raised { box-shadow: var(--shadow-2); }
.panel { border-radius: var(--r-panel); box-shadow: var(--shadow-2); }

/* ============================================================
   CONTROLS — the press lands on POINTERDOWN, written by site.js.
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
  border-radius: 999px; padding: .5rem 1rem;
  font-size: .9375rem; font-weight: 560; letter-spacing: -.011em;
  min-height: 38px; white-space: nowrap;
}
.btn-primary { background: var(--accent); color: var(--accent-ink); font-weight: 600; }
.btn-primary:hover { background: var(--accent-2); }
.btn-quiet { background: var(--surface-2); color: var(--text); border: 1px solid var(--line); }
.btn-quiet:hover { background: var(--surface-3); }
.btn-ghost { color: var(--text-2); }
.btn-ghost:hover { color: var(--text); }
.btn-lg { padding: .78rem 1.5rem; font-size: 1.0625rem; min-height: 50px; }
.btn-block { width: 100%; }
.icon-btn {
  position: relative; width: 34px; height: 34px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; color: var(--text-2);
}
.icon-btn:hover { background: var(--surface-3); color: var(--text); }
.icon-btn::after { content: ""; position: absolute; inset: -5px; }  /* invisible hit padding */
@media (pointer: coarse) {
  .icon-btn { width: 44px; height: 44px; }
  .btn { min-height: 44px; }
  /* The dots stay 7px visually, but the targets tile edge to edge instead of
     overlapping: 7px dot + 32px gap = a 39px pitch, so a thumb cannot land
     between two of them or hit the wrong one. */
  .deck-dots { gap: 2rem; }
  .deck-dot::after { inset: -16px; }
}

/* The pressed state is a transform so it composites, and it is applied by JS
   on pointerdown. No transition: on release the spring is not involved and the
   snap-back must be immediate, exactly like a native highlight. */
.is-pressed { transform: scale(.97); }

/* ============================================================
   FORM FIELDS — ported from index.html:175-185 rather than forked
   a third time (see the ponytail note at the top of this file).

   Two changes from the dashboard copy: 1rem base instead of
   .8125rem, and 44px unconditionally, which makes the dashboard's
   `@media (pointer: coarse)` override unnecessary here.

   The 1rem is LOAD-BEARING: iOS Safari zooms the viewport on focus
   for any input under 16px, and the sign-in field is the first
   thing a phone user ever touches.
   ============================================================ */
.field {
  width: 100%; padding: .7rem .85rem; min-height: 44px;
  font-size: 1rem; font-weight: 500; line-height: 1.4;
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
}
.field::placeholder { color: var(--text-3); }
.field:hover { border-color: var(--line-2); }
.field[aria-invalid="true"] { border-color: var(--sig-low); }

/* The reveal button is a flex SIBLING inside the bordered box, not an
   absolutely-positioned child centred with translateY(-50%): the global
   pointerdown handler writes `.is-pressed { transform: scale(.97) }` onto
   every button, which would clobber a centring transform and make the
   control jump on press. */
.field-wrap {
  display: flex; align-items: center; gap: .25rem; padding-right: .4rem;
  background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
}
.field-wrap:hover { border-color: var(--line-2); }
.field-wrap:focus-within { outline: 2px solid var(--accent); outline-offset: 3px; }
.field-wrap .field { border: 0; background: none; flex: 1; min-width: 0; }
.field-wrap .field:focus-visible { outline: none; }   /* the wrapper owns the ring */
.reveal {
  flex: none; min-height: 36px; padding: 0 .6rem; border-radius: 8px;
  font-size: .8125rem; font-weight: 560; color: var(--text-2);
}
.reveal:hover { color: var(--text); background: var(--surface-3); }

.form-row { display: block; margin-top: 1.1rem; }
.form-row > .t-label { display: block; margin-bottom: .4rem; }
.form-err { display: block; margin-top: .4rem; color: var(--sig-low); }
.form-hint { display: block; margin-top: .4rem; }

.chip {
  display: inline-flex; align-items: center; gap: .35rem; border-radius: 999px;
  padding: .25rem .7rem; font-size: .75rem; font-weight: 560; letter-spacing: -.004em;
  border: 1px solid transparent; white-space: nowrap;
}
.chip .dot { width: 5px; height: 5px; border-radius: 999px; background: currentColor; flex: none; }
.chip-neutral { background: var(--surface-2); color: var(--text-2); border-color: var(--line); }
.chip-accent  { background: var(--accent-soft); color: var(--accent); }
.chip-low     { background: var(--sig-low-soft);  color: var(--sig-low); }

/* No border-radius here: it reshaped every pill (999px) the instant a keyboard
   reached it. The outline already follows the element's own radius. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 200; }
.skip-link:focus { left: 12px; top: 12px; background: var(--surface); padding: .5rem .85rem; border-radius: var(--r-ctl); border: 1px solid var(--line-2); }

.brand { display: inline-flex; align-items: center; gap: .55rem; font-weight: 600; letter-spacing: -.014em; }
@media (pointer: coarse) { .brand { min-height: 44px; } }
.brand-mark {
  width: 28px; height: 28px; flex: none; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--accent-ink);
}
.brand-mark svg { width: 17px; height: 17px; }
.brand-mark-lg { width: 52px; height: 52px; border-radius: 16px; }
.brand-mark-lg svg { width: 29px; height: 29px; }

/* ============================================================
   MATERIALIZE — enter by blur + scale + opacity together, so the
   surface reads as material arriving rather than a picture fading.
   The transform is written by a spring; only the blur and the
   opacity ride a transition, and neither is grabbable.
   ============================================================ */
/* Hidden only once JS is confirmed present: with no script the content is simply
   there, rather than a page of invisible text waiting for an observer. */
.rise { transition: opacity .5s ease-out, filter .5s ease-out; will-change: transform, opacity; }
.js .rise { opacity: 0; filter: blur(6px); }
.js .rise.in { opacity: 1; filter: blur(0); }

/* ============================================================
   THE DECK — five condition types, thrown with a finger.
   Laws 2-6 live here: 1:1 tracking from the grab point, velocity
   handed off at release, momentum projected to find the landing
   card, rubber-band at the ends, and every frame interruptible
   because the position is a spring read from its presentation value.
   ============================================================ */
.deck {
  position: relative;
  /* pan-y, not none: a vertical swipe must still scroll the page. The
     horizontal axis is ours from the first move. */
  touch-action: pan-y;
  cursor: grab;
  overflow: hidden;
  padding-block: 1.25rem;
  /* The edges of the strip fade rather than being clipped by a hard line. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent);
}
.deck.grabbing { cursor: grabbing; user-select: none; }
.deck-track { display: flex; gap: var(--deck-gap, 1rem); will-change: transform; }
.deck-card {
  flex: none; width: var(--deck-w, 20rem);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); box-shadow: var(--shadow-2);
  padding: 1.15rem 1.25rem;
  /* Scale and opacity are written per-frame from the same spring that moves
     the track, so the focused card is always the one under the finger. */
  will-change: transform, opacity;
}
.deck-head { display: flex; align-items: center; gap: .55rem; margin-bottom: .9rem; }
.deck-sym {
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: .7rem; font-weight: 600;
  letter-spacing: .03em; padding: .22rem .45rem; border-radius: 6px;
  background: var(--surface-2); color: var(--text-2);
}
.deck-val { font-size: clamp(2rem, 1.6rem + 1.4vw, 2.6rem); font-weight: 500; line-height: 1; }
.deck-dots { display: flex; justify-content: center; gap: .45rem; margin-top: 1.1rem; }
.deck-dot {
  /* padding:0 is load-bearing — a button's default horizontal padding exceeds
     7px, so with border-box sizing the dot cannot shrink to the width we set
     and renders as a lozenge. */
  width: 7px; height: 7px; padding: 0; border-radius: 999px; background: var(--line-2);
  position: relative; flex: none;
}
.deck-dot::after { content: ""; position: absolute; inset: -8px; }   /* hit padding */
.deck-dot[aria-current="true"] { background: var(--accent); }

/* ============================================================
   SEGMENTED CONTROL — the selection pill is a spring, so a second
   press mid-flight retargets from where the pill actually is.
   ============================================================ */
.seg {
  position: relative; display: inline-flex; padding: 3px; gap: 2px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 999px;
}
.seg-pill {
  position: absolute; top: 3px; bottom: 3px; left: 0; width: 0;
  border-radius: 999px; background: var(--surface); box-shadow: var(--shadow-1);
  will-change: transform, width; pointer-events: none;
}
.seg button {
  position: relative; z-index: 1; padding: .4rem .95rem; border-radius: 999px;
  font-size: .875rem; font-weight: 560; letter-spacing: -.008em; color: var(--text-2);
  min-height: 34px;
}
.seg button[aria-pressed="true"] { color: var(--text); }
@media (pointer: coarse) { .seg button { min-height: 40px; } }

/* ============================================================
   THE LAG BILL — an itemised receipt, because the total is the
   claim and the middle line is the one nobody else admits to.
   Values are tabular so the column does not jitter as the
   timeframe changes.
   ============================================================ */
.lag-bill {
  max-width: 22rem; margin: 1.75rem auto 0; text-align: left;
  font-size: .8125rem; line-height: 1.5; color: var(--text-2);
}
.lag-bill > div {
  display: flex; align-items: baseline; gap: .75rem;
  padding: .55rem 0; border-bottom: 1px solid var(--line);
}
.lag-bill dt { flex: 1; min-width: 0; }
.lag-bill dd { margin: 0; color: var(--text); font-weight: 500; }

/* ============================================================
   STEPS + FEATURES
   ============================================================ */
.steps { display: grid; gap: clamp(1.5rem, 4vw, 2.5rem); }
@media (min-width: 780px) { .steps { grid-template-columns: repeat(4, 1fr); } }
.step-n {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .75rem; font-weight: 600; color: var(--accent);
  padding-bottom: .55rem; margin-bottom: .55rem; border-bottom: 1px solid var(--line);
}
.features { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr)); }
.feature { padding: 1.4rem 1.5rem; }
.feature-icon {
  width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center;
  background: var(--surface-2); color: var(--text-2); margin-bottom: .9rem;
}
.feature-icon svg { width: 18px; height: 18px; }

/* ============================================================
   AUTH — the card shared by /signin, /signup and /reset. It was
   duplicated in three <style> blocks; one copy here so a change to
   the sign-in card cannot land on two of the three pages.
   ============================================================ */
.auth-card { width: min(25rem, 100%); padding: clamp(1.75rem, 6vw, 2.5rem); }
/* Google's mark is fixed white-on-brand in both themes — that is the point of a
   provider button, and a themed one stops being recognisable. */
.google-btn {
  background: #fff; color: #1f1f1f; min-height: 50px; font-size: 1rem; font-weight: 560;
  border: 1px solid rgba(0,0,0,.12); box-shadow: var(--shadow-1);
}
.google-btn:hover { background: #f1f3f5; }
.auth-step { display: flex; align-items: flex-start; gap: .7rem; }
.auth-n {
  flex: none; width: 22px; height: 22px; border-radius: 999px; display: grid; place-items: center;
  font-size: .6875rem; font-weight: 600; background: var(--accent-soft); color: var(--accent); margin-top: .1rem;
}
.well-note {
  margin-top: 1.5rem; padding: .9rem 1rem;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-ctl);
}
/* A labelled rule between the two ways in. aria-hidden on the word in markup:
   it is punctuation, and a screen reader announcing "or" between two named
   buttons adds nothing. */
.or-rule { display: flex; align-items: center; gap: .75rem; margin: 1.25rem 0; }
.or-rule::before, .or-rule::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.or-rule > span { font-size: .75rem; font-weight: 560; color: var(--text-3); }

/* Password length, shown as it is typed rather than as a rejection on submit.
   Width is the only channel: a colour-only meter says nothing to the ~8% of men
   with a colour-vision deficiency, and nothing at all to a screen reader — the
   text beside it carries the message. */
.pw-bar { height: 4px; border-radius: 999px; background: var(--surface-3); overflow: hidden; margin-top: .5rem; }
.pw-bar > i { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--sig-low); }
.pw-bar[data-ok="true"] > i { background: var(--sig-high); }

/* ============================================================
   THE WIZARD — /welcome. Panels sit side by side on one track and
   only the track moves, so the whole stepper is ONE spring on ONE
   value and can be reversed mid-flight.
   ============================================================ */
.wz { width: min(32rem, 100%); padding: clamp(1.5rem, 5vw, 2.25rem); overflow: hidden; }
.wz-dots { display: flex; justify-content: center; gap: .45rem; margin-bottom: 1.5rem; }
.wz-dot { width: 7px; height: 7px; border-radius: 999px; background: var(--line-2); }
.wz-dot[data-current="true"] { background: var(--accent); }
.wz-view { overflow: hidden; }
.wz-track { display: flex; align-items: flex-start; will-change: transform; }
.wz-panel { flex: none; width: 100%; }
.wz-panel[inert] { visibility: hidden; }   /* out of the a11y tree AND the tab order */

/* ============================================================
   CONDITION PICKER — the same schema the dashboard renders, in this
   page's vocabulary.

   ponytail: the dashboard has its own renderer (index.html,
   buildParams/paramField). Two skins, ONE schema — conditions.schema()
   over /api/meta decides which params exist, their gates and their
   defaults, so an added indicator lights up both without an edit.
   Extract a shared module when a THIRD surface needs it.
   ============================================================ */
.param-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .7rem; margin-top: .9rem; }
.param-grid > .col-2 { grid-column: 1 / -1; }
.param-group {
  grid-column: 1 / -1; margin-top: .5rem;
  font-size: .6875rem; line-height: 1.4; letter-spacing: .085em;
  font-weight: 620; text-transform: uppercase; color: var(--text-3);
}
.param > span { display: block; margin-bottom: .3rem; font-size: .75rem; font-weight: 560; color: var(--text-2); }
.param .field { font-size: .9375rem; min-height: 40px; padding: .45rem .6rem; }
/* A gated field is disabled in the DOM, and the dimming is driven off that
   attribute rather than a parallel class — the two cannot disagree, and a
   verification pass reads the same fact the browser does. */
.param:has(> .field:disabled) > span, .param:has(> .field:disabled) { color: var(--text-3); }
.param > .field:disabled { opacity: .5; cursor: not-allowed; }

.switch { display: flex; align-items: center; gap: .6rem; cursor: pointer; }
.switch .track {
  flex: none; width: 38px; height: 22px; border-radius: 999px; position: relative;
  background: var(--surface-3); border: 1px solid var(--line);
}
.switch .track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--text-2);
  transition: transform .18s ease-out, background-color .18s ease-out;
}
.switch input:checked + .track { background: var(--accent-soft); border-color: var(--accent); }
.switch input:checked + .track::after { transform: translateX(16px); background: var(--accent); }
.switch input:focus-visible + .track { outline: 2px solid var(--accent); outline-offset: 3px; }
.switch input:disabled + .track { opacity: .5; }
.switch-label { font-size: .8125rem; font-weight: 540; }

/* ============================================================
   RADAR — ambient decoration on the 404. Nothing grabs it, so a
   keyframe is the honest tool here.
   ============================================================ */
.radar {
  position: relative; width: 132px; height: 132px; border-radius: 50%; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--shadow-2);
}
.radar::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-radial-gradient(circle at 50% 50%, transparent 0 21px, var(--line) 21px 22px);
}
.radar::after {
  content: ""; position: absolute; inset: 0;
  background: conic-gradient(from 0deg, var(--accent-soft) 0deg, transparent 52deg);
  animation: sweep 3.6s linear infinite;
}
@keyframes sweep { to { transform: rotate(360deg); } }
.pulse { animation: pulse 2.4s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* ============================================================
   THE THREE reduced-* SIGNALS, HANDLED INDEPENDENTLY.
   Reduced motion is a non-vestibular EQUIVALENT, not the absence
   of feedback: the press still reads, the deck still moves — the
   spring is replaced by a jump, and the travel by a cross-fade.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .is-pressed { transform: none; background: var(--surface-3); }
  .rise { transition: opacity .2s ease; filter: none; }
  .rise.in { filter: none; }
  .radar::after, .pulse { animation: none; }
  .deck { -webkit-mask-image: none; mask-image: none; }
  /* The switch keeps its state change; it just stops sliding to get there. */
  .switch .track::after { transition: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .bar { background: var(--surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .bar::before { opacity: 1; }
}
/* Specificity, not order, decides — and bare `:root` (0,1,0) LOSES to the
   system-light block's `:root:not([data-theme="dark"])` (0,2,0) above. For the
   commonest state of all — OS in light mode, no explicit theme stored — that
   made this whole block a no-op. Every selector here now carries the same
   weight as the theme block it has to beat, and the resolved-light cases come
   last so they win the ties. */
@media (prefers-contrast: more) {
  :root:not([data-theme="light"]) { --line: rgba(255,255,255,.34); --line-2: rgba(255,255,255,.5); --text-2: #c8ced6; --text-3: #aab1ba; }
  :root[data-theme="light"] { --line: rgba(16,20,26,.34); --line-2: rgba(16,20,26,.5); --text-2: #333a42; --text-3: #454c55; }
  .bar { background: var(--surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .card, .deck-card, .seg, .field, .field-wrap, .switch .track { border-color: var(--line-2); }
  .bar::before { opacity: 1; }
}
@media (prefers-contrast: more) and (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) { --line: rgba(16,20,26,.34); --line-2: rgba(16,20,26,.5); --text-2: #333a42; --text-3: #454c55; }
}
