:root {
  --bg: #f4f1ea;
  --panel: #ffffff;
  --text: #201d18;
  --muted: #6b6356;
  --accent: #7a2e1f;
  --accent-text: #ffffff;
  --border: #d6cfc2;
  --error: #a3261b;
  /* Spacing, radius and touch size: every page uses these, so a new screen takes its sizes from here (docs/design/visual-standard.md). */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --radius-sm: 6px;
  --radius: 8px;
  --radius-lg: 12px;
  --touch: 44px;
  --focus: #1d4ed8;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #17150f;
    --panel: #242018;
    --text: #efe9dc;
    --muted: #a39a88;
    --accent: #c9583f;
    --accent-text: #17150f;
    --border: #3d3629;
    --error: #ff8a7a;
  }
}

:root[data-theme="dark"] {
  --bg: #17150f;
  --panel: #242018;
  --text: #efe9dc;
  --muted: #a39a88;
  --accent: #c9583f;
  --accent-text: #17150f;
  --border: #3d3629;
  --error: #ff8a7a;
}

/* Rarity aura colours (docs/design/rarity-palette.md, section 4). Proposed, not confirmed by Bailey: a card's frame keeps its type colour,
   and the rarity colour is only ever the ring outside it. */
:root {
  --rarity-c: #6b7280;
  --rarity-s: #0f8f6a;
  --rarity-r: #1d4ed8;
  --rarity-x: #7e22ce;
  --rarity-u: #b45309;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --rarity-c: #9ca3af;
    --rarity-s: #2fd3a0;
    --rarity-r: #3b82f6;
    --rarity-x: #a855f7;
    --rarity-u: #f5b400;
  }
}

:root[data-theme="dark"] {
  --rarity-c: #9ca3af;
  --rarity-s: #2fd3a0;
  --rarity-r: #3b82f6;
  --rarity-x: #a855f7;
  --rarity-u: #f5b400;
}

/* A purchased card or hex. The frame is the same for every tier; the tier shows as a ring outside it, with a different line
   for each tier so the tier reads without colour. The ring is a pseudo-element so its motion (motion.css) can change opacity only. */
.reveal-card {
  position: relative;
  isolation: isolate;
  margin: 10px 4px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.reveal-card::after, .tile::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 12px;
  pointer-events: none;
  z-index: -1;
}
.reveal-card.rarity-c::after, .tile.rarity-c::after { border: 1px solid var(--rarity-c); }
.reveal-card.rarity-s::after, .tile.rarity-s::after { border: 2px double var(--rarity-s); }
.reveal-card.rarity-r::after, .tile.rarity-r::after { border: 3px solid var(--rarity-r); }
.reveal-card.rarity-x::after, .tile.rarity-x::after { border: 2px dotted var(--rarity-x); }
.reveal-card.rarity-u::after, .tile.rarity-u::after { border: 4px double var(--rarity-u); }
.sheen-clip { position: absolute; inset: 0; overflow: hidden; border-radius: inherit; pointer-events: none; }
.sheen { display: none; }

/* Details open under the row they belong to (detail.js). */
/* The details pop out to the right of the page, over the list, and never push the list aside (detail.js inlinePanel). */
.detail-drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 40; width: min(440px, 92vw); overflow-y: auto; padding: 16px 18px 28px; box-sizing: border-box; color: var(--text); background: radial-gradient(circle at 100% 0%, hsl(var(--hue, 200) 55% 24% / 0.55), transparent 55%), var(--panel); border-left: 1px solid hsl(var(--hue, 36) 60% 50% / 0.6); box-shadow: -24px 0 48px rgba(0, 0, 0, 0.5); }
.detail-drawer .drawer-close { float: right; width: auto; margin: 0 0 8px; }
.detail-drawer::after { content: ""; display: block; clear: both; }
.detail-body p { margin: 4px 0; }
[data-key].open { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A character's details: the portrait in a frame lit in the character's hue, three numbers, and the ability as a callout. */
.char-hero { display: grid; grid-template-columns: 136px 1fr; gap: 16px; align-items: center; }
.char-frame { position: relative; padding: 6px; border-radius: 14px; background: linear-gradient(160deg, hsl(var(--hue) 70% 58%), hsl(var(--hue) 45% 16%)); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.18), 0 12px 32px hsl(var(--hue) 70% 30% / 0.55); }
.char-frame img { display: block; width: 100%; height: auto; border-radius: 10px; }
.char-kicker { margin: 0; font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; color: hsl(var(--hue) 85% 72%); }
.char-title h3 { margin: 2px 0 6px; font-size: 1.7rem; line-height: 1.1; }
.char-deck { margin: 0; font-size: 0.85rem; color: var(--muted); }
.char-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 18px 0 14px; }
.char-stat { display: grid; justify-items: center; gap: 2px; padding: 10px 6px; border-radius: 10px; background: hsl(var(--hue) 45% 22% / 0.55); border: 1px solid hsl(var(--hue) 60% 55% / 0.45); }
.char-stat-label { font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.char-stat-value { font-size: 2.1rem; font-weight: 800; line-height: 1.05; font-variant-numeric: tabular-nums; }
.char-stat-note { font-size: 0.72rem; color: var(--muted); }
.char-ability { position: relative; margin: 14px 0; padding: 12px 14px 12px 38px; border-left: 3px solid hsl(var(--hue) 70% 58%); border-radius: 0 10px 10px 0; background: hsl(var(--hue) 40% 14% / 0.6); font-style: italic; }
.char-ability-mark { position: absolute; left: 13px; top: 10px; font-style: normal; color: hsl(var(--hue) 85% 70%); }
@media (max-width: 480px) { .char-hero { grid-template-columns: 1fr; } .char-frame { max-width: 190px; } }

/* Hex detail: the hex outline, its open doors and the side numbers (detail.js). */
.hex-detail { max-width: 520px; }
.door-diagram { width: 100%; max-width: 240px; margin: 8px auto; display: block; }
.composite { display: block; width: 100%; max-width: 520px; height: auto; margin: 0 auto 8px; border-radius: 6px; }
.card-art, .hex-art { display: block; width: 100%; max-width: 280px; height: auto; margin: 0 auto 8px; border-radius: 6px; }
.card-art { aspect-ratio: 5 / 7; object-fit: cover; }
.hex-art { aspect-ratio: 1; object-fit: contain; }
.door-diagram .wall { fill: var(--panel); stroke: var(--border); stroke-width: 0.04; }
.door-diagram .door { stroke: var(--accent); stroke-width: 0.16; stroke-linecap: round; }
.door-diagram .side { fill: var(--muted); font-size: 0.14px; }
.door-diagram .side.open { fill: var(--accent); font-weight: 500; }

[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* No bar across the top: the controls float in the corners over the page, so the board can take the height. */
.bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 8px;
  pointer-events: none;
}
.bar > * { pointer-events: auto; }

.brand { font-weight: 700; letter-spacing: 0.04em; }

.card {
  box-sizing: border-box;
  max-width: 420px;
  margin: 48px auto;
  padding: 24px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
}

@media (max-width: 480px) {
  .card { margin: 16px; }
}

h1 { margin: 0 0 16px; font-size: 1.5rem; }

label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 16px;
}

