/* vapour.css — the shared look. Chunky, cel-shaded, joyful.
   Every page includes this; each game overrides --accent and nothing else. */

:root {
  --ink: #26203b;
  --paper: #fbf3e2;
  --card: #fffdf7;
  --accent: #ffc53d;
  --good: #3bceac;
  --bad: #ff5d73;
  --info: #5aa9e6;
  --muted: #8f87a3;
  --line: 3px;
  --r: 16px;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  /* Display face. Headings, wordmarks, HUD numbers, buttons — anything meant
     to be read at a glance rather than actually read. Body text stays on the
     system stack, which is faster and better at small sizes. */
  --display: "Titan One", var(--font);
  /* Buttons are objects, not rectangles of colour. A hard offset drop shadow
     puts them above the page; a light inset along the top and a dark one
     along the bottom give them a thickness to press. */
  --bevel-light: inset 0 0.15em 0.2em rgba(255, 255, 255, 0.55);
  --bevel-dark: inset -0.06em -0.16em 0.14em rgba(38, 32, 59, 0.24);
}

@font-face {
  font-family: "Titan One";
  src: url("/fonts/titanone.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;   /* the page is readable before it arrives */
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--paper);
  background-image: radial-gradient(rgba(38, 32, 59, 0.05) 1.2px, transparent 1.3px);
  background-size: 22px 22px;
  color: var(--ink);
  font-family: var(--font);
  line-height: 1.4;
}

h1, h2, h3 { line-height: 1.1; margin: 0; }

/* ---------------------------------------------------------------- chunks */

.v-card {
  background: var(--card);
  border: var(--line) solid var(--ink);
  border-radius: var(--r);
  box-shadow: 0 6px 0 var(--ink);
}

.v-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  background: var(--card);
  border: var(--line) solid var(--ink);
  border-radius: var(--r);
  box-shadow: 0 5px 0 var(--ink), var(--bevel-dark), var(--bevel-light);
  color: var(--ink);
  font-family: var(--display);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: 0.01em;
  padding: 0.6em 1.1em;
  cursor: pointer;
  user-select: none;
  transition: transform 0.08s ease, box-shadow 0.08s ease, filter 0.15s ease;
}
.v-btn:hover { filter: brightness(1.03); transform: translateY(-1px); box-shadow: 0 6px 0 var(--ink), var(--bevel-dark), var(--bevel-light); }
/* pressed flips the bevel: the light edge moves to the bottom, which is what
   a real button does when it goes in */
.v-btn:active { transform: translateY(4px); box-shadow: 0 1px 0 var(--ink), inset 0 -0.12em 0.16em rgba(255,255,255,0.4), inset 0 0.18em 0.2em rgba(38,32,59,0.28); }
.v-btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: 0 5px 0 var(--ink); }
.v-btn.primary { background: var(--accent); }
.v-btn.good { background: var(--good); }
.v-btn.bad { background: var(--bad); color: #fff; }
.v-btn.big { font-size: 1.25rem; padding: 0.7em 1.4em; border-radius: 20px; }

.v-input {
  font-family: var(--font);
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
  background: #fff;
  border: var(--line) solid var(--ink);
  border-radius: 12px;
  padding: 0.5em 0.8em;
  box-shadow: inset 0 3px 0 rgba(38, 32, 59, 0.08);
  outline: none;
}
.v-input:focus { box-shadow: inset 0 3px 0 rgba(38, 32, 59, 0.08), 0 0 0 3px var(--accent); }

.v-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  background: var(--card);
  border: var(--line) solid var(--ink);
  border-radius: 999px;
  box-shadow: 0 3px 0 var(--ink);
  font-weight: 800;
  font-size: 0.85rem;
  padding: 0.25em 0.8em;
}

.v-title {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: 0;
}

/* Shouted text: wordmarks, headings over artwork, the big numbers on a HUD.
   An ink stroke round the glyph and a hard offset in the accent — the same
   trick the karts use, so type and geometry are lit the same way. Anything
   sitting over a 3D scene needs the stroke to stay readable at all. */
.v-shout {
  font-family: var(--display);
  font-weight: 400;
  color: var(--paper);
  -webkit-text-stroke: 0.09em var(--ink);
  paint-order: stroke fill;         /* or the stroke eats the counters */
  text-shadow: 0.07em 0.07em 0 var(--accent), 0.12em 0.12em 0 rgba(38, 32, 59, 0.25);
  letter-spacing: 0.01em;
}

