/* Gokab game surface — the design handoff (Space Grotesk + DM Mono, yellow accent, auto light/dark).
 *
 * EVERYTHING is scoped under .gk-app. The kit layout's `stylesheet_link_tag :app` globs every CSS
 * file in app/assets, so this file is parsed on the marketing/auth pages too — and the kit's
 * Terminal system already owns :root tokens with names this design also uses (--ink). Hence the
 * --gk-* prefix and the .gk-app scope: nothing here can leak into the kit's chrome, and nothing
 * there can leak in.
 *
 * Theme: Light is the default; Dark comes from prefers-color-scheme, which means the palette
 * follows the device and re-renders live when the OS setting changes, with no JS involved.
 */

/* ------------------------------------------------------------------ Fonts --- */
/* Self-hosted (app/assets/fonts). Propshaft rewrites these url()s to digested paths. */

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/space-grotesk-latin-dfda626c.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/space-grotesk-latin-ext-f523b595.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/dm-mono-400-latin-bf792450.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/dm-mono-400-latin-ext-c908ed67.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/dm-mono-500-latin-e06c27ef.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/dm-mono-500-latin-ext-ba1b3349.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ----------------------------------------------------------------- Tokens --- */

/* .gk-flashes shares the scope because it is the one piece of game UI that sits OUTSIDE .gk-app —
   the layout renders it as a sibling, above whatever the page yields. Without it every var() in
   .gk-flashmsg resolves to nothing and the message renders as unstyled serif on a dark ground,
   which is exactly how it first shipped. */
