/* css/main.css (WP0) — the design system (visual pass 2, 2026-10-15: docs/DECISIONS.md, docs/ART_DIRECTION.md):
   tokens, reset, app shell, z-layers, loading screen and the base components (.btn, .panel, .pill, .chip, .tag,
   .badge, .progress, .num). Every other stylesheet takes its values from the tokens below — one type scale (6 sizes,
   3 weights), one radius scale (xs/sm/md/lg + pill), six navies, one 2 px border, two hard bottom edges and one modal
   shadow, all lit from the top. No gradients, blur glows, inset highlights, uppercase or letter-spacing in the UI.
   Font: ONE alias family, "QC Rush Sans" — CSS and every canvas / WebGL font string name it, never a face directly.
   It maps to the bundled variable woff2 files in css/fonts/ (licence: css/fonts/LICENSE), latin + latin-ext (Polish
   ą ć ę ł ń ó ś ź ż). The files are Nunito (SIL OFL 1.1), swapped in from Geist on 2026-10-16. */

/* "QC Rush Sans" = the Nunito variable font (css/fonts/nunito-*.woff2, from @fontsource-variable/nunito 5.1.0), copyright
   2014 The Nunito Project Authors (https://github.com/googlefonts/nunito), bundled unmodified under the SIL Open Font
   License, Version 1.1 (https://openfontlicense.org). Owner 2026-10-16: „dawaj Nunito”. */
