/* ============================================================
   Deja Clue City — conversational thread
   Design A's shell, extended to cover every segment type.
   No framework. Skin = CSS custom properties, swapped per city.
   ============================================================ */

:root {
  --paper:#0b1014; --paper-2:#111a21; --paper-3:#16242e;
  /* Field-test 2026-08: the old greys (#8fa9b8/#5b7788) washed out in direct
     sun — secondary text must stay legible on a phone at noon, so both are
     lifted well past AA. --instr is a dedicated instruction color, distinct
     from body grey and from the cyan interactive accent. */
  --ink:#e6f1f7; --ink-2:#c9dbe6; --ink-3:#9fb6c4;
  --instr:#e3c98e;
  --accent:#3fd0e0; --accent-2:#1b7f8c; --rule:#1e3542;
  --ok:#54d6a0; --warn:#f0a04b; --bad:#e8564b;
  /* ---- five type roles. A city may point any of them anywhere. ----
     Splitting them is what lets prose move to a serif without dragging the
     terminal chrome with it: --display was previously doing two unrelated
     jobs (big titles AND small caps labels), so neither could move alone.
       body     narrative prose — the thing people actually read
       display  titles, names, headings
       label    eyebrows, tags, buttons, stamps (caps + letterspaced)
       mono     codes, data values, inputs, prices — anything aligned or typed
       quote    text quoted from the physical world (inscriptions)
     Declare the families in skin.fonts.families or they will not load. */
  --body-font:'Newsreader',Georgia,serif;
  --display-font:'Newsreader',Georgia,serif;
  --label-font:'Space Mono',ui-monospace,monospace;
  --mono-font:'Space Mono',ui-monospace,monospace;
  --quote-font:'Newsreader',Georgia,serif;
  --radius:3px;
  --pad:16px; --thread-max:620px;
  /* particle tint; per-effect fallbacks live on the ambient rules */
  --ambient-color:;
  /* height of the sticky Ashoka bar — cards offset by this when scrolled to,
     so auto-scroll doesn't tuck the top of a card under it */
  --topbar-h:92px;
}

/* Light-base default set. `skin.base: "light"` previously did nothing — a light
   city inherited the dark values above for any token it didn't restate, which
   fails silently and looks broken. These are FALLBACKS: applySkin writes the
   skin's own tokens inline on <html>, and inline always wins, so a skin still
   overrides any of these. Set on :root (not body) so the inline skin values
   stay closer in the cascade than these defaults. */
:root[data-base="light"]{
  --paper:#f5f1e8; --paper-2:#fffdf8; --paper-3:#ebe4d6;
  --ink:#1b1a17; --ink-2:#38342e; --ink-3:#4c463c;
  --instr:#7a5b16;
  --accent:#a8432e; --accent-2:#c98a6b; --rule:#ddd4c2;
  --ok:#2f7d5a; --warn:#a1661e; --bad:#a33028;
  /* every pair above clears AA on --paper-2. Re-run the contrast check if you
     retune these — --ink-3 and --warn were both a hair under 4.5 at first. */
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--paper); color:var(--ink);
  font-family:var(--body-font); font-size:17px; line-height:1.55;
  -webkit-text-size-adjust:100%;
  overflow-x:hidden;
}

/* Texture: cheap, one gradient, no image request */
body[data-texture="grid"]::before{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:0; opacity:.5;
  background-image:linear-gradient(var(--rule) 1px,transparent 1px),
                   linear-gradient(90deg,var(--rule) 1px,transparent 1px);
  background-size:44px 44px; mask-image:radial-gradient(ellipse at 50% 0%,#000 30%,transparent 78%);
}
body[data-texture="paper-grain"]::before{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:0; opacity:.5;
  background-image:repeating-linear-gradient(45deg,rgba(0,0,0,.03) 0 2px,transparent 2px 5px);
}

/* ---------------- app shell ---------------- */
.app{position:relative;z-index:1;max-width:var(--thread-max);margin:0 auto;padding:0 var(--pad) 140px}

.topbar{
  position:sticky; top:0; z-index:20; background:var(--paper);
  border-bottom:1px solid var(--rule); margin:0 calc(var(--pad)*-1);
  padding:10px var(--pad) 8px;
}
.gm{display:flex;align-items:center;gap:10px}
.gm-avatar{
  width:44px;height:44px;border-radius:50%;flex:none;overflow:hidden;
  border:1px solid var(--accent-2); background:var(--paper-3);
  display:grid;place-items:center;
}
.gm-avatar img{width:100%;height:100%;object-fit:cover;object-position:50% 12%;display:block}
.gm-name{font-family:var(--label-font);font-weight:700;letter-spacing:.10em;font-size:13px}
.gm-role{font-family:var(--label-font);font-size:10px;letter-spacing:.16em;color:var(--ink-3);text-transform:uppercase}
.gm-meta{min-width:0}

.progress{display:flex;align-items:center;gap:8px;margin-top:9px}
.progress-label{font-family:var(--label-font);font-size:10px;letter-spacing:.16em;color:var(--ink-3);white-space:nowrap;text-transform:uppercase}
.progress-rule{flex:1;height:1px;background:var(--rule)}
.pips{display:flex;gap:5px}
.pip{width:15px;height:15px;border-radius:50%;border:1px dashed var(--ink-3);flex:none;appearance:none;padding:0;background:transparent}
.pip[data-state="done"]{border:1px solid var(--accent);background:var(--accent);box-shadow:0 0 9px -1px var(--accent)}
.pip[data-state="current"]{border:1px solid var(--accent);animation:pulse 2s ease-in-out infinite}
/* Phase 3: an enterable-but-unvisited stop — solid ring, hollow centre. */
.pip[data-state="open"]{border:1px solid var(--accent)}
button.pip{cursor:pointer}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}

/* ---------------- thread ---------------- */
.thread{display:flex;flex-direction:column;gap:14px;padding-top:20px}
/* scrollIntoView honours scroll-margin — this is what keeps a revealed card
   clear of the fixed header instead of sliding under it */
.thread > *{scroll-margin-top:calc(var(--topbar-h) + 12px)}
.enter{animation:rise .34s cubic-bezier(.2,.7,.3,1) both}
@keyframes rise{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}

/* Split beats arrive in one DOM batch (one scroll) and are staggered purely
   here. `both` fill-mode holds them at opacity 0 through the delay.
   --beat is capped in JS, so a run is fully revealed within ~1.2s + 0.34s —
   long enough to read as a conversation, short enough that nobody is standing
   in the sun waiting for text. */
.beat{animation-delay:calc(var(--beat, 0) * 240ms)}
/* A follow-up message from the same speaker sits tighter than a new turn. */
.beat-cont{margin-top:-7px}
.beat-title{margin:0 0 6px;font-family:var(--display-font);font-size:16px}

@media (prefers-reduced-motion:reduce){
  .enter{animation:none}
  .beat{animation-delay:0s}
  .pip[data-state="current"]{animation:none}
  .petal{display:none!important}
}