.gk-app,
.gk-flashes {
  /* Accent — yellow only. Bright yellow is illegible as small text on a light ground, so accent
     TEXT on Light is a darkened sibling on the same hue; fills stay bright with dark text on top. */
  --gk-accent: #ffd21e;
  --gk-accent-text: #e0a400;
  --gk-on-accent: #0c1206;
  --gk-heat-end: #ff7a1a;

  /* Light (default) */
  --gk-bg: #eef1f6;
  --gk-bg-grad: linear-gradient(180deg, #f7f9fd 0%, #e8ecf4 100%);
  --gk-ink: #161824;
  --gk-dim: #6b6f83;
  --gk-dim2: #565a6d;
  --gk-mute: #9298ac;
  --gk-line: #e1e4ef;
  --gk-tile: #ffffff;
  --gk-tile-line: #e4e7f1;
  --gk-tile-shadow: 0 2px 5px rgba(22, 30, 60, .06);
  --gk-track: #e3e6f2;
  --gk-dot: #dde1ee;
  --gk-bad: #f0405e;
  --gk-bad-text: #d31e4c;
  --gk-bad-tile: #ffe6ec;
  --gk-pill-border: rgba(22, 24, 36, .16);

  --gk-glow: #ffd21e33;
  --gk-glow-op: .55;
  --gk-flash-op: .16;
  --gk-flash-blend: multiply;
  --gk-vignette: none;
  --gk-shadow-btn: 0 12px 30px -10px var(--gk-accent);
  --gk-ring-glow-op: .65;
}

@media (prefers-color-scheme: dark) {
  .gk-app,
  .gk-flashes {
    --gk-accent-text: var(--gk-accent); /* bright yellow reads fine as text on the dark ground */
    --gk-on-accent: #07070e;

    --gk-bg: #07070e;
    --gk-bg-grad: none;
    --gk-ink: #f2f2ff;
    --gk-dim: #8686a6;
    --gk-dim2: #a6a6c4;
    --gk-mute: #6a6a88;
    --gk-line: #262642;
    --gk-tile: #141428;
    --gk-tile-line: #262642;
    --gk-tile-shadow: none;
    --gk-track: #1b1b30;
    --gk-dot: #20203a;
    --gk-bad: #ff3b6b;
    --gk-bad-text: #ff3b6b;
    --gk-bad-tile: #2a1220;
    --gk-pill-border: rgba(255, 255, 255, .14);

    --gk-glow: #ffd21e22;
    --gk-glow-op: .42;
    --gk-flash-op: .5;
    --gk-flash-blend: screen;
    --gk-vignette: radial-gradient(120% 90% at 50% 0%, transparent 40%, #07070e 100%);
    --gk-shadow-btn: 0 0 40px -8px var(--gk-accent);
    --gk-ring-glow-op: .5;
  }
}

/* The body colour has to be set outside .gk-app too, or overscroll on iOS shows white behind a
   dark game. Scoped to the game layout's body class so kit pages are untouched. */
.gk-body { margin: 0; background: #eef1f6; }
@media (prefers-color-scheme: dark) { .gk-body { background: #07070e; } }

/* ------------------------------------------------------------------ Shell --- */

.gk-app {
  /* Where the content starts on the screens that show the wordmark. It is a token because the
     hamburger is absolutely positioned and has to line up with a wordmark it isn't laid out with —
     see .gk-menu. Safe-area inset so an installed PWA clears the notch. */
  --gk-top: max(clamp(40px, 9vh, 84px), env(safe-area-inset-top));

  /* How far a top row has to stay clear of the hamburger. The button is 38px wide and sits 20px in
     from .gk-col's right edge, so it reaches 58px in; the screens pad 30px, which puts 28px of it
     over their content box. Any row that reaches the right edge at the top of a screen needs this,
     or the button lands on it — which is what happened to the streak pill.

     56px, not the 28px that merely avoids a collision: the pill and the button are both dark
     rounded shapes on a dark ground, and at 12px apart they read as one damaged control rather than
     as two. The gap has to be big enough to say they are separate things. */
  --gk-menu-reserve: 56px;

  position: relative;
  min-height: 100dvh;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--gk-bg-grad, none), var(--gk-bg);
  color: var(--gk-ink);
  font-family: 'Space Grotesk', system-ui, -apple-system, sans-serif;
  -webkit-tap-highlight-color: transparent;
}

.gk-app *, .gk-app *::before, .gk-app *::after { box-sizing: border-box; }
/* [hidden] must always beat component display rules, or hidden screens leak through. */
.gk-app [hidden] { display: none !important; }

/* The slow accent bloom behind everything. */
.gk-glow {
  position: absolute;
  width: 700px;
  height: 700px;
  top: 50%;
  left: 50%;
  margin: -350px 0 0 -350px;
  background: conic-gradient(from 0deg, var(--gk-glow), transparent 30%, var(--gk-glow), transparent 62%, var(--gk-glow));
  filter: blur(72px);
  opacity: var(--gk-glow-op);
  animation: gk-spin 26s linear infinite;
  pointer-events: none;
}
/* Dimmed once play starts so the word and tiles stay the hero. */
.gk-app[data-phase="warmup"] .gk-glow,
.gk-app[data-phase="run"] .gk-glow,
.gk-app[data-phase="over"] .gk-glow { opacity: calc(var(--gk-glow-op) * .72); }

.gk-vignette {
  position: absolute;
  inset: 0;
  background: var(--gk-vignette);
  pointer-events: none;
}

/* Full-screen answer feedback. Re-triggered by swapping the animation via a data attribute. */
.gk-flash {
  position: absolute;
  inset: 0;
  opacity: 0;
  mix-blend-mode: var(--gk-flash-blend);
  pointer-events: none;
}
.gk-flash[data-flash="ok"]  { background: var(--gk-accent); animation: gk-flash .5s ease forwards; }
.gk-flash[data-flash="bad"] { background: var(--gk-bad);    animation: gk-flash .5s ease forwards; }

/* The play column: full-bleed on a phone, a centred 460px column on a desktop.

   min-width: 0 because this is a flex item, and a flex item's default `min-width: auto` refuses to
   shrink below its content's min-content width. One long unbreakable headline is then enough to
   push the whole column — cards, buttons and all — past the right edge of a narrow phone, where
   .gk-app's `overflow: hidden` quietly clips it. */
.gk-col {
  position: relative;
  z-index: 2;
  width: 100%;
  min-width: 0;
  max-width: 460px;
  min-height: 100dvh;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
}

.gk-screen { flex: 1; display: flex; flex-direction: column; }
.gk-screen[hidden] { display: none; }

/* Safe-area insets keep the column clear of the notch and the home indicator when installed.
   max() so the design's padding still wins on hardware without insets. */
.gk-screen--landing {
  justify-content: space-between;
  padding:
    var(--gk-top)
    max(30px, env(safe-area-inset-right))
    max(40px, env(safe-area-inset-bottom))
    max(30px, env(safe-area-inset-left));
  animation: gk-rise .5s ease;
}
.gk-screen--play {
  padding:
    max(clamp(28px, 5vh, 44px), env(safe-area-inset-top))
    max(22px, env(safe-area-inset-right))
    max(26px, env(safe-area-inset-bottom))
    max(22px, env(safe-area-inset-left));
}
/* These two start at --gk-top like the landing does, and not at a smaller number of their own. The
   hamburger is absolutely positioned once, for every screen, at the height of the landing wordmark;
   a screen that begins higher than that puts its own heading above the button and the two read as
   two rows that missed each other. Same top, one band. */
.gk-screen--result {
  padding:
    var(--gk-top)
    max(30px, env(safe-area-inset-right))
    max(30px, env(safe-area-inset-bottom))
    max(30px, env(safe-area-inset-left));
  animation: gk-rise .45s ease;
}
.gk-screen--stats {
  padding:
    var(--gk-top)
    max(30px, env(safe-area-inset-right))
    max(36px, env(safe-area-inset-bottom))
    max(30px, env(safe-area-inset-left));
  animation: gk-rise .45s ease;
}

/* ------------------------------------------------------------ Type helpers --- */

.gk-mono { font-family: 'DM Mono', ui-monospace, Menlo, monospace; }
.gk-accent { color: var(--gk-accent-text); }

.gk-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .22em;
  padding: 6px 13px;
  border-radius: 30px;
  white-space: nowrap;
  color: var(--gk-accent-text);
  background: transparent;
  border: 1px solid var(--gk-pill-border);
}
.gk-pill--solid {
  color: var(--gk-on-accent);
  background: var(--gk-accent);
  border-color: transparent;
}

.gk-bolt { flex-shrink: 0; fill: currentColor; }

/* Value-over-label stat, mono value in accent (landing) */
.gk-stat__value {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 26px;
  line-height: 1;
  white-space: nowrap;
  color: var(--gk-accent-text);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.gk-stat__label { margin-top: 6px; font-size: 12px; line-height: 1.2; color: var(--gk-mute); }

/* Bigger sans stat (result + stats screens) */
.gk-bigstat__value {
  font-size: 32px;
  font-weight: 700;
  line-height: 1;
  color: var(--gk-ink);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.gk-bigstat__label { margin-top: 5px; font-size: 12px; color: var(--gk-mute); }

/* --------------------------------------------------------------- Buttons --- */

.gk-btn {
  display: block;
  width: 100%;
  font-family: inherit;
  font-size: 19px;
  font-weight: 700;
  color: var(--gk-on-accent);
  background: var(--gk-accent);
  border: none;
  border-radius: 18px;
  padding: 19px;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  transition: transform .1s;
  box-shadow: var(--gk-shadow-btn);
}
.gk-btn:active { transform: scale(.97); }
.gk-btn--compact { font-size: 17px; border-radius: 16px; padding: 17px; }

.gk-ghost {
  flex: 1;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--gk-ink);
  background: transparent;
  border: 1px solid var(--gk-line);
  border-radius: 14px;
  /* 12px sides rather than 14: at 320px the column is 260px and the clock beside "Your stats" came
     to 261.6. The height is what carries the tap target, so the sides can give up the 4px. */
  padding: 14px 12px;
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  /* ≥44px hit target even though the label is small. */
  min-height: 48px;
}
.gk-ghost--accent {
  color: var(--gk-on-accent);
  background: var(--gk-accent);
  border-color: transparent;
  font-weight: 700;
}
.gk-ghost[disabled] { opacity: .55; cursor: default; }

.gk-btnrow { display: flex; gap: 11px; }

/* Once the day is spent, "Play again" becomes a clock, and flex: 1 had that clock holding exactly
   half the row: wide enough at some viewport widths and not others, so the same button broke onto
   two lines on one phone and not the next.

   Three things together, because no one of them is enough. Nothing in this row wraps. The clock —
   disabled is exactly when it carries one, since gokab_controller sets the text and the disabled
   state on the same line — takes the width its text needs instead of a half share. And the label is
   "Next in", not "Next game in": at 320px the column is 260px, and the longer copy cannot fit beside
   "Your stats" at any font size worth reading. */
.gk-btnrow .gk-ghost { white-space: nowrap; }
.gk-btnrow .gk-ghost[disabled] { flex: 0 1 auto; }

.gk-countdown {
  margin-top: 14px;
  text-align: center;
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .05em;
  color: var(--gk-mute);
}

/* --------------------------------------------------------------- Landing --- */

.gk-wordmark {
  font-weight: 700;
  font-size: 26px;
  letter-spacing: .03em;
  line-height: 1;
  color: var(--gk-ink);
}
/* It is a link back to the picker on every screen except the picker itself. Inline-block so the
   hit area is the wordmark and not the full column width, and the colour/decoration are pinned
   because the game layout has no link reset of its own. */
a.gk-wordmark {
  display: inline-block;
  text-decoration: none;
  color: var(--gk-ink);
  transition: opacity .16s ease;
}
a.gk-wordmark:hover { opacity: .72; }
a.gk-wordmark:focus-visible {
  outline: 2px solid var(--gk-accent);
  outline-offset: 6px;
  border-radius: 4px;
}

/* The leading G underlined by an accent bar — the app icon, echoed in type. */
.gk-wordmark__g {
  display: inline-block;
  border-bottom: 4px solid var(--gk-accent);
  padding-bottom: 3px;
}

.gk-greeting {
  margin-top: 30px;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--gk-dim2);
}
.gk-greeting span { color: var(--gk-accent-text); }

.gk-headline {
  margin: 10px 0 0;
  font-size: clamp(40px, 10.5vw, 54px);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--gk-ink);
}
.gk-headline span { color: var(--gk-accent-text); }

.gk-intro {
  margin-top: 20px;
  max-width: 310px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--gk-dim2);
}

/* Three stats now, so the gap gives way on a narrow phone rather than pushing "revives" off the
   edge. .gk-col clips at the viewport, and the overflow would be silent. */
.gk-statrow { display: flex; align-items: flex-start; gap: clamp(24px, 8vw, 44px); margin-bottom: 24px; }

/* -------------------------------------------------------------- Gameplay --- */

.gk-topbar { display: flex; align-items: center; justify-content: space-between; }
.gk-level {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 14px;
  letter-spacing: .04em;
  color: var(--gk-dim);
}
.gk-level span { color: var(--gk-accent-text); font-weight: 500; }

/* Heat bar: grows with the streak, and the multiplier bumps beside it. */
.gk-heatrow { display: flex; align-items: center; gap: 12px; margin: 16px 0 4px; }
.gk-heat { flex: 1; height: 6px; border-radius: 4px; background: var(--gk-track); overflow: hidden; }
.gk-heat__fill {
  height: 100%;
  width: 0;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--gk-accent), var(--gk-heat-end));
  transition: width .3s ease;
}
.gk-mult {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 15px;
  font-weight: 500;
  color: var(--gk-mute);
}
.gk-mult.is-live { color: var(--gk-accent-text); animation: gk-bump .4s; }