/* A game is not a document, so its page does not scroll — only the hub does.
   The window is the whole surface. Anything with more content than fits
   scrolls inside its own box instead, the way a panel in a game does, because
   clamping the page without that just makes content unreachable.

   The WHOLE selector is wrapped in :where(), ancestor included, so these
   carry zero specificity and a game's own plain `.wrap` rule beats them.
   Writing `html.v-game :where(.wrap)` would not do it — the ancestor still
   scores a class and an element, which quietly outranks `.wrap` and hands
   every game 14px of padding it did not ask for. */
:where(html.v-game), :where(html.v-game body) { height: 100%; overflow: hidden; }
:where(html.v-game body) { display: flex; flex-direction: column; }
:where(html.v-game .wrap) { flex: 1; min-height: 0; display: flex; flex-direction: column; padding-bottom: 14px; }
:where(html.v-game .screen) { min-height: 0; }
:where(html.v-game .screen.on) { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }

/* A game surface, not a document: nothing to select, nothing to drag out,
   no long-press menu on a control you meant to tap. */
.v-stage {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
}

.v-sub { color: var(--muted); font-weight: 600; }

.v-kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 800;
  letter-spacing: 0.18em;
}

/* ---------------------------------------------------------------- motion */

@keyframes v-pop {
  0% { transform: scale(0.5); opacity: 0; }
  70% { transform: scale(1.06); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
.pop { animation: v-pop 0.35s cubic-bezier(0.2, 1.4, 0.4, 1) both; }

@keyframes v-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-8px) rotate(-1deg); }
  40% { transform: translateX(8px) rotate(1deg); }
  60% { transform: translateX(-6px); }
  80% { transform: translateX(6px); }
}
.shake { animation: v-shake 0.4s ease both; }

@keyframes v-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}
.bob { animation: v-bob 2.6s ease-in-out infinite; }

@keyframes v-wiggle {
  0%, 100% { transform: rotate(0); }
  25% { transform: rotate(-2deg); }
  75% { transform: rotate(2deg); }
}
.wiggle { animation: v-wiggle 0.25s ease 2; }

@keyframes v-float-up {
  0% { transform: translateY(0); opacity: 1; }
  100% { transform: translateY(-60px); opacity: 0; }
}
.float-up { animation: v-float-up 1.1s ease-out both; }

@media (prefers-reduced-motion: reduce) {
  .pop, .shake, .bob, .wiggle, .float-up { animation: none; }
}

/* ---------------------------------------------------------------- night mode
   Toggled from the ⌘K palette (stored as vapour.theme), defaults to the OS
   preference. Same chunky line work, lights off: ink flips light, paper goes
   deep, accents keep their day colours. */

[data-theme="dark"] {
  --ink: #ece6fa;
  --paper: #161228;
  --card: #241e40;
  --muted: #9a90b8;
}
[data-theme="dark"] body {
  background-image: radial-gradient(rgba(255, 255, 255, 0.05) 1.2px, transparent 1.3px);
}
[data-theme="dark"] .v-input { background: #1c1736; box-shadow: inset 0 3px 0 rgba(0, 0, 0, 0.35); }
[data-theme="dark"] .v-btn.bad { color: #1c1736; }
/* anything sitting on an accent surface keeps day-ink text in both themes */
[data-theme="dark"] .v-btn.primary, [data-theme="dark"] .v-btn.good, [data-theme="dark"] .v-btn.sel { color: #26203b; }

/* Night mode stops at the door of a game.

   A game is its own world: it paints a fixed atmosphere — Toot King's velvet
   hall, Drizzle's wet studio, the fog's murk, Quiz Night's dark set — and
   then reaches for --paper when it wants light ink on top of it and --card
   when it wants a light surface. Those tokens flipping underneath it does not
   turn a dark world into a light one, it just inverts the furniture: every
   wordmark on the shelf rendered #161228 on #161228 and vanished, leaving
   only its accent shadow, and Quiz Night's unpicked packs went pale-on-white.

   Dario Kart found this first and pinned its own HUD fill and stroke against
   it ("the race view is its own world and must not follow the site's night
   mode"). This is that fix moved to where it belongs — one rule, every game,
   and any game added later gets it without knowing it exists. The hub has no
   .v-game, so the storefront still turns the lights off, and the ⌘K toggle
   still works there.

   Specificity does the work: (0,2,1) against the (0,1,0) above. */
html.v-game[data-theme="dark"] {
  --ink: #26203b;
  --paper: #fbf3e2;
  --card: #fffdf7;
  --muted: #8f87a3;
}
