/* ============================================================
   GODMODE client — screen layouts over the godmode.css system.
   One 1920×1080 stage, scaled to the window by js/fit.js.
   ============================================================ */

body { overflow: hidden; }

.hidden { display: none !important; }

/* ================================================== title screen */

#screen-title .center {
  position: absolute; inset: 0; z-index: 10;
  display: flex; flex-direction: column; align-items: center;
  padding-top: 208px;
}
#screen-title .tagline {
  margin-top: 34px;
  font-family: var(--f-ui); font-weight: 500; font-size: 24px;
  letter-spacing: .52em; text-transform: uppercase; color: var(--dim);
  text-indent: .52em;
}
#screen-title .menu { margin-top: 96px; display: flex; flex-direction: column; gap: 22px; width: 480px; }
#screen-title .menu .btn { width: 100%; }
#screen-title .foot {
  position: absolute; bottom: 34px; left: 0; right: 0; z-index: 10;
  display: flex; justify-content: space-between; padding: 0 48px;
  font-family: var(--f-data); font-size: 14px; color: var(--steel-d);
}
/* first-visit privacy consent — one compact bar above the foot, shown once
   (localStorage 'godmode_consent'); kept intentionally small so it barely
   intrudes on the title screen */
#screen-title .consent {
  /* was bottom:84px — with a taller menu (4 buttons, "Continue" showing)
     this sat close enough to overlap the Leaderboard button. Top of the
     stage is empty space (the ribbon lives in the corner, not center). */
  position: absolute; left: 50%; top: 24px; transform: translateX(-50%);
  z-index: 12; display: flex; align-items: center; gap: 18px;
  max-width: 1060px; padding: 10px 14px 10px 20px;
  background: rgba(11, 19, 34, .82); backdrop-filter: blur(6px);
  border: 1px solid var(--edge); border-radius: 10px;
  font-family: var(--f-data); font-size: 13px; line-height: 1.45; color: var(--dim);
  box-shadow: 0 8px 30px rgba(0, 0, 0, .45);
}
#screen-title .consent .consent-ok {
  flex: none; cursor: pointer; white-space: nowrap;
  padding: 6px 16px; border: 1px solid var(--edge-bright); border-radius: 7px;
  background: rgba(47, 157, 232, .14); color: var(--cyan);
  font-weight: 700; letter-spacing: .04em;
}
#screen-title .consent .consent-ok:hover { background: rgba(47, 157, 232, .26); color: var(--text); }
/* welcome-screen planet backdrop — the same space.jpg the game console shows,
   carried up brighter so it reads as the hero image behind the wordmark */
#screen-title .bg-planet {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: url(../assets/bg/space.jpg) center / cover no-repeat;
  opacity: .5;
}
/* ...and once the world has fallen, the menu belongs to the world that fell.
   Stepping out to the title mid-spiral used to show Earth from orbit at .5
   over the ruin wash the stage carries at .16 — two backdrops in one frame.
   The hero layer becomes the ruins (same center/cover box as the stage wash,
   so they land pixel-aligned and simply read as one image), and the star
   field goes: there is nothing up there to look at anymore. */
body.spiral #screen-title .bg-planet {
  background-image: url(../assets/bg/spiral.jpg);
  filter: saturate(.62) brightness(.9);
}
body.spiral #screen-title .bg-stars,
body.spiral #screen-title .halo { display: none; }
#screen-title .halo {
  position: absolute; top: 96px; left: 50%; transform: translateX(-50%);
  width: 980px; height: 460px; z-index: 5; pointer-events: none; opacity: .5;
}

/* starfield: a scatter of static pin-lights that breathe slowly, plus two
   shooting stars that sweep the sky on a long, offset cycle so they read as
   occasional — not a constant rain. Pure CSS, sits above the planet backdrop
   and below the wordmark (z 1). */
