/* ============================================
   Tokens
   ============================================ */
:root {
  --color-bg: #1a1a2e;
  --color-text: #e8e8e0;
  --color-text-dim: #8888a0;
  --color-accent: #7dd956;
  --color-surface: rgba(255, 255, 255, 0.06);
  --font-sans: 'Inter', -apple-system, system-ui, sans-serif;
  --font-pixel: 'Press Start 2P', monospace;
}

/* ============================================
   Reset & Base
   ============================================ */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html, body {
  height: 100%;
  overflow: hidden;
}

body {
  font-family: var(--font-sans);
  background: var(--color-bg);
  color: var(--color-text);
  -webkit-font-smoothing: antialiased;
}

#scene {
  display: block;
  width: 100%;
  height: 100%;
  position: fixed;
  inset: 0;
  touch-action: none; /* the game handles all touch gestures itself */
}

/* ============================================
   Panel (top-left)
   ============================================ */
.panel {
  position: fixed;
  top: 1.5rem;
  left: 1.5rem;
  z-index: 10;
  padding: 1.25rem 1.5rem;
  background: rgba(26, 26, 46, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  max-width: 260px;
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity 0.7s cubic-bezier(0.25, 1, 0.5, 1),
              transform 0.7s cubic-bezier(0.25, 1, 0.5, 1);
  pointer-events: auto;
}

.panel.show {
  opacity: 1;
  transform: none;
}

/* Tucked: the intro folds away while the visitor is zoomed in (or hid
   it by hand) — opacity + transform only, so the fold never causes
   layout work. Declared after .show so it wins when both apply. */
.panel.show.tucked {
  opacity: 0;
  transform: translateY(-10px) scale(0.98);
  pointer-events: none;
}

.panel-tuck {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--color-text-dim);
  font: 600 14px/1 system-ui, sans-serif;
  cursor: pointer;
}
.panel-tuck:hover { background: rgba(255, 255, 255, 0.08); }

/* The pill the panel folds into — same corner, thumb-sized */
.panel-pill {
  position: fixed;
  top: 1.5rem;
  left: 1.5rem;
  z-index: 10;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(26, 26, 46, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  color: var(--color-accent);
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  letter-spacing: 0.05em;
  cursor: pointer;
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
  transition: opacity 0.35s cubic-bezier(0.25, 1, 0.5, 1),
              transform 0.35s cubic-bezier(0.25, 1, 0.5, 1);
}
.panel-pill.show {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

/* A pinch that starts on the overlays must not zoom the PAGE (iOS
   Safari); manipulation keeps taps working and kills double-tap zoom */
.panel, .controls, .panel-pill, .chat { touch-action: manipulation; }

.panel-header {
  margin-bottom: 0.75rem;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-pixel);
  font-size: 0.45rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 0.5rem;
}

.badge-dot {
  width: 5px;
  height: 5px;
  background: var(--color-accent);
  border-radius: 0;
  animation: blink 1s steps(1) infinite;
}

@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

.panel-name {
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.panel-role {
  font-size: 0.75rem;
  color: var(--color-text-dim);
  margin-top: 0.15rem;
}

.panel-desc {
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--color-text-dim);
  margin-bottom: 0.75rem;
}

/* One action per row, each filling the panel's width. A wrapped row of
   three could not hold a ranking: buttons landed wherever the width let
   them, so "most important" was decided by reflow. Stacked, position and
   weight agree — first and heaviest is the primary action. */
.panel-links {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.link-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 38px;
  padding: 0.5rem 0.9rem;
  font-size: 0.75rem;
  font-weight: 500;
  border-radius: 7px;
  text-decoration: none;
  /* Labels wrap rather than clip. Measured: at a 320px viewport the panel
     is 144px, leaving ~104px inside a button, while "Connect on LinkedIn"
     needs ~147px on one line — nowrap there is a clipped label, not a
     tidy one. A centred two-line row is still a clean full-width button;
     min-height already holds the rhythm. balance keeps the break even
     instead of orphaning the last word. */
  text-align: center;
  line-height: 1.25;
  text-wrap: balance;
  /* A transparent border on every tier so the three sit on identical box
     metrics — without it the outlined tier is 2px taller than its
     neighbours and the stack's gaps read as uneven. */
  border: 1px solid transparent;
  /* Only what hover actually changes — `all` also transitioned the
     backdrop context and made the boot fade-in costlier than it looked */
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease,
    transform 0.2s ease;
}

/* Three explicit tiers, never positional. The accent used to be
   :first-child, which meant document order silently decided the primary
   action — and .link-btn has no background of its own, so anything
   displaced from first position rendered as bare text.

   PRIMARY — solid accent. The one thing a recruiter should click. */
.link-btn--primary {
  background: var(--color-accent);
  color: #1a1a2e;
  font-weight: 600; /* the only tier that earns extra weight */
}

.link-btn--primary:hover {
  background: #8ee968;
  transform: translateY(-1px);
}

/* SECONDARY — outlined. Present and clickable, but clearly answering to
   the primary above it. */
.link-btn--secondary {
  background: var(--color-surface);
  color: var(--color-text);
  border-color: rgba(255, 255, 255, 0.14);
}

.link-btn--secondary:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.22);
  transform: translateY(-1px);
}