.gk-dots { display: flex; gap: 7px; justify-content: center; margin: 16px 0 4px; }
.gk-dots span {
  width: 28px;
  height: 5px;
  border-radius: 3px;
  background: var(--gk-dot);
  transition: background .2s;
}
.gk-dots span.is-done { background: var(--gk-accent); }

.gk-stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
}

.gk-warmlabel {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: .2em;
  color: var(--gk-mute);
}

/* Timer ring */
.gk-ring { position: relative; width: 132px; height: 132px; }
.gk-ring__glow {
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  box-shadow: 0 0 34px -4px var(--gk-accent);
  opacity: var(--gk-ring-glow-op);
}
.gk-ring__svg { transform: rotate(-90deg); position: relative; display: block; }
.gk-ring__track { fill: none; stroke: var(--gk-track); stroke-width: 9; }
.gk-ring__bar {
  fill: none;
  stroke: var(--gk-accent);
  stroke-width: 9;
  stroke-linecap: round;
  transition: stroke-dashoffset .09s linear;
}
.gk-ring__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.gk-ring__secs {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 34px;
  font-weight: 500;
  line-height: 1;
  color: var(--gk-ink);
}
.gk-ring__unit {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: .16em;
  color: var(--gk-mute);
  margin-top: 2px;
}
/* Under a third of the clock left: everything goes red and pulses. */
.gk-ring.is-danger .gk-ring__bar { stroke: var(--gk-bad); }
.gk-ring.is-danger .gk-ring__glow { box-shadow: 0 0 34px -4px var(--gk-bad); opacity: .9; animation: gk-pulse .55s infinite; }
.gk-ring.is-danger .gk-ring__secs { color: var(--gk-bad); animation: gk-pulse .55s infinite; }