.card{border:1px solid var(--rule);border-radius:var(--radius);background:var(--paper-2);padding:13px 15px}
.card-tag{
  font-family:var(--mono-font);font-size:9.5px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-3);display:block;margin-bottom:7px;
}

/* dispatch = narrator bubble */
.bubble{max-width:86%}
.bubble p{margin:0 0 .6em}
.bubble p:last-child{margin-bottom:0}

/* player reply chips + sent chip */
.replies{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}
.reply{
  font-family:var(--mono-font);font-size:13.5px;color:var(--ink);
  background:transparent;border:1px solid var(--ink-3);border-radius:999px;
  padding:9px 17px;cursor:pointer;min-height:42px;
}
.reply:hover,.reply:focus-visible{border-color:var(--accent);color:var(--accent);outline:none}
.sent{align-self:flex-end;background:var(--accent);color:var(--paper);
  border-radius:999px;padding:9px 17px;font-family:var(--mono-font);font-size:13.5px;font-weight:700}

/* walk-to = location card */
.walk{background:var(--paper-3);border-color:var(--accent-2)}
.walk .card-tag{color:var(--accent)}
.walk h3{font-family:var(--display-font);font-size:18px;margin:0 0 7px;letter-spacing:.02em}
.walk .cue{color:var(--ink-2);font-size:16px;margin:0}
.walk-actions{display:flex;gap:9px;margin-top:12px;flex-wrap:wrap}

/* field note = found text / inscription */
.note{border-left:3px solid var(--accent);background:transparent;font-style:italic;color:var(--ink-2)}

/* dead end */
.deadend{border-color:var(--bad);background:color-mix(in srgb,var(--bad) 9%,var(--paper-2))}
.deadend .card-tag{color:var(--bad)}

/* ---------------- gate widget ---------------- */
.gate{border-color:var(--accent-2)}
.gate-head{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:9px}
.gate-head .card-tag{margin:0;color:var(--accent)}
/* Instructions get their OWN color (field-test): they were the same grey as
   everything else and players skimmed past them in the sun. */
.gate-body{white-space:pre-wrap;color:var(--instr);font-size:16px;margin-bottom:13px}
.gate-prompt{font-family:var(--display-font);font-size:17px;margin:0 0 11px;color:var(--ink)}

/* The answer terminal is a proper boxed input now (field-test: the bare
   underline read as decoration and was too small to hit while walking). */
.field{
  width:100%;background:var(--paper-3);border:1.5px solid var(--accent-2);
  border-radius:var(--radius);
  color:var(--ink);font-family:var(--mono-font);font-size:22px;padding:13px 12px;
  letter-spacing:.14em;
}
.field:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--accent) 30%,transparent)}
.field::placeholder{color:var(--ink-3);opacity:.4;letter-spacing:.06em}

/* code = per-character slots. The slot after the last filled one carries a
   blinking baseline caret so the widget reads as "type here", not as a static
   diagram — the single biggest polish gap vs Design A's numeric key. */
.slots{display:flex;gap:7px;margin-bottom:11px}
.slot{
  flex:1;max-width:60px;aspect-ratio:3/4;border:1.5px solid var(--ink-3);border-radius:var(--radius);
  display:grid;place-items:center;font-family:var(--mono-font);font-size:26px;background:var(--paper-3);
  position:relative;transition:border-color .12s, transform .12s;
}
.slot[data-filled]{border-color:var(--accent);color:var(--accent);animation:slot-pop .16s ease-out}
.slot[data-next]::after{
  content:'';position:absolute;left:22%;right:22%;bottom:18%;height:2px;
  background:var(--accent);animation:caret-blink 1.1s steps(1) infinite;
}
@keyframes slot-pop{0%{transform:scale(.88)}100%{transform:scale(1)}}
@keyframes caret-blink{50%{opacity:0}}

/* choice = MCQ options. A picked option earns a stamp tick — selection should
   read at a glance in daylight, not hide in a border-color change. */
.opts{display:flex;flex-direction:column;gap:9px}
.opt{
  display:flex;gap:11px;align-items:flex-start;text-align:left;width:100%;
  background:var(--paper);border:1px solid var(--rule);border-radius:var(--radius);
  padding:12px;cursor:pointer;color:var(--ink);font-family:var(--body-font);font-size:14.5px;
  position:relative;transition:border-color .12s, background .12s;
}
.opt:hover,.opt:focus-visible{border-color:var(--accent);outline:none}
.opt[aria-pressed="true"]{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 11%,var(--paper))}
.opt[aria-pressed="true"]::after{
  content:'✓';position:absolute;top:9px;right:11px;
  font-family:var(--mono-font);font-size:13px;color:var(--accent);
  border:1px solid var(--accent);border-radius:999px;width:20px;height:20px;
  display:grid;place-items:center;transform:rotate(-8deg);
}
.opt img{width:62px;height:62px;object-fit:contain;flex:none;background:var(--paper-3);border-radius:2px}
/* Authored SVG artwork options (Stop 2's erased envelopes) stack the image
   full-width above the label — the art IS the puzzle material. */
.opt.opt-figure{flex-direction:column;align-items:stretch}
.opt.opt-figure img{width:100%;height:auto;max-height:220px}
.opt-label{font-weight:700;display:block;margin-bottom:3px}
.opt-detail{color:var(--ink-2);font-size:14px;line-height:1.45}

/* grid = worksheet matrix. Header row + first column stay put while scrolling a
   wide matrix (§6 VITM) — position:sticky needs an opaque background so cells
   don't show through, and a scroll container with a max-height. */
.gridwrap{overflow:auto;margin-bottom:11px;-webkit-overflow-scrolling:touch;max-height:62vh;border:1px solid var(--rule);border-radius:var(--radius)}
table.matrix{border-collapse:separate;border-spacing:0;width:100%;min-width:380px;font-size:12.5px}
table.matrix th,table.matrix td{border-right:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:7px;text-align:center;background:var(--card)}
table.matrix th{color:var(--ink-3);font-weight:400;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase}
table.matrix th.row-h{text-align:left;color:var(--ink-2);font-size:12px;text-transform:none;letter-spacing:0}
table.matrix input{accent-color:var(--accent);width:17px;height:17px}
/* freeze the header row */
table.matrix tr:first-child th{position:sticky;top:0;z-index:2;background:var(--paper-2)}
/* freeze the first (label) column */
table.matrix th.row-h,table.matrix tr th:first-child{position:sticky;left:0;z-index:1;background:var(--paper-2)}
/* top-left corner sits above both */
table.matrix tr:first-child th:first-child{z-index:3}

