/* ---- Bluejay brand type (self-hosted; licensed — see brand kit) ----
   Guidelines: short headlines = Terrane Serif Bold, tracking -10;
   longer headlines = Terrane Sans; body = Haffer Regular, tracking +20;
   small caps headers = Haffer, tracking +60. */
@font-face { font-family: "Terrane Serif"; src: url("assets/fonts/TerraneSerifWeb-Bold.woff2") format("woff2");  font-weight: 700; font-display: block; }
@font-face { font-family: "Terrane Serif"; src: url("assets/fonts/TerraneSerifWeb-Black.woff2") format("woff2"); font-weight: 900; font-display: block; }
@font-face { font-family: "Terrane Sans";  src: url("assets/fonts/TerraneSansWeb-Regular.woff2") format("woff2"); font-weight: 400; font-display: block; }
@font-face { font-family: "Terrane Sans";  src: url("assets/fonts/TerraneSansWeb-Bold.woff2") format("woff2");    font-weight: 700; font-display: block; }
@font-face { font-family: "Haffer"; src: url("assets/fonts/Haffer-Regular.woff2") format("woff2"); font-weight: 400; font-display: block; }
@font-face { font-family: "Haffer"; src: url("assets/fonts/Haffer-Medium.woff2") format("woff2");  font-weight: 500; font-display: block; }

:root {
  /* display (short headlines) / UI + body */
  --display: "Terrane Serif", Georgia, serif;
  --sans: "Terrane Sans", "Haffer", system-ui, sans-serif;
  --body: "Haffer", system-ui, -apple-system, sans-serif;

  /* Bluejay palette — primary from the brand guidelines, secondary used for glow/atmosphere.
     #3174FF is the brand blue but sits dark on near-black, so Cyan carries on-dark text + strokes. */
  --jay: #3174FF;
  --jay-lite: #6FD0FB;
  --jay-deep: #1B3FA8;
  --purple: #8A5EEB;
  --pink: #FFBEFF;
  --ink: #070911;
  --panel: #101426;
  --line: rgba(111, 208, 251, 0.32);
  --text: #FAFAFA;
  --dim: #8FA3BD;
  --danger: #ff4d5e;
  --gold: #ffd166;

  /* ---- SPACING SCALE — 4pt base. No spacing value outside this set. ---- */
  --s1: 4px;   --s2: 8px;   --s3: 12px;  --s4: 16px;
  --s5: 24px;  --s6: 32px;  --s7: 48px;  --s8: 64px;

  /* ---- TYPE SCALE — 7 steps, only weights that actually ship. ----
     Terrane Serif has ONLY 700 + 900. Haffer has ONLY 400 + 500.
     Never set 600/800 or `bold` on --body: the browser fakes it and the
     letterforms smear. Need emphasis in a small size? Use --t-label, not weight. */
  --t-xl:     900 clamp(34px, 6vw, 56px)/1.0  var(--display);   /* screen titles */
  --t-lg:     900 32px/1.05                   var(--display);   /* panel titles */
  --t-md:     700 20px/1.15                   var(--display);   /* buttons, card titles */
  --t-num:    900 22px/1                      var(--display);   /* counters only */
  --t-body:   400 14px/1.45                   var(--body);      /* descriptions */
  --t-strong: 500 14px/1.45                   var(--body);      /* values, chips */
  --t-label:  500 10px/1.2                    var(--body);      /* caps eyebrows, meters */
  --tracking-label: 0.14em;
  --tracking-display: -0.02em;

  /* ---- ELEVATION — glow is a signal, not a texture. ----
     --lift  : everything that is merely raised (cards, panels, chips)
     --focus : the one thing the player can act on right now
     --alarm : danger only */
  --lift: 0 12px 32px -12px rgba(0,0,0,0.7);
  --lift-lg: 0 30px 70px -20px rgba(0,0,0,0.8);
  --focus: 0 0 24px rgba(111,208,251,0.45);
  --alarm: 0 0 24px rgba(255,77,94,0.45);

  /* shared cut-corner shape language — cards/tabs/buttons chamfer top-right + bottom-left.
     borders are inset box-shadows (clip-path clips a real border away, inset shadows follow the cut). */
  --chamfer: polygon(0 0, calc(100% - 13px) 0, 100% 13px, 100% 100%, 13px 100%, 0 calc(100% - 13px));
  --chamfer-sm: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 7px 100%, 0 calc(100% - 7px));
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  background: var(--ink);
  font-family: var(--body);
  letter-spacing: 0.02em;
  color: var(--text);
  overflow: hidden;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(1200px 600px at 50% -10%, rgba(111,208,251,0.10), transparent 60%),
    var(--ink);
}

#stage {
  position: relative;
  width: min(96vw, calc(96vh * 16 / 9));
  aspect-ratio: 16 / 9;
  /* no hairline frame — a border around the playfield reads as a widget on a page.
     The drop shadow alone lifts it off the backdrop. */
  box-shadow: 0 30px 80px rgba(0,0,0,0.7);
  border-radius: 10px;
  overflow: hidden;
}

#game {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* custom aiming reticle — JAY ring + gold pip, hotspot dead-center */
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='34' viewBox='0 0 34 34'%3E%3Cg fill='none' stroke='%236FD0FB' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='17' cy='17' r='9'/%3E%3Cline x1='17' y1='2' x2='17' y2='8'/%3E%3Cline x1='17' y1='26' x2='17' y2='32'/%3E%3Cline x1='2' y1='17' x2='8' y2='17'/%3E%3Cline x1='26' y1='17' x2='32' y2='17'/%3E%3C/g%3E%3Ccircle cx='17' cy='17' r='2' fill='%23ffd166'/%3E%3C/svg%3E") 17 17, crosshair;
  background: var(--ink);
}

/* Edge vignette only — frames the playfield without taxing contrast.
   The old scanline gradient + `mix-blend-mode: overlay` washed out every
   surface in the game (it was most of why the photo biomes read milky).
   Multiply darkens the corners and leaves the middle alone. */
#crt {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 68%, rgba(2,6,14,0.42) 100%);
  mix-blend-mode: multiply;
}

/* ---------- overlays ---------- */
.overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: rgba(4, 9, 18, 0.82);
  backdrop-filter: blur(6px);
  text-align: center;
  padding: 24px;
  z-index: 5;
}
.overlay.hidden { display: none; }

/* Bluejay mark, demoted to a corner stamp. It used to sit centred between the
   mascot and the wordmark — three brand marks stacked in one column, so the eye
   had no idea which one was the title. */
.lockup {
  position: absolute;
  top: var(--s5);
  left: var(--s5);
  width: 104px;
  opacity: 0.6;
  filter: none;
}

/* the flock's world peeks through on the title screen, dimmed for the storm.
   The gradient is bottom-heavy so the wordmark + buttons sit on the darkest
   part of the plate and the sky stays bright at the top. */
.menu-scene {
  background:
    linear-gradient(180deg, rgba(4,9,18,0.58) 0%, rgba(4,9,18,0.66) 40%, rgba(4,9,18,0.92) 100%),
    url("assets/scene_01.jpg") center / cover no-repeat;
}

/* Title screen is one centred column with a single rhythm: mascot, wordmark,
   the choice, then everything else. Gaps come from the scale, not from taste. */
#menu.menu-scene { gap: 0; justify-content: center; }

#menuBird {
  height: 132px;
  margin-bottom: var(--s5);
  filter: drop-shadow(0 12px 24px rgba(0,0,0,0.5));
  animation: hover 2.6s ease-in-out infinite;
}
@keyframes hover {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-12px) rotate(2deg); }
}

#overBird {
  height: 96px;
  filter: drop-shadow(0 10px 20px rgba(0,0,0,0.5)) saturate(0.8);
}

/* Titles carry weight through SIZE, not through a stack of glows. The old
   `0 3px 0 var(--jay-deep)` hard offset read as a printing misregistration. */
h1, h2 {
  font: var(--t-xl);
  letter-spacing: var(--tracking-display);
  color: var(--text);
  text-shadow: none;
}
h1 span { color: var(--jay-lite); }
h2 { font: var(--t-lg); letter-spacing: var(--tracking-display); }
.hub-panel h2, .codex-head h2 { text-shadow: none; }
h2.dead { color: var(--danger); text-shadow: none; }

.tag { font: var(--t-label); color: var(--dim); letter-spacing: var(--tracking-label); text-transform: uppercase; }
.hint { font: var(--t-body); color: var(--dim); }

kbd {
  font: var(--t-label);
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: var(--s1);
  padding: var(--s1) var(--s2);
  background: var(--panel);
  color: var(--jay-lite);
}

.controls {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  justify-content: center;
  font-size: 10px;
  color: var(--dim);
  margin: 6px 0 4px;
}

/* The primary button is the one glowing thing on a screen — that's what makes
   it read as "press this". It only earns --focus on hover; at rest a lift is
   enough, so a screen with several buttons still has a single loudest element. */
