/* ============================================================
   DejaClue 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;
  --ink:#dcebf2; --ink-2:#8fa9b8; --ink-3:#5b7788;
  --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:#4a4640; --ink-3:#7b7467;
  --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:16px; 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}
.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}
@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:14.5px;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)}
.gate-body{white-space:pre-wrap;color:var(--ink-2);font-size:14.5px;margin-bottom:13px}
.gate-prompt{font-family:var(--display-font);font-size:15.5px;margin:0 0 11px;color:var(--ink)}

.field{
  width:100%;background:transparent;border:0;border-bottom:1px solid var(--ink-3);
  color:var(--ink);font-family:var(--mono-font);font-size:19px;padding:9px 2px;
  letter-spacing:.14em;
}
.field:focus{outline:none;border-bottom-color:var(--accent)}
.field::placeholder{color:var(--ink-3);letter-spacing:.06em}

/* code = per-character slots */
.slots{display:flex;gap:7px;margin-bottom:11px}
.slot{
  flex:1;max-width:52px;aspect-ratio:3/4;border:1px solid var(--ink-3);border-radius:var(--radius);
  display:grid;place-items:center;font-family:var(--mono-font);font-size:23px;background:var(--paper);
}
.slot[data-filled]{border-color:var(--accent);color:var(--accent)}

/* choice = MCQ options */
.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;
}
.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 img{width:62px;height:62px;object-fit:contain;flex:none;background:var(--paper-3);border-radius:2px}
.opt-label{font-weight:700;display:block;margin-bottom:3px}
.opt-detail{color:var(--ink-3);font-size:12.5px;line-height:1.45}

/* grid = worksheet matrix */
.gridwrap{overflow-x:auto;margin-bottom:11px;-webkit-overflow-scrolling:touch}
table.matrix{border-collapse:collapse;width:100%;min-width:380px;font-size:12.5px}
table.matrix th,table.matrix td{border:1px solid var(--rule);padding:7px;text-align:center}
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}

/* cipher decoder tool */
.decoder{background:var(--paper);border:1px dashed var(--ink-3);border-radius:var(--radius);padding:12px;margin-bottom:11px}
.cipher-text{font-family:var(--mono-font);font-size:23px;letter-spacing:.34em;color:var(--accent);text-align:center;margin:5px 0 11px;word-break:break-all}
.shift-row{display:flex;align-items:center;gap:11px}
.shift-row input[type=range]{flex:1;accent-color:var(--accent)}
.decoded{font-family:var(--mono-font);font-size:19px;letter-spacing:.24em;text-align:center;margin-top:11px;color:var(--ink);word-break:break-all}

/* 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-row{display:flex;gap:10px;align-items:center}
.frag-row .field{flex:1;font-size:15px;letter-spacing:.06em}
.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:11.5px;
  letter-spacing:.14em;color:var(--ink-3);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-2);
  padding:7px 15px;font-family:var(--mono-font);font-size:12px;cursor:pointer;min-height:38px;
}
.tier-btn:hover{border-color:var(--accent);color:var(--accent)}
.tier-body{margin-top:9px;color:var(--ink-2);font-size:14px;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:13.5px;color:var(--ink-3);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}

/* ---------------- 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-3);font-size:13.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, "🔸 Tejas ➔ Value: 15".
   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}
}