/* anchor gallery — reference imagery that IS the puzzle material (§1) */
.card.anchors .anchor-grid{display:grid;grid-template-columns:1fr;gap:11px;margin-top:9px}
@media(min-width:520px){.card.anchors .anchor-grid{grid-template-columns:1fr 1fr}}
figure.anchor{margin:0;border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden;background:var(--paper)}
figure.anchor img{display:block;width:100%;height:150px;object-fit:cover;background:var(--paper-2)}
.anchor-cap{display:flex;gap:9px;padding:9px 10px}
.anchor-n{flex:none;width:22px;height:22px;display:grid;place-items:center;border:1px solid var(--accent);border-radius:999px;color:var(--accent);font-family:var(--mono-font);font-size:12px}
.anchor-label{display:block;font-family:var(--label-font);font-size:12.5px;letter-spacing:.03em;color:var(--ink);margin-bottom:2px}
.anchor-desc{display:block;font-size:12.5px;color:var(--ink-2);line-height:1.4}
/* Authored SVG artwork (book spines) shows whole, never cover-cropped. */
figure.anchor.anchor-figure img{height:auto;max-height:340px;object-fit:contain}

/* cipher decoder wheel — letter-by-letter (§3) */
.decoder{background:var(--paper);border:1px dashed var(--ink-3);border-radius:var(--radius);padding:12px;margin-bottom:11px}
.decoder-help{font-size:12px;color:var(--ink-2);margin:4px 0 11px}
.shift-row{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:12px}
.shift-btn{width:38px;height:38px;border:1px solid var(--ink-3);background:var(--paper-2);color:var(--ink);border-radius:var(--radius);font-size:20px;line-height:1;cursor:pointer}
.shift-val{font-family:var(--label-font);font-size:12px;letter-spacing:.1em;color:var(--accent);min-width:74px;text-align:center;text-transform:uppercase}
.cipher-strip{display:flex;flex-wrap:wrap;gap:6px;justify-content:center}
.cipher-gap{flex-basis:100%;height:0}
.cipher-tile{display:flex;flex-direction:column;align-items:center;gap:2px;padding:5px 3px;min-width:30px;border:1px solid var(--rule);border-radius:8px;background:var(--card);cursor:pointer}
.cipher-tile .ct-top{font-family:var(--mono-font);font-size:18px;color:var(--accent);letter-spacing:.04em}
.cipher-tile .ct-bot{font-family:var(--mono-font);font-size:15px;color:var(--ink-3)}
.cipher-tile[data-done]{border-color:var(--accent)}
.cipher-tile[data-done] .ct-bot{color:var(--ink);animation:slot-pop .16s ease-out}

/* grid: make the picked cell readable at a glance, not just a filled radio */
table.matrix td:has(input:checked){background:color-mix(in srgb,var(--accent) 12%,var(--card))}

/* dial = stepped rotary control (Phase 2). The ring rotates so the chosen
   label sits under the fixed pointer; ticks fade with distance. Pure CSS
   transforms — no canvas, no per-frame JS, same rules as ambient. */
.dial{margin-bottom:11px}
.dial-face{
  position:relative;width:min(230px,72vw);aspect-ratio:1;margin:4px auto 10px;
  border:1px solid var(--ink-3);border-radius:999px;background:var(--paper);
  display:grid;place-items:center;overflow:hidden;
}
.dial-ring{
  position:absolute;inset:0;transform:rotate(var(--rot,0deg));
  transition:transform .28s cubic-bezier(.3,1.4,.6,1);
}
@media(prefers-reduced-motion:reduce){.dial-ring{transition:none}}
.dial-tick{
  position:absolute;left:50%;top:50%;
  transform:translate(-50%,-50%) rotate(var(--a)) translateY(calc(min(230px,72vw)/-2 + 19px)) ;
  font-family:var(--mono-font);font-size:11px;letter-spacing:.06em;color:var(--ink-3);
}
.dial-tick[data-at]{color:var(--accent);font-weight:700}
.dial-pointer{position:absolute;top:2px;left:50%;transform:translateX(-50%);color:var(--accent);font-size:14px}
.dial-val{
  font-family:var(--mono-font);font-size:26px;color:var(--ink);letter-spacing:.08em;
  border:1px dashed var(--rule);border-radius:var(--radius);padding:7px 15px;background:var(--paper-2);
}

/* order = tap-to-sequence tiles (Phase 2) */
.order-seq{
  font-family:var(--mono-font);font-size:13px;color:var(--ink);min-height:20px;
  border-bottom:1px dashed var(--ink-3);padding:0 2px 8px;margin-bottom:11px;letter-spacing:.03em;
}
.order-seq[data-empty="1"]{color:var(--ink-3);letter-spacing:.06em}
.order-pool{display:flex;flex-wrap:wrap;gap:9px;margin-bottom:11px}
.order-tile{
  display:flex;align-items:center;gap:8px;padding:10px 13px;cursor:pointer;
  background:var(--paper);border:1px solid var(--rule);border-radius:var(--radius);
  color:var(--ink);font-family:var(--body-font);font-size:14px;
  transition:border-color .12s, background .12s;
}
.order-tile:hover,.order-tile:focus-visible{border-color:var(--accent);outline:none}
.order-tile[data-placed]{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 11%,var(--paper))}
.order-pos{
  min-width:20px;height:20px;display:grid;place-items:center;
  border:1px solid var(--ink-3);border-radius:999px;
  font-family:var(--mono-font);font-size:11.5px;color:var(--ink-3);
}
.order-tile[data-placed] .order-pos{border-color:var(--accent);color:var(--accent)}

/* keypad = on-screen digit pad feeding the code slots */
.keypad{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;max-width:250px;margin:0 auto 11px}
.key{
  aspect-ratio:5/3;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--paper);color:var(--ink);font-family:var(--mono-font);font-size:18px;cursor:pointer;
  transition:border-color .12s, background .12s;
}
.key:hover,.key:focus-visible{border-color:var(--accent);outline:none}
.key:active{background:color-mix(in srgb,var(--accent) 11%,var(--paper))}
.key-del{color:var(--ink-3)}

/* cryptex = letter wheels stepped up/down */
.cryptex{display:flex;gap:9px;justify-content:center;margin-bottom:8px;flex-wrap:wrap}
.cryptex-wheel{display:flex;flex-direction:column;align-items:center;gap:5px}
.wheel-btn{
  width:40px;height:26px;border:1px solid var(--ink-3);background:var(--paper-2);color:var(--ink-3);
  border-radius:var(--radius);font-size:11px;line-height:1;cursor:pointer;
}
.wheel-btn:hover,.wheel-btn:focus-visible{border-color:var(--accent);color:var(--accent);outline:none}
.wheel-char{
  width:40px;aspect-ratio:3/4;display:grid;place-items:center;
  border:1px solid var(--ink-3);border-radius:var(--radius);background:var(--paper);
  font-family:var(--mono-font);font-size:20px;color:var(--ink-3);
}
.cryptex-wheel[data-set] .wheel-char{border-color:var(--accent);color:var(--accent)}
.widget-help{font-size:14px;color:var(--instr);text-align:center;margin-bottom:11px}