@font-face {
  font-family: "QC Rush Sans";
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url("fonts/nunito-latin-ext.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: "QC Rush Sans";
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url("fonts/nunito-latin.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;
}

/* ---------------------------------------------------------------- tokens */
/* owner 2026-10-16: macOS-style vector cursors (desktop only; touch screens never show them) */
:root {
  --cur: url("../promo/cursor.svg") 5 3, auto;
  --cur-hand: url("../promo/cursor-hand.svg") 9 3, pointer;
}
html { cursor: var(--cur); }

:root {
  /* Site-mirrored colours: --kr-canvas / --kr-fg from the site's src/app/globals.css (plan §3.9).
     Used by the page background and the loading screen only. */
  --kr-canvas: #0a0a0b;
  --kr-fg: #f4f4f5;

  /* type: one scale, three weights */
  --font: "QC Rush Sans", "Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif;
  --fs-12: 0.75rem;
  --fs-14: 0.875rem;
  --fs-16: 1rem;
  --fs-20: 1.25rem;
  --fs-24: 1.5rem;
  --fs-32: 2rem;
  --fs-40: 2.5rem; /* the crate reveal headline (2026-10-21) */
  --fw-body: 700;
  --fw-label: 800;
  --fw-num: 900;
  --lh-tight: 1.15; /* titles, numbers, buttons, one-line rows */
  --lh-body: 1.35;  /* paragraphs */
  --fs-title: var(--fs-20); /* every sheet / screen / modal title, phones too */
  --fs-sec: var(--fs-14);   /* every section heading */

  /* radius */
  --r-xs: 4px;     /* tags, swatches, handles, anything 20 px tall or less */
  --r-sm: 8px;     /* chips, small buttons, icon wells, keys inside a track */
  --r-md: 12px;    /* buttons, cards, rows, list groups, HUD blocks, toasts, inputs */
  --r-lg: 20px;    /* sheets, modals, nav bar, big stage panels */
  --r-pill: 999px; /* progress bars and count badges only; 50 % only on square elements */
  --radius-sm: var(--r-md); /* legacy aliases */
  --radius: var(--r-md);
  --radius-lg: var(--r-lg);
  --radius-pill: var(--r-pill);

  /* spacing */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;

  /* surfaces: six navies, nothing else */
  --bg: #0b1020;            /* recesses: tracks, wells, the QC backdrop, the segment track */
  --panel-solid: #151b2b;   /* sheets, modals, HUD blocks, nav, toasts */
  --panel: var(--panel-solid);
  --panel-raised: #1d2540;  /* cards, rows, list groups inside a panel */
  --raised: var(--panel-raised);
  --raised-2: #26304f;      /* selected / active fill, secondary button, own row, tag fill */
  --panel-border: #2b3658;  /* THE border, always 2 px */
  --line: var(--panel-border);
  --edge-c: #0a0e1a;        /* bottom edge of navy pressables */
  --scrim: rgba(5, 8, 16, 0.62);

  /* text */
  --text: #ffffff;
  --text-2: #b9c2d8;
  --text-dim: #7d87a3;
  --ink: #1b2330;           /* text / outlines on light or yellow surfaces, in-world outlines */
  --paper: #fff6df;         /* Kim's bubble + in-world speech bubbles */

  /* roles: green = act / claim, yellow = XP / level / rank, gem = gems, red = badges / errors / problems */
  --green: #36c64a;
  --green-dark: #1f9e35;
  --yellow: #f7c51e;
  --yellow-dark: #b88f0c;
  --gem: #a24bf0;
  --gem-light: #d08bff;
  --gem-dark: #7a2fc0;
  --red: #ef3b3b;
  --red-dark: #b82424;
  --brand-red: #e3212f;     /* the KakoBuy brand inside the world only, never UI */
  --fans: #ff8cbc;          /* the fans icon only (js/ui/fans.js) */
  --medal-2: #c9cfdf;       /* league ranks 2 / 3 (rank 1 = --yellow) */
  --medal-3: #d08a4e;

  /* in-world */
  --lane: #e9b824;
  --cardboard-1: #c99558;
  --cardboard-2: #b07e45;
  --cardboard-3: #8f6334;
  --shirt: #f2c21b;

  /* depth: one light, from the top */
  --edge: 4px;      /* buttons 44 px tall or more */
  --edge-sm: 3px;   /* everything else that is pressable or floats over the hall */
  --sh-edge: 0 var(--edge) 0 var(--edge-c);
  --sh-edge-sm: 0 var(--edge-sm) 0 var(--edge-c);
  --sh-modal: 0 12px 32px rgba(0, 0, 0, 0.5); /* modals only */
  --shadow: var(--sh-modal); /* legacy alias */
  --ring-new: 0 0 0 2px var(--panel);  /* separates a badge / dot from what it sits on */
  --ring-go: 0 0 0 2px var(--green);   /* „ready / claim me” — static, never blurred */
  --num-shadow: 0 2px 0 rgba(0, 0, 0, 0.45); /* only text drawn straight over the 3D hall */

  /* chunky pass (2026-10-20, owner: „bardziej jak Roblox”): a thicker lip under every .btn that a press sinks into,
     a hard light line on the top edge, a hard dark lip under titles / button labels. All hard (no blur). */
  --btn-lip: 6px;     /* buttons 44 px tall or more */
  --btn-lip-sm: 4px;  /* small buttons (36 px) */
  --btn-lip-pressed: 2px; /* what is left of the lip while pressed (the button sinks the rest) */
  --btn-shine: inset 0 3px 0 rgba(255, 255, 255, 0.24);      /* coloured buttons: the lit top edge */
  --btn-shine-soft: inset 0 2px 0 rgba(255, 255, 255, 0.07); /* navy (ghost) buttons */
  --btn-shine-off: inset 0 0 0 transparent;                  /* text / disabled buttons */
  --tx-lip: 0 2px 0 var(--edge-c);  /* titles on the navy panels */
  --tx-ink: 0 2px 0 var(--ink), 0 -1px 0 var(--ink), 1px 0 0 var(--ink), -1px 0 0 var(--ink); /* white on colour */
  /* „sign finish” (owner 2026-10-22: the 3D station signs' look everywhere — glossy saturated fill, a lit top band, a
     bevelled darker rim, a hard drop; no blur, no filter: one gradient layer + inset / offset shadows, cheap on every tier) */
  --sign-gloss: linear-gradient(180deg, rgba(255, 255, 255, 0.26) 0%, rgba(255, 255, 255, 0.10) 46%, rgba(255, 255, 255, 0) 50%, rgba(0, 0, 0, 0.12) 100%);
  --sign-gloss-soft: linear-gradient(180deg, rgba(255, 255, 255, 0.14) 0%, rgba(255, 255, 255, 0.05) 46%, rgba(255, 255, 255, 0) 50%, rgba(0, 0, 0, 0.16) 100%);
  --sign-gloss-panel: linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0, rgba(255, 255, 255, 0) 96px);
  --sign-hi: inset 0 3px 0 rgba(255, 255, 255, 0.32);      /* the lit top strip */
  --sign-hi-soft: inset 0 2px 0 rgba(255, 255, 255, 0.16); /* … on navy */
  --sign-rim: inset 0 -3px 0 rgba(0, 0, 0, 0.22);          /* the darker bevel along the bottom */
  --sign-drop: 0 7px 0 rgba(5, 8, 16, 0.28);                /* a hard step under the lip (no blur) */
  --sign-tx: var(--tx-ink);
  --blue: #2f80ed;
  --blue-dark: #1b58b3;
  --orange: #f28c1e;
  --orange-dark: #b9620b;
  --pink: #ee3f86;
  --pink-dark: #ae2459;
  --mo-tap: 60ms;     /* the press itself (the release springs back with --mo-spring) */
  --mo-pop: 520ms;    /* reward pops: scale in past 1, settle */

  /* motion: three durations, two curves (--mo-spring is the only overshoot) */
  --mo-fast: 120ms;
  --mo-med: 200ms;
  --mo-slow: 320ms;
  --mo-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --mo-spring: cubic-bezier(0.3, 1.35, 0.5, 1);

  --tap: 44px;

  /* Safe areas */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);

  /* Z-layers (TECH_SPEC §3) */
  --z-scene: 0;
  --z-hud: 10;
  --z-fx: 20;
  --z-sheets: 30;
  --z-qc: 40;
  --z-modals: 50;
  --z-toasts: 60;
  --z-loading: 70;
  --z-rotate: 80;

  color-scheme: dark;
}