/* TERTIARY — text only. No fill, no border, dim: available without
   competing. It keeps the full button box so the tap target survives
   (the row is still 38px+), it just doesn't paint one. */
.link-btn--tertiary {
  background: transparent;
  /* NOT --color-text-dim (#8888a0): the panel is 82% opaque, so the live
     canvas bleeds through, and over the lit platform that dim ink measures
     ~3.99:1 — under AA for 12px text. Every other use of the dim token is
     decorative copy; this is the first INTERACTIVE label to carry it.
     #a5a5bb holds ~5:1 even over the brightest ground. */
  color: #a5a5bb;
}

.link-btn--tertiary:hover {
  color: var(--color-text);
  background: rgba(255, 255, 255, 0.05);
}

/* ============================================
   Controls (top-right)
   ============================================ */
.controls {
  position: fixed;
  top: 1.5rem;
  right: 1.5rem;
  z-index: 10;
  display: flex;
  gap: 0.5rem;
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity 0.7s cubic-bezier(0.25, 1, 0.5, 1),
              transform 0.7s cubic-bezier(0.25, 1, 0.5, 1);
}

.controls.show {
  opacity: 1;
  transform: none;
}

.ctrl-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text-dim);
  background: rgba(26, 26, 46, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 8px;
  cursor: pointer;
  /* Only what hover/active actually change (see .link-btn note) */
  transition: color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.ctrl-btn:hover {
  color: var(--color-text);
  border-color: rgba(255, 255, 255, 0.2);
}

.ctrl-btn:active {
  transform: scale(0.96);
}

.ctrl-btn--icon {
  padding: 0.5rem 0.6rem;
}

/* Sound button: slash shows when muted */
#soundBtn .snd-slash { display: none; }
#soundBtn.muted .snd-slash { display: block; }
#soundBtn.muted .snd-waves { display: none; }

/* ============================================
   Hotbar (bottom-center block palette)
   ============================================ */
.hotbar {
  position: fixed;
  bottom: 1.25rem;
  left: 50%;
  transform: translateX(-50%) translateY(10px);
  z-index: 10;
  display: flex;
  gap: 0.4rem;
  padding: 0.4rem;
  background: rgba(26, 26, 46, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
  opacity: 0;
  transition: opacity 0.7s cubic-bezier(0.25, 1, 0.5, 1),
              transform 0.7s cubic-bezier(0.25, 1, 0.5, 1);
}

.hotbar.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.slot {
  position: relative;
  width: 54px;
  height: 62px;   /* icon + name caption */
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.04);
  cursor: pointer;
  /* The bar is a drag SOURCE (press a slot, drag onto the platform), so it
     owns its touches exactly like the canvas does — without this a phone
     claims the drag as a page gesture and it dies on the first move. */
  touch-action: none;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.slot:hover {
  background: rgba(255, 255, 255, 0.09);
  transform: translateY(-2px);
}

.slot.selected {
  border-color: var(--color-accent);
  background: rgba(125, 217, 86, 0.12);
}

.slot-icon {
  display: block;
  width: 32px;
  height: 32px;
  margin: 4px auto 0;
  image-rendering: pixelated;
}

/* Top-right, not bottom-right: the name caption owns the bottom now */
.slot-key {
  position: absolute;
  right: 4px;
  top: 3px;
  font-family: var(--font-pixel);
  font-size: 0.4rem;
  color: var(--color-text-dim);
}

.slot.selected .slot-key {
  color: var(--color-accent);
}

/* Sans, not the pixel face: at 9px 'Press Start 2P' is unreadable, and
   this word is the whole point of the caption. */
.slot-name {
  display: block;
  margin-top: 1px;
  font-family: var(--font-sans);
  font-size: 0.5625rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--color-text-dim);
  transition: color 0.15s ease;
}

.slot.selected .slot-name {
  color: var(--color-accent);
}

/* ============================================
   Codex (collection panel)
   ============================================ */
.codex[hidden] { display: none; } /* author display beats the hidden attr otherwise */

.codex {
  position: fixed;
  top: 5rem;
  right: 1.5rem;
  z-index: 15;
  width: 300px;
  max-height: min(560px, calc(100vh - 8rem));
  display: flex;
  flex-direction: column;
  background: rgba(26, 26, 46, 0.92);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  overflow: hidden;
  /* Replays on every open: [hidden] toggles display, which restarts the
     animation — a transition would not fire across that boundary. */
  animation: codex-in 0.45s cubic-bezier(0.25, 1, 0.5, 1);
}

@keyframes codex-in {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}

.codex-header {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.9rem 1rem 0.7rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

.codex-title {
  font-family: var(--font-pixel);
  font-size: 0.7rem;
  color: #ffd968;
  letter-spacing: 0.06em;
}

.codex-count {
  font-size: 0.68rem;
  color: var(--color-text-dim);
  margin-left: auto;
}

.codex-close {
  background: none;
  border: none;
  color: var(--color-text-dim);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 0.1rem;
}

.codex-close:hover { color: var(--color-text); }

.codex-list {
  overflow-y: auto;
  padding: 0.5rem;
}

.codex-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.35rem 0.45rem;
  border-radius: 9px;
}

/* Locked rows are BUTTONS (the plan must be keyboard-reachable), so undo
   the UA button chrome and keep them reading as list rows. */
button.codex-row {
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
  transition: background 0.18s ease;
}
button.codex-row:hover,
button.codex-row:focus-visible { background: rgba(255, 255, 255, 0.05); }

/* Locked thumbnail: a neutral plate, NOT the monument silhouette. The
   model never resembles its recipe (the torii's gap is carved by the
   transform), so showing it taught a shape that can never match. A div,
   not a canvas: nothing to draw, and canvas can't carry generated text. */
.codex-plate {
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.035);
  border: 1px dashed rgba(255, 255, 255, 0.13);
  border-radius: 8px;
  font-size: 1.15rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.38);
}