/* arrows = directional lock pad, laid out as a D-pad cross */
.arrow-pad{
  display:grid;grid-template-areas:". u ." "l x r" ". d ." "z z z";
  gap:8px;max-width:190px;margin:0 auto 11px;
}
.arrow-btn{
  width:52px;height:52px;border:1px solid var(--rule);border-radius:var(--radius);
  background:var(--paper);color:var(--ink);font-size:20px;cursor:pointer;
  transition:border-color .12s, background .12s;justify-self:center;
}
.arrow-btn:nth-child(1){grid-area:u}
.arrow-btn:nth-child(2){grid-area:l}
.arrow-btn:nth-child(3){grid-area:d}
.arrow-btn:nth-child(4){grid-area:r}
.arrow-btn:hover,.arrow-btn:focus-visible{border-color:var(--accent);outline:none}
.arrow-btn:active{background:color-mix(in srgb,var(--accent) 11%,var(--paper))}
.arrow-undo{grid-area:z;width:100%;font-size:16px;color:var(--ink-3)}

/* symbols = free-tap sequence tiles (reuses order tile chrome) */
.symbol-tile .order-label{font-size:17px}
.symbol-undo .order-label{color:var(--ink-3)}

/* switches = toggle bank */
.switchbank{display:flex;flex-direction:column;gap:8px;margin-bottom:11px}
.switch{
  display:flex;align-items:center;gap:11px;padding:9px 12px;cursor:pointer;text-align:left;
  background:var(--paper);border:1px solid var(--rule);border-radius:var(--radius);
  color:var(--ink);font-family:var(--body-font);font-size:14px;
  transition:border-color .12s, background .12s;
}
.switch:hover,.switch:focus-visible{border-color:var(--accent);outline:none}
.switch[data-on]{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 9%,var(--paper))}
.switch-knob{
  flex:none;width:34px;height:19px;border:1px solid var(--ink-3);border-radius:999px;position:relative;
  transition:border-color .12s;
}
.switch-knob::after{
  content:'';position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:999px;
  background:var(--ink-3);transition:left .14s, background .12s;
}
.switch[data-on] .switch-knob{border-color:var(--accent)}
.switch[data-on] .switch-knob::after{left:17px;background:var(--accent)}

/* sliders = labelled stepper rows */
.sliderbank{display:flex;flex-direction:column;gap:8px;margin-bottom:11px}
.slider-row{display:flex;align-items:center;gap:10px}
.slider-name{flex:1;font-size:13.5px;color:var(--ink-2)}
.slider-val{
  min-width:52px;text-align:center;font-family:var(--mono-font);font-size:16px;color:var(--ink-3);
  border:1px dashed var(--rule);border-radius:var(--radius);padding:6px 8px;background:var(--paper-2);
}
.slider-val[data-set]{color:var(--accent);border-color:var(--accent);border-style:solid}

/* anagram = letter tiles (reuses order chrome, square letters) */
.anagram-tile{padding:0;width:44px;aspect-ratio:3/4;justify-content:center}
.anagram-tile .order-label{font-family:var(--mono-font);font-size:18px}
.anagram-pool{justify-content:center}

/* morse = code strip + reference chart */
.morse-strip{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:11px;align-items:center}
.morse-group{
  font-family:var(--mono-font);font-size:17px;letter-spacing:.14em;color:var(--ink);
  border:1px solid var(--rule);border-radius:8px;background:var(--card);padding:6px 9px;
}
.morse-chart{display:grid;grid-template-columns:repeat(auto-fill,minmax(64px,1fr));gap:5px;margin-top:9px}
.morse-cell{
  display:flex;gap:6px;align-items:baseline;font-size:12px;
  border:1px solid var(--rule);border-radius:8px;padding:4px 7px;background:var(--card);
}
.morse-cell b{font-family:var(--mono-font);color:var(--accent);font-weight:700}
.morse-cell i{font-style:normal;font-family:var(--mono-font);color:var(--ink-2);letter-spacing:.1em}

/* hotspot = tappable grid over an image */
.hotspot{position:relative;margin-bottom:8px;border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden}
.hotspot-img{display:block;width:100%}
.hotspot-grid{
  position:absolute;inset:0;display:grid;
  grid-template-columns:repeat(var(--hcols),1fr);grid-template-rows:repeat(var(--hrows),1fr);
}
.hotspot-cell{
  border:1px dashed color-mix(in srgb,var(--ink) 30%,transparent);background:transparent;
  cursor:pointer;position:relative;padding:0;
}
.hotspot-cell:hover,.hotspot-cell:focus-visible{border-color:var(--accent);outline:none}
.hotspot-cell[aria-pressed="true"]{
  border:2px solid var(--accent);background:color-mix(in srgb,var(--accent) 18%,transparent);
}
.hotspot-tag{
  position:absolute;top:3px;left:4px;font-family:var(--mono-font);font-size:10px;
  color:color-mix(in srgb,var(--ink) 55%,transparent);
}
.hotspot-cell[aria-pressed="true"] .hotspot-tag{color:var(--accent);font-weight:700}

/* completion case file (§8). The holder reserves the 4:5 box up front so the
   thread doesn't jump when the canvas render lands a moment later. */
.card.certificate .cert-holder{
  aspect-ratio:1080/1350; margin:10px 0 12px; overflow:hidden;
  border:1px solid var(--rule); border-radius:var(--radius); background:var(--paper);
  display:grid; place-items:center;
}
.card.certificate .cert-wait{
  font-family:var(--mono-font); font-size:11px; letter-spacing:.18em;
  text-transform:uppercase; color:var(--ink-3);
}
.card.certificate .cert-img{display:block;width:100%;height:100%;object-fit:contain}
.cert-actions{display:flex;gap:10px;flex-wrap:wrap}
.cert-actions .btn,.cert-actions .btn-ghost{flex:1;text-align:center;text-decoration:none}

/* Phase 4: personal scorecard on the case file — machine block treatment
   (bounded, mono), matching the provenance system (P1.2). */
.scorecard{
  border:1px solid var(--rule);border-radius:var(--radius);
  padding:14px 14px 12px;margin:0 0 12px;text-align:center;
}
.scorecard-score{
  font-family:var(--mono-font);font-size:34px;font-weight:700;
  color:var(--accent);letter-spacing:.04em;line-height:1;
}
.scorecard-score-l{
  font-family:var(--label-font,var(--mono-font));font-size:10.5px;
  letter-spacing:.22em;color:var(--ink-3);margin:4px 0 10px;
}
.scorecard-rows{border-top:1px solid var(--rule)}
.scorecard-row{
  display:flex;justify-content:space-between;gap:12px;
  padding:6px 2px;border-bottom:1px solid var(--rule);
}
.scorecard-k{font-family:var(--label-font,var(--mono-font));font-size:10.5px;letter-spacing:.16em;color:var(--ink-3)}
.scorecard-v{font-family:var(--mono-font);font-size:13px;color:var(--ink)}
.scorecard-board{
  display:inline-block;margin-top:10px;font-family:var(--mono-font);
  font-size:12px;letter-spacing:.06em;color:var(--accent);text-decoration:none;
}