/* ---------------------------------------------------------------- reset */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
  overscroll-behavior: none;
  background: var(--kr-canvas);
  /* no page zoom on phones (owner 2026-10-14): panning/scrolling only — no pinch zoom, no double-tap zoom. With the
     viewport meta (maximum-scale=1, user-scalable=no) for Chrome / Android; iOS Safari's pinch is stopped in js/boot.js. */
  touch-action: pan-x pan-y;
}

body {
  /* Desktop backdrop around the centred 480 px column: a calm grey→black gradient with a soft glow behind the
     game (owner 2026-10-11: the old diagonal stripes „aż oczy bolą”). Fixed so it doesn't move on scroll. */
  background:
    radial-gradient(ellipse 60% 70% at 50% 40%, rgba(120, 128, 145, 0.16), transparent 70%),
    linear-gradient(180deg, #2a2d33 0%, #17191d 45%, #0a0a0b 100%) fixed,
    var(--kr-canvas);
  color: var(--text);
  font: var(--fw-body) var(--fs-16)/var(--lh-body) var(--font);
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

h1, h2, h3, h4, p, ul, ol, figure {
  margin: 0;
}

ul, ol {
  padding: 0;
  list-style: none;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

button {
  cursor: var(--cur-hand);
}

img,
svg,
canvas {
  display: block;
}

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

[hidden] {
  display: none !important;
}

:focus-visible {
  outline: 3px solid var(--yellow);
  outline-offset: 2px;
}

/* the ring is for keyboard users: after a tap / click (js/boot.js sets data-input="pointer") a button focused by a
   script (Kim's „Dalej”, a crate) shows no yellow outline — every :focus-visible ring in the game, hence !important */
:root[data-input="pointer"] :focus-visible,
:root[data-input="pointer"] :focus-visible + .scr-switch__knob {
  outline: none !important;
}

/* ---------------------------------------------------------------- app shell + layers */
#app {
  position: fixed;
  inset: 0;
  max-width: 480px;
  height: 100vh;
  height: 100dvh;
  margin: 0 auto;
  overflow: hidden;
  background: var(--panel-solid);
  /* Game layer */
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: pan-x pan-y; /* like manipulation (no double-tap zoom), minus the page pinch zoom */
  /* 2026-10-22 (owner, 786×864 tablet): #app is the containing block of every position: fixed HUD bit (the crown /
     Sklep / Szafa dock) — in the centred phone window they were placed against the viewport, outside the frame */
  contain: layout;
}

/* Desktop / wide screens: the game is a rounded "phone window" floating on the gradient (owner 2026-10-11:
   „okienko gry z zaokrąglonymi rogami”). Phones keep the full-bleed layout. */
@media (min-width: 560px) and (min-height: 640px) {
  #app {
    inset: 16px 0;
    height: auto;
    border-radius: var(--r-lg);
    box-shadow: 0 0 0 2px var(--line), var(--sh-modal);
    isolation: isolate; /* clip the WebGL canvas + layers to the rounded corners */
  }
}

/* Big monitors (owner 2026-10-11, 32": „wygląda dziwnie, może to poszerzyć”): a wider, tablet-like portrait
   window — up to 640 px, never wider than ~62 % of the window height so it stays portrait. */
@media (min-width: 900px) and (min-height: 760px) {
  #app { max-width: min(640px, calc((100dvh - 32px) * 0.62)); }
}

/* BACKLOG 17 (owner 2026-10-11: „na komputerze fullscreen, rozciągnij na maxa; na telefonie zostaw”): wide
   landscape windows drop the phone window — #app fills the whole window. Phones / portrait keep the rules above. */
@media (min-width: 900px) and (orientation: landscape) {
  #app {
    inset: 0;
    max-width: none;
    height: 100vh;
    height: 100dvh;
    border-radius: 0;
    box-shadow: none;
  }
}