/* The affordance: a hint system nobody can find is no hint system. */
.codex-reveal {
  display: inline-block;
  margin-top: 0.28rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #8fb7ff;
}
/* Disclosure, not a link: the plan opens and shuts. */
.codex-reveal::after { content: ' \25B8'; }
.codex-row.open .codex-reveal::after { content: ' \25BE'; }
button.codex-row:hover .codex-reveal { color: #b6d0ff; }

/* The revealed plan: generated from the cell data, so it can never drift
   from the truth. Carries the constraints the game never stated. */
/* Blueprints get more room than monument thumbnails: a 3x3 recipe with
   dashed empty cells is unreadable at 44px. */
.codex-row.open .codex-thumb { width: 58px; height: 58px; }

.codex-plan {
  margin-top: 0.28rem;
  font-size: 0.72rem;
  line-height: 1.45;
  color: #8fb7ff;
  animation: plan-in 0.25s ease-out;
}
@keyframes plan-in { from { opacity: 0; } to { opacity: 1; } }

.codex-row + .codex-row { margin-top: 0.15rem; }

.codex-row.found { background: rgba(255, 217, 104, 0.06); }

.codex-thumb {
  width: 44px;
  height: 44px;
  flex: none;
}

.codex-name {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--color-text-dim);
}

.codex-row.found .codex-name { color: #ffd968; }

.codex-hint {
  /* Readable: was 0.66rem italic dim-grey at 75% opacity (dimmer still
     once found) — four legibility hits stacked on the one line that
     tells you HOW to build a monument. */
  font-size: 0.8rem;
  color: #b8b8cc;
  line-height: 1.4;
  margin-top: 0.15rem;
}

/* Discovered: a small gold pill beside the name (the hint stays — it's
   the how-to-rebuild reference). Tracking/padding/size are tuned for the
   longer word: the looser values that suited "FOUND" pushed FOUR of the
   fifteen rows onto a second line; these leave only "The Hanging
   Gardens" (the longest name, 2.9px over) wrapping, which reads fine.
   Don't shave further — measured, the next notch buys no rows back and
   only crowds an already-8px pill. */
.codex-found {
  display: inline-block;
  margin-left: 0.32rem;
  padding: 0.06rem 0.24rem;
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  white-space: nowrap;
  text-transform: uppercase;
  color: #ffd968;
  background: rgba(255, 217, 104, 0.12);
  border: 1px solid rgba(255, 217, 104, 0.35);
  border-radius: 999px;
  vertical-align: 0.12em;
}

#codexBadge {
  font-family: var(--font-pixel);
  font-size: 0.42rem;
  color: #ffd968;
}