.hint { color: var(--muted); font-size: 0.875rem; }

input {
  box-sizing: border-box;
  width: 100%;
  padding: 10px;
  font: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
}

button {
  width: 100%;
  padding: 10px;
  font: inherit;
  font-weight: 600;
  color: var(--accent-text);
  background: var(--accent);
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

button.link {
  width: auto;
  padding: 0 0 0 12px;
  color: var(--accent);
  background: none;
}

/* The Learn page: the episodes as cards in order, the suggested next one marked, and the caption of the current beat. */
.episodes { list-style: none; margin: 16px 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.episode-card { padding: 14px 16px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); }
.episode-card.next { border-color: var(--accent); }
.episode-card.done { opacity: 0.7; }
.episode-card a { color: var(--text); font-weight: 700; text-decoration: none; }
.episode-card p { margin: 6px 0 0; }
.caption { font-size: 1.1em; margin: 12px 0; min-height: 3em; }
.controls { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.controls button { width: auto; min-width: 120px; }
/* An episode is watched, not played: its decision buttons are shown but cannot be pressed (the server plays the episode's choices). */
.watching #game-root .decision button { pointer-events: none; opacity: 0.55; }
#episode-title { margin-bottom: 4px; }

/* The settings menu (game page): a dialog with one row per setting, and a switch for each on or off setting. */
dialog#settings-dialog { width: min(440px, calc(100% - 32px)); padding: 20px; color: var(--text); background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); }
dialog#settings-dialog::backdrop { background: rgba(0, 0, 0, 0.6); }
.setting { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 12px 0 16px; }
.setting-name { margin: 0; font-weight: 700; }
.setting-note { margin: 4px 0 0; color: var(--muted); font-size: 0.9em; }
.switch { position: relative; flex: none; width: 48px; height: 26px; padding: 0; background: var(--border); border: none; border-radius: 999px; cursor: pointer; }
.switch[aria-checked="true"] { background: var(--accent); }
.switch:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.switch-knob { position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; background: #fff; border-radius: 50%; transition: transform 150ms; }
.switch[aria-checked="true"] .switch-knob { transform: translateX(22px); }

.error { color: var(--error); margin: 0 0 12px; }

.wide { box-sizing: border-box; max-width: 960px; margin: 24px auto; padding: 0 16px; }

/* Button hierarchy: one primary (accent) per view; secondary is an outline; the link style is for text-only actions. */
button.secondary { color: var(--text); background: var(--bg); border: 1px solid var(--border); }
button:disabled { opacity: 0.45; cursor: not-allowed; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (max-width: 480px) {
  button { min-height: var(--touch); }
  /* Task 9 (2026-11-03): the header links are touch targets too (measured 21 to 24 px on the game page) */
  header a { display: inline-flex; align-items: center; min-height: var(--touch); }
}

.wordmark { margin: 48px auto 0; text-align: center; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); }
.wordmark + .card { margin-top: 16px; }

/* Segmented control: the top-level sections of a page (the shop's tabs, the deck builder's panes on a phone). */
.seg { display: inline-flex; max-width: 100%; margin: 12px 0; padding: 3px; gap: 3px; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); }
.seg button { width: auto; flex: 1 1 auto; padding: 8px 14px; color: var(--muted); background: none; border: 0; border-radius: calc(var(--radius) - 2px); }
.seg button[aria-selected="true"], .seg button[aria-pressed="true"] { color: var(--accent-text); background: var(--accent); }

/* Tiles: a grid of items, each a picture or cover with its name, state and one or two actions. */
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--space-3); margin-top: var(--space-3); }
.tile { position: relative; isolation: isolate; display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; padding: var(--space-3); background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); }
.tile.dim { opacity: 0.6; }
.tile h3 { margin: 0; font-size: 1rem; line-height: 1.3; }
.tile .meta { margin: 0; color: var(--muted); font-size: 0.875rem; }
.tile .meta.warn { color: var(--error); }
.tile-cover { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; aspect-ratio: 5 / 7; overflow: hidden; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm); text-align: center; color: var(--muted); }
.tile-cover img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.tile-cover .big { font-size: 2rem; font-weight: 700; color: var(--accent); line-height: 1; }
.tile-picture { display: block; width: 100%; height: auto; max-width: none; margin: 0; border-radius: var(--radius-sm); }
.tile-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: auto; }
.tile-actions button { width: auto; flex: 1 1 auto; }
.tile .counter { justify-content: space-between; }
.tile .counter button { width: 40px; }