#scene {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: var(--z-scene);
  touch-action: none; /* pan on the scene (+ the scrims below and the sheet drag handles) */
}

/* mouse: the hall is dragged (grab hand); js/render/scene-input.js shows a pointer hand over what a click does
   something on and „grabbing” while the hall pans */
@media (hover: hover) and (pointer: fine) {
  #scene { cursor: grab; }
}

/* pan-through (js/render/scene-input.js): a drag on the dimmed hall around an open sheet / QC side panel pans the hall
   (its taps still close a half sheet), so the browser must not claim the touch for scrolling */
.sheet-scrim,
.qc-scrim {
  touch-action: none;
}

#hud,
#layer-sheets,
#layer-qc,
#layer-modals,
#layer-toasts {
  position: absolute;
  inset: 0;
  pointer-events: none; /* layers are click-through; their interactive children opt back in */
}

#hud > *,
#layer-sheets > *,
#layer-qc > *,
#layer-modals > *,
#layer-toasts > * {
  pointer-events: auto;
}

#hud { z-index: var(--z-hud); }
#fx { position: absolute; inset: 0; width: 100%; height: 100%; z-index: var(--z-fx); pointer-events: none; }
#layer-sheets { z-index: var(--z-sheets); }
#layer-qc { z-index: var(--z-qc); }
#layer-modals { z-index: var(--z-modals); }
#layer-toasts { z-index: var(--z-toasts); }

/* Inputs where text selection must work again */
textarea,
input,
.selectable {
  user-select: text;
  -webkit-user-select: text;
}

/* ---------------------------------------------------------------- safe-area helpers */
.safe-top { padding-top: max(8px, var(--safe-top)); }
.safe-bottom { padding-bottom: max(6px, var(--safe-bottom)); }
.safe-x { padding-left: max(8px, var(--safe-left)); padding-right: max(8px, var(--safe-right)); }

/* ---------------------------------------------------------------- loading / title screen
   Background/text mirror the site's --kr-canvas (#0a0a0b) / --kr-fg (#f4f4f5) from globals.css. */
#loading {
  position: absolute;
  inset: 0;
  z-index: var(--z-loading);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  justify-content: safe center; /* a panel taller than the screen (landscape phone) starts at the top and scrolls */
  gap: var(--sp-3);
  overflow-x: hidden;
  overflow-y: auto;
  padding: max(24px, var(--safe-top)) max(20px, var(--safe-right)) max(24px, var(--safe-bottom)) max(20px, var(--safe-left));
  background: var(--kr-canvas);
  color: var(--kr-fg);
  text-align: center;
  transition: opacity var(--mo-slow) var(--mo-out), visibility var(--mo-slow);
}

#loading.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Game icon (promo/icon.svg, 72 px; static in index.html so it paints before the modules load). */
.loading__icon {
  display: block;
  width: 72px;
  height: 72px;
  border-radius: var(--r-md);
}

#loading.loading--error .loading__icon,
#loading.loading--pick .loading__icon,
#loading.loading--pick .wordmark {
  display: none; /* the corrupt-save panel / the cloud save picker need the room on small screens */
}

@media (max-height: 480px) {
  /* landscape phone: the whole picker on one screen */
  #loading.loading--pick {
    gap: var(--sp-2);
    padding-top: max(12px, var(--safe-top));
    padding-bottom: max(12px, var(--safe-bottom));
  }
  #loading.loading--pick .loading__sub { display: none; }
}

.wordmark {
  font-weight: var(--fw-label);
  font-size: var(--fs-16);
  color: var(--kr-fg);
  opacity: 0.9;
  animation: kr-pulse 1.6s ease-in-out infinite;
}

#loading.is-ready .wordmark,
#loading.loading--error .wordmark {
  animation: none;
}

/* the title stamp: ink-on-paper rubber stamp, rotated like every stamp in the game */
.loading__stamp {
  display: inline-block;
  margin-top: var(--sp-1);
  padding: var(--sp-2) var(--sp-4);
  border: 3px solid var(--kr-fg);
  border-radius: var(--r-md);
  font-size: var(--fs-32);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
  transform: rotate(-4deg);
  color: var(--kr-fg);
}

.loading__sub {
  max-width: 28ch;
  font-weight: var(--fw-body);
  font-size: var(--fs-16);
  color: var(--text-2);
}

#loading .loading__play {
  margin-top: var(--sp-4);
  min-width: 200px;
}

/* Login gate (BACKLOG 14): message + Discord / retry button under the stamp. */
.loading__gate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
}

.loading__gate a.btn {
  text-decoration: none;
}