button {
  font: var(--t-md);
  letter-spacing: var(--tracking-display);
  color: #04101e;
  background: linear-gradient(180deg, #6FD0FB, var(--jay) 58%, var(--jay-deep));
  border: none;
  border-radius: var(--s2);
  padding: var(--s3) var(--s6);
  cursor: pointer;
  box-shadow: var(--lift), inset 0 1px 0 rgba(255,255,255,0.5);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
button:hover { transform: translateY(-2px); box-shadow: var(--focus), inset 0 1px 0 rgba(255,255,255,0.5); }
button:active { transform: translateY(1px); }

button.ghost {
  font: var(--t-strong);
  letter-spacing: 0;
  background: rgba(8,16,32,0.7);
  color: var(--jay-lite);
  border: 1px solid var(--line);
  box-shadow: none;
}
button.ghost:hover { box-shadow: none; border-color: var(--jay-lite); color: var(--text); }
button.small { font: var(--t-label); letter-spacing: var(--tracking-label); text-transform: uppercase; padding: var(--s2) var(--s4); }

/* ---------- mode select ---------- */
/* SOLO is the primary path and reads that way: it's wider and it's the only
   thing on the screen that glows. FLOCK is a real choice, so it stays a
   sibling — but a quieter one, not a second equally-loud gold button. */
.modes { display: flex; gap: var(--s3); flex-wrap: wrap; justify-content: center; margin-top: var(--s6); }
.mode-btn {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  width: 232px;
  padding: var(--s4) var(--s5);
  text-align: center;
}
#soloBtn { box-shadow: var(--focus), inset 0 1px 0 rgba(255,255,255,0.5); }
.mode-btn .mode-title { font: var(--t-md); letter-spacing: var(--tracking-display); }
.mode-btn .mode-sub {
  font: var(--t-label);
  letter-spacing: var(--tracking-label);
  opacity: 0.75;
  text-transform: uppercase;
}
.mode-btn.alt {
  background: rgba(8,16,32,0.72);
  color: var(--jay-lite);
  border: 1px solid var(--line);
  box-shadow: none;
}
.mode-btn.alt:hover { border-color: var(--jay-lite); color: var(--text); box-shadow: none; }

/* ---------- multiplayer menus ---------- */
input {
  font-family: var(--body);
  font-size: 14px;
  color: var(--text);
  background: rgba(8,16,32,0.85);
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 11px 14px;
  text-align: center;
  outline: none;
  width: 220px;
}
input:focus { border-color: var(--jay-lite); box-shadow: none; }
#codeInput { width: 110px; text-transform: uppercase; letter-spacing: 0.3em; }

.mp-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: center; }
.or { color: var(--dim); font-size: 10px; }
.hint.warn { color: var(--danger); min-height: 16px; }

#roomCodeBig {
  font-family: var(--display); font-weight: 500;
  font-size: 64px;
  letter-spacing: 0.28em;
  color: var(--gold);
  text-shadow: none;
  margin-right: -0.28em;
}

#lobbyPlayers { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; max-width: 560px; }
.pchip {
  font-size: 10px;
  padding: 7px 14px;
  border-radius: 999px;
  background: rgba(8,16,32,0.85);
  border: 1.5px solid var(--c, var(--jay));
  color: var(--c, var(--jay-lite));
  animation: cardIn 0.3s cubic-bezier(0.2, 1.4, 0.4, 1) backwards;
}

/* ---------- chapter card ---------- */
#chapter {
  position: absolute;
  top: 18%;
  left: 0; right: 0;
  text-align: center;
  pointer-events: none;
  z-index: 4;
}
#chapter.hidden { display: none; }
#chapter h3 {
  font-family: var(--body);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.5em;
  color: var(--gold);
  margin-right: -0.5em;
}
#chapter h2 { font-size: clamp(30px, 5vw, 52px); }
#chapter.animate h3 { animation: chapterKickerIn 2.6s cubic-bezier(0.16, 1, 0.3, 1) both; }
#chapter.animate h2 { animation: chapterIn 2.6s cubic-bezier(0.16, 1, 0.3, 1) both; }
#chapter.animate::after {
  content: "";
  display: block;
  margin: 10px auto 0;
  width: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--jay), var(--gold), transparent);
  animation: chapterSweep 2.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes chapterKickerIn {
  0% { opacity: 0; letter-spacing: 0.9em; filter: blur(6px); transform: translateY(10px) scale(0.94); }
  14%, 78% { opacity: 1; letter-spacing: 0.5em; filter: blur(0); transform: translateY(0) scale(1); }
  100% { opacity: 0; letter-spacing: 0.3em; filter: blur(3px); transform: translateY(-10px) scale(1.03); }
}
@keyframes chapterIn {
  0% { opacity: 0; transform: translateY(28px) scale(0.9); filter: blur(10px); }
  14%, 78% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
  100% { opacity: 0; transform: translateY(-16px) scale(1.06); filter: blur(6px); }
}
@keyframes chapterSweep {
  0% { width: 0; opacity: 0; }
  14% { width: 240px; opacity: 1; }
  78% { width: 240px; opacity: 1; }
  100% { width: 0; opacity: 0; }
}

@keyframes cardIn { from { opacity: 0; transform: translateY(26px) scale(0.92); } }

/* ---------- game over stats ---------- */
/* one hero number, then a quiet supporting row. six equally-weighted cells gave
   the screen no focal point at all. */
#stats {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s4);
  background: none;
  border: none;
  padding: 0;
  margin: var(--s2) 0 var(--s5);
}
#stats .hero { display: flex; flex-direction: column; align-items: center; gap: var(--s1); }
#stats .hero .k { font: var(--t-label); letter-spacing: var(--tracking-label); color: var(--dim); }
#stats .hero .v {
  font: 900 64px/1 var(--display);
  letter-spacing: var(--tracking-display);
  color: var(--gold);
}
#stats .hero .sub { font: var(--t-label); letter-spacing: var(--tracking-label); color: var(--jay-lite); }
#stats .minor {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--s2) var(--s5);
  font: var(--t-body); color: var(--dim);
}
#stats .minor b { font-weight: 500; color: var(--text); }

/* ---------- settings chips ---------- */
/* Third tier on the title screen: quiet, small, and never competing with the
   mode buttons. AUTOFIRE + HOW TO PLAY were cut from here — autofire is a
   mid-run preference, and SOLO already force-shows the tutorial on first run. */
.settings { display: flex; gap: var(--s2); margin-top: var(--s5); }
.chip {
  font: var(--t-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--dim);
  background: rgba(8, 16, 32, 0.7);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: var(--s2) var(--s3);
  cursor: pointer;
  box-shadow: none;
  transition: color 0.12s, border-color 0.12s;
}
.chip:hover { border-color: var(--jay-lite); color: var(--text); box-shadow: none; }
.chip.on { color: var(--jay-lite); border-color: var(--jay-lite); background: rgba(111,208,251, 0.1); }
.chip.hidden { display: none; }

/* ---------- The Perch hub panel ---------- */
/* full-stage dark wash behind the panel — centers it and dims the world */
.hub-scrim {
  position: absolute;
  inset: 0;
  z-index: 19;
  /* Centred. This used to be bottom-docked so the bough and the keeper stayed visible
     above the panel, but a 66vh dock made every station scroll — cards clipped mid-row
     and the panel read as cramped and text-heavy. A centred, wider panel fits more of a
     station on screen at once, which is the thing that actually made it confusing. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s5);   /* even now that it centres — the old bottom-heavy pad was for the dock */
  background: linear-gradient(to bottom, rgba(4, 9, 20, 0.1) 0%, rgba(4, 9, 20, 0.62) 34%, rgba(4, 9, 20, 0.88) 100%);
  backdrop-filter: blur(3px) saturate(0.9);
  -webkit-backdrop-filter: blur(3px) saturate(0.9);
}
.hub-scrim.hidden { display: none; }

/* The purse is a persistent readout. It used to be drawn on the CANVAS at the Perch and
   re-created in the DOM the moment a station opened — so opening a panel made your acorns
   blink out and animate back in, blurred by the scrim on the way. It is one node now,
   living OUTSIDE the scrim for the whole time you are at the Perch: it never re-appears,
   never animates, and the scrim's backdrop-filter never touches it. */