.gk-word {
  font-size: clamp(54px, 16vw, 70px);
  font-weight: 700;
  letter-spacing: -.03em;
  line-height: 1;
  text-align: center;
  color: var(--gk-ink);
  animation: gk-pop .35s ease;
}

.gk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; margin-top: 8px; }
.gk-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: inherit;
  font-size: 19px;
  font-weight: 600;
  color: var(--gk-ink);
  background: var(--gk-tile);
  border: 1px solid var(--gk-tile-line);
  box-shadow: var(--gk-tile-shadow);
  border-radius: 16px;
  padding: 22px 10px;
  min-height: 72px;
  cursor: pointer;
  transition: all .12s;
}
.gk-tile__mark { font-weight: 700; }
.gk-tile.is-correct {
  background: var(--gk-accent);
  color: var(--gk-on-accent);
  border-color: var(--gk-accent);
  box-shadow: 0 0 26px -6px var(--gk-accent);
}
.gk-tile.is-wrong {
  background: var(--gk-bad-tile);
  color: var(--gk-bad-text);
  border-color: var(--gk-bad);
  animation: gk-shake .42s;
}
.gk-tile.is-muted { opacity: .35; }
.gk-tile[disabled] { cursor: default; }

/* ---------------------------------------------------------------- Result --- */

/* Just the pill now — the date moved into the menu panel, which is where a thing you consult rather
   than read belongs. The reserve stays: the pill is the only item here, and "ONE WRONG TAP" is long
   enough that a longer status would reach the hamburger. */