/* Guest mode (BACKLOG 31): secondary „Graj jako gość” button + the local-save-only warning under the Discord button. */
#loading .loading__guest {
  margin-top: var(--sp-2);
  min-width: 200px;
}

.loading__note {
  max-width: 34ch;
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-14);
  line-height: var(--lh-body);
  color: var(--text-2);
}

/* Cloud save picker (BACKLOG 20, js/core/cloud-save.js): „Który zapis zostawić?” — in a .loading__panel at boot,
   in a .modal--cloud in-game. Two tappable cards: where · cash · level · date · „Zostaw ten”. */
.modal.modal--cloud { width: min(100%, 440px); }

.cloud-pick {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  text-align: left;
}

.cloud-pick__title {
  font-size: var(--fs-20);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
}

.cloud-pick__body {
  font-size: var(--fs-14);
  font-weight: var(--fw-body);
  color: var(--text-2);
}

.cloud-pick__opts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-2);
}

/* a pressable card: raised fill + the small hard edge; the newer save carries the static „ready” ring */
.cloud-pick__opt {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-1);
  min-width: 0;
  padding: var(--sp-3);
  border: 0;
  border-radius: var(--r-md);
  background: var(--raised);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: var(--cur-hand);
  box-shadow: var(--sh-edge-sm);
  transition: transform var(--mo-fast) var(--mo-out), box-shadow var(--mo-fast) var(--mo-out), background-color var(--mo-fast) var(--mo-out);
}

.cloud-pick__opt.is-newer { box-shadow: var(--ring-go), var(--sh-edge-sm); }
.cloud-pick__opt:hover,
.cloud-pick__opt:focus-visible { background: var(--raised-2); }
.cloud-pick__opt:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--edge-c); }
.cloud-pick__opt.is-newer:active { box-shadow: var(--ring-go), 0 1px 0 var(--edge-c); }

.cloud-pick__where {
  font-size: var(--fs-12);
  font-weight: var(--fw-label);
  color: var(--text-dim);
}

/* „Nowszy”: a go tag on the card's top edge */
.cloud-pick__newer {
  position: absolute;
  top: -10px;
  right: var(--sp-2);
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-xs);
  background: var(--green);
  color: #ffffff;
  font-size: var(--fs-12);
  font-weight: var(--fw-label);
  line-height: 1;
}

.cloud-pick__cash {
  max-width: 100%;
  overflow-wrap: anywhere;
  font-size: var(--fs-20);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
  color: var(--green);
}

.cloud-pick__meta {
  font-size: var(--fs-12);
  font-weight: var(--fw-body);
  color: var(--text-2);
}

.cloud-pick__meta:nth-last-child(2) { padding-bottom: var(--sp-1); }

.cloud-pick__keep {
  align-self: stretch;
  margin-top: auto; /* both „Zostaw ten” buttons on one line, whatever wraps above them */
  padding: var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--raised-2);
  font-size: var(--fs-14);
  font-weight: var(--fw-label);
  text-align: center;
}

.cloud-pick__opt.is-newer .cloud-pick__keep { background: var(--green); color: #ffffff; }

@media (max-width: 340px) {
  .cloud-pick__opts { grid-template-columns: 1fr; }
}

/* id-qualified (playtest): .btn {position: relative}, declared later, pulled the chip into the column between the
   subtitle and „Graj” */
#loading > .loading__chip {
  position: absolute;
  top: max(12px, var(--safe-top));
  left: max(12px, var(--safe-left));
}

/* PL / EN before the game starts (js/ui/lang-pick.js, owner 2026-10-16): a two-segment track top-right of the title
   (the back chip is top-left), the same look as the Ustawienia segmented control. Only on the „Graj” title and the
   login gate — never over the loading pulse, a corrupt-save panel or the cloud picker. 44 px tap targets on phones. */
#loading > .lang-pick {
  position: absolute;
  top: max(12px, var(--safe-top));
  right: max(12px, var(--safe-right));
  display: none;
  gap: 2px;
  padding: 3px;
  border-radius: var(--r-md);
  background: var(--raised);
  box-shadow: var(--sh-edge-sm);
}

#loading.is-ready > .lang-pick,
#loading.loading--gate > .lang-pick { display: flex; }
#loading.loading--error > .lang-pick,
#loading.loading--pick > .lang-pick { display: none; }

.lang-pick__btn {
  min-width: 48px;
  min-height: 38px;
  padding: 0 var(--sp-2);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-2);
  font: inherit;
  font-size: var(--fs-14);
  font-weight: var(--fw-label);
  cursor: var(--cur-hand);
  transition: background-color var(--mo-fast) var(--mo-out), color var(--mo-fast) var(--mo-out);
}