.hub-purse {
  position: absolute;
  z-index: 21;   /* above the scrim, so the blur is behind it and not on it */
  top: 22px;
  right: 20px;
  display: flex;
  align-items: center;
  gap: 6px;
  font: 500 14px 'Haffer', system-ui, sans-serif;
  color: var(--gold, #ffd166);
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.75);
  pointer-events: none;
}
.hub-purse.hidden { display: none; }
.hub-purse .hp-v { display: flex; align-items: center; gap: 5px; }
.hub-purse .hp-star { color: #cfe6ff; }
.hub-purse svg { width: 17px; height: 17px; }

#hubPanel.hub-panel {
  position: relative;
  width: min(920px, 94%);
  /* min-width/min-height:0 are load-bearing. A flex item refuses to shrink below its
     content, and the Armory's scrollable weapon row is ~1900px wide — without these the
     panel outgrows the stage and centres half off the right edge. */
  min-width: 0;
  min-height: 0;
  /* % of the scrim, NOT vh. The scrim sits inside the CSS-scaled 960x540 stage, so a vh
     cap is measured against the browser window and has nothing to do with the space the
     panel actually has — it used to resolve taller than the stage and overflow it, which
     is most of why stations read as clipped and cramped. */
  max-height: 100%;
  transform-origin: var(--ox, 50%) 50%;
  display: flex;
  flex-direction: column;
  padding: 18px 22px 16px;
  /* flat, modern surface — one dark fill, a hairline border, a single soft drop shadow */
  background: #0b1424;
  border: 1px solid rgba(120, 160, 210, 0.22);
  border-top: 2px solid var(--jay-lite);
  border-radius: 16px;
  box-shadow: var(--lift-lg);
}
#hubPanel.hidden { display: none; }

/* ---------- Perch enter/exit motion (JS toggles .is-in / .is-out) ---------- */
.hub-scrim {
  opacity: 0;
  transition: opacity 0.18s ease, backdrop-filter 0.18s ease;
}
.hub-scrim.is-in { opacity: 1; }
.hub-scrim.is-out { opacity: 0; transition-duration: 0.14s; }

#hubPanel.hub-panel {
  opacity: 0;
  transform: scale(0.94) translateY(12px);
  transition:
    opacity 0.22s cubic-bezier(0.2, 0.9, 0.25, 1),
    transform 0.22s cubic-bezier(0.2, 0.9, 0.25, 1);
}
#hubPanel.hub-panel.is-in { opacity: 1; transform: scale(1) translateY(0); }
#hubPanel.hub-panel.is-out { opacity: 0; transform: scale(0.96) translateY(0); transition-duration: 0.14s; }

/* card entrance stagger — nothing pops in */
@keyframes hubCardIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
#hubPanelBody .card-grid > *,
#hubPanelBody .swatch-grid > * {
  animation: hubCardIn 0.26s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}
/* stagger from an inline --i the template strings write, not eight hand-numbered rules */
#hubPanelBody .card-grid > *,
#hubPanelBody .swatch-grid > * { animation-delay: calc(var(--i, 0) * 0.04s); }

@media (prefers-reduced-motion: reduce) {
  .hub-scrim,
  .hub-scrim.is-in,
  .hub-scrim.is-out { transition: opacity 0.12s linear; backdrop-filter: blur(3px) saturate(0.9); }
  #hubPanel.hub-panel {
    transform: none;
    transition: opacity 0.12s linear;
  }
  #hubPanel.hub-panel.is-in,
  #hubPanel.hub-panel.is-out { transform: none; }
  #hubPanelBody .card-grid > *,
  #hubPanelBody .swatch-grid > * {
    animation: none;
    opacity: 1;
    transform: none;
  }
}
.hub-panel-head {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid rgba(120, 160, 210, 0.2); gap: 10px;
}
.hub-panel-head h2 { font-family: var(--display); font-weight: 500; font-size: 14px; color: var(--jay-lite); text-shadow: none; margin: 0; display: flex; align-items: center; gap: 11px; }
/* the station glyph, bare. A tinted disc with a ring around one icon was a second
   silhouette in the header for no information — the glyph and the name do the whole job. */
.hub-avatar {
  flex-shrink: 0; width: 22px; height: 22px; display: grid; place-items: center;
  line-height: 1; color: var(--jay-lite);
}
.hub-id { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.hub-name { font-family: var(--display); font-weight: 500; font-size: 14px; line-height: 1; }
/* the keeper's line, in the header under the station name instead of a boxed card
   sitting on top of the body */
.hub-quip {
  font-family: var(--body); font-style: italic; font-size: 11px; line-height: 1.25;
  color: #8fa8c4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hub-panel-head .hub-role { font-family: var(--body); font-size: 10px; font-weight: 500; color: var(--dim); letter-spacing: 0.22em; }
.hub-panel-head .ghost.small { flex-shrink: 0; }
#hubPanelBody {
  position: relative;   /* .car-current anchors to this */
  display: flex; flex-direction: column;   /* so a panel can ask for the whole body and centre in it */
  transition: opacity 0.16s ease;
  overflow-y: auto;
  min-height: 0;   /* without this a flex child refuses to shrink below its content */
  padding: var(--s3) var(--s3) var(--s2);
  margin-top: var(--s2);
  background: linear-gradient(#080e1a, #0b1424);
  box-shadow: inset 0 14px 26px -16px #000;
  border-radius: 10px;
}

#hubPanelBody.swapping { opacity: 0; }

/* ---------- shop tabs (segmented category row) ---------- */
.shop-tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 4px 0 20px;
}
.shop-tab {
  font-family: var(--body);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--dim);
  background: rgba(8, 16, 32, 0.6);
  border: 1px solid rgba(120, 160, 210, 0.22);
  border-radius: 999px;
  padding: 8px 16px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s, box-shadow 0.12s, transform 0.12s;
}
.shop-tab:hover {
  color: var(--text);
  border-color: color-mix(in srgb, var(--c, var(--jay)) 55%, transparent);
  transform: translateY(-1px);
}
.shop-tab.active {
  color: #05131f;
  background: var(--c, var(--jay-lite));
  border-color: color-mix(in srgb, var(--c, var(--jay-lite)) 60%, #000);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
}
.shop-tab:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--c, var(--jay)) 75%, transparent);
  outline-offset: 2px;
}

/* armory Weapons/Specials tabs — segmented control matching the shop's drill-in feel */
.armory-tabs { display: flex; gap: 8px; margin: 2px 0 18px; }
.armory-tab {
  flex: 1;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-family: var(--body); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--dim);
  background: none;
  clip-path: var(--chamfer);
  border: none; padding: 11px 18px; cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
/* a segmented control: one recessed track, the active half filled. Two outlined pills
   side by side were two more frames on a panel that has too many. */
.armory-tab:hover { color: var(--text); background: rgba(255, 255, 255, 0.05); }
.armory-tab.active { color: #05131f; background: var(--jay-lite); }
.armory-tab.active .branch-ico { background: rgba(0, 0, 0, 0.12); }

/* compact close button — pinned to the panel's top-right corner */
.hub-close {
  position: absolute; top: 10px; right: 12px; z-index: 2;
  width: 29px; height: 29px; padding: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  font-family: var(--body); font-size: 14px; line-height: 1;
  color: var(--dim);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(120, 160, 210, 0.28);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transition: color 0.12s, background 0.12s, border-color 0.12s, box-shadow 0.12s, transform 0.12s;
}
.hub-close:hover {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  border-color: color-mix(in srgb, var(--danger) 55%, transparent);
  box-shadow: 0 0 12px color-mix(in srgb, var(--danger) 45%, transparent), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.hub-close:active { transform: translateY(1px); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.4); }
.hub-close:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--danger) 70%, transparent);
  outline-offset: 2px;
}

/* NPC dialogue — a little speech card so each stop reads as a character, not a menu */
/* branch section header (shop tree + weapons/specials groupings) */
/* A section head with a rule trailing off to the right pinned everything to the left
   edge of the panel. Centred, with the rule reaching out both ways, it reads as a divider
   for the row beneath it instead of a label stuck to a wall. */
.branch-head {
  display: flex; align-items: center; gap: 9px;
  grid-column: 1 / -1; font-weight: 500; font-size: 10px; letter-spacing: 1.4px;
  color: var(--c, var(--jay-lite)); margin: 11px 0 8px;
}
.branch-head::before, .branch-head::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--c, var(--jay)) 40%, transparent));
}
.branch-head::after { background: linear-gradient(90deg, color-mix(in srgb, var(--c, var(--jay)) 40%, transparent), transparent); }
.branch-head:first-of-type { margin-top: 2px; }
.branch-ico {
  font-size: 10px; width: 20px; height: 20px; border-radius: 6px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--c, var(--jay)) 16%, transparent);
}

/* shop-tree cards — flat surface, quiet hover lift */
/* 190px, not 212 — three fat cards a row turned every station into a scroll. Four
   tighter ones fit the same panel and the copy still reads at 11.5px. */
/* Flex, not grid. A branch holds 3, 4, 5 or 8 nodes, so no fixed column count divides
   evenly for all of them and auto-fill left the remainder hanging off the left edge —
   a centred last row is the only layout that looks deliberate at every count. */
.card-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s2); margin-bottom: var(--s2); }
.card-grid > * { flex: 1 1 178px; max-width: 232px; }
/* A card is a FILL, not an outline. Every one of these used to carry a 1.5px chamfered
   stroke in its state colour, so a grid of five was five hard-edged frames fighting each
   other and the panel border — that reads as chrome, not as content. State now comes from
   how bright the fill is plus the badge that was already there, and hierarchy comes from
   one soft top highlight. */