/* Phase 4: competition strip under the header. Chrome, not a card. */
.comp-banner{
  position:sticky;top:var(--topbar-h);z-index:9;
  display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:6px 16px;background:var(--paper-2);border-bottom:1px solid var(--rule);
}
.comp-banner-name{
  font-family:var(--label-font,var(--mono-font));font-size:11px;
  letter-spacing:.14em;color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.comp-banner-link{
  font-family:var(--mono-font);font-size:11.5px;letter-spacing:.06em;
  color:var(--accent);text-decoration:none;white-space:nowrap;
}

/* multi = fragment collector */
.frag{border-top:1px solid var(--rule);padding:12px 0}
.frag:first-child{border-top:0;padding-top:0}
.frag-title{font-family:var(--label-font);font-size:12px;letter-spacing:.1em;color:var(--accent);margin-bottom:5px}
.frag-prompt{font-size:14px;color:var(--ink-2);margin-bottom:8px}
.frag-extract{margin-bottom:7px;text-transform:none;letter-spacing:.02em;color:var(--ink-2);font-size:11.5px}
.frag-row{display:flex;gap:8px;align-items:center}
.frag-row .field{flex:1;font-size:15px;letter-spacing:.06em}
.frag-row .field[data-ok]{border-color:var(--ok)}
.frag-check{flex:none;padding:0 13px;height:38px;border:1px solid var(--accent);background:transparent;color:var(--accent);border-radius:var(--radius);font-family:var(--label-font);font-size:12px;letter-spacing:.06em;cursor:pointer;text-transform:uppercase}
.frag-check:active{background:color-mix(in srgb,var(--accent) 14%,transparent)}
.frag-rule{font-family:var(--label-font);font-size:10.5px;letter-spacing:.1em;color:var(--ink-3);white-space:nowrap;text-transform:uppercase}
.frag-yield{
  width:38px;height:38px;flex:none;display:grid;place-items:center;border-radius:var(--radius);
  border:1px solid var(--ink-3);font-family:var(--mono-font);font-size:19px;color:var(--ink-3);
}
.frag-yield[data-got]{border-color:var(--ok);color:var(--ok)}
.assembly{margin-top:14px;padding-top:13px;border-top:1px solid var(--rule)}
.letters{display:flex;gap:7px;justify-content:center;margin-bottom:11px;flex-wrap:wrap}
.letters span{
  width:34px;height:34px;display:grid;place-items:center;border:1px solid var(--accent);
  border-radius:var(--radius);font-family:var(--mono-font);color:var(--accent);font-size:17px;
}

/* upload */
.upload{
  border:1px dashed var(--ink-3);border-radius:var(--radius);padding:26px 15px;text-align:center;
  cursor:pointer;color:var(--ink-3);font-family:var(--label-font);font-size:13px;display:block;
}
.upload:hover{border-color:var(--accent);color:var(--accent)}
.upload img{max-width:100%;border-radius:var(--radius);margin-top:11px;display:block}

/* buttons */
.btn{
  width:100%;background:var(--accent);color:var(--paper);border:0;border-radius:var(--radius);
  padding:15px;font-family:var(--label-font);font-weight:700;font-size:14px;letter-spacing:.14em;
  text-transform:uppercase;cursor:pointer;min-height:50px;
}
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn-ghost{
  background:transparent;color:var(--ink-2);border:1px solid var(--rule);
  padding:10px 15px;font-size:12px;width:auto;min-height:42px;border-radius:var(--radius);
  font-family:var(--mono-font);letter-spacing:.1em;cursor:pointer;text-decoration:none;
  display:inline-flex;align-items:center;gap:7px;text-transform:uppercase;
}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent)}

.feedback{font-family:var(--label-font);margin-top:11px;font-size:13px;display:none}
.feedback[data-show]{display:block}
.feedback[data-kind="bad"]{color:var(--bad)}
.feedback[data-kind="ok"]{color:var(--ok)}
/* warn = the guess was never checked (offline, rate-limited). Visually
   distinct from "bad" on purpose — it is not a wrong answer. */
.feedback[data-kind="warn"]{color:var(--warn)}
.shake{animation:shake .34s}
@keyframes shake{25%{transform:translateX(-6px)}75%{transform:translateX(6px)}}
/* Re-announces a repeated identical message so a second wrong answer still
   visibly registers. NOT named `pulse` — that keyframe is already taken by the
   current-stop progress pip above, and redefining it silently replaces it. */