/* ============================================
   Chat with Abe
   Codex's sibling: same surface, same entrance, same non-modal
   contract. Green (action) header, not gold — gold stays reserved
   for discovery.
   ============================================ */
.chat[hidden] { display: none; } /* author display beats the hidden attr otherwise */

.chat {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 16;
  width: 340px;
  max-height: min(520px, calc(100vh - 7rem));
  display: flex;
  flex-direction: column;
  background: rgba(26, 26, 46, 0.92);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  overflow: hidden;
  animation: chat-in 0.45s cubic-bezier(0.25, 1, 0.5, 1);
}

@keyframes chat-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.chat-header {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 1rem 0.6rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

/* The portrait ships its own painted double-frame (part of the art),
   so no CSS border — just a whisper of rounding so the frame's hard
   corners sit comfortably on the panel radius. */
.chat-avatar {
  width: 48px;
  height: 48px;
  border-radius: 6px;
  flex-shrink: 0;
}

.chat-title {
  font-family: var(--font-pixel);
  font-size: 0.7rem;
  color: var(--color-accent);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.chat-close {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--color-text-dim);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 0.1rem;
}

.chat-close:hover { color: var(--color-text); }

.chat-log {
  flex: 1;
  min-height: 120px;
  overflow-y: auto;
  padding: 0.75rem 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.chat-msg {
  font-size: 0.78rem;
  line-height: 1.5;
  max-width: 88%;
  white-space: pre-wrap; /* model output may contain deliberate breaks */
}

.chat-msg--abe {
  align-self: flex-start;
  color: var(--color-text);
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}

/* The mini portrait riding each of Abe's lines — same art as the
   header, just smaller; its painted frame does the border work. */
.chat-msg-avatar {
  width: 24px;
  height: 24px;
  border-radius: 4px;
  flex-shrink: 0;
  margin-top: 1px; /* optically align the frame with the first text line */
}

.chat-msg-text { white-space: pre-wrap; }

.chat-msg--you {
  align-self: flex-end;
  color: var(--color-text);
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 10px;
  padding: 0.4rem 0.6rem;
}

/* "Abe is digging…" — three little mounds working the earth */
.chat-digging { color: var(--color-text-dim); }

.chat-dots { display: inline-block; margin-left: 0.15rem; }

.chat-dots i {
  display: inline-block;
  width: 4px;
  height: 4px;
  margin-left: 3px;
  border-radius: 50%;
  background: var(--color-text-dim);
  animation: chat-dig 1s ease-in-out infinite;
}
.chat-dots i:nth-child(2) { animation-delay: 0.15s; }
.chat-dots i:nth-child(3) { animation-delay: 0.3s; }

@keyframes chat-dig {
  0%, 60%, 100% { transform: none; }
  30%           { transform: translateY(-3px); }
}

.chat-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  padding: 0.25rem 0.9rem 0.5rem;
}

/* All chips spent → the row is empty; don't keep its padding as a
   dead stripe between the log and the input */
.chat-chips:empty { display: none; }

.chat-chip {
  font: inherit;
  font-size: 0.68rem;
  color: var(--color-text);
  background: rgba(125, 217, 86, 0.08);
  border: 1px solid rgba(125, 217, 86, 0.3);
  border-radius: 999px;
  padding: 0.32rem 0.7rem;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease;
}

.chat-chip:hover,
.chat-chip:focus-visible {
  background: rgba(125, 217, 86, 0.16);
  border-color: rgba(125, 217, 86, 0.55);
}

.chat-chip:active { transform: scale(0.96); }

.chat-form {
  display: flex;
  gap: 0.4rem;
  padding: 0.5rem 0.9rem;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.chat-input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 1rem; /* ≥16px so iOS doesn't zoom the page into the field */
  color: var(--color-text);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 9px;
  padding: 0.45rem 0.65rem;
}

.chat-input::placeholder { color: var(--color-text-dim); }

.chat-input:focus-visible {
  outline: none;
  border-color: rgba(125, 217, 86, 0.55);
}

.chat-input:disabled { opacity: 0.5; }

.chat-send {
  font: inherit;
  font-size: 0.85rem;
  color: var(--color-accent);
  background: rgba(125, 217, 86, 0.1);
  border: 1px solid rgba(125, 217, 86, 0.35);
  border-radius: 9px;
  padding: 0 0.7rem;
  cursor: pointer;
  transition: background 0.18s ease;
}

.chat-send:hover:not(:disabled) { background: rgba(125, 217, 86, 0.2); }
.chat-send:disabled { opacity: 0.4; cursor: default; }

.chat-foot {
  font-size: 0.6rem;
  color: var(--color-text-dim);
  padding: 0 0.9rem 0.6rem;
}

.chat-foot a {
  color: var(--color-accent);
  text-decoration: none;
}

.chat-foot a:hover { text-decoration: underline; }

/* ============================================
   Monument discovery card
   ============================================ */
.monument-card {
  position: fixed;
  top: 16%;
  left: 50%;
  transform: translateX(-50%) translateY(12px) scale(0.92);
  z-index: 20;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  /* Long titles must survive a phone: size to content, cap short of the
     viewport, and let both lines wrap. Without the cap, BANK OF NEW YORK
     (373px in this pixel font) runs off a 390px screen. width is
     max-content, not auto — a fixed element at left:50% otherwise
     shrink-wraps against HALF the viewport. */
  width: max-content;
  max-width: min(92vw, 30rem);
  transition: opacity 0.45s cubic-bezier(0.25, 1, 0.5, 1),
              transform 0.45s cubic-bezier(0.34, 1.4, 0.64, 1);
}

.monument-card.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0) scale(1);
}