.shop-node {
  padding: var(--s3) var(--s3) var(--s4);
  background: linear-gradient(rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.022));
  clip-path: var(--chamfer);
  transition: background 0.12s, transform 0.12s;
}
.shop-node.ok { background: linear-gradient(color-mix(in srgb, var(--jay) 16%, transparent), rgba(255, 255, 255, 0.03)); }
.shop-node.owned { background: linear-gradient(rgba(255, 209, 102, 0.13), rgba(255, 209, 102, 0.03)); }
.shop-node.branch-pick { background: linear-gradient(color-mix(in srgb, var(--c, var(--jay)) 15%, transparent), rgba(255, 255, 255, 0.025)); cursor: pointer; }
/* the five paths are one choice — they belong on one row, not four and an orphan */
/* the five paths are one choice — they belong on one row */
.card-grid:has(.branch-pick) > * { flex-basis: 130px; max-width: none; }
.shop-node.locked { opacity: 0.5; }
.shop-node.ok:hover, .shop-node.owned:hover, .shop-node.branch-pick:hover { transform: translateY(-2px); filter: brightness(1.18); }
/* Centred. A grid of equal cards each holding one short sentence read as a wall of
   ragged left edges; one shared centre line down every card is calmer and makes the
   name the thing you see first. */
.shop-node { text-align: center; }
.sn-top { display: flex; flex-direction: column; align-items: center; gap: 7px; }
.sn-top b { font: var(--t-md); font-size: 13.5px; letter-spacing: var(--tracking-display); }
.sn-desc { font: var(--t-body); font-size: 11.5px; line-height: 1.4; color: #9db4d0; margin-top: 7px; }

/* compact state badges: owned check, cost pill, lock, buy button */
.badge {
  font: var(--t-label); letter-spacing: var(--tracking-label); white-space: nowrap; border-radius: 6px; padding: 3px 8px; letter-spacing: 0.03em;
}
.badge.owned {
  color: var(--gold);
  background: rgba(255, 209, 102, 0.12);
  border: 1px solid rgba(255, 209, 102, 0.3);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.badge.cost {
  color: #9fb6d0;
  background: rgba(120, 160, 210, 0.1);
  border: 1px solid rgba(120, 160, 210, 0.22);
}
.badge.lock {
  color: #7d8fa8;
  background: rgba(120, 160, 210, 0.06);
  border: 1px solid rgba(120, 160, 210, 0.14);
}

/* pill action buttons — tactile flat with subtle gradient + inner highlight */
.badge-buy {
  font-family: var(--body); font-size: 10px; font-weight: 500; white-space: nowrap;
  letter-spacing: 0.04em; border: none; border-radius: 0; padding: 5px 12px; cursor: pointer;
  clip-path: var(--chamfer-sm);
  transition: filter 0.12s, transform 0.12s;
}
.badge-buy.buy { color: #2a1c00; background: var(--gold); }
.badge-buy.equip { color: #05131f; background: var(--jay-lite); }
.badge-buy:hover { filter: brightness(1.08); transform: translateY(-1px); }
.badge-buy:active { transform: translateY(1px); filter: brightness(0.96); }
.badge-buy.buy:focus-visible { outline: 2px solid color-mix(in srgb, var(--gold) 80%, transparent); outline-offset: 2px; }
.badge-buy.equip:focus-visible { outline: 2px solid color-mix(in srgb, var(--jay) 80%, transparent); outline-offset: 2px; }

/* ---------- Armory: a weapon RACK ----------
   Not a carousel. A row of weapons laid out side by side on a shelf, the one you're
   looking at lifted and lit, everything else still a weapon on the same shelf. No boxes,
   no arrows, no dots — the art IS the object. The detail and the bench live in fixed
   slots BELOW the rack, which is what keeps the panel one height on both tabs.
   Cards are absolutely positioned off a FLOAT --d (distance from the scroll position),
   so scrolling is a 1:1 translation and a focus change is a variable write, never a
   rebuild — rebuilding is what killed every transition here before. */
.armory-tabs {
  display: flex; gap: 4px; align-self: center;
  padding: 3px; border-radius: 10px;
  background: rgba(255, 255, 255, 0.05);
  margin: 0 auto var(--s3);
  width: max-content;
}
.armory-tab {
  padding: 5px 18px; border: 0; border-radius: 8px; cursor: pointer;
  background: transparent; color: var(--dim);
  font: var(--t-md); font-size: 11px; letter-spacing: var(--tracking-display);
  transition: background 0.16s, color 0.16s;
}
.armory-tab:hover { color: var(--text); }
.armory-tab.active { background: var(--jay-lite); color: #05131f; }

.rack {
  position: relative;
  height: 108px;
  overflow: hidden;
  touch-action: pan-y;
  cursor: grab;
  /* the shelf line the weapons sit on */
  background:
    radial-gradient(120% 60% at 50% 100%, rgba(111, 208, 251, 0.09), transparent 70%),
    linear-gradient(to bottom, transparent 78%, rgba(120, 160, 210, 0.22) 78.5%, transparent 80%);
  /* fade the ends so items leave the rack instead of being cut off */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent);
}
.rack.dragging { cursor: grabbing; }
.rack-track { position: absolute; inset: 0; }
.rack-item {
  --pitch: 96px;
  --s: 1;
  position: absolute;
  left: 50%; top: 50%;
  width: 76px; height: 76px;
  margin: 0; padding: 0; border: 0; background: none; box-shadow: none;
  display: grid; place-items: center;
  color: var(--c, var(--jay-lite));
  cursor: pointer;
  transform: translate(-50%, -50%) translateX(calc(var(--d, 0) * var(--pitch))) scale(var(--s));
  transition: none;   /* a 1:1 drag must not be smeared by a transition; .snap adds one */
}
/* only the settle and programmatic jumps animate */
.rack.snap .rack-item { transition: transform 0.3s cubic-bezier(0.22, 0.9, 0.24, 1), opacity 0.3s ease, filter 0.3s ease; }
.rack-item svg { width: 100%; height: 100%; filter: drop-shadow(0 0 10px currentColor); }
.rack-item.focus svg { filter: drop-shadow(0 0 18px currentColor); }
/* locked weapons read as unbought stock — present on the shelf, not yours */
.rack-item.locked { filter: grayscale(0.85) brightness(0.7); }
.rack-item.locked.focus { filter: grayscale(0.5) brightness(0.85); }
/* the equipped one wears a small mark instead of a border */
.rack-item.eq::after {
  content: ""; position: absolute; bottom: -2px; left: 50%; transform: translateX(-50%);
  width: 22px; height: 3px; border-radius: 2px;
  background: var(--gold); box-shadow: 0 0 8px var(--gold);
}

/* fixed slots. Both tabs render into the same two boxes, so the panel is one height
   whichever tab you're on — the old version let the bench exist only for weapons. */
/* the whole Armory column sits CENTRED in the body — the block is a fixed height, so a
   maxed weapon doesn't leave its dead air pooled at the bottom of the panel */
.armory-stack { flex: 0 0 auto; height: 272px; display: flex; flex-direction: column; justify-content: center; }

/* ONE block under the shelf: identity, then where you are on the track, then the only
   thing there is to press. It used to be two stacked slots, which read as two identical
   title+description pairs with a button wedged between them. */
.rack-info {
  height: 156px;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding-top: var(--s3);
}
.ri-name {
  position: relative;
  font: var(--t-lg); font-size: 21px; letter-spacing: var(--tracking-display);
  height: 27px;
}
/* the cadence hangs OFF the name absolutely, so the title stays dead-centre instead of
   being nudged left by half a chip */
.ri-chip {
  position: absolute; left: 100%; bottom: 3px; margin-left: 10px;
  font: var(--t-body); font-size: 9.5px; letter-spacing: 0.14em; color: #6a8099; white-space: nowrap;
}
.ri-tag { font: var(--t-body); font-size: 12px; color: #9db4d0; line-height: 1.35; height: 18px; max-width: 470px; overflow: hidden; }
/* the track: three nodes, no box. Glow and fill are this panel's whole language. */
/* the nodes + their step line are ONE group, held apart from the identity above */
/* the bar is WIDE now: every step names itself under its own dot, so the links have to be
   long enough that three captions sit side by side without touching */
.ri-track { position: relative; display: flex; align-items: center; gap: 7px; height: 68px; margin-top: var(--s3); }
.ri-track.locked { visibility: hidden; }
/* the step's own name, hanging under its dot. It does NOT take part in the flex row —
   absolute, so widening a caption can never move a dot. */
.bt-cap {
  position: absolute; left: 50%; top: 16px; transform: translateX(-50%);
  width: 130px; text-align: center;
  font: var(--t-md); font-size: 9.5px; letter-spacing: 0.11em; text-transform: uppercase;
  line-height: 1.25; color: var(--text); pointer-events: none;
}
/* a step you haven't reached is FADED, not hidden — colour, never opacity (an opacity here
   multiplies into the tooltip child and renders it see-through) */
.bt-node.later .bt-cap { color: #4d6076; }
.bt-node.done .bt-cap { color: #8ba3bd; }
.bt-node.live .bt-cap { color: var(--c, var(--jay)); }
/* the price hangs under the caption of the step it buys — never a centred row halfway
   down the panel, which put the cost nowhere near the thing being costed */
.bt-buy {
  position: absolute; left: 50%; top: 37px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 9px; white-space: nowrap;
}
/* the live step is ONE line — a label and its sentence, never a second heading */
.ri-step { font: var(--t-body); font-size: 11px; color: #8ba3bd; height: 17px; margin-top: var(--s2); max-width: 470px; overflow: hidden; }
.ri-step b {
  font: var(--t-md); font-size: 10px; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--text); margin-right: 8px;
}
.ri-act { margin-top: auto; display: flex; align-items: center; gap: 11px; min-height: 0; }
/* the chit is a quiet alternative, not a second chamfered tag. It has to un-set the
   global button lift + inset highlight + radius or it renders as an outlined pill. */
.ri-alt {
  font: var(--t-body); font-size: 10.5px; color: #8ba3bd;
  background: none; border: 0; border-radius: 0; padding: 0 0 1px; cursor: pointer;
  box-shadow: none; letter-spacing: 0;
  border-bottom: 1px solid rgba(120, 160, 210, 0.28);
}
.ri-alt:hover { color: var(--text); border-bottom-color: currentColor; box-shadow: none; transform: none; }
.ri-alt svg { width: 10px; height: 10px; vertical-align: -1px; margin: 0 2px; }

.bt-node {
  position: relative;
  width: 11px; height: 11px; border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--c, var(--jay)) 45%, transparent);
}
.bt-node.done { background: var(--c, var(--jay)); box-shadow: 0 0 8px var(--c, var(--jay)); }
.bt-node.live {
  background: var(--c, var(--jay));
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--c, var(--jay)) 22%, transparent), 0 0 14px var(--c, var(--jay));
  animation: btPulse 1.9s ease-in-out infinite;
}
/* dimmed by COLOUR, not opacity — an opacity on the node multiplies into its tooltip
   child and made the tip render see-through over whatever was behind it */
.bt-node.later { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--c, var(--jay)) 18%, transparent); }
.bt-link { width: 128px; height: 2px; border-radius: 1px; background: rgba(120, 160, 210, 0.16); }
.bt-link.on { background: color-mix(in srgb, var(--c, var(--jay)) 70%, transparent); }
@keyframes btPulse {
  50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--c, var(--jay)) 10%, transparent), 0 0 18px var(--c, var(--jay)); }
}
/* Every step says what it is on hover — including the ones you can't buy yet, so a dim
   dot is a goal instead of a mystery. Pure CSS off data-tip; the track is only ever three
   nodes, so there is nothing here worth a JS tooltip layer. */