#screen-title .bg-stars {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background-repeat: no-repeat;
  background-image:
    radial-gradient(1.4px 1.4px at 12% 22%, #cfefff 40%, transparent),
    radial-gradient(1.2px 1.2px at 78% 14%, #bfe4ff 40%, transparent),
    radial-gradient(1px 1px   at 34% 68%, #9fd4ff 40%, transparent),
    radial-gradient(1.6px 1.6px at 62% 40%, #ffffff 45%, transparent),
    radial-gradient(1px 1px   at 88% 58%, #a8d8ff 40%, transparent),
    radial-gradient(1.2px 1.2px at 24% 84%, #d6f0ff 40%, transparent),
    radial-gradient(1px 1px   at 52% 12%, #bfe4ff 40%, transparent),
    radial-gradient(1.3px 1.3px at 6% 54%, #e6f6ff 40%, transparent),
    radial-gradient(1px 1px   at 70% 78%, #9fd4ff 40%, transparent),
    radial-gradient(1.1px 1.1px at 44% 30%, #ffffff 40%, transparent);
  animation: star-breathe 6.5s ease-in-out infinite;
}
@keyframes star-breathe { 0%, 100% { opacity: .55; } 50% { opacity: .9; } }

/* the two shooting stars — each a tiny distant dot (the bright head) with a
   short fading tail trailing behind it. The element lies along its travel axis
   (rotate 150deg → points down-left), the bright dot at the leading +x end, so
   translateX carries the head forward and the tail always drags behind. Each
   idles invisible off the far corner for the rest of a long, offset cycle so a
   pass is a rare event, not a constant rain. */
#screen-title .bg-stars::before,
#screen-title .bg-stars::after {
  content: ""; position: absolute; top: -4%; left: 88%;
  width: 46px; height: 1.5px; border-radius: 1px; opacity: 0;
  background: linear-gradient(90deg, transparent 0%, rgba(180,222,255,.35) 78%, #eaf6ff 100%);
  box-shadow: 0 0 4px .5px rgba(200,232,255,.6);
  transform: rotate(150deg) translateX(0);
  will-change: transform, opacity;
}
#screen-title .bg-stars::before { animation: star-shoot 11s ease-in 3s infinite; }
#screen-title .bg-stars::after  { top: 6%; left: 96%; width: 38px; animation: star-shoot 17s ease-in 9s infinite; }
@keyframes star-shoot {
  0%   { opacity: 0; transform: rotate(150deg) translateX(0); }
  2.5% { opacity: .7; }
  13%  { opacity: 0; transform: rotate(150deg) translateX(135vmax); }
  100% { opacity: 0; transform: rotate(150deg) translateX(135vmax); }
}
@media (prefers-reduced-motion: reduce) {
  #screen-title .bg-stars { animation: none; }
  #screen-title .bg-stars::before,
  #screen-title .bg-stars::after { animation: none; }
}

/* ================================================== game console */

.console { position: absolute; inset: 0; z-index: 10; display: flex; flex-direction: column; gap: 14px; padding: 20px 28px; }

.topbar { height: 92px; flex: 0 0 92px; display: flex; align-items: center; padding: 0 32px; gap: 40px; }
.topbar .year { font-family: var(--f-display); font-weight: 700; font-size: 40px; letter-spacing: .08em; color: var(--cyan); text-shadow: 0 0 18px rgba(94,200,247,.45); }
.topbar .spacer { flex: 1; }
.topbar .chip { cursor: pointer; }
.ph { display: flex; align-items: center; gap: 18px; }
.ph .gauge { width: 74px; height: 74px; }
.ph .g-val { font-size: 22px; }

.grid-metrics { display: flex; gap: 14px; }
.grid-metrics .metric { flex: 1; }
.grid-markets { display: flex; gap: 14px; }
.market { flex: 1; height: 86px; display: flex; align-items: center; padding: 0 22px; gap: 18px; }
.market .m-label { position: static; }
.market .m-value { font-size: 26px; }
/* fixed size, not top+bottom: .market is a flex row (label/spark/value/delta
   side by side), so the spark is a flex item, not an absolute overlay —
   `top`/`bottom` on a position:relative replaced element only nudges it,
   doesn't size it, so height still fell back to the intrinsic 300:70
   viewBox ratio applied to its flex-shrunk width and overflowed downward
   past the row (same root cause as .metric .spark above). Fixed width/height
   + flex:none sidesteps the whole replaced-element sizing ambiguity; the
   row's own align-items:center handles vertical placement. */
.market .spark { width: 100px; height: 32px; flex: none; opacity: .55; }

.mid { flex: 1; display: flex; gap: 16px; min-height: 0; }

/* 612px, not a round 600 — matches the markets row's actual computed chip
   width exactly: (1864px content width - 2*14px gap) / 3 columns = 612px.
   600px was 12px short, so the map's right edge cut across the middle of
   the "Food price" chip above it instead of lining up with a column edge. */
.nodes { width: 612px; flex: 0 0 612px; padding: 24px 28px; display: flex; flex-direction: column; overflow: hidden; }
.node-row { display: flex; flex-direction: column; padding: 13px 0; border-bottom: 1px solid rgba(46,91,138,.16); }
.node-row:last-child { border-bottom: 0; }
.node-name { font-family: var(--f-ui); font-weight: 600; font-size: 21px; letter-spacing: .06em; }
.node-stats { font-family: var(--f-data); font-size: 14px; color: var(--dim); margin-top: 4px; padding-left: 19px; }

/* mini live map — a preview of the same choropleth openMap() shows full-screen;
   the whole .nodes plate is already click-to-open (data-act="map-open" above) */
.mini-map-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.mini-map { flex: 1; width: 100%; min-height: 0; }
.mini-map .mini-country { stroke: rgba(46,91,138,.35); stroke-width: .6; transition: fill-opacity .15s; }
.nodes--clickable:hover .mini-map .mini-country { fill-opacity: .85; }
.mini-map-loading {
  flex: 1; display: flex; align-items: center; justify-content: center;
  font-family: var(--f-ui); font-size: 14px; letter-spacing: .08em; text-transform: uppercase; color: var(--dim);
}
.mini-map-legend { display: flex; gap: 16px; margin-top: 10px; font-family: var(--f-data); font-size: 12px; color: var(--dim); }
.mini-map-legend .row { display: flex; align-items: center; gap: 6px; }
.mini-map-legend .sw { width: 9px; height: 9px; border-radius: 2px; }

/* event card / interlude / report share this plate */
.card { flex: 1; padding: 30px 40px; display: flex; flex-direction: column; min-width: 0; }
.card-head { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.card-title { font-family: var(--f-ui); font-weight: 700; font-size: 40px; letter-spacing: .04em; text-transform: uppercase; color: var(--cyan); text-shadow: 0 0 22px rgba(94,200,247,.35); margin-top: 16px; }
/* Everything above the buttons lives in here and scrolls; the buttons are a
   sibling and never move. Before this, .card-body carried overflow-y itself —
   which does nothing in a flex column without min-height:0, so a long
   consequence (feedback + deltas + a five-headline wire) grew the card past
   the plate: the text clipped mid-line and the Continue button went off the
   bottom, out of reach. */
.card-scroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: rgba(94,200,247,.35) transparent;
}
.card-scroll::-webkit-scrollbar { width: 8px; }
.card-scroll::-webkit-scrollbar-thumb {
  background: rgba(94,200,247,.3); border-radius: 4px;
}
.card-scroll::-webkit-scrollbar-thumb:hover { background: rgba(94,200,247,.5); }
.card-body { margin-top: 18px; max-width: 1180px; font-size: 23px; line-height: 1.6; }
.card-actions { flex: none; margin-top: auto; padding-top: 20px; display: flex; gap: 22px; }
.card-actions .action { flex: 1; display: flex; flex-direction: column; gap: 10px; min-width: 0; }
/* Card option labels are full sentences by design (min-height accounts for
   2-3 lines) — this is the one button that WANTS to wrap, so it opts back
   out of the base .btn nowrap rule (see godmode.css). */
.card-actions .btn { width: 100%; min-height: 96px; font-size: 20px; line-height: 1.35; padding: 18px 28px; white-space: normal; }
.discard { align-self: center; font-family: var(--f-ui); font-size: 15px; letter-spacing: .12em; text-transform: uppercase; color: var(--steel-d); cursor: pointer; background: none; border: 0; }
.discard:hover { color: var(--bad); }
.discard-note { align-self: center; font-family: var(--f-ui); font-size: 15px; letter-spacing: .12em; text-transform: uppercase; color: var(--steel-d); }

/* consequence deltas + newswire */
.deltas { font-family: var(--f-data); font-size: 17px; color: var(--cyan); margin-top: 16px; }
.wire { margin-top: 22px; }
.wire-item { display: flex; gap: 14px; margin-top: 10px; }
.wire-item .rg { font-family: var(--f-ui); font-weight: 600; font-size: 15px; letter-spacing: .1em; text-transform: uppercase; color: var(--cyan); white-space: nowrap; }

/* one highlighted, mechanically-meaningful headline — pinned above the wire */
.news-flag {
  display: flex; align-items: center; gap: 16px;
  margin: 4px 0 18px; padding: 13px 18px;
  border-left: 3px solid var(--cyan);
  background: linear-gradient(90deg, rgba(94,200,247,.14), rgba(94,200,247,.02));
  box-shadow: inset 0 0 0 1px rgba(94,200,247,.22);
}
.news-flag .nf-icon { font-size: 26px; line-height: 1; color: var(--cyan); text-shadow: 0 0 12px currentColor; }
.news-flag .nf-body { display: flex; flex-direction: column; gap: 3px; }
.news-flag .nf-tag { font-family: var(--f-ui); font-weight: 700; font-size: 13px; letter-spacing: .18em; text-transform: uppercase; color: var(--cyan); }
.news-flag .nf-text { font-family: var(--f-body); font-size: 18px; line-height: 1.45; color: var(--text); }
/* warning = amber/danger, echo = red reckoning, foreshadow keeps the cyan default */
.news-flag--warning { border-left-color: var(--warn); background: linear-gradient(90deg, rgba(255,180,84,.15), rgba(255,180,84,.02)); box-shadow: inset 0 0 0 1px rgba(255,180,84,.28); }
.news-flag--warning .nf-icon, .news-flag--warning .nf-tag { color: var(--warn); }
.news-flag--echo { border-left-color: var(--bad); background: linear-gradient(90deg, rgba(255,107,97,.15), rgba(255,107,97,.02)); box-shadow: inset 0 0 0 1px rgba(255,107,97,.28); }
.news-flag--echo .nf-icon, .news-flag--echo .nf-tag { color: var(--bad); }

/* report rows */
.report-rows { margin-top: 22px; max-width: 900px; }
.report-rows .readout td { font-size: 19px; padding: 10px 0; }

.panel-x { margin-left: auto; }

/* controls */
.controls { height: 58px; flex: 0 0 58px; display: flex; gap: 14px; align-items: center; }
.controls .btn { height: 52px; }
.controls .spacer { flex: 1; }
/* fresh-headlines dot on the Archive button (mirrors the mobile nav badge) */
.news-dot { display: inline-block; width: 7px; height: 7px; margin-left: 8px;
  border-radius: 50%; vertical-align: middle; background: var(--cyan);
  box-shadow: 0 0 8px rgba(94,200,247,.8); }

/* ================================================== overlays */

.overlay {
  position: absolute; inset: 0; z-index: 40;
  background: rgba(4, 7, 13, .97);
  display: flex; flex-direction: column; gap: 18px; padding: 26px 32px;
}
.ov-head { display: flex; align-items: center; gap: 28px; }
.ov-title { font-family: var(--f-display); font-weight: 700; font-size: 40px; letter-spacing: .1em; text-transform: uppercase; color: var(--steel-l); }
.ov-title .yr, .ov-title .cyan { color: var(--cyan); text-shadow: 0 0 18px rgba(94,200,247,.5); }
.ov-head .spacer { flex: 1; }
.ov-sub { font-family: var(--f-ui); font-size: 19px; letter-spacing: .1em; text-transform: uppercase; color: var(--dim); }
.ov-sub.hot { color: var(--good); }

/* war room grid */
.wr-grid { flex: 1; display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: 1fr 1fr; gap: 18px; min-height: 0; }
.domain { padding: 22px 26px; display: flex; flex-direction: column; min-height: 0; }
.domain .d-title { font-family: var(--f-ui); font-weight: 700; font-size: 25px; letter-spacing: .07em; text-transform: uppercase; color: var(--cyan); }
.domain .d-advisor { margin-top: 6px; font-family: var(--f-body); font-size: 17px; color: var(--text); }
.domain .d-role { font-family: var(--f-ui); font-size: 14px; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); margin-top: 2px; }
.domain .readout { margin-top: 12px; flex: 1; }
.domain .readout td { padding: 5px 0; font-size: 16px; }
.domain .btn { margin-top: 12px; width: 100%; }
.domain .hand { position: absolute; top: 14px; right: 18px; font-size: 30px; z-index: 3; filter: drop-shadow(0 0 10px rgba(99,230,165,.8)); }
.domain--hot { background: linear-gradient(160deg, rgba(99,230,165,.75), var(--edge) 45%, rgba(99,230,165,.5)); }
.domain--hot::before { background: linear-gradient(172deg, #0F2416 0%, #0B1322 40%, #081020 100%); }

/* advisor desk */
.desk-body { flex: 1; display: flex; gap: 18px; min-height: 0; }
.profile { width: 560px; flex: 0 0 560px; padding: 30px 34px; display: flex; flex-direction: column; overflow: hidden; }
.avatar {
  width: 128px; height: 128px; margin-bottom: 20px;
  clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
  background:
    radial-gradient(circle at 50% 34%, rgba(94,200,247,.5) 0%, rgba(94,200,247,.12) 46%, transparent 70%),
    linear-gradient(180deg, #14263E, #0A1424);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--f-display); font-weight: 900; font-size: 46px; color: var(--cyan);
  text-shadow: 0 0 22px rgba(94,200,247,.8);
  box-shadow: inset 0 0 0 1px var(--edge-bright);
}
.p-name { font-family: var(--f-ui); font-weight: 700; font-size: 34px; letter-spacing: .04em; color: var(--text); }
.p-role { font-family: var(--f-ui); font-weight: 600; font-size: 17px; letter-spacing: .18em; text-transform: uppercase; color: var(--cyan); margin-top: 6px; }
.p-since { font-family: var(--f-data); font-size: 14px; color: var(--dim); margin-top: 8px; }
.p-bio { margin-top: 18px; }

.consult { flex: 1; padding: 26px 30px; display: flex; flex-direction: column; min-width: 0; }
.chat { flex: 1; margin-top: 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 16px; padding-right: 18px; }
.msg { max-width: 82%; padding: 14px 20px; flex-shrink: 0; }
.msg .who { font-family: var(--f-ui); font-weight: 600; font-size: 13px; letter-spacing: .2em; text-transform: uppercase; margin-bottom: 6px; }
.msg--you { align-self: flex-end; }
.msg--you .who { color: var(--cyan); text-align: right; }
.msg--adv .who { color: var(--dim); }
.msg--pitch { box-shadow: inset 0 0 0 1px rgba(99,230,165,.35); }
.msg--pitch .who { color: var(--good); }
.ask-row { margin-top: 18px; display: flex; gap: 14px; align-items: stretch; }
.ask-input { flex: 1; }
.ask-input input {
  width: 100%; height: 58px; padding: 0 22px;
  background: transparent; border: 0; outline: none;
  font-family: var(--f-body); font-size: 18px; color: var(--text);
}
.ask-input input::placeholder { color: var(--dim); font-style: italic; }

/* candidate picker */
.cands { flex: 1; display: flex; flex-direction: column; gap: 18px; min-height: 0; overflow-y: auto; }
.cand { padding: 24px 30px; display: flex; align-items: center; gap: 30px; flex-shrink: 0; }
.cand .c-info { flex: 1; min-width: 0; }
.cand .c-name { font-family: var(--f-ui); font-weight: 700; font-size: 26px; color: var(--cyan); }
.cand .c-role { font-family: var(--f-ui); font-size: 14px; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); margin-top: 2px; }
.cand .c-bio { margin-top: 10px; }

/* history */
.hist-list { flex: 1; overflow-y: auto; padding-right: 20px; }
.hist-item { padding: 14px 4px; border-bottom: 1px solid rgba(46,91,138,.16); }
.hist-item .h-head { font-family: var(--f-ui); font-weight: 600; font-size: 20px; letter-spacing: .04em; }
.hist-item .h-head .h-year { font-family: var(--f-data); color: var(--cyan); margin-right: 14px; }
.hist-item .h-choice { font-family: var(--f-body); font-size: 16px; color: var(--dim); margin-top: 5px; padding-left: 4px; }

/* leaderboard */
.lb-stats {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-family: var(--f-data); font-size: 13px; color: var(--dim);
  padding: 0 30px 14px;
}
.lb-stats b { color: var(--cyan); font-weight: 700; }
.lb-stats-sep { color: var(--steel-d); }
.lb-list { flex: 1; overflow-y: auto; padding-right: 20px; display: flex; flex-direction: column; gap: 2px; }
.lb-row { display: flex; align-items: flex-start; gap: 18px; padding: 14px 4px; border-bottom: 1px solid rgba(46,91,138,.16); }
.lb-rank { font-family: var(--f-data); font-size: 20px; color: var(--dim); min-width: 44px; flex-shrink: 0; padding-top: 2px; }
.lb-main { flex: 1; min-width: 0; }
.lb-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
.lb-name { font-family: var(--f-ui); font-weight: 700; font-size: 20px; letter-spacing: .04em; text-transform: uppercase; color: var(--cyan); }
.lb-score { font-family: var(--f-data); font-weight: 700; font-size: 24px; }
.lb-score.good { color: var(--good); }
.lb-score.bad { color: var(--bad); }
.lb-sub { font-family: var(--f-body); font-size: 15px; color: var(--dim); margin-top: 4px; }
.lb-detail { font-family: var(--f-data); font-size: 13px; color: var(--dim); margin-top: 3px; opacity: .8; }

/* solutions */
.sol-panel { width: 860px; margin: auto; padding: 40px 48px; display: flex; flex-direction: column; max-height: 820px; }
.sol-list { margin-top: 24px; display: flex; flex-direction: column; gap: 16px; overflow-y: auto; }
.sol-row { display: flex; align-items: center; gap: 24px; padding: 8px 0; border-bottom: 1px solid rgba(46,91,138,.16); }
.sol-row .s-name { flex: 1; font-family: var(--f-ui); font-weight: 600; font-size: 21px; letter-spacing: .08em; text-transform: uppercase; }
.sol-row .s-due { font-family: var(--f-data); font-size: 15px; color: var(--good); }

/* finale */
.fin-nick { margin-top: 20px; display: flex; align-items: center; gap: 14px; }
.fin-nick input {
  flex: 1; height: 52px; padding: 0 20px;
  background: transparent; border: 0; outline: none;
  font-family: var(--f-body); font-size: 16px; color: var(--text);
}
.fin-nick input::placeholder { color: var(--dim); font-style: italic; }
.fin-nick-done { margin-top: 20px; font-family: var(--f-ui); font-size: 15px; letter-spacing: .06em; color: var(--good); }
.fin { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
/* One screen, no scroll (2026-07-14): was a single stacked column that
   overflowed max-height and clipped the action buttons under `overflow:
   hidden`; a plain overflow-y:auto fix worked but read as an odd first use
   of scroll on a finale that should read as a single tableau. Two columns
   (narrative+score on the left, stats+advisors on the right) use the
   panel's already-generous width instead of stacking everything vertically,
   plus trimmed font sizes/spacing throughout -- fits comfortably within
   90vh on a 720px-tall viewport without scrolling. */
/* A run with a full cabinet + epilogue can push the two columns past 90vh, so
   this has to scroll — but NOT on the .cham element itself. .cham paints its
   plate with an absolutely-positioned ::before sized to the visible box, so
   scrolling the chamfer slid the content out from under its own background:
   the overflow ran on past the plate as a bare stripe over the overlay, with
   the buttons half outside the frame. The frame stays fixed; .fin-scroll
   moves underneath it. */
.fin-panel { width: 1360px; max-width: 94vw; max-height: 90vh; padding: 24px 48px; display: flex; flex-direction: column; overflow: hidden; }
.fin-scroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain; padding-right: 8px;
  scrollbar-width: thin; scrollbar-color: rgba(94,200,247,.35) transparent;
}
.fin-scroll::-webkit-scrollbar { width: 8px; }
.fin-scroll::-webkit-scrollbar-thumb { background: rgba(94,200,247,.3); border-radius: 4px; }
.fin-body { display: grid; grid-template-columns: 1.1fr 1fr; gap: 0 48px; align-items: start; }
.fin-col--main { min-width: 0; }
.fin-col--side { min-width: 0; padding-top: 2px; }
.fin-tag { font-family: var(--f-ui); font-weight: 700; font-size: 15px; letter-spacing: .26em; text-transform: uppercase; }
.fin-title { font-family: var(--f-display); font-weight: 900; font-size: 32px; letter-spacing: .03em; text-transform: uppercase; color: var(--steel-l); margin-top: 6px; }
.fin-title .yr { color: var(--cyan); text-shadow: 0 0 20px rgba(94,200,247,.5); }
.fin-blurb { margin-top: 10px; }
.fin-after { margin-top: 6px; color: var(--dim); }
.score-row { display: flex; align-items: center; gap: 22px; margin-top: 12px; flex-wrap: wrap; }
.score-num { font-family: var(--f-data); font-weight: 700; font-size: 36px; }
.score-num small { font-size: 18px; color: var(--dim); font-weight: 400; }
.tier { font-family: var(--f-ui); font-weight: 700; font-size: 15px; letter-spacing: .16em; text-transform: uppercase; color: var(--warn); }
.numbers { display: flex; gap: 44px; margin-top: 22px; padding: 18px 28px; flex-wrap: wrap; }
.numbers--fin { gap: 10px 20px; padding: 10px 16px; margin-top: 0; }
.numbers .n { display: flex; flex-direction: column; gap: 6px; }
.numbers .n .k { font-family: var(--f-ui); font-weight: 600; font-size: 12px; letter-spacing: .18em; text-transform: uppercase; color: var(--dim); }
.numbers .n .v { font-family: var(--f-data); font-size: 18px; color: var(--text); }
.numbers--fin .n .v { font-size: 14px; }
.numbers--fin .n { gap: 3px; }
.fin-adv-head { margin-top: 8px; }
.reveal-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 3px 14px; margin-top: 8px; }
.rv { display: flex; align-items: center; gap: 6px; border-bottom: 1px solid rgba(46,91,138,.18); padding: 4px 0; min-width: 0; }
.rv .who { flex: 1; min-width: 0; display: flex; flex-direction: column; font-family: var(--f-body); font-size: 12px; color: var(--text); line-height: 1.25; }
.rv .who-name, .rv .who small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rv .who small { display: block; font-size: 9px; color: var(--dim); }
.rv .type { flex: 0 0 auto; font-family: var(--f-ui); font-weight: 700; font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: var(--cyan); }
.rv .type.fool { color: var(--bad); }
.fin-actions { display: flex; flex-wrap: wrap; gap: 14px 22px; margin-top: 14px; }

/* -- finale, desktop only (2026-07-20) -------------------------------------
   The rules above sized the panel in vh/vw, but fit.js scales the whole
   1920x1080 .stage to the window — so 90vh/94vw shrank the finale *inside* an
   already-shrinking canvas, and the 12/9px type it had been trimmed to became
   unreadable. Worse, on a tall window the vh-sized panel outgrew the 1080px
   stage and the page itself scrolled, exposing the body backdrop below it.
   Desktop now sizes the finale in stage pixels: one fixed tableau that fills
   the canvas, never scrolls, and reads at full size. Mobile is untouched —
   it keeps its own scrolling single-column layout in css/mobile.css. */
/* Height is content-driven, not a fixed 940px: pinning it to the canvas left a
   dead band across the middle of the panel. The 1000px cap keeps it inside the
   1080px stage (so the page itself can never scroll). The overflow safety
   valve moved to .fin-scroll inside — this rule used to carry overflow-y:auto
   itself, which scrolled the .cham plate out from under its own painted
   background and left the tail of a long run as a bare stripe with the
   buttons half outside the frame. */
body:not(.is-mobile) .fin-panel {
  width: 1740px; max-width: 1740px;
  max-height: 1000px;
  padding: 36px 56px 30px;
  overflow: hidden;
}
body:not(.is-mobile) .fin-body {
  grid-template-columns: 1.06fr 1fr; gap: 0 56px;
  align-items: stretch;          /* both columns share one height, no ragged foot */
}
body:not(.is-mobile) .fin-col { min-height: 0; display: flex; flex-direction: column; }
body:not(.is-mobile) .fin-tag { font-size: 19px; }
body:not(.is-mobile) .fin-title { font-size: 46px; margin-top: 10px; }
body:not(.is-mobile) .fin-col--main .ov-sub { font-size: 17px; }
body:not(.is-mobile) .fin-blurb { font-size: 19px; line-height: 1.5; margin-top: 16px; }
body:not(.is-mobile) .fin-after { font-size: 19px; line-height: 1.5; margin-top: 12px; }
body:not(.is-mobile) .score-row { margin-top: 24px; gap: 30px; }
body:not(.is-mobile) .score-num { font-size: 60px; }
body:not(.is-mobile) .score-num small { font-size: 26px; }
body:not(.is-mobile) .tier { font-size: 19px; }
body:not(.is-mobile) .fin-nick,
body:not(.is-mobile) .fin-nick-done { margin-top: 26px; }
body:not(.is-mobile) .fin-nick input { height: 62px; font-size: 19px; }
body:not(.is-mobile) .fin-nick-done { font-size: 19px; }
body:not(.is-mobile) .numbers--fin { gap: 14px 30px; padding: 18px 24px; }
body:not(.is-mobile) .numbers--fin .n .k { font-size: 14px; }
body:not(.is-mobile) .numbers--fin .n .v { font-size: 21px; }
body:not(.is-mobile) .fin-adv-head { margin-top: 22px; font-size: 15px; }
body:not(.is-mobile) .reveal-grid { grid-template-columns: 1fr 1fr; gap: 4px 30px; margin-top: 12px; }
body:not(.is-mobile) .rv { gap: 12px; padding: 9px 0; }
body:not(.is-mobile) .rv-portrait { width: 46px; height: 60px; flex: 0 0 46px; }
body:not(.is-mobile) .rv .who { font-size: 17px; }
body:not(.is-mobile) .rv .who small { font-size: 13px; }
body:not(.is-mobile) .rv .type { font-size: 12px; }
body:not(.is-mobile) .fin-epilogue { margin-top: 18px; font-size: 17px; padding: 12px 18px; }
body:not(.is-mobile) .fin-actions { margin-top: 26px; gap: 18px 26px; }

/* toast — transient error notice */
#toast {
  position: absolute; bottom: 40px; left: 50%; transform: translateX(-50%);
  z-index: 95; padding: 14px 28px;
  font-family: var(--f-ui); font-size: 17px; letter-spacing: .06em;
  color: var(--bad); background: rgba(20, 8, 8, .92);
  box-shadow: inset 0 0 0 1px rgba(255,107,97,.45);
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  opacity: 0; pointer-events: none; transition: opacity .25s;
}
#toast.show { opacity: 1; }

/* ================================================== newswire overlay */

.news-list { flex: 1; overflow-y: auto; padding-right: 20px; display: flex; flex-direction: column; gap: 26px; }
.news-year { }
.news-year .ny-head { font-family: var(--f-display); font-weight: 700; font-size: 26px; letter-spacing: .1em; color: var(--cyan); text-shadow: 0 0 14px rgba(94,200,247,.4); margin-bottom: 14px; }
.news-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.news-item { display: flex; gap: 0; overflow: hidden; }
.news-item .n-art { width: 220px; flex: 0 0 220px; align-self: stretch; }
.news-item .n-art svg,
.news-item .n-art img { width: 100%; height: 100%; display: block; object-fit: cover; }
.news-item .n-body { padding: 16px 20px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.news-item .n-tag { font-family: var(--f-ui); font-weight: 600; font-size: 12px; letter-spacing: .2em; text-transform: uppercase; color: var(--dim); }
.news-item .n-headline { font-family: var(--f-body); font-size: 17px; line-height: 1.45; color: var(--text); }

/* ================================================== tutorial */

#tutorial-root { position: absolute; inset: 0; z-index: 70; pointer-events: none; }
#tutorial-root.active { pointer-events: auto; }
.tut-hole {
  position: absolute;
  box-shadow: 0 0 0 4000px rgba(3, 5, 10, .82);
  outline: 1px solid rgba(94, 200, 247, .8);
  outline-offset: 3px;
  border-radius: 2px;
  transition: all .3s ease;
}
/* Multi-target steps (2+ spotlit elements at once): a single box-shadow
   cutout can't punch two holes without each one re-darkening the other's, so
   these steps mask a full-stage dim rect instead and draw the cyan outline
   separately per hole. See tutRender(). */
.tut-dim { position: absolute; inset: 0; }
.tut-hole-outline {
  position: absolute;
  outline: 1px solid rgba(94, 200, 247, .8);
  outline-offset: 3px;
  border-radius: 2px;
}
.tut-hole.tut-center { box-shadow: 0 0 0 4000px rgba(3, 5, 10, .88); outline: none; }
.tut-pop {
  position: absolute; width: 560px; padding: 28px 34px;
  display: flex; flex-direction: column; gap: 12px;
}
.tut-pop .t-step { font-family: var(--f-data); font-size: 13px; color: var(--dim); }
.tut-pop .t-title { font-family: var(--f-ui); font-weight: 700; font-size: 27px; letter-spacing: .06em; text-transform: uppercase; color: var(--cyan); text-shadow: 0 0 16px rgba(94,200,247,.4); }
.tut-pop .t-text { font-family: var(--f-body); font-size: 17px; line-height: 1.55; color: var(--text); }
.tut-pop .t-actions { display: flex; flex-wrap: wrap; gap: 10px 14px; margin-top: 10px; align-items: center; }
.tut-pop .t-skip { margin-left: auto; font-family: var(--f-ui); font-size: 14px; letter-spacing: .12em; text-transform: uppercase; color: var(--steel-d); cursor: pointer; background: none; border: 0; white-space: nowrap; flex-shrink: 0; }
.tut-pop .t-skip:hover { color: var(--bad); }

/* ================================================== cinematic intro */

#intro-root { position: absolute; inset: 0; z-index: 80; display: none; }
#intro-root.active { display: block; }

/* looping background video — drop assets/intro.mp4 to enable; until then the
   space backdrop shows through as a placeholder */
.intro-bg { position: absolute; inset: 0; overflow: hidden; background: #02040A; }
.intro-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 1.6s ease;
}
.intro-video.show { opacity: 1; }
/* video freezes on its opaque last frame; the briefing types over that frame.
   There is no image backdrop — only the video over the solid dark .intro-bg,
   so nothing can bleed through around it. */
.intro-scan {
  position: absolute; inset: 0; pointer-events: none; opacity: .45;
  background: repeating-linear-gradient(0deg,
    rgba(94,200,247,.05) 0 1px, transparent 1px 4px);
}
.intro-vign {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 72% 72% at 50% 46%,
    transparent 38%, rgba(2,4,10,.86) 100%);
}
.intro-stage {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column;
  /* strict top-down flow: text → "take command" button → "skip" link, each
     one below the last, scrollable. Centering was what let the button and
     the (formerly absolute) skip link overlap the briefing text. */
  justify-content: flex-start; overflow-y: auto;
  padding: 40px 200px;
  /* same click-to-fast-forward text as .cine-stage below — same fix */
  user-select: none;
}
.intro-tag {
  font-family: var(--f-data); font-size: 18px; letter-spacing: .28em;
  text-transform: uppercase; color: var(--cyan);
  text-shadow: 0 0 14px rgba(94,200,247,.6); margin-bottom: 28px;
}
.intro-tag::before {
  content: ""; display: inline-block; width: 9px; height: 9px;
  margin-right: 14px; vertical-align: 1px; background: var(--cyan);
  box-shadow: 0 0 10px var(--cyan); animation: intro-blink 1.1s steps(1) infinite;
}
.intro-text {
  font-family: var(--f-data); font-size: 25px; line-height: 1.72;
  color: #C6E9FF; text-shadow: 0 0 10px rgba(94,200,247,.35);
  max-width: 1200px; white-space: pre-wrap; min-height: 430px; cursor: pointer;
}
.intro-caret {
  display: inline-block; width: .6em; height: 1.02em; margin-left: .02em;
  vertical-align: -2px; background: #C6E9FF;
  box-shadow: 0 0 10px rgba(94,200,247,.7);
  animation: intro-blink 1s steps(1) infinite;
}
@keyframes intro-blink { 0%,50% { opacity: 1; } 50.01%,100% { opacity: 0; } }
/* lives INSIDE .intro-text (block after the typed span) so it can only ever
   sit below the last line of text — impossible to overlap it */
.intro-actions { margin-top: 40px; display: flex; flex-wrap: wrap; gap: 14px 22px; align-items: center; }
/* pinned at the top of the briefing, stays put while the text scrolls — an
   opaque chip so text passing under it on scroll stays legible (sticky, not
   absolute, so it reserves its own space and the text starts below it) */
.intro-skip {
  position: sticky; top: 0; z-index: 5; align-self: flex-end;
  margin-bottom: 10px; padding: 8px 16px;
  font-family: var(--f-ui); font-size: 15px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--steel-d);
  background: rgba(2, 4, 10, .93); border: 1px solid rgba(46, 91, 138, .5);
  box-shadow: 0 2px 16px rgba(2, 4, 10, .85); cursor: pointer;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
}
.intro-skip:hover { color: var(--cyan); border-color: rgba(94, 200, 247, .6); }

/* ================================================== cinematic interstitials */

#cine-root { position: absolute; inset: 0; z-index: 84; display: none; }
#cine-root.active { display: block; }
.cine-bg { position: absolute; inset: 0; overflow: hidden; background: #02040A; }
/* The stage is a 1920x1080 board scaled into the window, so on most windows the
   art ends in a hard horizontal edge with the page's own black underneath it —
   a thin lighter strip that reads as the picture not quite covering the screen
   (and shimmers whenever the dispatch repaints as it types). Both edges now
   dissolve into the page colour, so there is nothing to see a seam at. */
.cine-bg::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, var(--void) 0, transparent 3%,
    transparent 96%, var(--void) 100%);
}
/* The fade used to run 1.4s on top of however long the jpg took to arrive, so
   the art routinely landed well into the typed dispatch — the picture is the
   first thing the moment says, it should be there as the first words are. */
.cine-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity .45s ease;
}
.cine-img.show { opacity: 1; animation: cine-zoom 28s ease-out forwards; }
@keyframes cine-zoom { from { transform: scale(1); } to { transform: scale(1.07); } }
/* darken the lower third so the dispatch stays legible over any art */
.cine-scrim {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(2,4,10,.5) 0%, transparent 26%,
    transparent 48%, rgba(2,4,10,.92) 100%);
}
.cine-stage {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column; justify-content: flex-end;
  overflow-y: auto; padding: 40px 120px 64px;
  /* .cine-text is click-to-fast-forward — without this, a click that lands
     slightly off (or a fast double-click) selects the typed text instead of
     just advancing it, leaving a stray browser selection highlight sitting
     on top of the art after the click (reads as a rendering bug, isn't one:
     pre-existing behavior, just more noticeable against the new translucent
     material look). */
  user-select: none;
}
.cine-tag {
  font-family: var(--f-data); font-size: 15px; letter-spacing: .28em;
  text-transform: uppercase; color: var(--cyan);
  text-shadow: 0 0 14px rgba(94,200,247,.6); margin-bottom: 14px;
}
.cine--dark .cine-tag { color: var(--warn); text-shadow: 0 0 14px rgba(255,180,84,.55); }
.cine-tag::before {
  content: ""; display: inline-block; width: 8px; height: 8px;
  margin-right: 12px; vertical-align: 1px; background: currentColor;
  box-shadow: 0 0 10px currentColor; animation: intro-blink 1.1s steps(1) infinite;
}
.cine-title {
  font-family: var(--f-ui); font-weight: 800; font-size: 44px;
  letter-spacing: .08em; text-transform: uppercase; color: #EAF6FF;
  text-shadow: 0 2px 26px rgba(2,4,10,.9), 0 0 34px rgba(94,200,247,.3);
  margin: 0 0 16px;
}
.cine-text {
  font-family: var(--f-data); font-size: 21px; line-height: 1.7;
  color: #C6E9FF; text-shadow: 0 0 10px rgba(2,4,10,.9);
  max-width: 980px; white-space: pre-wrap; cursor: pointer;
}
/* One Continue on an ordinary moment; three on the crossing, where the
   dispatch ends in the question itself — so they wrap rather than squeeze. */
.cine-actions { margin-top: 26px; min-height: 56px; display: flex; gap: 14px 22px; align-items: center; flex-wrap: wrap; }
/* The crossing's actions carry the same footnote the modal spells out: what
   was lost, and what each button costs. Full-width so it sits under the row. */
.fall-note {
  flex: 0 0 100%; margin: 2px 0 0; max-width: 900px;
  font-family: var(--f-data); font-size: 16px; line-height: 1.55;
  color: rgba(198,233,255,.66);   /* the dispatch's own colour, stepped back */
  text-shadow: 0 0 10px rgba(2,4,10,.9);
}

/* Art that hasn't been drawn yet: a procedural backdrop in the moment's own
   tint, so the mode is fully playable before a single jpg exists. Drop
   assets/cinematics/<id>.jpg in later and the image takes over untouched. */
.cine-bg--placeholder {
  background:
    radial-gradient(ellipse 120% 80% at 50% 12%, var(--cine-tint, #FF7A2F) 0%, transparent 62%),
    repeating-linear-gradient(115deg, rgba(255,255,255,.028) 0 2px, transparent 2px 9px),
    linear-gradient(180deg, #0A1018 0%, #02040A 72%);
}
.cine-bg--placeholder::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 34%, rgba(255,255,255,.10) 0 8%, transparent 42%);
  mix-blend-mode: screen;
}

/* ================================================== downward spirals */

/* Depth readout — replaces the solutions chip once nothing is commissioned
   any more. It is the only score still moving after the point of no return. */
.chip--depth {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; font-family: var(--f-data); font-size: 13px;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--spiral-tint, var(--warn));
  border: 1px solid color-mix(in srgb, var(--spiral-tint, var(--warn)) 55%, transparent);
  background: color-mix(in srgb, var(--spiral-tint, var(--warn)) 10%, transparent);
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
.spiral-name {
  font-family: var(--f-ui); font-weight: 700; font-size: 15px;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--spiral-tint, var(--warn));
  text-shadow: 0 0 16px color-mix(in srgb, var(--spiral-tint, var(--warn)) 60%, transparent);
}

/* The console itself goes over to the spiral: the cyan accents that read as
   "systems nominal" are pulled toward the tint, and the whole stage is
   desaturated and dimmed. Deliberately restrained — the numbers must stay
   legible, this is a mood, not a filter. */
body.spiral .stage::before {
  content: ""; position: absolute; inset: 0; z-index: 40; pointer-events: none;
  background:
    radial-gradient(ellipse 90% 70% at 50% 0%,
      color-mix(in srgb, var(--spiral-tint, #FF7A2F) 16%, transparent) 0%, transparent 68%),
    linear-gradient(180deg, transparent 55%, rgba(2,4,10,.42) 100%);
}
/* ...and the star field behind the console gives way to the world you are
   still nominally governing. Same 16% wash as the space backdrop it replaces
   (the ::before that carried it is taken over by the tint above), pulled
   toward the spiral's own colour so each fate reads differently. */
body.spiral .stage::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: url(../assets/bg/spiral.jpg) center / cover no-repeat;
  opacity: .16; filter: saturate(.62) brightness(.9);
}
body.spiral .year { color: var(--spiral-tint, var(--warn)); }
body.spiral .overline--accent { color: var(--spiral-tint, var(--warn)); }
body.spiral .card-title { color: var(--spiral-tint, var(--warn)); }
body.spiral .topbar, body.spiral .cham { filter: saturate(.72); }

/* The street's meter: quiet is not shown at all, then it warms toward red as
   the square fills. Same chip grammar as the depth readout beside it. */
.chip--street {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px; font-family: var(--f-data); font-size: 13px;
  letter-spacing: .12em; text-transform: uppercase; color: var(--warn);
  border: 1px solid color-mix(in srgb, currentColor 55%, transparent);
  background: color-mix(in srgb, currentColor 10%, transparent);
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
.chip--street.st-unrest { color: var(--warn); }
.chip--street.st-gates, .chip--street.st-storm { color: var(--bad); }
.chip--street .mood-bar { width: 44px; }
.chip--street .mood-fill { background: currentColor; }

.fin-depth { margin: 18px 0 4px; padding: 14px 16px; }
.fin-depth .n { display: flex; justify-content: space-between; gap: 16px; margin-top: 7px;
  font-family: var(--f-data); font-size: 14px; }
.fin-depth .n .k { color: var(--steel-d); }
.fin-depth .score-num { color: var(--spiral-tint, var(--warn)); }

/* ================================================== LLM key modal */

.modal-wrap {
  position: absolute; inset: 0; z-index: 60;
  background: rgba(3, 5, 10, .8);
  display: flex; align-items: flex-start; justify-content: center;
  overflow-y: auto; padding: 5vh 24px;
}
/* margin:auto centers the plate when it fits, and lets tall content scroll the
   overlay (rather than an inner scrollbox) so the chamfered plate can grow to
   fit the whole story — its background is drawn to the plate's full height. */
.modal-wrap > * { margin: auto; }
.modal { position: relative; width: 720px; padding: 42px 52px; display: flex; flex-direction: column; gap: 16px; }
.modal .m-close {
  position: absolute; top: 20px; right: 22px; z-index: 2;
  width: 38px; height: 38px; padding: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid rgba(94,200,247,.28);
  color: var(--dim); font-family: var(--f-ui); font-size: 17px; line-height: 1;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.modal .m-close:hover {
  color: var(--cyan); border-color: rgba(94,200,247,.7);
  background: rgba(94,200,247,.08);
}

/* clickable headlines → full-story modal */
.news-item--click { cursor: pointer; transition: filter .12s ease, transform .12s ease; }
.news-item--click:hover { filter: brightness(1.18); transform: translateY(-1px); }
.news-item--click .n-more { margin-top: 8px; font-family: var(--f-ui); font-weight: 600; font-size: 14px; letter-spacing: .1em; text-transform: uppercase; color: var(--cyan); opacity: .0; transition: opacity .12s ease; }
.news-item--click:hover .n-more { opacity: .9; }
.wire-item--click { cursor: pointer; border-left: 2px solid transparent; padding-left: 8px; margin-left: -10px; transition: border-color .12s ease, background .12s ease; }
.wire-item--click:hover { border-left-color: var(--cyan); background: rgba(94,200,247,.06); }

.article-modal { width: 860px; gap: 14px; }
.article-modal .art-masthead { font-family: var(--f-ui); font-weight: 700; font-size: 15px; letter-spacing: .22em; text-transform: uppercase; color: var(--cyan); }
.article-modal .art-masthead .yr { color: var(--dim); }
.article-modal .art-hero { height: 220px; overflow: hidden; box-shadow: inset 0 0 0 1px var(--edge-bright); }
.article-modal .art-hero img { width: 100%; height: 100%; object-fit: cover; }
.article-modal .art-title { font-family: var(--f-ui); font-weight: 700; font-size: 32px; line-height: 1.15; letter-spacing: .02em; color: var(--text); }
.article-modal .art-body { display: flex; flex-direction: column; gap: 14px; }
.article-modal .art-p { font-size: 19px; line-height: 1.65; }
.art-dots::after { content: ""; animation: art-dots 1.2s steps(4, end) infinite; }
@keyframes art-dots { 0% { content: ""; } 25% { content: "."; } 50% { content: ".."; } 75% { content: "..."; } }
.modal .m-title { font-family: var(--f-ui); font-weight: 700; font-size: 30px; letter-spacing: .08em; text-transform: uppercase; color: var(--cyan); text-shadow: 0 0 16px rgba(94,200,247,.4); }
.modal .m-input { margin-top: 6px; }
.modal .m-input input {
  width: 100%; height: 58px; padding: 0 22px;
  background: transparent; border: 0; outline: none;
  font-family: var(--f-data); font-size: 16px; color: var(--text);
}
.modal .m-input input::placeholder { color: var(--steel-d); }
/* model dropdown row — a labelled <select> that matches the key input plate */
.modal .m-select { display: flex; align-items: center; gap: 14px; padding: 0 22px; height: 58px; }
.modal .m-select .ms-k {
  flex: none; font-family: var(--f-data); font-size: 12px; color: var(--dim);
  text-transform: uppercase; letter-spacing: .12em;
}
.modal .m-select select {
  flex: 1; height: 100%; min-width: 0; cursor: pointer;
  background: transparent; border: 0; outline: none;
  font-family: var(--f-data); font-size: 15px; color: var(--text);
  -webkit-appearance: none; appearance: none;
}
.modal .m-select select option { background: var(--panel); color: var(--text); }
.modal .m-note { font-family: var(--f-data); font-size: 13px; color: var(--dim); line-height: 1.6; }
.modal .m-actions { display: flex; flex-wrap: wrap; gap: 10px 16px; margin-top: 8px; }

/* settings gear on the title screen */
.title-gear { cursor: pointer; }
.title-gear:hover { color: var(--cyan); }

/* ================================================== advisor portraits */

.d-head { display: flex; gap: 14px; align-items: center; margin-top: 10px; }
.d-portrait {
  width: 58px; height: 76px; flex: 0 0 58px;
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  box-shadow: inset 0 0 0 1px var(--edge-bright);
  background: #0A1424;
}
.d-portrait img, .avatar img, .c-portrait img, .rv-portrait img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.domain .d-advisor { margin-top: 0; }
.domain .d-role { margin-top: 2px; }

.avatar { width: 148px; height: 196px; }

.c-portrait {
  width: 92px; height: 120px; flex: 0 0 92px;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  box-shadow: inset 0 0 0 1px var(--edge);
  background: #0A1424;
}

.rv-portrait {
  width: 26px; height: 34px; flex: 0 0 26px;
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px);
  background: #0A1424;
}

/* ================================================== LLM status indicator */

.chip--on {
  background: rgba(99, 230, 165, .1); color: var(--good);
  box-shadow: inset 0 0 0 1px rgba(99, 230, 165, .55), 0 0 14px rgba(99, 230, 165, .35);
  text-shadow: 0 0 10px rgba(99, 230, 165, .8);
  cursor: pointer;
}
.chip--off {
  background: rgba(124, 141, 176, .06); color: var(--steel-d);
  box-shadow: inset 0 0 0 1px rgba(78, 94, 112, .5);
  cursor: pointer;
}
.chip--off:hover { color: var(--dim); }
.chip--foot { font-size: 12px; padding: 3px 10px; }

/* ================================================== language toggle */

.lang-toggle { display: inline-flex; align-items: center; gap: 7px; }
/* muted flag icons — desaturated + dimmed until picked, the active one lit */
.lang-flag {
  cursor: pointer; font-size: 17px; line-height: 1; user-select: none;
  filter: grayscale(1) opacity(.4);
  transition: filter .15s ease, transform .15s ease;
}
.lang-flag:hover { filter: grayscale(.25) opacity(.85); transform: translateY(-1px); }
.lang-flag--on {
  filter: drop-shadow(0 0 5px rgba(94, 200, 247, .6));
  transform: scale(1.18);
}
.lang-flag--on:hover { filter: drop-shadow(0 0 6px rgba(94, 200, 247, .75)); }

/* ================================================== "science behind it" ribbon */

.ribbon {
  position: absolute; top: 74px; right: -78px; z-index: 30;
  transform: rotate(45deg);
  width: 400px; padding: 13px 0; text-align: center;
  font-family: var(--f-ui); font-weight: 600; font-size: 18px;
  letter-spacing: .3em; text-transform: uppercase; text-indent: .3em;
  text-decoration: none; color: #EAF7FF;
  background: linear-gradient(180deg, rgba(23, 54, 88, .95) 0%, rgba(12, 28, 52, .95) 100%);
  border-top: 1px solid rgba(94, 200, 247, .9);
  border-bottom: 1px solid rgba(94, 200, 247, .45);
  box-shadow: 0 0 22px rgba(94, 200, 247, .45), inset 0 0 18px rgba(94, 200, 247, .18);
  text-shadow: 0 0 12px rgba(94, 200, 247, .95);
  transition: filter .15s ease;
}
.ribbon::before, .ribbon::after {
  content: "///"; font-size: 13px; letter-spacing: .1em;
  color: rgba(94, 200, 247, .75); vertical-align: 1px;
}
.ribbon::before { margin-right: 18px; }
.ribbon::after { margin-left: 6px; }
.ribbon:hover { filter: brightness(1.35) drop-shadow(0 0 10px rgba(94, 200, 247, .6)); }

/* ================================================== global space backdrop */

.stage::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  background: url(../assets/bg/space.jpg) center / cover no-repeat;
  opacity: .3;
}

/* ================================================== title logo (vector w/ globe) */

#screen-title .title-logo {
  margin: 14px 0 6px;
  filter: drop-shadow(0 0 28px rgba(94, 200, 247, .28));
}

/* ================================================== detail chart overlay */

.plate-click { cursor: pointer; transition: filter .12s ease; }
.plate-click:hover { filter: brightness(1.25); }

.chart-panel { flex: 1; min-height: 0; padding: 24px 30px; display: flex; }
.chart-box { position: relative; flex: 1; }
.chart-box svg { width: 100%; height: 100%; display: block; }
.ch-tick { font-family: var(--f-data); font-size: 17px; fill: var(--dim); }
.chart-tip {
  position: absolute; z-index: 5; pointer-events: none;
  display: flex; align-items: baseline; gap: 8px;
  padding: 8px 14px; white-space: nowrap;
  background: rgba(8, 14, 26, .95);
  box-shadow: inset 0 0 0 1px var(--edge-bright), 0 4px 18px rgba(0,0,0,.5);
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}
.chart-tip .t-year { font-family: var(--f-ui); font-weight: 700; font-size: 16px; color: var(--text); }
.chart-tip .t-val { font-family: var(--f-data); font-weight: 700; font-size: 18px; }
.chart-tip .t-unit { font-family: var(--f-data); font-size: 12px; color: var(--dim); }

/* ================================================== trusted advisors (gold) */

.d-portrait.trusted, .c-portrait.trusted, .rv-portrait.trusted {
  box-shadow: inset 0 0 0 2px #E8C15A, 0 0 16px rgba(232, 193, 90, .55);
}
.avatar.trusted {
  box-shadow: inset 0 0 0 2px #E8C15A, 0 0 34px rgba(232, 193, 90, .5);
}
.trusted-mark {
  color: #E8C15A; text-shadow: 0 0 10px rgba(232, 193, 90, .8);
  font-size: .82em; margin-left: 4px;
}

/* ================================================== LLM provider picker */

.prov-tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.prov-tab {
  flex: 1 1 auto; min-width: 120px; padding: 11px 14px;
  background: rgba(11, 19, 34, .8); border: 0; cursor: pointer;
  font-family: var(--f-ui); font-weight: 600; font-size: 15px;
  letter-spacing: .06em; color: var(--dim);
  box-shadow: inset 0 0 0 1px var(--edge);
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  transition: color .12s, box-shadow .12s;
}
.prov-tab:hover { color: var(--text); }
.prov-tab.active {
  color: #EAF7FF;
  box-shadow: inset 0 0 0 1px var(--edge-bright), 0 0 12px rgba(94, 200, 247, .35);
  background: linear-gradient(180deg, #1A3A5F, #12294a);
}
.prov-endpoint {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 14px;
  margin-top: 4px; padding: 12px 16px;
  background: rgba(8, 14, 26, .6); box-shadow: inset 0 0 0 1px var(--edge);
  font-family: var(--f-data); font-size: 13px;
}
.prov-endpoint .pe-k { color: var(--dim); text-transform: uppercase; letter-spacing: .12em; }
.prov-endpoint .pe-v { color: var(--text); }

/* ================================================== advisor mood (cabinet politics) */

.mood {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 6px;
  font-family: var(--f-ui); font-weight: 600; font-size: 13px;
  letter-spacing: .1em; text-transform: uppercase;
}
.mood-dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor;
  box-shadow: 0 0 7px currentColor; flex: 0 0 8px; }
.mood-good { color: var(--good); }
.mood-cyan { color: var(--cyan); }
.mood-dim  { color: var(--dim); }
.mood-warn { color: var(--warn); }
.mood-bad  { color: var(--bad); }
.mood-gold { color: #E8C15A; }
.mood-bar {
  display: inline-block; width: 52px; height: 5px; margin-left: 4px;
  background: rgba(255,255,255,.08); box-shadow: inset 0 0 0 1px var(--edge);
}
.mood-fill { display: block; height: 100%; }
.mf-good { background: var(--good); } .mf-cyan { background: var(--cyan); }
.mf-dim { background: var(--steel-m); } .mf-warn { background: var(--warn); }
.mf-bad { background: var(--bad); }

/* cabinet-confidence meter: turn-screen wrapper (clickable, opens the war
   room) — the chip itself reuses .mood; kill its margin in header contexts */
.chip-conf { cursor: pointer; display: inline-flex; align-items: center; margin-right: 10px; }
.chip-conf .mood, .ov-head .mood { margin-top: 0; }

/* ================================================== cabinet blocs & rivalries */

.rel-line {
  margin-top: 5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-family: var(--f-ui); font-weight: 600; font-size: 12px;
  letter-spacing: .08em; text-transform: uppercase;
}
.rel-ally  { color: var(--good); }
.rel-rival { color: var(--bad); }
.rel-sep   { color: var(--steel-d); }
.fin-epilogue {
  margin-top: 10px; padding: 8px 14px;
  font-family: var(--f-body); font-size: 13px; color: var(--text); line-height: 1.4;
  background: rgba(94, 200, 247, .05);
  box-shadow: inset 0 0 0 1px var(--edge);
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
}

/* ================================================== cabinet crisis card */

.chip--cabinet {
  background: rgba(255, 180, 84, .1); color: var(--warn);
  box-shadow: inset 0 0 0 1px rgba(255, 180, 84, .45);
}
#card:has(.chip--cabinet) { box-shadow: inset 0 0 0 1px rgba(255,180,84,.35), 0 0 30px rgba(255,180,84,.08); }

/* ================================================== world map overlay */

.nodes--clickable { cursor: pointer; transition: box-shadow .15s; }
.nodes--clickable:hover { box-shadow: inset 0 0 0 1px var(--edge-bright), 0 0 20px rgba(94,200,247,.12); }
.nodes-map-hint {
  font-family: var(--f-ui); font-weight: 600; font-size: 12px; letter-spacing: .1em;
  color: var(--cyan); text-transform: none;
}

.map-overlay { background: #04070d; } /* opaque: globe canvas is transparent */
.map-tabs { margin-left: 8px; }
.map-body { flex: 1; display: flex; gap: 18px; min-height: 0; }
.map-wrap {
  flex: 1; position: relative; display: flex; flex-direction: column;
  min-width: 0; padding: 16px;
}
/* `contain: paint` scopes the map's repaints to this box — without it every
   marker animation invalidates the whole (transform-scaled) stage. */
#map-slot { flex: 1; min-height: 0; position: relative; overflow: hidden; contain: paint; }
#world-svg { position: relative; z-index: 1; width: 100%; height: 100%; display: block; }
.country { stroke: #67d4ff; stroke-width: .45; stroke-opacity: .55; }
.country:not(.has) { stroke-opacity: .25; }
.country.has { cursor: pointer; }
/* Hover highlight is a stroke swap, NOT a filter: `drop-shadow`/`brightness`
   on an SVG path forces a full re-rasterisation of that path every frame of
   the transition, and the stage is transform-scaled so it rasterises at the
   scaled resolution. Sweeping the pointer across ~180 countries made that the
   single most expensive thing on the screen. */
.country.has:hover { stroke: #DFF3FF; stroke-width: 1.4; stroke-opacity: 1; }

/* ---- map markers: CSS transform/opacity animations, not SMIL ----------------
   SMIL <animate> drives SVG *attributes*, so every frame re-runs layout+paint
   for that element. opacity/transform keyframes are composited on the GPU
   instead, and the browser suspends them when the tab is hidden. */
@keyframes map-pulse-fade { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
@keyframes map-pulse-ring {
  from { transform: scale(1);   opacity: .9; }
  to   { transform: scale(5.5); opacity: 0; }
}
@keyframes map-arc-war { 0%, 100% { opacity: 1; } 50% { opacity: .37; } }

.pulse-dot { animation: map-pulse-fade 1.6s ease-in-out infinite; }
.pulse-ring {
  transform-box: fill-box; transform-origin: center;
  animation: map-pulse-ring 1.6s linear infinite;
}
.arc-war { animation: map-arc-war 1.3s ease-in-out infinite; }
.ev-hot  { animation: map-pulse-fade 1.1s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .pulse-dot, .pulse-ring, .arc-war, .ev-hot { animation: none; }
  .pulse-ring { opacity: .35; }
}
/* faint grid + scanlines around the map — keeps it on-brand with the console */
#map-slot::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(rgba(94,200,247,.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(94,200,247,.045) 1px, transparent 1px);
  background-size: 64px 64px;
}
#map-slot::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 2;
  background: repeating-linear-gradient(0deg, rgba(94,200,247,.03) 0 1px, transparent 1px 4px);
}

.map-legend {
  display: flex; align-items: center; gap: 12px; margin-top: 10px;
  font-family: var(--f-data); font-size: 13px; color: var(--dim);
}
.map-legend .ml-bar {
  flex: 1; max-width: 320px; height: 10px;
  box-shadow: inset 0 0 0 1px var(--edge);
}
.map-layers-legend { gap: 18px; margin-top: 6px; font-size: 12px; flex-wrap: wrap; }

.map-info-slot { width: 340px; flex: 0 0 340px; }
.map-info {
  padding: 24px 28px;
  background: var(--surface-panel, rgba(11,19,34,.6));
  box-shadow: inset 0 0 0 1px var(--edge);
  clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
}
.map-info .mi-name {
  font-family: var(--f-ui); font-weight: 700; font-size: 26px;
  letter-spacing: .04em; color: var(--cyan); margin-bottom: 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.map-info .mi-close { padding: 2px 10px; font-size: 15px; }
.map-info .readout td { font-size: 17px; padding: 9px 0; }
.map-info--empty { color: var(--dim); font-family: var(--f-body); font-size: 17px; }

.stab-panel {
  margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--edge);
}
.stab-title {
  font-family: var(--f-ui); font-size: 14px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--dim); margin-bottom: 10px;
}
.stab-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.stab-panel--active .stab-status { color: var(--warn); font-family: var(--f-body); font-size: 16px; }

/* ============================================ the cabinet emptying (spiral entry)

   Three seats change hands the moment the world crosses over. It used to be
   two lines inside a five-headline wire, which is where the biggest cabinet
   event in a run went to be missed. Its own screen, with the faces: who left,
   who this world sent instead. */
.cc-modal { width: 880px; }
.cc-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 36px; margin-top: 4px; }
.cc-col { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.cc-person { display: flex; align-items: center; gap: 12px; min-width: 0; }
.cc-face {
  flex: 0 0 auto; width: 52px; height: 66px; overflow: hidden;
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  box-shadow: inset 0 0 0 1px rgba(94,200,247,.35);
}
.cc-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cc-who { min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.cc-name { font-family: var(--f-body); font-size: 15px; color: var(--text); }
.cc-who small { font-size: 12px; color: var(--dim); }
/* the ones who left read as already gone */
.cc-person--gone .cc-face { filter: grayscale(1) brightness(.6); box-shadow: inset 0 0 0 1px rgba(255,107,97,.3); }
.cc-person--gone .cc-name { color: var(--dim); text-decoration: line-through; text-decoration-color: rgba(255,107,97,.5); }
body.is-mobile .cc-modal { width: auto; }
body.is-mobile .cc-cols { grid-template-columns: 1fr; gap: 14px; }

/* ================================ the crossing question (resign or play on)

   Same plate as the cabinet screen it follows, tinted to the spiral the run
   just fell into: the offer belongs to that world, not to the console. Three
   actions is one more than a modal usually carries — the base .modal's 720px
   wasn't wide enough for all three in one row (the primary .btn--lg alone
   ate most of it), so "read the report first" wrapped onto its own line
   below, reading as detached from the other two. Widened so all three sit
   in a row here the same way they already do at the end of the crossing
   cinematic (.cine-actions); flex-wrap stays on as a safety net for a
   narrow viewport or a language whose labels run long (German especially),
   not as the expected default layout anymore. */
.fall-modal { width: 960px; border-top: 2px solid var(--spiral-tint, var(--bad)); }
.fall-modal .m-title { color: var(--spiral-tint, var(--bad)); text-shadow: none; }
.fall-modal .m-actions { flex-wrap: wrap; }

/* ================================================ the manager's report

   A debrief, not a scoreboard: dense, tabular, and deliberately plain. It
   scrolls (it is the one screen that legitimately has more than a screenful)
   and exports, so the numbers can leave the game. */
.mg-overlay { gap: 14px; }
.mg-body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 10px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px 18px; align-content: start;
  scrollbar-width: thin; scrollbar-color: rgba(94,200,247,.35) transparent;
}
.mg-body::-webkit-scrollbar { width: 8px; }
.mg-body::-webkit-scrollbar-thumb { background: rgba(94,200,247,.3); border-radius: 4px; }
.mg-block { padding: 16px 20px; display: flex; flex-direction: column; gap: 6px; }
.mg-table { width: 100%; margin-top: 8px; }
.mg-table th { font-family: var(--f-ui); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--dim); font-weight: 600; padding-bottom: 4px; }
.mg-table td { font-family: var(--f-data); font-size: 14px; padding: 3px 0; border-bottom: 1px solid rgba(46,91,138,.16); }
.mg-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.mg-table .dim, .mg-line .dim { color: var(--dim); }
.mg-table .good, .mg-line .good { color: var(--good); }
.mg-table .bad, .mg-line .bad { color: var(--bad); }
.mg-table .warn { color: var(--warn); }
.mg-line { display: flex; justify-content: space-between; gap: 16px; font-family: var(--f-data); font-size: 14px; padding: 3px 0; border-bottom: 1px solid rgba(46,91,138,.16); }
.mg-sub { margin-top: 10px; }
.mg-note { margin-top: 10px; line-height: 1.5; }
.mg-bar { display: grid; grid-template-columns: 128px 1fr 84px; align-items: center; gap: 10px; font-family: var(--f-data); font-size: 13px; padding: 2px 0; }
.mg-bar-t { height: 8px; background: rgba(46,91,138,.25); position: relative; }
.mg-bar-t i { position: absolute; inset: 0 auto 0 0; background: linear-gradient(90deg, var(--cyan), rgba(94,200,247,.35)); }
.mg-bar-v { text-align: right; color: var(--dim); }
body.is-mobile .mg-body { grid-template-columns: 1fr; }
body.is-mobile .mg-bar { grid-template-columns: 92px 1fr 66px; font-size: 12px; }
