/* ============================================================================
   Führerschein Hero base: tokens, ground, type, chrome
   ----------------------------------------------------------------------------
   THE WORLD: a night expedition along German roads, run against a legal clock.
   The ground is asphalt at night; the accent is signage gold, the yellow of a
   Vorfahrtstraße priority sign and an Ortstafel. Panels are sharp-cornered
   roadbook plates pinned onto that dark ground, lit from above.

   Every colour pair below carries its MEASURED WCAG contrast ratio. Dark themes
   are forgiving, so there is no excuse for missing 4.5:1 on body text.
   ========================================================================== */

/* ============================================================================
   Fonts, served from this origin and nowhere else
   ----------------------------------------------------------------------------
   These faces used to be fetched from fonts.googleapis.com. That request hands
   every visitor's IP address to Google before a single glyph is drawn, which
   LG München I held unlawful without consent (3 O 17493/20, January 2022), and
   it flatly contradicts the promise this app makes that nothing is uploaded
   anywhere. Self-hosting also deletes an origin whose compromise could have
   served hostile CSS.

   Only the faces the stylesheets actually select are here:
     Cinzel  700           headings, the wordmark, .eyebrow, .btn-amber
     DM Sans 400 to 700    body text, and every font-weight the views set inline
     DM Sans italic 400    the <em> that md() in js/ui.js builds from *text*
   DM Sans ships as one variable file per subset, so a single face spans 400 to
   700 (measured: 400, 500 and 700 render at three different widths from that
   one file). Cinzel 400 and 900 are deliberately absent because no rule in this
   project selects them, and inline font-weight:800/900 resolves to 700 under
   the CSS font-matching rules, exactly as it did before.

   The subset split is the one Google publishes, and unicode-range keeps it
   honest. Every German character this app renders (ä ö ü Ä Ö Ü ß) sits in the
   latin file, verified glyph by glyph rather than assumed, so latin-ext is only
   fetched when a visitor types a name that needs it.

   font-display: swap paints text immediately in the fallback stack, so a slow
   font can never blank an exam question. The stacks in --font and --font-display
   below stay intact and remain the real fallback.
   ========================================================================== */
/* latin-ext */
@font-face {
  font-family: 'Cinzel';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../assets/fonts/cinzel-700-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;
}
/* latin */
@font-face {
  font-family: 'Cinzel';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../assets/fonts/cinzel-700-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;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../assets/fonts/dm-sans-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;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../assets/fonts/dm-sans-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;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/dm-sans-italic-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;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/dm-sans-italic-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;
}