/* Filters above a grid: one select per question, so it works the same on a phone. */
.filters { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.filters select { width: auto; min-width: 150px; }

/* A purchase's result: the cards turned face up, above whichever tab is open. */
.reveal-tray { margin-top: var(--space-3); padding: var(--space-3); background: var(--panel); border: 1px solid var(--accent); border-radius: var(--radius); }
.reveal-tray > p:first-child { margin: 0 0 var(--space-2); font-weight: 600; }
.reveal-tray .reveal-card { display: inline-block; margin: 8px 10px; }
.reveal-tray .hide { float: right; }

/* The deck builder: a pile you build (left) and the collection to add from (right). On a phone, one pile at a time. */
.deck-layout { display: grid; gap: var(--space-4); margin-top: var(--space-3); }
.deck-layout .pane { min-width: 0; }
.deck-layout .pane .group-title:first-child { margin-top: 0; }
.deck-layout .pane-deck .row { border-bottom: 1px solid var(--border); }
.deck-layout .seg { display: flex; }
.deck-layout .seg button { text-align: center; }
@media (min-width: 800px) {
  .deck-layout { grid-template-columns: minmax(240px, 1fr) minmax(0, 2fr); align-items: start; }
  .deck-layout .seg { display: none; }
  .deck-layout .pane-deck { position: sticky; top: 16px; }
}
@media (max-width: 799px) {
  .deck-layout[data-pane="deck"] .pane-add { display: none; }
  .deck-layout[data-pane="add"] .pane-deck { display: none; }
}
.deck-layout .pane-add .tiles { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
@media (max-width: 480px) {
  .deck-layout .pane-add .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Queue: a bar that fills toward the target wait. It moves by transform only, and jumps when reduced motion is set. */
.timebar { height: 8px; margin: var(--space-3) 0; overflow: hidden; background: var(--bg); border: 1px solid var(--border); border-radius: 999px; }
.timebar-fill { width: 100%; height: 100%; background: var(--accent); transform-origin: left center; transform: scaleX(0); transition: transform 1s linear; }
/* Reduced motion: a lifted hand card and the response window's ring change at once (visual standard, principle 6).
   The setting in the settings menu (data-motion="full") shows motion whatever the browser asks for, so these only apply when it is not set. */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .timebar-fill { transition: none; }
  :root:not([data-motion="full"]) .hand .chip.picture,
  :root:not([data-motion="full"]) .hand .chip.picture.lift { transition: none; }
  :root:not([data-motion="full"]) .window-ring .w-arc { transition: none; }
}

/* Game start: each way to play is one option; the primary one first. */
.options-stack { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-3); }
.play-option { padding: var(--space-3); background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); }
.play-option.primary { border-color: var(--accent); }
.play-option h2 { margin: 0 0 4px; font-size: 1.05rem; }
.play-option p { margin: 0 0 var(--space-2); }
.play-option input { margin-bottom: var(--space-2); }

/* Lobby: one row for each seat, four seats in all. An open seat is dashed. */
.seats { display: flex; flex-direction: column; gap: var(--space-2); margin: var(--space-3) 0; }
.seat { display: flex; justify-content: space-between; align-items: center; gap: var(--space-3); padding: var(--space-3); background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); }
.seat.open { color: var(--muted); border-style: dashed; }
.seat.you { border-color: var(--accent); }
.seat .who { font-weight: 600; }

.wide .card { max-width: none; margin: 0 0 16px; }

.row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}

.row > span:first-of-type { flex: 1; }

.row-art { flex: none; width: 40px; height: 40px; object-fit: cover; border-radius: 4px; }

.row button { width: auto; padding: 6px 14px; }

.row button:disabled { opacity: 0.4; cursor: not-allowed; }

.price { color: var(--muted); white-space: nowrap; }

.result { margin-top: 16px; padding: 12px; border: 1px solid var(--border); border-radius: 6px; }

.result p { margin: 4px 0; }

select, .tabs button, .counter button {
  font: inherit;
}