.lang-pick__btn:hover { color: var(--text); }
.lang-pick__btn.is-on { background: var(--raised-2); color: var(--text); cursor: default; }
.lang-pick__btn:focus-visible { outline: 2px solid var(--yellow); outline-offset: -2px; }

@media (pointer: coarse) {
  .lang-pick__btn { min-height: 44px; min-width: 52px; }
}

/* the login gate: the site name once (the back chip), not the wordmark too */
#loading.loading--gate .wordmark { display: none; }

.loading__panel {
  width: min(100%, 420px);
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  text-align: left;
}

.loading__title {
  font-size: var(--fs-20);
  font-weight: var(--fw-num);
  line-height: var(--lh-tight);
}

/* the raw save code: the only monospace text in the game */
.loading__raw {
  width: 100%;
  min-height: 120px;
  padding: var(--sp-2);
  border: 2px solid var(--line);
  border-radius: var(--r-md);
  background: var(--bg);
  color: var(--text-2);
  font: 12px/1.4 ui-monospace, "Cascadia Mono", Consolas, monospace;
  resize: vertical;
  word-break: break-all;
}

.loading__row {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

.loading__error {
  color: var(--red);
  font-weight: var(--fw-label);
}

@keyframes kr-pulse {
  0%, 100% { opacity: 0.9; }
  50% { opacity: 0.45; }
}

/* ---------------------------------------------------------------- base components (spec §4) */

/* panel: the one opaque surface with THE border; no shadow (modals add --sh-modal in css/sheets.css) */
.panel {
  background: var(--panel);
  border: 2px solid var(--line);
  border-radius: var(--r-md);
  color: var(--text);
}

.num {
  font-weight: var(--fw-num);
  font-variant-numeric: tabular-nums;
}

/* Buttons: solid fill + a hard bottom lip (lit from the top: a hard light line on the top edge). Chunky pass
   (2026-10-20): the lip is 6 px (4 px on small buttons) and a press sinks the button into it — down by the lip minus
   2 px, the lip 2 px tall while held — then it springs back (css/motion.css). Labels are 900 with a hard lip of the
   button's edge colour. The font lives in custom properties, so a variant only swaps --btn-fs / --btn-fw; the
   second selector outranks the sheet / modal button reset in css/sheets.css (`font: inherit`, 0,1,1). */
.btn {
  --btn-bg: var(--green);
  --btn-edge: var(--green-dark);
  --btn-fg: #ffffff;
  --btn-fs: var(--fs-16);
  --btn-fw: var(--fw-num);
  --btn-hi: var(--btn-shine);
  --btn-ring: 0 0 0 2px var(--btn-edge);
  --btn-tx: 0 2px 0 var(--btn-edge), 0 -1px 0 var(--btn-edge), 1px 0 0 var(--btn-edge), -1px 0 0 var(--btn-edge);
  --e: var(--btn-lip);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-4);
  border: 0;
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  text-decoration: none;
  text-shadow: var(--btn-tx);
  box-shadow: var(--btn-ring), var(--btn-hi), 0 var(--e) 0 var(--btn-edge);
  transform: translateY(0);
  transition: transform var(--mo-fast) var(--mo-out), box-shadow var(--mo-fast) var(--mo-out);
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

.btn,
:is(.sheet, .modal, .tut) .btn {
  font: var(--btn-fw) var(--btn-fs)/var(--lh-tight) var(--font);
}

/* pressed: sinks into its lip (never less than 1 px — a lip-less text button just nudges) */
.btn:active,
.btn.is-pressed {
  transform: translateY(max(1px, calc(var(--e) - var(--btn-lip-pressed))));
  box-shadow: var(--btn-ring), var(--btn-hi), 0 min(var(--e), var(--btn-lip-pressed)) 0 var(--btn-edge);
}

.btn:disabled:active,
.btn[aria-disabled="true"]:active {
  transform: none;
  box-shadow: var(--btn-ring), var(--btn-hi), 0 var(--e) 0 var(--btn-edge);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  --btn-bg: var(--panel-raised);
  --btn-edge: var(--edge-c);
  --btn-fg: var(--text-dim);
  --btn-hi: var(--btn-shine-off);
  --btn-tx: none;
  cursor: default;
}

/* icons inside buttons: 20 px (16 px in a small button) */
.btn > svg.icon { flex: none; width: 20px; height: 20px; }
.btn--sm > svg.icon { width: 16px; height: 16px; }

.btn--primary { --btn-bg: var(--green); --btn-edge: var(--green-dark); --btn-fg: #ffffff; }
.btn--yellow { --btn-bg: var(--green); --btn-edge: var(--green-dark); --btn-fg: #ffffff; } /* deprecated: = primary */
.btn--ghost,
.btn--secondary { --btn-bg: var(--raised-2); --btn-edge: var(--edge-c); --btn-fg: var(--text); --btn-hi: var(--btn-shine-soft); --btn-tx: var(--tx-lip); }
.btn--gem { --btn-bg: var(--gem); --btn-edge: var(--gem-dark); --btn-fg: #ffffff; }
.btn--danger { --btn-bg: var(--red); --btn-edge: var(--red-dark); --btn-fg: #ffffff; }
.btn--text { --btn-bg: transparent; --btn-edge: transparent; --btn-fg: var(--text-2); --btn-hi: var(--btn-shine-off); --btn-ring: 0 0 0 0 transparent; --btn-tx: none; --e: 0px; }

.btn--sm { --e: var(--btn-lip-sm); --btn-fs: var(--fs-14); min-height: 36px; padding: 0 var(--sp-3); border-radius: var(--r-sm); }
.btn--lg,
.btn--xl { --btn-fs: var(--fs-20); --btn-fw: var(--fw-num); min-height: 52px; padding: 0 20px; }
.btn--icon { width: 44px; padding: 0; }
.btn--sm.btn--icon { width: 36px; }
.btn--block { display: flex; width: 100%; }

/* pill: a small HUD-style block (the HUD's own blocks live in css/hud.css) */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 0 var(--sp-3) 0 6px;
  border: 2px solid var(--line);
  border-radius: var(--r-md);
  background: var(--panel);
  color: var(--text);
  font-size: var(--fs-14);
  font-weight: var(--fw-label);
  line-height: var(--lh-tight);
  box-shadow: var(--sh-edge-sm);
}

/* chip: 32 px, raised-2, grey 14/800; a chip that is a button or a link gets the small edge */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--raised-2);
  color: var(--text-2);
  font-size: var(--fs-14);
  font-weight: var(--fw-label);
  line-height: var(--lh-tight);
  text-decoration: none;
}

:is(.sheet, .modal, .tut) button.chip { font: var(--fw-label) var(--fs-14)/var(--lh-tight) var(--font); }
button.chip,
a.chip { box-shadow: var(--sh-edge-sm); }

a.chip:hover {
  color: var(--text);
}

/* tag: 20 px label, 12/800, no caps, no border. Six variants; any other meaning comes from a 12 px leading glyph. */
.tag {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: var(--sp-1);
  height: 20px;
  padding: 0 6px;
  border: 0;
  border-radius: var(--r-xs);
  background: var(--raised-2);
  color: var(--text-2);
  font-size: var(--fs-12);
  font-weight: var(--fw-label);
  line-height: 1;
  white-space: nowrap;
}

.tag > svg { flex: none; width: 12px; height: 12px; }
.tag--go { background: var(--green); color: #ffffff; }
.tag--best { background: var(--yellow); color: var(--ink); }
.tag--bad { background: var(--red); color: #ffffff; }
.tag--gem { background: var(--gem); color: #ffffff; }
.tag--dim { background: transparent; color: var(--text-dim); }

/* role colours for a glyph (only on role glyphs: level / rank, ready, gems, problems, fans) */
.glyph--yellow { color: var(--yellow); }
.glyph--green { color: var(--green); }
.glyph--gem { color: var(--gem-light); }
.glyph--red { color: var(--red); }
.glyph--fans { color: var(--fans); }

/* count badge: red, white 12/900, a panel ring that separates it from what it sits on */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--red);
  color: #ffffff;
  font-size: var(--fs-12);
  font-weight: var(--fw-num);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  box-shadow: var(--ring-new);
}

/* progress: a recessed track, a flat fill (green = progress, yellow = XP, cardboard = packing) */
.progress {
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--bg);
  overflow: hidden;
}

.progress > span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--green);
}