.reannounce{animation:reannounce .4s}
@keyframes reannounce{0%{opacity:.25}100%{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .shake,.reannounce{animation:none}
}

/* ---------------- hints (tiered, groupable) ---------------- */
details.hints{border:1px solid var(--rule);border-radius:var(--radius);background:transparent}
details.hints>summary{
  padding:13px 15px;cursor:pointer;font-family:var(--mono-font);font-size:13px;
  letter-spacing:.14em;color:var(--ink-2);list-style:none;text-transform:uppercase;
}
details.hints>summary::-webkit-details-marker{display:none}
details.hints[open]>summary{color:var(--accent);border-bottom:1px solid var(--rule)}
.hint-group{padding:13px 15px 0}
.hint-group-title{font-family:var(--label-font);font-size:11.5px;letter-spacing:.1em;color:var(--ink-2);margin-bottom:9px;text-transform:uppercase}
.tier{border-top:1px solid var(--rule);padding:11px 0}
.tier:first-of-type{border-top:0}
.tier-btn{
  background:transparent;border:1px solid var(--ink-3);border-radius:999px;color:var(--ink);
  padding:8px 16px;font-family:var(--mono-font);font-size:13.5px;cursor:pointer;min-height:42px;
}
.tier-btn:hover{border-color:var(--accent);color:var(--accent)}
.tier-body{margin-top:9px;color:var(--ink-2);font-size:15.5px;white-space:pre-wrap}
.tier-warn{font-family:var(--label-font);color:var(--warn);font-size:11px;letter-spacing:.06em;margin-top:5px}
.hints-pad{padding-bottom:13px}

/* ---------------- history: two icon lanes ---------------- */
.lanes{display:grid;grid-template-columns:1fr 1fr;gap:11px}
.lane{
  background:var(--paper);border:1px solid var(--rule);border-radius:var(--radius);
  padding:15px 12px;text-align:center;cursor:pointer;color:var(--ink);
}
.lane:hover,.lane:focus-visible{border-color:var(--accent);outline:none}
.lane-icon{font-size:24px;display:block;margin-bottom:7px}
.lane-label{font-family:var(--mono-font);font-size:11px;letter-spacing:.09em;color:var(--ink-2);text-transform:uppercase}
.lane-open{margin-top:11px;white-space:pre-wrap;color:var(--ink-2);font-size:14.5px}
.lane-sub{margin-top:11px;padding-top:11px;border-top:1px solid var(--rule)}
.lane-sub-t{font-family:var(--mono-font);font-size:10px;letter-spacing:.14em;color:var(--accent);margin-bottom:5px;text-transform:uppercase}

/* ---------------- reveal ---------------- */
.reveal{border-color:var(--ok)}
.reveal .card-tag{color:var(--ok)}
.entry{border-top:1px solid var(--rule);padding:13px 0}
.entry:first-of-type{border-top:0}
.entry h4{font-family:var(--display-font);font-size:14.5px;margin:0 0 5px;letter-spacing:.05em}
.entry .hook{color:var(--accent);font-size:13px;margin:0 0 7px}
.entry p{margin:0 0 .6em;color:var(--ink-2);font-size:14.5px}
.entry .aside{border-left:2px solid var(--rule);padding-left:11px;font-size:15px;color:var(--ink-2);margin-top:9px}
.entry .aside b{font-family:var(--label-font);color:var(--ink-2);letter-spacing:.06em;font-size:11px;display:block;text-transform:uppercase}
/* Collapsed profile entries (feedback item 8) — tap the name to expand. */
.entry-toggle{display:block;width:100%;text-align:left;background:none;border:0;padding:0;cursor:pointer;color:var(--ink);font-family:var(--display-font);font-size:14.5px;letter-spacing:.05em}
.entry-toggle:hover,.entry-toggle:focus-visible{color:var(--accent);outline:none}
.entry-body{margin-top:7px}

/* ---------------- explore ---------------- */
.explore .card-tag{color:var(--ink-3)}
.xlink{
  display:flex;gap:11px;align-items:center;padding:11px 0;border-top:1px solid var(--rule);
  color:var(--ink);text-decoration:none;
}
.xlink:first-of-type{border-top:0}
.xlink:hover{color:var(--accent)}
.xkind{
  font-family:var(--mono-font);font-size:9px;letter-spacing:.1em;border:1px solid var(--rule);
  border-radius:2px;padding:3px 6px;color:var(--ink-3);flex:none;text-transform:uppercase;
}
.xempty{color:var(--ink-2);font-size:14.5px;font-style:italic}

/* ---------------- persistent map ---------------- */
.mapfab{
  position:fixed;right:16px;bottom:16px;z-index:30;
  background:var(--paper-3);border:1px solid var(--accent-2);color:var(--accent);
  border-radius:999px;padding:13px 19px;font-family:var(--mono-font);font-size:11.5px;
  letter-spacing:.12em;text-decoration:none;display:flex;align-items:center;gap:9px;
  box-shadow:0 5px 22px rgba(0,0,0,.45);min-height:50px;text-transform:uppercase;
}
.mapfab:hover{background:var(--accent);color:var(--paper)}

/* ---------------- stamp moment ---------------- */
.stamp{
  align-self:center;border:2px solid var(--accent);color:var(--accent);border-radius:var(--radius);
  padding:11px 21px;font-family:var(--label-font);font-weight:700;letter-spacing:.19em;font-size:14px;
  transform:rotate(-4deg);animation:thunk .45s cubic-bezier(.2,1.6,.4,1) both;text-transform:uppercase;
}
@keyframes thunk{from{opacity:0;transform:rotate(-4deg) scale(2.6)}to{opacity:1;transform:rotate(-4deg) scale(1)}}

/* ---------------- ambient (CSS only, no canvas, no rAF) ---------------- */
/* JS only supplies randomness (--px/--psize/--pdur/--pdelay/--drift); every
   look and speed decision is made here, so a new effect is a CSS block and a
   token, not a JS change. Tint is `skin.ambient.color` -> --ambient-color,
   with a sensible per-effect fallback. */
.ambient{position:fixed;inset:0;pointer-events:none;z-index:2;overflow:hidden}
.petal{
  position:absolute;top:-24px;left:var(--px);
  width:var(--psize);height:var(--psize);
  opacity:.75;will-change:transform;
  animation:fall linear infinite;
  animation-duration:var(--pdur);animation-delay:var(--pdelay);
}
body[data-ambient="sakura"] .petal{background:var(--ambient-color,#f7b8cd);border-radius:100% 0 100% 0}
body[data-ambient="snow"]   .petal{background:var(--ambient-color,#fff);border-radius:50%}
body[data-ambient="dust"]   .petal{background:var(--ambient-color,#d8c9a3);border-radius:50%;opacity:.4}
body[data-ambient="embers"] .petal{background:var(--ambient-color,#ff9a4d);border-radius:50%;box-shadow:0 0 7px var(--ambient-color,#ff6b1a)}
/* `rain` was listed in SCHEMA.md and had no rule at all — it rendered as
   invisible transparent squares. Rain falls straight, fast, and thin. */
body[data-ambient="rain"] .petal{
  background:var(--ambient-color,#9dc4de);
  width:1.5px;height:calc(var(--psize) * 1.9);border-radius:0;opacity:.4;
  animation-name:fall-straight;animation-duration:calc(var(--pdur) * .17);
}
@keyframes fall{
  0%{transform:translate3d(0,-24px,0) rotate(0)}
  100%{transform:translate3d(var(--drift,26px),102vh,0) rotate(420deg)}
}
@keyframes fall-straight{
  0%{transform:translate3d(0,-24px,0)}
  100%{transform:translate3d(calc(var(--drift,0px) * .18),102vh,0)}
}

/* ---------------- city select ---------------- */
.cities{display:flex;flex-direction:column;gap:13px;padding-top:22px}
.city{
  display:block;text-align:left;width:100%;cursor:pointer;color:var(--ink);
  background:var(--paper-2);border:1px solid var(--rule);border-radius:var(--radius);
  padding:0;overflow:hidden;
}
.city:hover{border-color:var(--accent)}
.city-bar{height:4px;background:var(--cc,var(--accent))}
.city-in{padding:15px}
.city-country{font-family:var(--mono-font);font-size:10px;letter-spacing:.17em;color:var(--ink-3);text-transform:uppercase}
.city-name{font-family:var(--display-font);font-size:23px;margin:5px 0 3px}
.city-sub{font-style:italic;color:var(--ink-2);font-size:14.5px;margin:0}
.city-meta{
  display:flex;justify-content:space-between;margin-top:13px;padding-top:11px;
  border-top:1px solid var(--rule);font-family:var(--mono-font);font-size:11.5px;color:var(--ink-3);
}
.masthead{padding:30px 0 6px}
.masthead h1{font-family:var(--display-font);font-size:31px;letter-spacing:.06em;margin:0 0 9px;line-height:1.12}
.masthead .kicker{font-family:var(--mono-font);font-size:10.5px;letter-spacing:.24em;color:var(--ink-3);text-transform:uppercase}
.masthead p{color:var(--ink-2);font-size:15px;margin:9px 0 0}

.devbadge{
  position:fixed;left:8px;bottom:8px;z-index:40;background:var(--warn);color:#000;
  font-family:var(--mono-font);font-size:9.5px;letter-spacing:.1em;padding:5px 9px;border-radius:2px;
}

/* ============================================================
   Section-wise formatting inside the thread
   Blocks derived from the narrative text by richBody().

   ORGANISING IDEA: every block answers "where did this come from?"
     voice     handler talking      — no panel at all
     evidence  filed clue           — teal left rule
     machine   terminal readout     — bounded box, cyan
     interrupt advisory             — amber, the only saturated ground
     world     carved inscription   — RAISED surface, serif, no left rule
   Provenance, not decoration: the treatment tells you what kind of thing
   you are reading before you have read it.

   WHY NOT JUST DIFFERENT BACKGROUNDS (the obvious fix):
   measured on this palette, --paper / --paper-2 / --paper-3 sit within
   1.09-1.11 contrast of each other, and even PURE BLACK is only 1.19 from
   the card — the card is already so dark there is no headroom downward.
   Fills max out around 1.5; a left rule gives 3.7 (teal) to 9.5 (cyan).
   So edge, type and rhythm carry the signal and fill only supports it.
   That also survives the real viewing condition: a phone in daylight.
   ============================================================ */

/* voice — the baseline every other block is read against. No panel. */
.blk-p{margin:0 0 .7em;color:var(--ink-2)}
.blk-p:last-child{margin-bottom:0}

/* section heading — ALL CAPS lines like "QUANTUM CYPHER VALUES" */
.blk-head{
  font-family:var(--label-font);font-size:11.5px;letter-spacing:.2em;color:var(--accent);
  text-transform:uppercase;margin:18px 0 10px;padding-bottom:7px;position:relative;
  border-bottom:1px solid var(--rule);
}
.blk-head::after{content:"";position:absolute;left:0;bottom:-1px;width:38px;height:1px;background:var(--accent)}
.blk-head:first-child{margin-top:0}

/* sub-heading — short Title Case lines */
.blk-sub{font-family:var(--display-font);font-size:14px;color:var(--ink);margin:15px 0 7px;letter-spacing:.02em}

/* EVIDENCE — "[Clue A - For the Observers]: ..."
   The fill was --paper: 1.09 against the card, i.e. invisible. Dropping it
   costs nothing and buys air; the teal rule (3.74) does the identifying. */
.blk-clue{
  display:block;border-left:3px solid var(--accent-2);
  padding:2px 0 2px 13px;margin:0 0 14px;
}
.blk-clue-tag{
  display:inline-block;font-family:var(--mono-font);font-size:9.5px;letter-spacing:.15em;
  text-transform:uppercase;color:var(--accent);border:1px solid var(--accent-2);
  border-radius:999px;padding:3px 10px;margin-bottom:8px;
}
.blk-clue-body{display:block;color:var(--ink-2);font-size:14.5px}

/* MACHINE — value table, "🔸 Item ➔ Value: NN".
   A bounded instrument set into the panel: the only blocks with a border on
   all four sides, which is what separates them from the left-rule blocks.
   The inset highlight gives a real edge that a fill shift cannot. */
.blk-data{
  border:1px solid var(--rule);border-radius:var(--radius);overflow:hidden;margin:0 0 15px;
  background:var(--paper);
  box-shadow:inset 0 1px 0 color-mix(in srgb,var(--ink) 7%,transparent);
}
.blk-data-row{
  display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:9px 13px;border-top:1px solid var(--rule);
}
.blk-data-row:first-child{border-top:0}
.blk-data-row:nth-child(odd){background:color-mix(in srgb,var(--ink) 3%,transparent)}
.blk-data-k{font-family:var(--mono-font);font-size:14px;color:var(--ink)}
.blk-data-v{
  font-family:var(--mono-font);font-size:15px;color:var(--accent);font-weight:700;
  letter-spacing:.06em;white-space:nowrap;
}

/* MACHINE — the live one. "Key = (A x B) + C" is the thing the team actually
   computes with, so it is the loudest element on the card: full cyan dashed
   box (9.48 against the card) reading as an active field rather than a note. */
.blk-formula{
  font-family:var(--mono-font);font-size:17px;letter-spacing:.06em;color:var(--ink);
  background:var(--paper);border:1px dashed var(--accent);border-radius:var(--radius);
  padding:15px 14px;text-align:center;margin:0 0 15px;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 6%,transparent);
}

/* INTERRUPT — "AGENT ADVISORY: ...". The only saturated ground in the system.
   Reserving colour-as-fill for exactly one block is what lets it read as an
   interruption rather than as one more panel; if the others were tinted too,
   this would stop meaning anything. */
.blk-callout{
  background:color-mix(in srgb,var(--warn) 11%,var(--paper-2));
  border-left:3px solid var(--warn);border-radius:0 var(--radius) var(--radius) 0;
  padding:12px 14px;margin:15px 0;font-size:14px;color:var(--ink);
}
.blk-callout-tag{
  display:block;font-family:var(--mono-font);font-size:9.5px;letter-spacing:.15em;
  color:var(--warn);margin-bottom:5px;
}

/* WORLD — the inscription. The one block whose content is not from the
   terminal at all: it is text carved on the GPO monument and the statue
   plinths, which the team is standing in front of and reading off stone.

   So it is the only RAISED surface (mixing toward --ink is the one direction
   with headroom on this palette — 1.54 vs the card, where going darker tops
   out at 1.19), and the only one set in a serif. Newsreader was already being
   downloaded by index.html and never used, so the serif costs no request.

   No left rule — chisel hairlines above and below instead — so it reads as
   lifted material rather than another filed panel. This is the deliberate
   genre break in a monospace world, and the break is the point: the fiction
   is that CHRONOS-NET is showing you something physical. */
.blk-quote{
  margin:15px 0;padding:15px 17px;
  font-family:var(--quote-font);font-style:italic;font-size:16.5px;line-height:1.5;
  color:var(--ink);
  background:color-mix(in srgb,var(--ink) 16%,var(--paper-2));
  border:0;border-top:1px solid color-mix(in srgb,var(--ink) 26%,transparent);
  border-bottom:1px solid color-mix(in srgb,var(--ink) 26%,transparent);
  border-radius:0;
}

/* PROCEDURE — flush like the voice blocks, because a numbered list is the
   handler talking in steps, not a separate artefact. Hairline rows give it
   rhythm without making it a panel. */
.blk-list{margin:0 0 15px;padding-left:0;list-style:none}
.blk-list li{
  position:relative;padding:7px 0 7px 22px;color:var(--ink-2);font-size:14.5px;
  border-top:1px solid var(--rule);
}
.blk-list li:first-child{border-top:0}
.blk-list li::before{
  content:"▸";position:absolute;left:4px;color:var(--accent);font-size:12px;top:8px;
}

/* ---- card grounds: depth instead of flat panels ---- */
body{
  background:
    radial-gradient(1100px 620px at 50% -8%, color-mix(in srgb,var(--accent) 7%,transparent), transparent 62%),
    radial-gradient(760px 520px at 100% 100%, color-mix(in srgb,var(--accent-2) 8%,transparent), transparent 60%),
    var(--paper);
  background-attachment:fixed;
}
.card{
  background-image:linear-gradient(180deg, color-mix(in srgb,var(--ink) 3%,transparent), transparent 78px);
  box-shadow:0 1px 0 color-mix(in srgb,var(--ink) 6%,transparent) inset;
}
.bubble{border-left:2px solid var(--accent-2)}
.gate{
  background-image:
    linear-gradient(180deg, color-mix(in srgb,var(--accent) 6%,transparent), transparent 96px);
  box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 12%,transparent),
             0 10px 30px -18px color-mix(in srgb,var(--accent) 55%,transparent);
}
.walk{
  background-image:
    repeating-linear-gradient(135deg, color-mix(in srgb,var(--accent) 5%,transparent) 0 8px, transparent 8px 17px),
    linear-gradient(180deg, color-mix(in srgb,var(--accent) 8%,transparent), transparent 76px);
}
.reveal{background-image:linear-gradient(180deg, color-mix(in srgb,var(--ok) 8%,transparent), transparent 96px)}
.deadend{background-image:linear-gradient(180deg, color-mix(in srgb,var(--bad) 11%,transparent), transparent 76px)}

/* the thread gets a spine so cards read as a sequence */
.thread{position:relative}
.thread::before{
  content:"";position:absolute;left:-1px;top:12px;bottom:12px;width:1px;
  background:linear-gradient(180deg,transparent,var(--rule) 8%,var(--rule) 92%,transparent);
  opacity:.6;
}
@media (max-width:520px){ .thread::before{display:none} }

/* ============================================================
   City index — dossier shelf
   ============================================================ */
.masthead{padding:46px 0 10px}
.masthead .kicker{
  font-family:var(--mono-font);font-size:10px;letter-spacing:.3em;color:var(--ink-3);
  text-transform:uppercase;display:block;margin-bottom:16px;
}
.masthead h1{
  font-family:var(--display-font);font-size:clamp(34px,11vw,52px);line-height:.95;
  letter-spacing:.02em;margin:0 0 14px;color:var(--ink);
}
.masthead p{color:var(--ink-2);font-size:15px;margin:0;max-width:44ch;line-height:1.6}

.cases-bar{
  display:flex;justify-content:space-between;align-items:center;
  margin:32px 0 14px;padding-bottom:9px;border-bottom:1px solid var(--rule);
  font-family:var(--mono-font);font-size:10px;letter-spacing:.22em;
  color:var(--ink-3);text-transform:uppercase;
}
.cases-count{color:var(--accent)}

.cities{display:flex;flex-direction:column;gap:16px}

.city{
  position:relative;display:block;text-align:left;width:100%;cursor:pointer;color:var(--ink);
  background:var(--paper-2);border:1px solid var(--rule);border-radius:var(--radius);
  padding:0;overflow:hidden;
  background-image:linear-gradient(180deg, color-mix(in srgb,var(--cc) 8%,transparent), transparent 130px);
  transition:border-color .18s, transform .18s;
}
.city:not(.sealed):hover{border-color:var(--cc);transform:translateY(-2px)}
.city:not(.sealed):focus-visible{border-color:var(--cc);outline:none}
.city-bar{height:4px;background:var(--cc)}

.city-top{display:flex;gap:14px;align-items:flex-start;padding:17px 16px 0}
.city-id{min-width:0;flex:1}
.city-country{
  font-family:var(--mono-font);font-size:9.5px;letter-spacing:.2em;color:var(--ink-3);
  text-transform:uppercase;margin-bottom:6px;
}
.city-name{font-family:var(--display-font);font-size:29px;line-height:1.05;letter-spacing:.01em;margin-bottom:5px}
.city-case{font-family:var(--display-font);font-size:14px;color:var(--cc);letter-spacing:.03em;margin-bottom:7px}
.city-sub{color:var(--ink-2);font-size:13.5px;margin:0;line-height:1.5}

/* the postmark seal */
.city-seal{
  width:74px;height:74px;flex:none;border-radius:50%;border:1.5px dashed var(--cc);
  display:grid;place-content:center;text-align:center;opacity:.85;
  color:var(--cc);transform:rotate(-7deg);
}
.city-seal-city{display:block;font-family:var(--mono-font);font-size:9px;letter-spacing:.12em;text-transform:uppercase}
.city-seal-era{display:block;font-family:var(--mono-font);font-size:13px;font-weight:700;margin-top:2px}

.city-meta{
  display:flex;justify-content:space-between;align-items:flex-end;gap:12px;
  padding:15px 16px 0;
}
.city-gm{display:flex;flex-direction:column;gap:2px;min-width:0}
.city-gm-l{font-family:var(--mono-font);font-size:9px;letter-spacing:.2em;color:var(--ink-3);text-transform:uppercase}
.city-gm-n{font-family:var(--display-font);font-size:13.5px;color:var(--ink)}
.city-stats{display:flex;gap:12px;font-family:var(--mono-font);font-size:11px;color:var(--ink-3);flex-wrap:wrap;justify-content:flex-end}

.city-foot{
  display:flex;justify-content:space-between;align-items:center;
  margin:15px 0 0;padding:12px 16px;border-top:1px solid var(--rule);
  background:color-mix(in srgb,var(--ink) 3%,transparent);
}
.city-price{font-family:var(--mono-font);font-size:18px;font-weight:700;color:var(--ink)}
.city-cta{font-family:var(--mono-font);font-size:11px;letter-spacing:.14em;color:var(--cc);text-transform:uppercase}

.city.sealed{opacity:.62;cursor:not-allowed}
.city.sealed .city-name,.city.sealed .city-case{color:var(--ink-3)}
.city-stamp{
  position:absolute;top:52px;right:14px;transform:rotate(-11deg);
  border:2px solid var(--bad);color:var(--bad);border-radius:2px;
  font-family:var(--label-font);font-size:13px;font-weight:700;letter-spacing:.2em;
  text-transform:uppercase;padding:5px 12px;opacity:.75;pointer-events:none;
}

.index-foot{
  margin:26px 0 0;text-align:center;color:var(--ink-3);
  font-family:var(--mono-font);font-size:10.5px;letter-spacing:.08em;
}

@media (max-width:400px){
  .city-seal{width:60px;height:60px}
  .city-name{font-size:25px}
}

/* ---------- skip a stop (feedback item 1) ---------- */
.skip-stop{margin-top:12px;padding-top:10px;border-top:1px dashed var(--rule)}
.skip-stop .skip-btn{width:100%;text-align:center}
.skip-stop .skip-btn.armed{border-color:var(--warn);color:var(--warn)}
.skip-stop .tier-warn{margin-bottom:8px}

/* ---------- certificate personalization (feedback item 4) ---------- */
.cert-pers{margin-top:12px;border-top:1px solid var(--rule);padding-top:8px}
.cert-pers summary{
  cursor:pointer;font-family:var(--label-font,var(--mono-font));
  font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);
}
.cert-pers-input{
  display:block;width:100%;margin:10px 0;padding:10px 12px;
  font-family:var(--mono-font);font-size:14px;color:var(--ink);
  background:var(--paper-2);border:1px solid var(--rule);border-radius:6px;
}
.cert-pers-photo{position:relative;display:inline-block;margin-right:10px;cursor:pointer}

/* ---------- per-stop scorecard breakdown (feedback item 2) ---------- */
.scorecard-detail{margin-top:8px;border-top:1px solid var(--rule);padding-top:6px}
.scorecard-detail summary{
  cursor:pointer;font-family:var(--label-font,var(--mono-font));
  font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-3);
  padding:4px 0;
}
.scorecard-detail .scorecard-row{font-size:12px}

/* note under a gate prompt (team-name → leaderboard disclosure) */
.gate-note{font-size:.86rem;color:var(--slate,#405065);margin:-.25rem 0 .6rem;line-height:1.35}