.gk-resulthead {
  display: flex;
  align-items: center;
  padding-right: var(--gk-menu-reserve);
}

.gk-resultbody { flex: 1; display: flex; flex-direction: column; justify-content: center; }

.gk-kicker {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: .22em;
  color: var(--gk-dim);
  margin-bottom: 6px;
}

.gk-hero { display: flex; align-items: baseline; gap: 2px; }
.gk-hero__num {
  font-size: clamp(104px, 34vw, 140px);
  line-height: .78;
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--gk-ink);
}
.gk-hero__pct {
  font-size: clamp(44px, 13vw, 58px);
  font-weight: 700;
  color: var(--gk-accent-text);
}
.gk-hero__sub { margin: 10px 0 0; font-size: 16px; color: var(--gk-dim2); }

/* You against everyone else, on one line. */
.gk-meter { margin-top: 24px; }
.gk-meter__track { position: relative; height: 12px; border-radius: 8px; background: var(--gk-track); }
.gk-meter__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 0;
  border-radius: 8px;
  background: var(--gk-accent);
  box-shadow: 0 0 18px -4px var(--gk-accent);
  transition: width .7s cubic-bezier(.2, .8, .2, 1);
}
.gk-meter__dot {
  position: absolute;
  top: 50%;
  left: 0;
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  border-radius: 50%;
  background: var(--gk-accent);
  border: 3px solid var(--gk-bg);
  box-shadow: 0 0 12px -2px var(--gk-accent);
  transition: left .7s cubic-bezier(.2, .8, .2, 1);
}
.gk-meter__ends {
  display: flex;
  justify-content: space-between;
  margin-top: 9px;
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .08em;
}
.gk-meter__you { color: var(--gk-accent-text); font-weight: 500; }
.gk-meter__world { color: var(--gk-mute); }

/* One square per ranked answer — the shareable trail. */
.gk-trail { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 26px; }
.gk-trail span { width: 22px; height: 22px; border-radius: 5px; background: var(--gk-accent); opacity: .92; }
.gk-trail span.is-miss { background: var(--gk-bad); opacity: 1; }

.gk-bigstats { display: flex; gap: 34px; margin-top: 24px; }

/* The word that ended the run — the one bit of teaching the result screen keeps. */
.gk-taught {
  margin: 22px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--gk-dim2);
}
.gk-taught b { color: var(--gk-accent-text); font-weight: 600; }
.gk-taught i { font-style: normal; color: var(--gk-mute); }

.gk-actions { display: flex; flex-direction: column; gap: 11px; }

/* ----------------------------------------------------------------- Stats --- */

/* padding-right, because the hamburger is absolutely positioned over this row and knows nothing
   about it: with space-between the streak pill went to the content edge and the button landed on top
   of it. See --gk-menu-reserve for where the number comes from. */
.gk-statshead {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-right: var(--gk-menu-reserve);
  margin-bottom: 30px;
}
/* line-height 1 like .gk-wordmark's. The default line box is 35.5px against the wordmark's 26px, and
   since the hamburger is placed once for every screen — centred on the wordmark's caps — a taller
   box here drops this heading below the button it is supposed to sit level with. */
.gk-statshead h2 {
  margin: 0;
  font-size: 28px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--gk-ink);
}

.gk-week { display: flex; align-items: flex-end; gap: 10px; height: 160px; }
.gk-week__day {
  flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}
.gk-week__bar {
  width: 100%;
  min-height: 8px;
  border-radius: 6px;
  background: var(--gk-dot);
  transition: height .5s ease;
}
.gk-week__day.is-today .gk-week__bar { background: var(--gk-accent); box-shadow: 0 0 20px -6px var(--gk-accent); }
.gk-week__label { font-family: 'DM Mono', ui-monospace, monospace; font-size: 11px; color: var(--gk-mute); }

.gk-rule { height: 1px; background: var(--gk-line); margin: 28px 0; }
.gk-statsrow { display: flex; justify-content: space-between; }
.gk-note { margin-top: 20px; font-size: 15px; line-height: 1.5; color: var(--gk-dim2); }
.gk-spacer { flex: 1; }

/* ------------------------------------------------------------- Challenge --- */
/* The /share/:date/:code page a friend opens. Same shell, no game. */

.gk-screen--challenge {
  justify-content: space-between;
  padding:
    var(--gk-top)
    max(30px, env(safe-area-inset-right))
    max(40px, env(safe-area-inset-bottom))
    max(30px, env(safe-area-inset-left));
  animation: gk-rise .5s ease;
}

/* ------------------------------------------------------------------ Docs --- */

/* Support and Terms & Privacy: the only long-form reading in the app, reached from the menu the
   game itself carries. They used to render in the kit's Terminal system, which is a different
   product's palette and typography — tapping Support looked like being handed off to somebody
   else's website, which is the last thing a legal page should feel like.

   The one place a screen has to behave like a document rather than a screen: no space-between, no
   single viewport height, just a column that runs as long as the words do and scrolls. */