.progress--xp > span { background: var(--yellow); }
.progress--pack > span { background: var(--cardboard-1); }
.progress--hud { height: 6px; }

/* Visually hidden, still read by screen readers (e.g. "(otwiera się w nowej karcie)"). */
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* ---------------------------------------------------------------- motion */
@media (prefers-reduced-motion: reduce) {
  /* the in-game "Reduce motion: off" (data-reduce-motion="false", js/ui/motion.js) overrides the OS; "on" → css/motion.css */
  :root:not([data-reduce-motion="false"]) *,
  :root:not([data-reduce-motion="false"]) *::before,
  :root:not([data-reduce-motion="false"]) *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------------- phone held sideways (playtest)
   A touch phone in landscape got a 480 × 390 letterbox (Rozwój without a buy card, a ~120 px QC photo): one calm card
   over everything asks for portrait (js/main.js mountRotateHint); tablets / desktops never match. */
.rotate-hint { display: none; }
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  .rotate-hint {
    position: fixed;
    inset: 0;
    z-index: var(--z-rotate);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 16px;
    background: var(--bg);
    color: var(--text);
    text-align: center;
  }
}
.rotate-hint__phone {
  width: 36px;
  height: 60px;
  margin-bottom: 8px;
  border: 3px solid var(--text-2);
  border-radius: var(--r-sm);
  animation: rotate-hint var(--mo-slow) var(--mo-out) both;
}
.rotate-hint__t { font-size: var(--fs-20); font-weight: 900; }
.rotate-hint__s { font-size: var(--fs-14); font-weight: 700; color: var(--text-2); }
@keyframes rotate-hint {
  from { transform: rotate(-90deg); }
  to { transform: rotate(0deg); }
}