.bt-node[data-tip] { cursor: help; }
.bt-node[data-tip]::after {
  content: attr(data-tip);
  position: absolute; left: 50%; bottom: calc(100% + 10px); transform: translateX(-50%);
  width: max-content; max-width: 250px;
  padding: 7px 10px; border-radius: 8px;
  background: #08101f; color: #dcebfa;
  font: var(--t-body); font-size: 10.5px; font-style: normal; line-height: 1.35; text-align: center;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6);
  opacity: 0; pointer-events: none; transition: opacity 0.13s ease 0.04s;
  z-index: 40;
}
.bt-node[data-tip]:hover::after { opacity: 1; }

/* the block re-renders on every focus change, so the entrance runs itself — it rises
   into place instead of popping, staggered so the name leads the line */
@keyframes rackSlotIn { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
.rack-info > * { animation: rackSlotIn 0.26s cubic-bezier(0.2, 0.9, 0.25, 1) both; }
.rack-info > *:nth-child(2) { animation-delay: 0.03s; }
.rack-info > *:nth-child(3) { animation-delay: 0.06s; }
.rack-info > *:nth-child(4) { animation-delay: 0.09s; }
.rack-info > *:nth-child(5) { animation-delay: 0.12s; }

/* skins — color swatch grid */
/* 8 skins on an auto-fill grid came out 6 + an orphan row of 2. A small fixed set gets a
   column count that divides into it — 4 x 2. */
.swatch-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.swatch-card {
  display: flex; flex-direction: column; align-items: center; gap: 9px; text-align: center;
  padding: 17px 12px;
  background: linear-gradient(rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.022));
  clip-path: var(--chamfer);
  transition: background 0.12s, transform 0.12s;
}
.swatch-card:hover { transform: translateY(-2px); filter: brightness(1.18); }
.swatch-card.wearable { cursor: pointer; }
/* the whole card is the button, so the affordance is a word, not a second tag */
.ic-hint { font: var(--t-body); font-size: 10px; letter-spacing: 0.1em; color: #6a8099; text-transform: uppercase; }
.swatch-card.wearable:hover .ic-hint { color: var(--jay-lite); }
.swatch-card.locked { opacity: 0.55; }
/* the equipped skin is a brighter fill and a gold check, not a gold frame */
.swatch-card.eq { background: linear-gradient(rgba(255, 209, 102, 0.16), rgba(255, 209, 102, 0.03)); }
.swatch-chip {
  width: 36px; height: 36px; border-radius: 50%; border: 2px solid rgba(255, 255, 255, 0.25);
  box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.35), 0 0 10px rgba(0, 0, 0, 0.3);
}

/* ---------- the roost (permanent meta upgrades) ---------- */
/* px, not %: #roost is a shrink-to-fit flex column, so a percentage width resolves
   against a parent that is itself sized by this child. */
#roostList { width: 660px; max-width: 100%; margin: 22px 0; }

/* hub roost station — current-build loadout chips + owned-upgrade/perk tags */
.loadout-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-bottom: 9px; }
.loadout-chip {
  display: flex; align-items: center; gap: 6px;
  border-radius: 999px;
  padding: 5px 12px 5px 7px; background: rgba(255, 255, 255, 0.06);
  font-size: 10px; font-weight: 500; color: var(--text);
}
.loadout-chip .ic-icon { width: 18px; height: 18px; }
.loadout-chip .swatch-chip { width: 18px; height: 18px; border-width: 1px; }
.loadout-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-bottom: 14px; }
.tag-chip {
  font-size: 10px; font-weight: 500; color: #9fb6d0; background: rgba(120, 160, 210, 0.14);
  border-radius: 999px; padding: 4px 10px;
}
.tag-chip.perk { color: var(--gold); background: rgba(255, 209, 102, 0.14); }
.tag-chip.dim { opacity: 0.5; }

/* ---------- quick level-up pick (1-of-3, between chapters) ---------- */
/* level-up: dim the still-living gameplay behind the cards, don't black it out */
#levelup { background: rgba(6, 12, 24, 0.44); backdrop-filter: blur(2px); }
/* level-up screen slides in — the scrim fades, the heading + cards stagger up (nothing pops in) */
@keyframes overlayIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes lvlCardIn { from { opacity: 0; transform: translateY(16px) scale(0.97); } to { opacity: 1; transform: none; } }
#levelup:not(.hidden) { animation: overlayIn 0.2s ease both; }
#levelup:not(.hidden) h2 { animation: lvlCardIn 0.3s cubic-bezier(0.2, 0.9, 0.25, 1) both; }
#levelCards { display: flex; flex-direction: row; gap: 16px; margin: 18px 0; flex-wrap: wrap; justify-content: center; }
.lvl-card {
  position: relative; width: 208px; cursor: pointer; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  padding: 22px 16px 16px;
  background: linear-gradient(color-mix(in srgb, var(--c, var(--jay)) 26%, rgba(10, 18, 32, 0.9)), rgba(10, 18, 32, 0.92));
  clip-path: var(--chamfer);
  transition: background 0.15s, transform 0.15s, filter 0.15s;
  animation: lvlCardIn 0.34s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}
.lvl-card:nth-child(1) { animation-delay: 0.07s; }
.lvl-card:nth-child(2) { animation-delay: 0.14s; }
.lvl-card:nth-child(3) { animation-delay: 0.21s; }
.lvl-card:hover {
  transform: translateY(-3px);
  background: color-mix(in srgb, var(--c, var(--jay)) 16%, rgba(10, 18, 32, 0.85));
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--c, var(--jay)) 85%, #fff);
}
.lvl-ico { width: 42px; height: 42px; }
.lvl-ico svg { width: 100%; height: 100%; }
.lvl-ico .ico-fallback { font-size: 32px; line-height: 42px; }
.lvl-name { font-size: 14px; font-weight: 500; color: var(--text); font-family: var(--display); font-weight: 500; letter-spacing: 0.02em; }
.lvl-desc { font-size: 10px; color: #9db4d0; line-height: 1.4; }
.lvl-key {
  position: absolute; top: 8px; left: 10px;
  font-family: var(--body); font-size: 10px; font-weight: 500; color: #06101c;
  width: 18px; height: 18px; display: grid; place-items: center;
  background: var(--c, var(--jay)); clip-path: var(--chamfer-sm);
}
@media (prefers-reduced-motion: reduce) {
  #levelup:not(.hidden), #levelup:not(.hidden) h2, .lvl-card { animation: none; }
}

/* ---------- codex / field guide (bestiary overlay) ---------- */
/* the codex is an .overlay; .codex adds a centered panel in the .hub-panel family */
#codex.overlay.codex {
  /* above the chrome buttons — the Field Guide chip used to show THROUGH the guide it opens */
  z-index: 30;
}
.codex .codex-panel {
  position: relative;
  width: min(760px, 92%);
  max-height: 86vh;
  display: flex;
  flex-direction: column;
  padding: 24px;
  background:
    linear-gradient(180deg, rgba(111,208,251, 0.06), transparent 42%),
    rgba(6, 12, 24, 0.97);
  border: 1px solid rgba(120, 160, 210, 0.22);
  border-top: 2px solid var(--jay-lite);
  border-radius: 16px;
  text-align: left;
  overflow-y: auto;
  /* stacked depth: inner top rim-light + two drops + JAY hairline + soft bloom */
  box-shadow: var(--lift-lg);
}