.mc-title {
  font-family: var(--font-pixel);
  font-size: 1.35rem;
  letter-spacing: 0.08em;
  line-height: 1.3; /* the pixel font's default leading gaps wrapped lines */
  text-wrap: balance; /* a two-line title splits evenly, not 90/10 */
  color: #ffd968;
  text-shadow: 0 0 18px rgba(255, 217, 104, 0.55),
               0 2px 0 rgba(0, 0, 0, 0.6);
}

.mc-sub {
  margin-top: 0.6rem;
  font-family: var(--font-pixel);
  font-size: 0.55rem;
  letter-spacing: 0.2em;
  line-height: 1.7; /* wrapped role · years lines need air at 9px */
  text-wrap: balance; /* keeps a wrap from splitting the year range */
  text-transform: uppercase;
  color: var(--color-text-dim);
}

@media (prefers-reduced-motion: reduce) {
  .monument-card { transition: opacity 0.45s ease; transform: translateX(-50%); }
  .monument-card.show { transform: translateX(-50%); }
}

/* ============================================
   Colour swatches (above the hotbar)
   ============================================ */
.swatches {
  position: fixed;
  bottom: 5.9rem; /* just above the hotbar (+0.875rem when slots gained captions) */
  left: 50%;
  transform: translateX(-50%) translateY(6px);
  z-index: 10;
  display: flex;
  gap: 0.3rem;
  padding: 0.3rem;
  background: rgba(26, 26, 46, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
}

.swatches.show {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}

.swatch {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 2px solid rgba(255, 255, 255, 0.18);
  border-radius: 7px;
  cursor: pointer;
  transition: transform 0.15s ease, border-color 0.15s ease;
}

.swatch:hover { transform: translateY(-2px); }

.swatch.selected { border-color: var(--color-accent); }

.swatch--random {
  background: conic-gradient(#e05050, #f0d040, #50c878, #5090e0, #a070d0, #e05050);
  color: #fff;
  font-family: var(--font-pixel);
  font-size: 0.5rem;
  line-height: 1;
}

/* ============================================
   Hint (bottom-center)
   ============================================ */
.hint {
  position: fixed;
  bottom: 9.1rem; /* above the hotbar AND the swatch row (+0.875rem for captions) */
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  font-size: 0.6875rem;
  color: var(--color-text-dim);
  background: rgba(26, 26, 46, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 0.4rem 1rem;
  border-radius: 100px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  opacity: 0;
  transition: opacity 1s cubic-bezier(0.5, 0, 0.75, 0); /* slow fade out */
  white-space: nowrap;
}

.hint.show {
  opacity: 0.6;
  transition: opacity 0.6s cubic-bezier(0.25, 1, 0.5, 1); /* quicker fade in */
}

/* ============================================
   Reduced Motion
   ============================================ */
@media (prefers-reduced-motion: reduce) {
  .badge-dot { animation: none; }
  .panel, .controls, .hint {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .hotbar {
    opacity: 1 !important;
    transform: translateX(-50%) !important;
    transition: none !important;
  }
  .swatches {
    transition: none !important;
  }
  .codex { animation: none; }
  .codex-plan { animation: none; }
  .chat { animation: none; }
  .chat-dots i { animation: none; } /* the dig dots hold still — the text says it */
  .chat-chip:active { transform: none; }
}

/* ============================================
   Mobile
   ============================================
   Two ways to be a phone: narrow (portrait) or short-with-touch
   (landscape — 844px wide but only 390px tall, so a width test
   alone hands it desktop paddings it has no room for). */
@media (max-width: 600px), (pointer: coarse) and (max-height: 600px) {
  .panel {
    top: 1rem;
    left: 1rem;
    /* Never reach the controls: 100vw − controls (40vw) − side
       margins (2rem) − 1rem breathing room. At 320px wide the two
       used to overlap by 40px. */
    max-width: min(200px, calc(60vw - 3rem));
    padding: 1rem 1.25rem;
  }

  .panel-desc { display: none; } /* keep the platform visible on small screens */

  /* All three actions ship on phones. The old wrapped row could not hold
     them (three rows deep in a 185px panel) so LinkedIn was dropped here;
     stacked full-width rows cost the same height and keep the full set.
     The hidden .panel-desc above pays for the extra row. */
  /* 44px touch target, and tighter side padding to buy label width back:
     the panel is only ~144px at 320px wide, so every pixel inside the
     button is the difference between one line and two. */
  .link-btn {
    min-height: 44px;
    padding-inline: 0.5rem;
  }

  .controls {
    top: 1rem;
    right: 1rem;
    flex-wrap: wrap;
    justify-content: flex-end;
    max-width: 40vw;
  }

  /* The codex had no phone styles at all: fixed 300px at right 1.5rem
     overflows a 320px screen. Clamp the width, and clear the controls,
     which wrap to two rows inside 40vw here (taller than desktop). */
  .codex {
    top: 6.5rem;
    right: 1rem;
    width: min(300px, calc(100vw - 2rem));
    max-height: min(560px, calc(100vh - 11rem)); /* stay clear of the hotbar */
  }

  /* Chat becomes a bottom sheet. dvh, not vh: when the iOS keyboard
     rises the dynamic viewport shrinks with it, so the input stays
     visible instead of hiding under the keyboard. */
  .chat {
    left: 0.5rem;
    right: 0.5rem;
    bottom: 0.5rem;
    width: auto;
    max-height: 65dvh;
  }

  .hotbar {
    bottom: 0.9rem;
    gap: 0.3rem;
    padding: 0.3rem;
  }

  .slot {
    width: 48px;  /* ≥44px touch-target floor */
    height: 58px; /* icon + name caption */
  }

  .slot-icon {
    width: 28px;
    height: 28px;
    margin-top: 5px;
  }

  .slot-name {
    font-size: 0.5rem;
  }

  .swatches {
    bottom: 5.3rem;
    /* Ten fixed swatches = 355px — clipped 17px each end on a 320px
       screen. Cap the row and let the swatches shrink to fit. */
    max-width: calc(100vw - 0.75rem);
  }

  .swatch {
    flex: 0 1 30px; /* fatter touch targets on mobile; shrink to fit narrow screens */
    width: 30px;
    min-width: 0;
    height: auto;
    aspect-ratio: 1;
  }

  .hint {
    bottom: 8.3rem;
    font-size: 0.625rem;
    max-width: 92vw;
    white-space: normal;
    text-align: center;
  }
}

/* Sound button quiet state: inner wave only — the outer arc dims.
   (Off keeps the existing slash; both wave paths share .snd-waves.) */
#soundBtn.quiet .snd-wave-2 { opacity: 0.25; }