:root {
  /* --- ground & surfaces --- */
  --bg: #14161f;          /* deep asphalt */
  --bg-deep: #0e1017;     /* vignette floor */
  --surface: #1d2130;     /* roadbook plate */
  --surface-2: #232838;   /* raised plate / inset row */
  --surface-3: #2a3043;   /* hover plate */
  --plaque: #2a1f0c;      /* signage plaque fill (behind gold borders) */
  --line: #333a4d;        /* decorative hairline divider only */
  --line-strong: #46506a; /* structural edge on non-interactive plates */
  /* WCAG 1.4.11 wants 3:1 for a control's boundary when the boundary is what
     identifies the control. Measured on every surface we use:
       --line-strong  1.63–2.25:1  FAILS
       --line-control 3.15–4.34:1  PASSES
     So every focusable control's border uses --line-control. */
  --line-control: #6e7e90;

  /* --- text ramp (ratios vs --surface #1d2130) --- */
  --ink: #e8ecf1;         /* 13.49:1, primary text */
  --dim: #b6c2d2;         /*  8.87:1, secondary text */
  --muted: #9aa7b8;       /*  6.55:1, captions and meta */
  --muted-2: #8b97a8;     /*  5.40:1, dimmest usable */

  /* --- signage gold: the one accent that matters --- */
  --gold: #e0a82e;        /*  7.47:1 as text on --surface */
  --gold-bright: #f5d98a; /* 11.56:1, highlight and hover */
  --gold-dark: #b8831c;   /* borders, pressed */
  --gold-deep: #7a5610;   /* button underside */
  --gold-glow: rgba(224, 168, 46, .28);

  /* --- semantics, tuned for a dark ground --- */
  --primary: #4ade80;       /*  9.18:1 as text on --surface */
  --primary-dark: #1f7a4d;  /* button fill; white label 5.32:1 */
  --primary-deep: #145435;  /* button underside */
  --primary-soft: #17301f;  /* soft panel fill */
  --blue: #7cb0ff;          /*  7.25:1 as text */
  --blue-dark: #2f5fb8;     /* button fill; white label 4.82:1 */
  --blue-soft: #182338;
  --amber: var(--gold);
  --amber-dark: var(--gold-dark);
  --amber-text: var(--gold);
  --amber-soft: #2a2413;
  --red: #ff7b7e;           /*  6.39:1 as text */
  --red-btn: #c0393d;       /* white label 5.39:1 */
  --red-dark: #a32f33;
  --red-deep: #7d2427;
  --red-soft: #33191b;
  --purple: #b9a2f0;
  --purple-soft: #241f33;

  /* --- shape: sharp roadbook plates, not soft pillows --- */
  --r-lg: 4px;
  --r-md: 3px;
  --r-sm: 2px;
  --r-pill: 999px;

  /* --- depth: dark themes read depth from light, not from drop shadows --- */
  --shadow-card: 0 1px 0 rgba(255, 255, 255, .04) inset, 0 8px 24px -14px rgba(0, 0, 0, .8);
  --shadow-pop: 0 14px 40px -16px rgba(0, 0, 0, .9);
  --glow-gold: 0 0 0 1px var(--gold-dark), 0 0 22px -4px var(--gold-glow);

  /* --- type --- */
  --font: 'DM Sans', -apple-system, 'Segoe UI', system-ui, sans-serif;
  --font-display: 'Cinzel', 'Iowan Old Style', Georgia, 'Times New Roman', serif;

  --header-h: 64px;
  --tabs-h: 56px;

  /* Fine asphalt grain, generated inline so it works with no network at all.
     Low frequency would read as clouds and high as TV static; 0.9 with 3
     octaves at 3.5% opacity is just enough to kill the flatness. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='0.035'/%3E%3C/svg%3E");
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; background: var(--bg); }
body {
  margin: 0;
  font-family: var(--font);
  color: var(--ink);
  /* Stops Chrome Android reloading the page when an overscroll is misread as
     pull-to-refresh, which would destroy an exam in progress. */
  overscroll-behavior-y: contain;
  line-height: 1.6;
  font-size: 16px;
  /* German compound nouns (Fahrerlaubnisbehörde, Kartenführerschein…) are long
     enough to overflow a phone on their own. */
  overflow-wrap: break-word;
  /* The ground, in layers: a cool highlight where the "headlights" fall, a warm
     one low and right, then the asphalt base. */
  background:
    radial-gradient(1100px 520px at 50% -12%, rgba(124, 176, 255, .10), transparent 70%),
    radial-gradient(760px 420px at 88% 8%, rgba(224, 168, 46, .07), transparent 68%),
    linear-gradient(180deg, #171a25 0%, var(--bg) 42%, var(--bg-deep) 100%);
  background-attachment: fixed;
}
/* Grain + vignette as one fixed overlay: cheap, never scrolls, never intercepts
   a tap. */
body::before {
  content: '';
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    var(--grain),
    radial-gradient(circle at 50% 42%, transparent 42%, rgba(0, 0, 0, .55) 100%);
}
body > * { position: relative; z-index: 1; }

img, svg { max-width: 100%; }
a { color: var(--gold); text-decoration: none; }
a:hover { text-decoration: underline; text-decoration-color: var(--gold-dark); }

/* Display face is used SPARINGLY: headings and primary calls to action only.
   Everything else is DM Sans, which is what keeps the display face special. */
h1, h2, h3, h4 {
  line-height: 1.18; margin: 0 0 .4em;
  font-family: var(--font-display);
  font-weight: 700; letter-spacing: .01em;
}
h1 { font-size: 1.6rem; letter-spacing: .02em; }
h2 { font-size: 1.22rem; }
h3 { font-size: 1.02rem; letter-spacing: .015em; }
h4 { font-size: .95rem; }
p { margin: .4em 0 .9em; }
small { color: var(--muted); }
b, strong { font-weight: 700; color: var(--ink); }
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .92em;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 2px; padding: 1px 5px; }
button { font-family: inherit; }
::selection { background: var(--gold-dark); color: #fff; }

/* ---------- focus ---------- */
:focus-visible {
  outline: 3px solid var(--gold);
  outline-offset: 2px;
  border-radius: 2px;
}
.card:focus-visible, .btn:focus-visible, .option:focus-visible { outline-offset: 3px; border-radius: inherit; }
.tabs a:focus-visible { outline-offset: -2px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .card.clickable:hover, .card.clickable:active { transform: none; }
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The German flag as an inlaid strip at the very top edge of the world. */
.flag-stripe {
  height: 3px;
  background: linear-gradient(90deg, #0b0b0b 0 33.3%, #b31217 33.3% 66.6%, var(--gold) 66.6% 100%);
  box-shadow: 0 1px 12px rgba(0, 0, 0, .8);
}

/* ---------- header ---------- */
.app-header {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  height: var(--header-h);
  padding: 0 clamp(14px, 3vw, 28px);
  background: linear-gradient(180deg, rgba(23, 26, 37, .96), rgba(20, 22, 31, .92));
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .03), 0 10px 30px -20px #000;
}
.brand { display: flex; align-items: center; gap: 10px; color: var(--ink); }
.brand:hover { text-decoration: none; }
.brand-emoji { font-size: 1.55rem; filter: drop-shadow(0 0 10px rgba(224, 168, 46, .35)); }
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-text strong {
  font-family: var(--font-display); font-weight: 700; font-size: 1rem;
  letter-spacing: .06em; color: var(--gold-bright);
}
.brand-text small { font-size: .68rem; color: var(--muted-2); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; }
.header-stats { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }

.stat-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: 4px 12px;
  font-weight: 700; font-size: .8rem; white-space: nowrap; color: var(--dim);
}
.stat-chip .emoji { font-size: .92rem; }
.stat-chip.level { background: var(--purple-soft); border-color: #3b3358; color: var(--purple); }
.stat-chip.xp { background: var(--amber-soft); border-color: #4a3c14; color: var(--gold); }
.stat-chip.streak { background: var(--red-soft); border-color: #4d2528; color: var(--red); }
.chip-unit { opacity: .7; font-weight: 500; }
.xp-mini-bar { width: 52px; height: 5px; border-radius: 3px; background: #3a3116; overflow: hidden; }
.xp-mini-bar i { display: block; height: 100%; background: linear-gradient(90deg, var(--gold-dark), var(--gold-bright)); }

/* ---------- tabs ---------- */
.tabs {
  position: sticky; top: var(--header-h); z-index: 55;
  display: flex; gap: 2px; justify-content: center;
  padding: 6px clamp(8px, 2vw, 20px) 0;
  background: linear-gradient(180deg, rgba(20, 22, 31, .95), rgba(20, 22, 31, .78));
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
  overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs a {
  display: inline-flex; align-items: center; gap: 7px; position: relative;
  padding: 9px 16px 11px; border-radius: var(--r-sm) var(--r-sm) 0 0;
  color: var(--muted); font-weight: 500; font-size: .88rem; white-space: nowrap;
  letter-spacing: .02em;
}
/* Labels are <em> for semantics but must not render italic, because italic
   below 10px is the worst legibility case there is. */
.tabs a em { font-style: normal; }
.tabs a:hover { text-decoration: none; background: rgba(255, 255, 255, .04); color: var(--ink); }
/* The active tab is marked by a gold caret and a gold underline, never by
   colour alone (WCAG 1.4.1). */
.tabs a.active { color: var(--gold-bright); font-weight: 700; background: rgba(224, 168, 46, .07); }
.tabs a.active::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--gold);
}
.tabs a.active::after {
  content: ''; position: absolute; left: 50%; bottom: 0; transform: translate(-50%, 50%) rotate(45deg);
  width: 7px; height: 7px; background: var(--gold);
}
.tabs a span { font-size: 1.02rem; }

/* ---------- main view ---------- */
.view {
  max-width: 980px;
  margin: 0 auto;
  padding: clamp(16px, 3vw, 30px) clamp(14px, 3vw, 28px) 96px;
  outline: none;
}

/* ---------- mobile: bottom tab bar ---------- */
@media (max-width: 720px) {
  .tabs {
    position: fixed; top: auto; bottom: 0; left: 0; right: 0;
    border-top: 1px solid var(--line); border-bottom: none;
    background: linear-gradient(180deg, rgba(24, 27, 38, .97), rgba(15, 17, 24, .99));
    justify-content: space-around; gap: 0;
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    box-shadow: 0 -10px 30px -16px #000;
  }
  .tabs a {
    flex-direction: column; gap: 2px; padding: 5px 6px 6px;
    font-size: .66rem; min-height: 48px; justify-content: center;
    border-radius: var(--r-sm);
  }
  .tabs a span { font-size: 1.2rem; }
  .tabs a.active { background: rgba(224, 168, 46, .09); }
  /* On the bottom bar the marker belongs on the top edge. */
  .tabs a.active::before { top: 0; bottom: auto; left: 22%; right: 22%; height: 2px; }
  .tabs a.active::after { top: 0; bottom: auto; transform: translate(-50%, -50%) rotate(45deg); }
  .view { padding-bottom: 120px; }

  /* The header is a fixed 64px box, so its content must stay on ONE row.
     Otherwise the brand wraps, the chips wrap, and both spill outside it. */
  .app-header { padding: 0 12px; gap: 8px; }
  .brand-emoji { font-size: 1.4rem; }
  .brand-text small { display: none; }
  .brand-text strong { font-size: .9rem; white-space: nowrap; }
  .header-stats { flex-wrap: nowrap; gap: 6px; }
  .header-stats .xp-mini-bar { display: none; }
  .stat-chip { padding: 4px 9px; font-size: .74rem; gap: 4px; }
}
/* Below ~430px three stat chips and the wordmark cannot coexist; the car emoji
   keeps the home affordance and the app name lives in the tab title. */
@media (max-width: 430px) {
  .brand-text { display: none; }
}

/* ---------- utilities ---------- */
.grid { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.center { text-align: center; }
.muted { color: var(--muted); }
.mt0 { margin-top: 0; } .mb0 { margin-bottom: 0; }
.hide { display: none !important; }
.big-emoji { font-size: 2.8rem; line-height: 1; filter: drop-shadow(0 4px 14px rgba(0, 0, 0, .6)); }
/* German terms are gold rather than italic grey, because they are part of the
   world, not an aside. Kept in DM Sans: the display face is inscriptional and
   would make every compound noun wrap. */

.de-term {
  color: var(--gold); font-weight: 500; font-style: normal;
  font-size: .82rem; letter-spacing: .01em;
}
/* A small tracked label, used above panels the way a route sheet labels a leg. */
.eyebrow {
  font-family: var(--font-display); font-size: .68rem; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase; color: var(--muted);
}

#confetti { position: fixed; inset: 0; pointer-events: none; z-index: 200; }