.codex-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(120, 160, 210, 0.2);
}
.codex-head h2 {
  font-family: var(--display); font-weight: 700;
  font-size: 20px;
  color: var(--jay-lite);
  margin: 0;
  line-height: 1;
}
/* round close button — mirrors .hub-close (muted, DANGER on hover) */
.codex-close {
  flex-shrink: 0;
  width: 29px; height: 29px; padding: 0; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  font-family: var(--body); font-size: 14px; line-height: 1;
  color: var(--dim);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(120, 160, 210, 0.28);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transition: color 0.12s, background 0.12s, border-color 0.12s, box-shadow 0.12s, transform 0.12s;
}
.codex-close:hover {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 16%, transparent);
  border-color: color-mix(in srgb, var(--danger) 55%, transparent);
  box-shadow: 0 0 12px color-mix(in srgb, var(--danger) 45%, transparent), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.codex-close:active { transform: translateY(1px); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.4); }
.codex-close:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--danger) 70%, transparent);
  outline-offset: 2px;
}

/* segmented control — BOSSES / ENEMIES, in the .shop-tab spirit */
.codex-tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 4px 0 18px;
}
.codex-tab {
  font-family: var(--body);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--dim);
  background: rgba(8, 16, 32, 0.6);
  border: 1px solid rgba(120, 160, 210, 0.22);
  border-radius: 999px;
  padding: 8px 18px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s, box-shadow 0.12s, transform 0.12s;
}
.codex-tab:hover {
  color: var(--text);
  border-color: color-mix(in srgb, var(--jay) 55%, transparent);
  transform: translateY(-1px);
}
.codex-tab.active {
  color: #05131f;
  background: var(--jay-lite);
  border-color: color-mix(in srgb, var(--jay-lite) 60%, #000);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
}
.codex-tab:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--jay) 75%, transparent);
  outline-offset: 2px;
}

/* responsive grid of entry cards */
/* FIFTEEN keepers, so the column count has to divide into fifteen — five across, three
   rows, no orphan and nothing below the fold. `auto-fill` gave four and left a short row
   of three hanging off the left edge with the last three entries scrolled out of sight. */
.codex-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 12px;
}

/* clickable entry card — resting depth + hover lift */
.codex-card {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 14px 14px 15px;
  border-radius: 14px;
  cursor: pointer;
  background:
    linear-gradient(180deg, rgba(111,208,251, 0.08), transparent 55%),
    rgba(8, 16, 32, 0.7);
  border: 1px solid rgba(120, 160, 210, 0.22);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    0 8px 18px -10px rgba(0, 0, 0, 0.6);
  transition: transform 0.14s ease, border-color 0.14s ease, box-shadow 0.14s ease;
}
.codex-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--jay) 55%, transparent);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 14px 26px -10px rgba(0, 0, 0, 0.7),
    0 0 22px rgba(111,208,251, 0.18);
}
.codex-card:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--jay) 70%, transparent);
  outline-offset: 2px;
}
.cx-name {
  font-family: var(--display); font-weight: 500;
  font-size: 14px;
  color: var(--text);
  line-height: 1.15;
}
.cx-title {
  font-family: var(--body);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--dim);
}
/* locked entry — muted, no lift, reads as "not yet discovered" */
.codex-card.locked {
  opacity: 0.5;
  filter: saturate(0.35);
  cursor: default;
  background: rgba(8, 16, 32, 0.5);
  border-color: rgba(120, 160, 210, 0.14);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
}
.codex-card.locked:hover {
  transform: none;
  border-color: rgba(120, 160, 210, 0.14);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
}
.codex-card.locked .cx-name { color: var(--dim); }

/* ---------- portraits ----------
   A keeper's plate is its own biome's shipped artwork under a wash of the boss's colour;
   a common bird is its source silhouette masked to a flat shape. Neither is a procedural
   sprite blown up past the size it was drawn for. */