.gk-screen--doc {
  justify-content: flex-start;
  padding:
    var(--gk-top)
    max(30px, env(safe-area-inset-right))
    max(48px, env(safe-area-inset-bottom))
    max(30px, env(safe-area-inset-left));
  animation: gk-rise .45s ease;
}

/* Clear of the hamburger, which is absolutely positioned over the top of every screen. The title is
   the only row up here wide enough to reach it. */
.gk-doc__title {
  margin: 8px 0 0;
  padding-right: var(--gk-menu-reserve);
  font-size: clamp(32px, 8.5vw, 42px);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: -.03em;
  color: var(--gk-ink);
}

.gk-doc__lede {
  margin: 14px 0 0;
  font-size: 15px;
  line-height: 1.55;
  color: var(--gk-dim);
}

/* Sections are set by the space above them rather than by rules or weight: a legal page is a list
   of questions, and the gap is what says a new one has started. */
.gk-doc h2 {
  margin: 44px 0 0;
  font-size: 21px;
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--gk-ink);
}
.gk-doc h3 {
  margin: 28px 0 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--gk-ink);
}
.gk-doc p {
  margin: 9px 0 0;
  font-size: 15px;
  line-height: 1.62;
  color: var(--gk-dim2);
}
.gk-doc strong { color: var(--gk-ink); font-weight: 700; }
.gk-doc a {
  color: var(--gk-accent-text);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.gk-doc__note { color: var(--gk-mute); }

.gk-doc__foot { margin-top: 44px; }

/* The false-friends list.

   Two lines per row rather than three columns. The app column is 460px everywhere by design, and
   three columns inside it gives the gloss about 140px, which wrapped nearly every one of them onto
   a second line anyway. So the pair that has to be read together (what you want to say, and the word
   that says it) shares the first line, and the gloss gets the full width underneath. */
.gk-ff {
  margin: 22px 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--gk-line);
}
.gk-ff__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: 3px 16px;
  align-items: baseline;
  padding: 12px 0;
  border-bottom: 1px solid var(--gk-line);
}
.gk-ff__en {
  font-size: 15px;
  line-height: 1.4;
  color: var(--gk-dim);
}
/* The answer is the one thing on the row worth memorising, so it gets the ink and the mono face the
   game already uses for a word under test. */
.gk-ff__answer {
  font-family: 'DM Mono', ui-monospace, Menlo, monospace;
  font-size: 15px;
  line-height: 1.4;
  color: var(--gk-ink);
}
/* Italic and muted, matching how the death screen renders the same gloss. */
.gk-ff__gloss {
  grid-column: 1 / -1;
  font-size: 14px;
  line-height: 1.45;
  font-style: italic;
  color: var(--gk-mute);
}

/* ---------------------------------------------------------------- Motion --- */

@keyframes gk-shake {
  0%, 100% { transform: translateX(0); }
  18% { transform: translateX(-9px); }
  36% { transform: translateX(8px); }
  54% { transform: translateX(-5px); }
  72% { transform: translateX(4px); }
}
@keyframes gk-pop {
  0% { transform: scale(.7); opacity: 0; }
  55% { transform: scale(1.08); }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes gk-rise {
  0% { transform: translateY(14px); opacity: 0; }
  100% { transform: translateY(0); opacity: 1; }
}
@keyframes gk-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.12); }
}
@keyframes gk-spin { to { transform: rotate(360deg); } }
@keyframes gk-flash {
  0% { opacity: var(--gk-flash-op); }
  100% { opacity: 0; }
}
@keyframes gk-bump {
  0% { transform: scale(1); }
  40% { transform: scale(1.35); }
  100% { transform: scale(1); }
}

/* The arcade energy is decoration, not information — everything it conveys is also in the layout,
   the numbers, and the colours. Honour a request for calm. */
@media (prefers-reduced-motion: reduce) {
  .gk-app *, .gk-app *::before, .gk-app *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .gk-glow { animation: none; }
}

/* ------------------------------------------------------------------- Menu --- */

/* The game's only chrome. Anchored to .gk-col, not .gk-app: the column is full-bleed on a phone so
   the two are identical there, but on a desktop .gk-app is the whole viewport and the button would
   drift hundreds of pixels away from the content it belongs to.

   The top is derived from --gk-top rather than picked, because this button has to look level with a
   wordmark it is not laid out with. The wordmark's box starts at --gk-top and its caps centre ~13px
   below that (26px type at line-height 1); the toggle is 38px tall, so centring it means starting
   6px higher. The max() is the notch guard: when the safe-area inset is what set --gk-top, the
   button stops at the inset instead of sliding under the status bar. */