/* ---------------------------------------------------------------- sign finish (owner 2026-10-22)
   The station signs' finish as one utility: .sign-finish + a colour variant (sets --sf-bg / --sf-edge). Fill = the
   colour under --sign-gloss, THE 2 px ring in the edge colour, the lit top strip, the dark bottom bevel, the 4 px lip
   and a hard drop under it; white 900 text outlined in the edge colour; an optional round icon badge (.sign-finish__ico)
   on the left. The HUD blocks, buttons, tabs, cards and sheet heads use the same tokens in their own files. */
.sign-finish {
  --sf-bg: var(--raised-2);
  --sf-edge: var(--edge-c);
  background-color: var(--sf-bg);
  background-image: var(--sign-gloss);
  color: #ffffff;
  text-shadow: 0 2px 0 var(--sf-edge), 0 -1px 0 var(--sf-edge), 1px 0 0 var(--sf-edge), -1px 0 0 var(--sf-edge);
  box-shadow: 0 0 0 2px var(--sf-edge), var(--sign-hi), var(--sign-rim), 0 var(--edge) 0 var(--sf-edge), var(--sign-drop);
}
.sign-finish--green { --sf-bg: var(--green); --sf-edge: var(--green-dark); }
.sign-finish--blue { --sf-bg: var(--blue); --sf-edge: var(--blue-dark); }
.sign-finish--orange { --sf-bg: var(--orange); --sf-edge: var(--orange-dark); }
.sign-finish--purple,
.sign-finish--gem { --sf-bg: var(--gem); --sf-edge: var(--gem-dark); }
.sign-finish--pink { --sf-bg: var(--pink); --sf-edge: var(--pink-dark); }
.sign-finish--yellow { --sf-bg: var(--yellow); --sf-edge: var(--yellow-dark); }
.sign-finish--red { --sf-bg: var(--red); --sf-edge: var(--red-dark); }
.sign-finish--navy { --sf-bg: var(--raised-2); --sf-edge: var(--edge-c); background-image: var(--sign-gloss-soft); box-shadow: 0 0 0 2px var(--line), var(--sign-hi-soft), var(--sign-rim), 0 var(--edge) 0 var(--edge-c); }
.sign-finish__ico {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #ffffff;
  color: var(--sf-edge);
  box-shadow: 0 0 0 2px var(--sf-edge), inset 0 -2px 0 rgba(0, 0, 0, 0.15);
}

/* buttons take the finish: the gloss over --btn-bg and the bottom bevel (text / disabled buttons stay flat) */
.btn { background-color: var(--btn-bg); background-image: var(--btn-gloss, var(--sign-gloss)); box-shadow: var(--btn-ring), var(--btn-hi), var(--btn-rim, var(--sign-rim)), 0 var(--e) 0 var(--btn-edge); }
.btn:active,
.btn.is-pressed { box-shadow: var(--btn-ring), var(--btn-hi), var(--btn-rim, var(--sign-rim)), 0 min(var(--e), var(--btn-lip-pressed)) 0 var(--btn-edge); }
.btn--ghost,
.btn--secondary { --btn-gloss: var(--sign-gloss-soft); }
.btn--text { --btn-gloss: none; --btn-rim: inset 0 0 0 transparent; }
.btn:disabled,
.btn[aria-disabled=true] { --btn-gloss: none; --btn-rim: inset 0 0 0 transparent; }
.btn:disabled:active,
.btn[aria-disabled=true]:active { box-shadow: var(--btn-ring), var(--btn-hi), var(--btn-rim, var(--sign-rim)), 0 var(--e) 0 var(--btn-edge); }
.pill { background-image: var(--sign-gloss-soft); box-shadow: var(--sign-hi-soft), var(--sign-rim); }