select {
  width: 100%;
  padding: 10px;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

.tabs button {
  width: auto;
  padding: 6px 12px;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
}

.tabs button.on { color: var(--accent-text); background: var(--accent); border-color: var(--accent); }

.summary { margin: 12px 0; color: var(--muted); }

.section-title { margin: 20px 0 4px; font-size: 1.1rem; }

.counter { display: flex; align-items: center; gap: 6px; }

.counter button { width: 32px; padding: 4px 0; }

.counter button:disabled { opacity: 0.4; cursor: not-allowed; }

.actions { display: flex; gap: 12px; margin-top: 20px; }

.actions button { width: auto; flex: 1; }

.actions button.secondary { color: var(--text); background: var(--bg); border: 1px solid var(--border); }

.note { color: var(--muted); font-size: 0.875rem; }

.alt { margin: 16px 0 0; color: var(--muted); text-align: center; }

a { color: var(--accent); }

/* Deck builder (layout review 2026-10-23, P-01 to P-04): the bar with the total and Save stays in view; reasons are written out. */
.deck-bar { position: sticky; top: 0; z-index: 1; background: var(--panel); padding: 10px 0; border-bottom: 1px solid var(--border); }
.deck-bar .status { margin: 0 0 8px; font-size: 0.9375rem; }
.deck-bar .status.good { color: var(--text); }
.deck-bar .status.bad { color: var(--error); }
.reason { margin: -4px 0 8px; }
.group-title { margin: 16px 0 4px; font-size: 0.95rem; color: var(--muted); }

/* Phone width: the owned count goes on its own line under the name, so the +/- control keeps its place (layout review, deck builder). */
@media (max-width: 480px) {
  .row { flex-wrap: wrap; }
  .row > span:first-of-type { flex: 1 1 0; min-width: 0; }
  .counter { order: 2; }
  .price { order: 3; width: 100%; white-space: normal; }
}

/* The game page */
/* The board takes the window's height less what the hand bar and the page's margins need, so the hand bar sits below it, not over it. */
.board { width: 100%; height: clamp(200px, calc(100vh - 300px), 720px); margin: 0; display: block; }
/* The zoom buttons sit in the board's bottom-right corner; the hand is pinned under the board while the board is in view. */
.board-frame { position: relative; }
/* A hex's details in a popup beside the hex on the board (request 10); game.js placeHexPopup moves it with the board. */
.hex-popup { position: absolute; width: 240px; max-height: 70%; overflow: auto; z-index: 15; padding: 8px 10px; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35); }
.hex-popup .hex-contents { margin-top: 0; padding: 0; border: 0; }
.hex-popup .popup-close { position: absolute; top: 4px; right: 6px; padding: 0 6px; line-height: 1.4; }
/* The hover lens (game.js placeLens): the hex drawn large, its markers in a translucent band above the text panel. Mouse only. */
.hex-lens { position: fixed; width: 220px; height: 220px; z-index: 12; pointer-events: none; transform: none; opacity: 0; transition: transform 380ms cubic-bezier(0.2, 0.9, 0.3, 1.05), opacity 220ms; }
.hex-lens.shown { opacity: 1; }
.hex-lens.leaving { transform: scale(0.55); opacity: 0; transition-duration: 200ms; }
.hex-lens > svg { width: 100%; height: 100%; display: block; }
.lens-markers { position: absolute; top: 50%; left: 50%; width: 58%; transform: translate(-50%, -50%); display: flex; flex-wrap: wrap; align-content: center; justify-content: center; gap: 4px; padding: 6px; border-radius: 12px; background: rgba(12, 10, 8, 0.3); }
.lens-chip { min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px; font-size: 13px; font-weight: 700; line-height: 22px; text-align: center; color: #fff; background: rgba(20, 20, 20, 0.5); opacity: 0.75; box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25); }
.lens-chip.player { background: rgba(176, 90, 0, 0.6); }
.lens-chip.monster { background: rgba(180, 35, 24, 0.6); }
.lens-chip.agent { background: rgba(40, 90, 160, 0.6); }
.lens-chip.key { background: rgba(212, 160, 23, 0.7); color: #111; }
.lens-chip.more { background: rgba(20, 20, 20, 0.6); }
.lens-markers.compact .lens-chip { min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; font-size: 10px; line-height: 16px; }
.lens-markers.tight .lens-chip { min-width: 12px; height: 12px; padding: 0 2px; border-radius: 6px; font-size: 8px; line-height: 12px; }
.board .marker, .board .marker-text { opacity: 0.6; }
.board-tools { position: absolute; right: 8px; bottom: 8px; display: flex; gap: 4px; }
.zoom-btn { padding: 2px 8px; font-size: 0.8rem; line-height: 1.4; }
.hand-dock { position: sticky; bottom: 0; z-index: 5; padding: 6px 0 8px; background: var(--panel); }
.hand-dock .hint { margin: 0 0 4px; }
.board .hex { fill: var(--bg); stroke: var(--border); stroke-width: 1.5; }
.board .hex.start { stroke: var(--accent); stroke-width: 2.5; }
.board .hex.art { fill: none; }
/* Only the board's own labels (class "label"): a hex's picture keeps its printed text sizes and colours, as in the detail view. */
.board text.label { fill: var(--text); text-anchor: middle; font-size: 8px; paint-order: stroke; stroke: var(--bg); stroke-width: 2px; }
.board .hex-name { fill: var(--muted); font-size: 6.5px; }
.board .token { font-weight: 700; }
/* A selected card's target: a tinted ring on its hex, tapped to use the card there. */
.board .target-choice { cursor: pointer; }
.board .target-choice:focus-visible { outline: none; }
.board .target-ring { fill: var(--accent); fill-opacity: 0.14; stroke: var(--accent); stroke-width: 2.5; }
.board .target-choice:hover .target-ring, .board .target-choice:focus-visible .target-ring { fill-opacity: 0.3; stroke-width: 3.5; }
/* Two targets on one hex (a player and a monster in it): smaller rings, a little stronger so each reads as a ring. */
.board .target-ring.shared { fill-opacity: 0.25; stroke-width: 3.5; }
/* Markers on a hex: a monster (its life), an agent, the Keys lying there, and a barrier along its door. */
.board .marker { stroke: var(--bg); stroke-width: 1.2; }
.board .marker.monster { fill: var(--error); }
.board .marker.agent { fill: var(--accent); }
.board .marker.key { fill: #d4a017; }
.board .marker-text { fill: #fff; font-size: 5.5px; font-weight: 700; stroke: none; paint-order: normal; }
.board .barrier-line { stroke: var(--error); stroke-width: 3; stroke-linecap: round; }
.board .exit-mark { fill: var(--accent); font-size: 6.5px; font-weight: 700; letter-spacing: 0.08em; }
.board .token.you { fill: var(--accent); }
.board .hex-cell { cursor: pointer; }
.board .hex-cell:focus-visible { outline: none; }
.board .hex-ring { fill: none; stroke: var(--border); stroke-width: 1.2; }
.board .hex-ring.start { stroke: var(--accent); stroke-width: 2.5; }
.board .hex-cell:hover .hex-ring, .board .hex-cell:focus-visible .hex-ring { stroke: var(--accent); }
/* Move arrows: the accent colour; an explore arrow (an unexplored door) is grey and dashed. The label sits on the next hex. */
.board .move-arrow { cursor: pointer; }
.board .move-arrow:focus-visible { outline: none; }
.board .arrow-shaft { stroke: var(--accent); stroke-width: 4; stroke-linecap: round; }
.board .arrow-head, .board .arrow-chip { fill: var(--accent); stroke: var(--bg); stroke-width: 1.5; }
.board .arrow-verb { fill: var(--accent-text); stroke: none; font-size: 7.5px; font-weight: 700; paint-order: normal; }
.board .move-arrow:hover .arrow-chip, .board .move-arrow:focus-visible .arrow-chip { stroke: var(--text); stroke-width: 2; }
.board .move-arrow.explore .arrow-shaft { stroke: var(--muted); stroke-dasharray: 5 4; }
.board .move-arrow.explore .arrow-head, .board .move-arrow.explore .arrow-chip { fill: var(--muted); }
.board-tools { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.board-tools button { width: auto; }
.hex-open { margin-top: 8px; }
.hex-open button { width: auto; margin-top: 4px; }
.players p { margin: 4px 0; }
/* The banner that says the page is waiting for you (turn-status.js). A defense pulses; a response window comes often, so it is quiet. */
/* Pinned to the top of the screen while it shows, so a player scrolled down to the board still sees it. */
/* The decision window's heading says what is asked, coloured by its type (turn-status.js): a defence, a response, or a move. */
.decision h2 { margin: 0 0 10px; padding: 10px 12px; border-radius: 6px; font-size: 1.05rem; font-weight: 800; letter-spacing: 0.02em; }
.decision-defense h2 { color: #fff; background: #b42318; animation: pulse 1s ease-in-out infinite; }
.decision-respond h2 { color: var(--text); background: var(--bg); border: 2px solid #c98a00; }
.decision-act h2 { color: var(--accent-text); background: var(--accent); }
@keyframes pulse { 50% { box-shadow: 0 0 0 6px rgba(180, 35, 24, 0.35); } }
@media (prefers-reduced-motion: reduce) { :root:not([data-motion="full"]) .banner-defense { animation: none; } }
/* The cards you can play now sit at the top of the page, large, one click each. */
.play-now { border: 2px solid var(--accent); }
.play-now h2 { margin-top: 0; }
.play-now-card { font-size: 1.05rem; padding: 12px 16px; min-width: 160px; }
/* Each player is a row that opens their status; the row whose turn it is is bold. */
/* The players: compact cards. Hover (or tap) opens the rest of a card (player-more). */
.player-card { margin: 0 0 6px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); }
.player-card.turn { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.player-card.defeated { opacity: 0.55; }
.player-head { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; text-align: left; color: var(--text); background: none; border: 0; cursor: pointer; }
.portrait { flex: 0 0 34px; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; font-weight: 800; font-size: 1rem; color: #fff; background: hsl(var(--hue, 200) 45% 38%); border: 2px solid hsl(var(--hue, 200) 60% 62%); }
.player-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
.player-name { font-size: 0.9rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player-life { font-size: 0.75rem; color: var(--muted); line-height: 1; }
.lifebar { display: block; height: 6px; border-radius: 3px; background: var(--border); overflow: hidden; }
.lifefill { display: block; height: 100%; background: #2e8b57; border-radius: 3px; }
.lifebar.mid .lifefill { background: #c98a00; }
.lifebar.low .lifefill { background: #b42318; }
.player-more { display: none; padding: 0 10px 8px; font-size: 0.85rem; }
.player-more p { margin: 4px 0; }
.player-more h4 { margin: 8px 0 2px; font-size: 0.9rem; }
.player-card:hover .player-more, .player-card:focus-within .player-more, .player-card.open .player-more { display: block; }
/* The turn timer: an ornate dial in the top right of the board (tickTurnClock in game.js). Its ticks drop as the time runs out, the number
   pops each second, and it grows to its large size with a spring on the player's own decision. It ignores the pointer, so the hexes
   beneath still get clicks. */
.turn-timer { position: absolute; top: 8px; right: 8px; z-index: 6; width: 84px; height: 84px; pointer-events: none; opacity: 0.85; --timer: #2e8b57; transition: width 500ms cubic-bezier(0.34, 1.56, 0.64, 1), height 500ms cubic-bezier(0.34, 1.56, 0.64, 1), opacity 300ms; }
.turn-timer.mine { width: 150px; height: 150px; opacity: 1; animation: t-enter 700ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.turn-timer svg { width: 100%; height: 100%; display: block; overflow: visible; }
.turn-timer .t-halo { fill: none; stroke: var(--timer); stroke-width: 1.5; opacity: 0.45; }
.turn-timer .t-bezel { fill: rgba(18, 15, 12, 0.85); stroke: #c9a24a; stroke-width: 2.5; }
.turn-timer .t-filigree { fill: none; stroke: #c9a24a; stroke-width: 0.9; opacity: 0.7; transform-origin: 60px 60px; animation: t-spin 18s linear infinite; }
.turn-timer .t-tick { stroke: rgba(255, 255, 255, 0.15); stroke-width: 1.6; stroke-linecap: round; transition: stroke 250ms; }
.turn-timer .t-tick.major { stroke-width: 2.4; }
.turn-timer .t-tick.lit { stroke: var(--timer); filter: drop-shadow(0 0 2px var(--timer)); }
.turn-timer .t-tick.drop { animation: t-drop 450ms ease-out; }
.turn-timer .t-arc { fill: none; stroke: var(--timer); stroke-width: 5; stroke-linecap: round; transition: stroke 400ms, stroke-dashoffset 300ms linear; filter: drop-shadow(0 0 3px var(--timer)); }
/* The response window's ring (game-view.js windowRing): top left of the board, the arc empties as the window closes. */
.window-ring { position: absolute; top: 8px; left: 8px; z-index: 6; width: 56px; height: 56px; pointer-events: none; color: var(--text); }
.window-ring[hidden] { display: none; }
.window-ring svg { width: 100%; height: 100%; display: block; }
.window-ring .w-track { fill: rgba(18, 15, 12, 0.7); stroke: rgba(255, 255, 255, 0.2); stroke-width: 4; }
.window-ring .w-arc { fill: none; stroke: #d9a441; stroke-width: 5; stroke-linecap: round; transition: stroke-dashoffset 250ms linear; }
.window-ring .timer-num { position: absolute; inset: 0; display: grid; place-items: center; font-weight: 800; font-size: 1.1rem; color: #fff; }
.turn-timer .t-dial { fill: #15110d; stroke: var(--timer); stroke-width: 1; opacity: 0.9; }
.turn-timer .t-jewel { fill: #c9a24a; stroke: #fff3c4; stroke-width: 0.8; }
.turn-timer .timer-num { position: absolute; inset: 0; display: grid; place-items: center; font-size: 1.9rem; font-weight: 800; color: #fff; text-shadow: 0 0 8px var(--timer), 0 1px 3px #000; font-variant-numeric: tabular-nums; transition: font-size 500ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.turn-timer.mine .timer-num { font-size: 3.2rem; }
.turn-timer .timer-num.pop { animation: t-pop 380ms cubic-bezier(0.2, 1.4, 0.4, 1); }
.turn-timer[data-phase="mid"] { --timer: #c98a00; }
.turn-timer[data-phase="urgent"] { --timer: #e0601a; }
.turn-timer[data-phase="urgent"] .t-halo { animation: t-pulse 1s ease-in-out infinite; }
.turn-timer[data-phase="critical"] { --timer: #e53a2a; animation: t-shake 0.35s linear infinite; }
.turn-timer[data-phase="critical"] .t-halo { animation: t-pulse 0.5s ease-in-out infinite; }
@keyframes t-spin { to { transform: rotate(360deg); } }
@keyframes t-enter { 0% { transform: scale(0.4) rotate(-200deg); opacity: 0; } 70% { transform: scale(1.08) rotate(8deg); opacity: 1; } 100% { transform: scale(1) rotate(0); } }
@keyframes t-drop { 0% { stroke: #fff; stroke-width: 4; } 100% { stroke: rgba(255, 255, 255, 0.15); stroke-width: 1.6; } }
@keyframes t-pop { 0% { transform: scale(1.45); color: #ffe9a8; } 60% { transform: scale(0.94); } 100% { transform: scale(1); color: #fff; } }
@keyframes t-pulse { 50% { opacity: 0.1; } }
@keyframes t-shake { 0%, 100% { translate: 0 0; } 25% { translate: -2px 0; } 75% { translate: 2px 0; } }
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion="full"]) .turn-timer, :root:not([data-motion="full"]) .turn-timer * { animation: none !important; }
}
/* The event log, grouped by turn. Each category has a colour and a shape (LOG_CATEGORIES in game.js). */
.event-log { margin-top: 12px; padding: 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); }
.event-log summary { cursor: pointer; font-weight: 700; }
.log-legend { display: flex; flex-wrap: wrap; gap: 4px 8px; margin: 8px 0 4px; font-size: 0.72rem; color: var(--muted); }
.log-key { display: inline-flex; align-items: center; gap: 4px; color: var(--cat); }
.log-key .log-icon { width: 12px; height: 12px; }
/* Each line has a symbol and a faint tint in its category's colour. */
.log-icon { flex: 0 0 auto; width: 14px; height: 14px; margin-top: 2px; fill: none; stroke: var(--cat); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.log-turn { margin: 0 0 6px; }
.log-turn-head { display: flex; justify-content: space-between; margin: 10px 0 4px; padding-bottom: 3px; border-bottom: 1px solid var(--border); font-size: 0.78rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.log-life { font-weight: 600; text-transform: none; letter-spacing: 0; white-space: nowrap; margin-left: 8px; }
.log-items { list-style: none; margin: 0; padding: 0; }
.log-item { display: flex; gap: 6px; align-items: flex-start; margin: 3px 0; padding: 4px 8px 4px 6px; border-radius: 4px; background: color-mix(in srgb, var(--cat) 12%, transparent); font-size: 0.85rem; line-height: 1.35; }
.cat-act { --cat: #2f6fb3; }
.cat-move { --cat: #6b7280; }
.cat-explore { --cat: #2e8b57; }
.cat-defend { --cat: #c98a00; }
.cat-harm { --cat: #b42318; }
.cat-cards { --cat: #7b4fb3; }
.cat-hex { --cat: #17807e; }
.hex-contents { margin-top: 12px; padding: 12px; border: 1px solid var(--border); border-radius: 6px; }
.player-detail h4, .hex-contents h4 { margin: 12px 0 4px; font-size: 1rem; }
.player-detail p, .hex-contents p { margin: 4px 0; }
.tabled, .hex-entity { display: flex; align-items: center; gap: 8px; }
.hex-entity { width: 100%; margin: 4px 0; text-align: left; color: var(--text); background: none; border: 0; padding: 0; font-size: 0.95rem; }
button.hex-entity { cursor: pointer; text-decoration: underline; }
/* A card shown small beside its name: a tabled card, or a card in a hex. */
.tabled-card { display: block; flex: 0 0 56px; line-height: 0; }
.tabled-card .hand-card { display: block; width: 100%; height: auto; max-width: none; margin: 0; border-radius: 4px; }
/* The hand is one row. Cards sit a little apart; they overlap only when a full row would not fit the width (game.js spaceHand sets --overlap). */
.hand { display: flex; flex-wrap: nowrap; align-items: flex-end; gap: 6px; padding: 6px 4px 0; }
.hand-row { display: flex; align-items: flex-end; gap: 10px; }
.hand-row .hand { flex: 1 1 0; min-width: 0; }
.hand .chip.picture { flex: 0 0 auto; }
.attack-button { flex: 0 0 auto; width: auto; white-space: nowrap; }
.attack-panel { margin-top: 8px; }
.chip { width: auto; padding: 4px 10px; color: var(--text); background: var(--bg); border: 1px solid var(--border); border-radius: 12px; font-size: 0.875rem; cursor: pointer; }
.chip.on { color: var(--accent-text); background: var(--accent); border-color: var(--accent); }
/* A hand card as its picture: the card's own proportions, three to a row on a phone. */
.chip.picture { width: 84px; padding: 3px; border-radius: 8px; line-height: 0; }
/* Each card after the first is pulled over the one before by --overlap: zero while the hand has room, so the cards stay apart. */
.hand .chip.picture + .chip.picture { margin-left: calc(var(--overlap, 0px) * -1); }
/* On a phone the cards are smaller. */
@media (max-width: 480px) {
  .hand .chip.picture { width: 64px; }
}
.chip.picture .hand-card { display: block; width: 100%; height: auto; max-width: none; margin: 0; border-radius: 5px; }
/* A hand card grows when the cursor passes over it, or a finger lifts it (game.js handTouch): scaled from its bottom edge, so it covers
   the board above the hand and the page does not scroll. Cards not under the cursor stay at rest (request 4). */
/* The mouse lifts a card by its sideways position (game.js handLift), not by CSS :hover, so the grown card never catches the cursor meant for
   its neighbour. The hand is the offset parent of its cards, so their offsetLeft and offsetTop are the rest positions. */
.hand { touch-action: none; position: relative; }
.hand .chip.picture { position: relative; transform-origin: 50% 100%; opacity: 0.95; transition: transform 420ms cubic-bezier(0.2, 0.9, 0.3, 1.05), opacity 420ms ease-out, filter 300ms; }
/* A lifted card grows to about three and a half times its rest size, and turns fully opaque as it grows. */
.hand .chip.picture.lift { transform: translateY(-14px) scale(3.4); z-index: 20; opacity: 1; filter: none; }
/* Request 7: a card that can be played now has an accent ring; one that cannot is slightly darkened. */
.hand .chip.picture.playable { box-shadow: 0 0 0 2px var(--accent); }
.hand .chip.picture.dim { opacity: 0.55; filter: grayscale(0.4); }
/* At rest a hand card shows only its top half, the picture; the text is not readable at this size. A lifted card shows the whole card. */
.hand .chip.picture { box-sizing: border-box; aspect-ratio: 1 / 0.685; overflow: hidden; }
.hand .chip.picture.lift { aspect-ratio: auto; overflow: visible; opacity: 1; filter: none; }
/* The target's name under the cursor while a card's target is picked (game.js showTargetTip). Ignores the pointer so the ring still gets it. */
.target-tip { position: fixed; z-index: 50; pointer-events: none; padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--text); font-size: 0.875rem; font-weight: 700; }
.target-tip[hidden] { display: none; }
/* Exploring (game.js explorePanel): the new hex is shown large and prominent above the board, turned by two buttons and placed. */
.card.explore { border: 2px solid var(--accent); text-align: center; }
.explore-title { margin: 0 0 8px; }
.explore-hex { display: block; width: min(440px, 90vw); height: auto; margin: 0 auto; }
/* The explored hex on the board, before it is placed: lightly faded with an accent glow, so it reads as not yet placed. */
.explore-ghost { opacity: 0.85; filter: drop-shadow(0 0 8px var(--accent)); pointer-events: none; }
.explore-turn { flex-direction: row; flex-wrap: wrap; justify-content: center; }
.play-card { margin-top: 12px; padding: 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); }
.card-details { margin-top: 12px; padding: 12px; border: 1px solid var(--border); border-radius: 6px; }
.card-details h3 { margin: 0 0 4px; font-size: 1.05rem; }
.card-details p { margin: 4px 0; }
.options { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.options button { text-align: left; }
.options button:disabled { opacity: 0.5; cursor: not-allowed; }

/* The game screen (the game page, and an episode on the Learn page): status on the left, the board and decisions in the middle, the log on the right (stacked on a narrow screen).
   It takes the whole window width: the 960px cap of .wide is for text pages only. */
body.game-screen main.wide { max-width: 1280px; margin-top: 0; }
/* The board's window starts at the top of the page: no padding above it. */
body.game-screen .game-main > section.card:first-child { margin-top: 0; padding-top: 0; }
/* The floating bar (Pause, Crypt, the name and Log out) sits over the top of the page, so the side columns start below it. */
/* The floating bar's controls end at about 56px; the side columns start below them, so nothing lies on the items beneath. */
body.game-screen .game-status, body.game-screen .game-log { padding-top: 44px; }
/* Less margin at the sides, so the board can be wider. */
body.game-screen main.wide { max-width: none; padding: 0 6px; }
@media (min-width: 960px) {
  body.game-screen .game-layout { grid-template-columns: 176px minmax(0, 1fr) 212px; gap: 8px; }
}
/* The board fills the screen height. The hand is pinned to the bottom of the screen and overlaps the board's lower part. */
body.game-screen .board { height: calc(100vh - 12px); }
/* The hand's strip is see-through, so the board shows through it; only the "Your hand" label has a faint background. */
body.game-screen .hand-dock { background: none; }
body.game-screen .hand-dock .hint { display: inline-block; padding: 2px 8px; border-radius: 4px; background: color-mix(in srgb, var(--panel) 80%, transparent); }
/* The zoom buttons are a column down the right edge of the board; Reset is a symbol. */
body.game-screen .board-tools { right: 8px; top: 50%; bottom: auto; translate: 0 -50%; flex-direction: column; gap: 6px; }
body.game-screen .zoom-btn { width: 30px; height: 30px; padding: 0; display: grid; place-items: center; font-size: 1.1rem; line-height: 1; }
.zoom-reset svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* The attack button sits above the hand, at the bottom right of the board. */
body.game-screen .attack-button { position: absolute; right: 12px; bottom: calc(100% + 10px); }

/* The Learn episode: a panel fixed over the game, so the board does not move. It sits at the bottom right until learn.js places it
   beside the spotlight target. The spotlight is a see-through box whose shadow darkens everything else. */
#spotlight { position: fixed; z-index: 55; pointer-events: none; border-radius: 10px; box-shadow: 0 0 0 100vmax rgba(8, 6, 4, 0.72); outline: 2px solid var(--accent); }
#episode { position: fixed; z-index: 60; right: 16px; bottom: 16px; width: min(380px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto; box-sizing: border-box; padding: 14px 16px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5); }
#episode.placed { right: auto; bottom: auto; }
#episode .tutorial-top { display: flex; justify-content: space-between; align-items: center; margin: 0 0 6px; }
#episode h1 { margin: 0 0 4px; font-size: 1.15rem; }
#episode .controls { display: flex; justify-content: space-between; align-items: center; }
#episode .controls button { width: auto; }
#episode.collapsed #episode-body { display: none; }
.game-layout { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
.game-main { min-width: 0; }
.game-status, .game-log { min-width: 0; }
.turn-box { padding: 12px; margin-bottom: 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); }
/* The turn and its clock sit at the top of the decision window, so they are not a box of their own. */
.decision .turn-box { padding: 0; margin: 0 0 10px; border: 0; background: none; }
/* In the left column the decision heading is smaller, so the window stays short enough for the players to show below it. */
.game-status .decision { padding: 10px; }
.game-status .decision h2 { font-size: 0.9rem; margin: 0 0 6px; padding: 6px 8px; }
.game-status .decision .turn-title { margin: 0; font-size: 0.9rem; font-weight: 700; }
.game-status .options { gap: 4px; margin-top: 4px; }
.game-status .options button { padding: 6px 8px; font-size: 0.875rem; }
.turn-title { margin: 0 0 4px; font-weight: 700; }
.turn-clock-label { margin: 4px 0 0; font-size: 0.85rem; color: var(--muted, var(--text)); }
@media (min-width: 960px) {
  .game-layout { grid-template-columns: 220px minmax(0, 1fr) 280px; align-items: start; }
  .game-status { position: sticky; top: 12px; }
  .game-log { position: sticky; top: 12px; max-height: calc(100vh - 24px); overflow: auto; }
  .game-log .event-log { margin-top: 0; }
}

/* Testing only: the pause button at the top left of the bar, before the brand. */
.bar-left { display: flex; align-items: center; gap: 12px; }
.pause-toggle[hidden] { display: none; }