.gk-menu {
  position: absolute;
  top: max(calc(var(--gk-top) - 6px), env(safe-area-inset-top));
  right: max(20px, calc(env(safe-area-inset-right) + 8px));
  z-index: 6;
  font-family: 'DM Mono', ui-monospace, monospace;
}

/* Never mid-run. One stray tap costs a run the player only gets one of per day.
   The values are the ones gokab_controller.js actually assigns — it sets "warmup" and "run", never
   "play", which is the SCREEN name. An earlier version of this rule keyed on "play" and therefore
   never matched at all. */
.gk-app[data-phase="warmup"] .gk-menu,
.gk-app[data-phase="run"] .gk-menu { display: none; }

.gk-menu__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--gk-pill-border);
  border-radius: 999px;
  background: var(--gk-tile);
  color: var(--gk-dim2);
  cursor: pointer;
  transition: border-color .16s ease, color .16s ease;
}
.gk-menu__toggle:hover { border-color: var(--gk-accent); color: var(--gk-ink); }
.gk-menu__toggle:focus-visible { outline: 2px solid var(--gk-accent); outline-offset: 3px; }
.gk-menu__icon { width: 19px; height: 19px; }
.gk-menu__icon--close { display: none; }
.gk-menu__toggle[aria-expanded="true"] { border-color: var(--gk-accent); color: var(--gk-ink); }
.gk-menu__toggle[aria-expanded="true"] .gk-menu__icon--open { display: none; }
.gk-menu__toggle[aria-expanded="true"] .gk-menu__icon--close { display: inline-flex; }

.gk-menu__panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 258px;
  max-height: min(78vh, 620px);
  overflow-y: auto;
  padding: 8px;
  border: 1px solid var(--gk-line);
  border-radius: 16px;
  background: var(--gk-tile);
  box-shadow: 0 22px 48px -20px rgba(10, 12, 30, .6);
  animation: gk-rise .18s ease;
}

/* Today's date, at the top of the panel. Mono and accent-coloured rather than muted like a heading:
   it is the one line here that is information rather than a way out of the screen. */
.gk-menu__day {
  margin: 6px 10px 2px;
  font-size: 12px;
  letter-spacing: .08em;
  color: var(--gk-accent-text);
}

.gk-menu__heading {
  margin: 8px 10px 6px;
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gk-mute);
}

.gk-menu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: 10px;
  background: none;
  font: inherit;
  font-size: 13px;
  text-align: left;
  text-decoration: none;
  color: var(--gk-ink);
  cursor: pointer;
}
.gk-menu__item:hover { background: var(--gk-track); }
.gk-menu__item.is-current { color: var(--gk-accent-text); }
.gk-menu__item--plain { display: block; }

.gk-menu__flag { font-size: 15px; line-height: 1; }
.gk-menu__name { font-size: 13px; }

.gk-menu__email {
  margin: 0 10px 4px;
  font-size: 12px;
  color: var(--gk-dim2);
  overflow-wrap: anywhere;
}
.gk-menu__note {
  margin: 4px 10px 6px;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--gk-mute);
}
.gk-menu__rule { height: 1px; margin: 8px 4px; background: var(--gk-line); }
/* The shared provider button, sized down for a 258px panel. */
.gk-menu__panel .gk-provider { padding: 10px; font-size: 12.5px; border-radius: 10px; }
.gk-menu__panel .gk-provider__mark { width: 16px; height: 16px; }

/* nav_controller.js locks body scroll while the menu is open. The rule it relies on lives in the
   kit's application.css, which the game layout deliberately does not load. */
body.nav-locked { overflow: hidden; }

/* ---------------------------------------------------------- Language picker --- */

/* The root page for anyone with no language cookie yet. Four links, nothing else. */
.gk-picker { display: flex; flex-direction: column; gap: 10px; margin-top: 30px; }

.gk-picker__option {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 15px 18px;
  border: 1px solid var(--gk-tile-line);
  border-radius: 16px;
  background: var(--gk-tile);
  box-shadow: var(--gk-tile-shadow);
  text-decoration: none;
  color: var(--gk-ink);
  transition: border-color .16s ease, transform .16s ease;
}
.gk-picker__option:hover { border-color: var(--gk-accent); transform: translateX(3px); }

.gk-picker__flag { font-size: 26px; line-height: 1; }
.gk-picker__text { flex: 1; }
.gk-picker__name { display: block; font-size: 17px; font-weight: 600; }
.gk-picker__variant {
  display: block;
  margin-top: 2px;
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 11px;
  color: var(--gk-mute);
}
.gk-picker__go { color: var(--gk-accent-text); font-size: 18px; }

/* -------------------------------------------------------------- Revives --- */