.cx-plate {
  position: relative;
  height: 82px;
  margin: -14px -14px 10px;
  border-radius: 13px 13px 0 0;
  overflow: hidden;
  background-color: #0a1424;
  box-shadow: inset 0 -22px 26px -14px rgba(6, 11, 24, 0.95);
}
.cx-plate::after {
  content: "";
  position: absolute; inset: 0;
  background:
    radial-gradient(120% 90% at 70% 22%, color-mix(in srgb, var(--g, #6FD0FB) 34%, transparent), transparent 66%),
    linear-gradient(180deg, rgba(6, 11, 24, 0.34) 0%, rgba(6, 11, 24, 0.15) 34%, rgba(6, 11, 24, 0.94));
}
/* the photo itself sits back — these are somewhere a keeper is, not a holiday postcard */
.cx-plate { filter: saturate(0.82) contrast(1.04) brightness(0.82); }
.cx-plate.big { height: 150px; margin: 0 0 12px; border-radius: 14px; }
.cx-plate.empty { background: rgba(120, 160, 210, 0.06); }
.cx-plate-name {
  position: absolute; left: 10px; bottom: 6px; z-index: 1;
  font: var(--t-body); font-size: 9px; letter-spacing: 0.2em;
  color: color-mix(in srgb, var(--g, #6FD0FB) 70%, #fff);
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.9);
}
/* a keeper's plate: its own place behind it, the keeper itself in front. The backdrop is
   pushed back with a heavier vignette than the enemy plates get, because something is
   standing on it now. */
.cx-plate.keeper::after {
  background:
    radial-gradient(58% 62% at 50% 46%, color-mix(in srgb, var(--g, #6FD0FB) 46%, transparent), transparent 72%),
    linear-gradient(180deg, rgba(6, 11, 24, 0.5) 0%, rgba(6, 11, 24, 0.3) 34%, rgba(6, 11, 24, 0.96));
}
.cx-plate.keeper i {
  position: absolute; inset: 10% 8% 14%;
  z-index: 1;
  background: color-mix(in srgb, var(--bird, #6FD0FB) 34%, #eaf5ff);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.75)) drop-shadow(0 0 18px color-mix(in srgb, var(--g) 60%, transparent));
}
/* the orbiters are machines, so they wear the flywheel — smaller, dead-centred, and
   turning slowly enough that it reads as a mechanism rather than a loading spinner */
.cx-plate.keeper.machine i {
  inset: 20% 8% 22%;
  background: color-mix(in srgb, var(--g) 70%, #eaf6ff);
  animation: cxSpin 22s linear infinite;
}
@keyframes cxSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cx-plate.keeper.machine i { animation: none; } }
.cx-plate.bird { background: linear-gradient(165deg, color-mix(in srgb, var(--g) 34%, #16233a), #080f1d); }
.cx-plate.bird i {
  position: absolute; inset: 12% 0;
  /* several species palettes bottom out near black, so the silhouette is mixed
     toward light rather than taken straight from the palette */
  background: color-mix(in srgb, var(--g) 42%, #d7e9ff);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
  filter: drop-shadow(0 0 14px color-mix(in srgb, var(--g) 55%, transparent));
}
.cx-hexico { width: 30px; height: 30px; margin-bottom: 4px; }
.cx-hexico svg { width: 100%; height: 100%; }
.cx-desc { font: var(--t-body); font-size: 11px; color: #9db4d0; line-height: 1.45; margin-top: 4px; }
.codex-card.static { cursor: default; }
.codex-card.static:hover { transform: none; border-color: rgba(120, 160, 210, 0.22); }
.codex-grid.wide { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

/* single-entry reading view */
.codex-detail {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.cx-detail-name {
  font-family: var(--display); font-weight: 500;
  font-size: 22px;
  color: var(--jay-lite);
  line-height: 1.1;
}
.cx-detail-title {
  font-family: var(--body);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--gold);
  margin-bottom: 8px;
}
.cx-lore {
  max-width: 62ch;
}
.cx-lore p {
  font-size: 14px;
  line-height: 1.6;
  color: #cfe6ff;
  margin: 0 0 12px;
}
.cx-lore p:last-child { margin-bottom: 0; }
/* ghost pill back button */
.codex-back {
  align-self: flex-start;
  margin-top: 18px;
  font-family: var(--body);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--jay-lite);
  background: rgba(8, 16, 32, 0.7);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 8px 18px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s, box-shadow 0.12s, transform 0.12s;
}
.codex-back:hover {
  transform: translateY(-1px);
  color: var(--text);
  box-shadow: 0 0 18px rgba(111,208,251, 0.28);
}
.codex-back:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--jay) 70%, transparent);
  outline-offset: 2px;
}

/* entrance motion — inner container fades + scales in when #codex gets .is-in */
#codex.overlay .codex-panel {
  opacity: 0;
  transform: scale(0.94) translateY(12px);
  transition:
    opacity 0.2s cubic-bezier(0.2, 0.9, 0.25, 1),
    transform 0.2s cubic-bezier(0.2, 0.9, 0.25, 1);
}
#codex.overlay.is-in .codex-panel {
  opacity: 1;
  transform: scale(1) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  #codex.overlay .codex-panel {
    transform: none;
    transition: opacity 0.12s linear;
  }
  #codex.overlay.is-in .codex-panel { transform: none; }
}

/* ---------- chrome buttons (Field Guide, Pause) ----------
   One shared treatment. These are utilities, not calls to action: they rest at half
   opacity with no border and no glow, and only come up under the pointer. Positions are
   % of the FIXED 960x540 stage — never vh/vw, which measures the browser window and has
   nothing to do with the space these actually sit in. */
.chrome-btn {
  position: absolute;
  z-index: 12;
  width: 28px; height: 28px; padding: 0;
  display: grid; place-items: center;
  border: 0; border-radius: 9px;
  background: rgba(5, 11, 24, 0.55);
  color: var(--dim);
  cursor: pointer;
  box-shadow: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.18s ease, transform 0.18s ease, color 0.12s ease, background 0.12s ease;
}
.chrome-btn:not(.hidden) { opacity: 0.5; transform: translateY(0); }
.chrome-btn:hover { opacity: 1; color: var(--jay-lite); background: rgba(5, 11, 24, 0.8); }
.chrome-btn:active { transform: translateY(1px) scale(0.97); }
.chrome-btn:focus-visible { outline: 2px solid color-mix(in srgb, var(--jay) 75%, transparent); outline-offset: 2px; opacity: 1; }
.chrome-btn.hidden { display: none; }
.chrome-btn svg { width: 17px; height: 17px; fill: currentColor; }
/* The Field Guide is a SECONDARY action of the Perch, so by UI LAW it sits as a quiet chip
   row directly under that screen's heading — not floating alone in a corner that already
   has a job, and not stacked under the purse. Centred with a margin, never a transform:
   .chrome-btn animates its own transform on enter and on :active. */
#codexBtn {
  left: 50%; margin-left: -63px; top: 15.4%;
  width: 126px; height: 26px; padding: 0 11px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: 13px;
}
#codexBtn svg { width: 15px; height: 15px; flex: 0 0 15px; }
#codexBtn span {
  font: var(--t-md); font-size: 9.5px; letter-spacing: 0.18em;
  text-transform: uppercase; white-space: nowrap;
}
#pauseBtn { right: 1.67%; bottom: 2.96%; }    /* 16px, 16px */

/* the settings gear — as quiet as a control can be and still be findable. It sits in the
   title screen's chip row, so it shows and hides with the menu and needs no visibility
   plumbing of its own. */
.gear-btn {
  width: 26px; height: 26px; padding: 0; flex: 0 0 26px;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: transparent;
  color: var(--dim);
  opacity: 0.42;
  cursor: pointer;
  transition: opacity 0.16s ease, color 0.16s ease, transform 0.3s ease;
}
.gear-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.gear-btn:hover { opacity: 1; color: var(--jay-lite); transform: rotate(60deg); }
.gear-btn:active { transform: rotate(60deg) scale(0.94); }
.gear-btn:focus-visible { outline: 2px solid color-mix(in srgb, var(--jay) 75%, transparent); outline-offset: 2px; opacity: 1; }

/* ---------- settings / pause menu ---------- */
/* #settingsBlock is a single node the settings screen and the pause overlay pass between
   them, so the rows, the wiring and the persistence exist exactly once. */
.settings-mount { display: contents; }
#settings h2 { margin-bottom: var(--s2); }
#settings .primary { margin-top: var(--s5); }
/* the pause MODAL: a card over the frozen game, not a full screen that replaces it.
   The scrim stays light enough to see what you paused. One fixed card size across all
   three views (menu / settings / confirm) so nothing resizes under the cursor. */
.pause-modal { background: rgba(4, 9, 18, 0.58); backdrop-filter: blur(3px) saturate(0.9); }
.pause-card {
  width: min(400px, 86%);
  height: 296px;
  padding: var(--s5) var(--s5) var(--s4);
  border-radius: 16px;
  background: rgba(7, 14, 28, 0.94);
  box-shadow: inset 0 0 0 1px rgba(120, 160, 210, 0.18), 0 24px 60px rgba(0, 0, 0, 0.55);
  display: flex; flex-direction: column;
}
.pause-card .pv { flex: 1; display: flex; flex-direction: column; align-items: stretch; justify-content: center; min-height: 0; }
.pause-card .pv.hidden { display: none; }
.pause-card h2 { font-size: 19px; margin-bottom: var(--s4); }
.pause-card .hint { margin-top: var(--s3); }
.pause-card .pv[data-view="leave"] .hint { margin: 0 0 var(--s4); }
.pause-card .pv[data-view="settings"] .pause-list { margin-top: var(--s4); }
/* one stacked list, primary first — the shape every pause menu in every game has */
.pause-list { display: flex; flex-direction: column; gap: var(--s2); }
.pause-list .primary { width: 100%; margin: 0; }
.pause-row {
  width: 100%;
  font: var(--t-strong); letter-spacing: var(--tracking-display);
  background: rgba(255, 255, 255, 0.05);
  color: var(--jay-lite);
  border: 0; border-radius: var(--s2);
  padding: var(--s3) var(--s4);
  box-shadow: none;
}
.pause-row:hover { background: rgba(255, 255, 255, 0.1); color: var(--text); box-shadow: none; }
.pause-row svg { width: 14px; height: 14px; vertical-align: -2px; margin-right: 6px; }
/* leaving is the quietest thing on the card and it opens a confirm, never fires directly */
.pause-row.quiet { background: none; color: var(--dim); }
.pause-row.quiet:hover { background: rgba(255, 77, 94, 0.1); color: var(--danger); }
/* no box inside a box — the card is already the surface */
.pause-card #settingsBlock { width: 100%; margin: 0; padding: 0; background: none; box-shadow: none; }
.pause-menu {
  display: flex; flex-direction: column; gap: var(--s2);
  width: min(420px, 82%);
  margin-top: var(--s5);
  padding: var(--s3) var(--s4);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.035);
  box-shadow: inset 0 0 0 1px rgba(120, 160, 210, 0.16);
}
.pause-menu .tag { align-self: flex-start; margin-bottom: var(--s1); }
.vol-row { display: flex; align-items: center; gap: var(--s3); }
.vol-row.muted .vol-label, .vol-row.muted .vol-slider { opacity: 0.45; }
.vol-label { font: var(--t-md); font-size: 12px; letter-spacing: var(--tracking-display); color: var(--text); min-width: 66px; text-align: left; }
.vol-sub { font: var(--t-body); font-size: 10.5px; color: var(--dim); flex: 1; text-align: left; }
.vol-mute {
  width: 26px; height: 26px; padding: 0; flex: 0 0 26px;
  display: grid; place-items: center;
  border: 0; border-radius: 7px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--jay-lite); cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.vol-mute svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.vol-mute:hover { background: rgba(255, 255, 255, 0.1); }
.vol-mute.off { color: #43566b; }   /* dimmed, not slashed — a mute is a state, not an error */
.vol-slider { flex: 1; accent-color: var(--jay-lite); height: 18px; cursor: pointer; }
.auto-row .chip { margin-left: auto; }

/* ---------- overlay entrances — nothing pops in ---------- */
@keyframes overlayIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes overlayLift { from { opacity: 0; transform: translateY(10px) scale(0.985); } to { opacity: 1; transform: none; } }
.overlay:not(.hidden) { animation: overlayIn 0.16s ease-out both; }
#paused:not(.hidden) .pause-card { animation: overlayLift 0.22s cubic-bezier(0.2, 0.8, 0.25, 1) both; }
@media (prefers-reduced-motion: reduce) {
  .overlay:not(.hidden), #paused:not(.hidden) .pause-card { animation-duration: 0.1s; }
}

/* ---------- first-flight tutorial ---------- */
#tutorial { gap: 4px; }
/* one gutter for every row: keys right-aligned into it, meaning left-aligned
   off it. nothing here is centred, so there is a single vertical edge to read. */
.tut-grid {
  display: grid;
  grid-template-columns: 148px 1fr;
  align-items: baseline;
  column-gap: var(--s4);
  row-gap: var(--s2);
  margin: var(--s5) 0;
  width: min(560px, 100%);
  text-align: left;
}
.tut-grid > div { display: contents; }
.tut-keys { display: flex; gap: var(--s1); justify-content: flex-end; align-items: center; }
.tut-grid > div > span + span { font: var(--t-body); color: var(--dim); }
.tut-head {
  grid-column: 1 / -1;
  font: var(--t-label);
  letter-spacing: var(--tracking-label);
  color: var(--jay-lite);
  margin-top: var(--s3);
}
.tut-head:first-child { margin-top: 0; }
.tut-ico { width: 20px; height: 20px; color: var(--gold); }


/* ---------- inline UI icons (window.UI_ICONS) ----------
   One rule per context instead of a width/height on every SVG string — the icon
   markup is shared between the DOM and the canvas rasteriser, so it carries no size. */
.badge svg, .badge-buy svg { width: 11px; height: 11px; vertical-align: -1px; margin-right: 3px; }
.badge.owned svg, .badge.lock svg { margin-right: 0; }
.badge.owned svg + *, .badge.lock svg + * { margin-left: 3px; }
.branch-ico svg { width: 13px; height: 13px; }
.hub-avatar svg { width: 22px; height: 22px; }
.tut-ico { display: inline-grid; place-items: center; }
.tut-ico svg { width: 19px; height: 19px; color: var(--jay-lite); }
.codex-back svg { width: 13px; height: 13px; vertical-align: -2px; margin-right: 5px; }
.hub-close svg, .codex-close svg { width: 15px; height: 15px; }
#codexBtn svg { width: 16px; height: 16px; }
#pauseCodexBtn svg { width: 14px; height: 14px; vertical-align: -2px; margin-right: 5px; }
#mpBack svg, #roostBack svg { width: 13px; height: 13px; vertical-align: -2px; margin-right: 4px; }
.pchip svg { width: 12px; height: 12px; vertical-align: -2px; color: var(--gold); }

.tag-chip.perk svg { width: 10px; height: 10px; vertical-align: -1px; margin-right: 4px; }


/* ---------- pre-flight loadout footer (the Armory panel used before a run) ---------- */
.hub-panel-foot {
  /* three tracks so FLY stays dead-centre no matter how wide the kit readout gets */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding-top: var(--s4);
  margin-top: var(--s3);
  border-top: 1px solid rgba(120, 160, 210, 0.18);
}
.hub-panel-foot.hidden { display: none; }
#loadoutGo {
  min-width: 200px;
  font: var(--t-md);
  letter-spacing: var(--tracking-display);
  color: #2a1c00;
  background: var(--gold);
  border: none;
  padding: var(--s3) var(--s6);
  cursor: pointer;
  clip-path: var(--chamfer);
  box-shadow: 0 0 24px rgba(255, 209, 102, 0.35);
  transition: filter 0.12s, transform 0.12s;
}
#loadoutGo:hover { filter: brightness(1.08); transform: translateY(-1px); }
#loadoutGo:active { transform: translateY(1px); filter: brightness(0.96); }
#loadoutGo:focus-visible { outline: 2px solid color-mix(in srgb, var(--gold) 80%, transparent); outline-offset: 3px; }

/* room rules toggle in the lobby (host only) */
.rule-toggle {
  display: flex; align-items: center; gap: var(--s2);
  font: var(--t-strong); color: #9fb6d0;
  cursor: pointer; user-select: none;
  margin: var(--s2) 0;
}
.rule-toggle.hidden { display: none; }
.rule-toggle input { accent-color: var(--danger); width: 16px; height: 16px; cursor: pointer; }
.rule-toggle:hover span { color: var(--text); }

.hub-scrim.loadout-mode { padding-bottom: var(--s5); backdrop-filter: blur(5px) saturate(0.85); -webkit-backdrop-filter: blur(5px) saturate(0.85); }

/* ---------- Old Root's long tally ----------
   One strip, not four boxed cards. These are things that have already happened; boxing
   them gave four read-only numbers the same visual weight as the upgrades you came here
   to spend them on, and stacked another row of chrome above the only interactive part
   of the panel. */
/* the long tally: one centred row of numbers, no left rule and no left-weighted wash —
   it is a read-only readout, so it gets no plate at all */
.tally-strip {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
  gap: 6px var(--s5);
  padding: var(--s3) var(--s3);
  margin: var(--s2) 0 var(--s4);
  background: linear-gradient(90deg, transparent, rgba(255, 209, 102, 0.05) 50%, transparent);
}
.tally-stat { display: inline-flex; align-items: baseline; gap: 6px; }
.tally-stat svg { width: 13px; height: 13px; color: var(--gold); align-self: center; }
.tally-stat b { font: var(--t-md); font-size: 15px; color: var(--gold); letter-spacing: var(--tracking-display); }
.tally-stat > span { font: var(--t-body); font-size: 11px; color: #8ba3bd; }

/* ---------- the footer kit row: what you're flying with, beside FLY ---------- */
.kit-row { display: flex; align-items: center; gap: 12px; min-width: 0; }
.kit-item { display: inline-flex; align-items: center; gap: 6px; }
.kit-item svg { width: 15px; height: 15px; flex: 0 0 15px; filter: drop-shadow(0 0 6px currentColor); }
.kit-item b { font: var(--t-body); font-size: 10.5px; color: var(--text); white-space: nowrap; }
#loadoutGo.hidden { display: none; }

/* Pre-flight loadout sits over the LIVE chapter-one sky (openLoadout hides the title
   overlay). An opaque panel painted the sky right back out, so in loadout mode — and only
   there — the surface goes translucent and leans on the blur. The Perch stays opaque: its
   backdrop is the bough and the keepers, and the panel is deliberately solid in front. */
.hub-scrim.loadout-mode #hubPanel.hub-panel {
  /* 0.86, not 0.5 — the chapter-one sky has a MOON in it, and a panel light enough to
     read the skyline through is a panel you cannot read the text on. Blur does the
     "you're standing in front of the sky" work; the alpha only has to let colour bleed. */
  background: rgba(6, 13, 26, 0.76);
  backdrop-filter: blur(22px) saturate(1.2);
  -webkit-backdrop-filter: blur(22px) saturate(1.2);
  border-color: rgba(140, 180, 225, 0.3);
}
.hub-scrim.loadout-mode #hubPanelBody {
  background: linear-gradient(rgba(4, 10, 20, 0.5), rgba(6, 14, 26, 0.58));
}

/* ---------- the Roost milestone track ----------
   Not a shop. The four upgrades are gates on a tally that only ever climbs and is never
   spent, so they get a BAR you fill, not a price you pay — an acorn cost badge next to a
   purse in the corner said "buy me" about something the purse cannot buy. */
.roost-track { position: relative; margin: 6px 0 var(--s5); padding: 0 6px; }
.rt-rail {
  position: relative; height: 8px; border-radius: 99px;
  background: rgba(255, 209, 102, 0.09);
}
.rt-fill {
  position: absolute; inset: 0 auto 0 0; border-radius: 99px;
  background: linear-gradient(90deg, rgba(255, 209, 102, 0.5), var(--gold));
  box-shadow: 0 0 14px rgba(255, 209, 102, 0.45);
  transition: width 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.rt-head {
  position: absolute; right: 0; top: -22px; transform: translateX(50%);
  display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
  font: var(--t-md); font-size: 12px; color: var(--gold);
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.rt-head svg { width: 12px; height: 12px; }
.rt-nodes { position: relative; height: 74px; }
.rt-node {
  position: absolute; top: -13px; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  width: 148px; text-align: center;
}
.rt-dot {
  width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center;
  background: #1a2436; color: #0b1424;
  box-shadow: 0 0 0 3px #0b1424;
}
.rt-dot svg { width: 11px; height: 11px; }
.rt-node.owned .rt-dot { background: var(--gold); }
.rt-node.ready .rt-dot { background: var(--gold); animation: rtPulse 1.5s ease-in-out infinite; }
@keyframes rtPulse {
  0%, 100% { box-shadow: 0 0 0 3px #0b1424, 0 0 0 0 rgba(255, 209, 102, 0.55); }
  70% { box-shadow: 0 0 0 3px #0b1424, 0 0 0 11px rgba(255, 209, 102, 0); }
}
.rt-label b { display: block; font: var(--t-md); font-size: 11.5px; color: #8ba3bd; letter-spacing: var(--tracking-display); }
.rt-label em { font-style: normal; color: var(--gold); }
.rt-label i { display: block; font: var(--t-body); font-style: normal; font-size: 10.5px; line-height: 1.35; color: #64798f; }
.rt-node.owned .rt-label b { color: var(--gold); }
.rt-node.ready { cursor: pointer; }
.rt-node.ready .rt-label b { color: var(--gold); }
.rt-node.ready .rt-label i { color: #b9cbdd; }
.rt-node.ready:hover .rt-label b { color: #fff; }