/* The balance, as the third stat on the landing row. A button that has to look like the two plain
   <div> stats beside it, so every button default is reset and the label is re-blocked (the stat's
   parts are spans here, inside the hit area). */
.gk-stat--buy {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: opacity .16s ease;
}
.gk-stat--buy .gk-stat__label { display: block; }
.gk-stat--buy:hover { opacity: .78; }
.gk-stat--buy:focus-visible { outline: 2px solid var(--gk-accent); outline-offset: 4px; border-radius: 4px; }
/* Nothing to spend. Dimmed rather than hidden: a zero is what makes the tap worth taking. */
.gk-stat--buy[data-empty="true"] { opacity: .55; }

/* The revive CTA. Accent-bordered rather than accent-filled, so it reads as the offer it is and
   does not outshout "Challenge a friend", which is still the growth loop. */
.gk-btn--revive {
  background: transparent;
  border: 1px solid var(--gk-accent);
  color: var(--gk-accent-text);
  box-shadow: none;
}
.gk-btn--revive:hover { background: color-mix(in srgb, var(--gk-accent) 12%, transparent); }

.gk-buyform { display: contents; }

/* ------------------------------------------------------------------ Auth --- */

/* Sign-in, in the game's clothes.
   These two pages (the email form and the code entry) used to render in the kit's Terminal system,
   which is a different product's look entirely — and since the menu now sends players here mid-run,
   the jump was jarring. The kit's own flows at /register keep the Terminal styling; these are the
   ones a Gokab player actually meets. */
.gk-auth { width: 100%; max-width: 380px; margin: 0 auto; padding: 0 4px; }
.gk-auth__head { text-align: center; margin-bottom: 26px; }
.gk-auth__title { margin: 22px 0 6px; font-size: 22px; font-weight: 700; color: var(--gk-ink); }
.gk-auth__sub { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--gk-dim2); }

.gk-field { margin-bottom: 14px; }
.gk-field label {
  display: block;
  margin-bottom: 7px;
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gk-mute);
}
.gk-field input {
  width: 100%;
  padding: 13px 14px;
  border: 1px solid var(--gk-tile-line);
  border-radius: 12px;
  background: var(--gk-tile);
  font: inherit;
  font-size: 15px;
  color: var(--gk-ink);
}
.gk-field input::placeholder { color: var(--gk-mute); }
.gk-field input:focus {
  outline: none;
  border-color: var(--gk-accent);
}

/* The six digits. Wide tracking so the code reads as separate characters, which is how people
   check one they are copying from an email. */
.gk-field input.gk-code {
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 26px;
  letter-spacing: 12px;
  text-align: center;
  padding-left: 12px;
}

.gk-auth .gk-btn { width: 100%; }
.gk-auth__or {
  margin: 20px 0 14px;
  text-align: center;
  font-family: 'DM Mono', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: .18em;
  color: var(--gk-mute);
}
.gk-auth__fine {
  margin-top: 22px;
  text-align: center;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--gk-mute);
}
.gk-auth__fine a { color: var(--gk-dim2); }

/* One provider button, shared by the menu and this page, so the two can never drift apart. */
.gk-provider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  margin-bottom: 8px;
  padding: 13px;
  border: 1px solid var(--gk-tile-line);
  border-radius: 12px;
  background: var(--gk-tile);
  font: inherit;
  font-size: 14px;
  color: var(--gk-ink);
  cursor: pointer;
  transition: border-color .16s ease;
}
.gk-provider:hover { border-color: var(--gk-accent); }
.gk-provider__mark { width: 17px; height: 17px; flex-shrink: 0; }

/* The flash. The game layout had none at all, so a redirect carrying "check your email" showed
   nothing — which is exactly the message this flow depends on. */
.gk-flashes {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 8;
  padding: 14px;
  font-family: 'Space Grotesk', system-ui, -apple-system, sans-serif;
}
.gk-flashmsg {
  max-width: 420px;
  margin: 0 auto 8px;
  padding: 11px 14px;
  border: 1px solid var(--gk-tile-line);
  border-radius: 12px;
  background: var(--gk-tile);
  font-size: 13px;
  line-height: 1.45;
  color: var(--gk-ink);
  text-align: center;
}
.gk-flashmsg--alert { border-color: var(--gk-bad); color: var(--gk-bad-text); }

/* The mid-run hearts readout. Sits between the phase pill and the level, so the row reads
   "what I'm doing / what it costs me / how far I am". */
.gk-runhearts { font-size: 12px; line-height: 1; letter-spacing: 1px; }

/* A spent heart is a hollow emoji, which platforms draw at full strength — knocked back so the row
   reads as "two left of three" rather than as two different kinds of heart. */
.gk-heart--spent { opacity: .4; }
