/* Klopjacht · Nachtjacht · Smokkeljacht — huisstijl v1.12.0 (designoverhaul).
   Bron: het designpakket (tokens.css/SCHERMEN.md/PROMPT.md), zie docs/DESIGN-OVERHAUL.md.

   Wat er veranderde t.o.v. 1.11.x: de drie merken zijn één donkere familie geworden. Klopjacht
   was licht (beige/oranje) met een dark-mode-variant; dat is vervallen — color-scheme staat op
   alle drie de merken vast op dark, want dit spel wordt buiten en 's avonds gespeeld en een
   lichte pagina verblindt daar. De zachte, ronde "webapp-house-style" (18px radius, zachte
   schaduwen) maakt plaats voor een hoekige HUD-taal: schuine knoppen (--skew), panelen van 4px
   met een 1px rand, condensed italic koppen, labels in kleinkapitaal-stijl en HUD-cijfers.

   Tokennamen zijn BEWUST ongewijzigd gebleven (--accent, --bg, --surface, --line, --radius, …).
   De ~3.100 regels componentcode hieronder lezen allemaal deze namen; door alleen de wáárden om
   te zetten verandert de hele app mee zonder dat elke component aangeraakt hoeft te worden. De
   pakketnamen staan per token in commentaar, zodat tokens.css en dit bestand naast elkaar te
   leggen zijn.

   Lettertypen: zelfgehost in public/fonts/ (latin-subset, SIL OFL — zie public/fonts/OFL.txt).
   Het pakket schrijft een @import van fonts.googleapis.com voor; dat kan hier niet, de CSP is
   style-src 'self' (src/lib/adsense.ts' buildCsp). Saira Condensed heeft geen echte italic —
   de schuinstelling in de mockups is gesynthetiseerd, en dat laten we zo (font-synthesis: style
   staat standaard aan). */
@font-face {
  font-family: "Saira Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/saira-condensed-700.woff2") format("woff2");
}
@font-face {
  font-family: "Saira Condensed";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("/fonts/saira-condensed-800.woff2") format("woff2");
}
@font-face {
  font-family: "Saira Condensed";
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url("/fonts/saira-condensed-900.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/barlow-condensed-700.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/barlow-400.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/barlow-600.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/barlow-700.woff2") format("woff2");
}

/* ---------------------------------------------------------------------------
   :root — het gedeelde deel (typografie, ritme, vorm, motion) PLUS het
   Klopjacht-palet. De twee andere merken overschrijven verderop alleen kleur en
   raakvlakhoogte; alles wat hier staat geldt overal.
   --------------------------------------------------------------------------- */
:root {
  color-scheme: dark;

  /* ---- typografie ---- */
  --font-display: "Saira Condensed", "Arial Narrow", system-ui, sans-serif;
  --font-label: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
  --font-body: "Barlow", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
  /* Blijft bestaan omdat de componentregels verderop 'm al lazen (v1.11.0 zette 'm op 400 voor
     Pirata One). Nu overal 800 — de terugval in die var()-aanroepen is dezelfde waarde. */
  --font-display-weight: 800;
  --font-display-tracking: 0.01em;
  --size-hud: 30px;
  --size-body: 16px;
  --size-label: 12px;
  --tracking-label: 0.16em;
  --tracking-badge: 0.2em;

  /* ---- vorm ----
     Het pakket: primaire knoppen zijn schuin (nooit rond), panelen 4px, telefoonframes 26px. */
  --skew: polygon(12px 0, 100% 0, calc(100% - 12px) 100%, 0 100%);
  --radius: 4px;
  --radius-sm: 3px;
  --radius-lg: 6px;
  --radius-frame: 26px;

  /* ---- motion ----
     160ms ease-out, pulse 2s, en alléén transform/opacity animeren (zie de motionlaag verderop
     en de kop daar over accuverbruik). --spring blijft bestaan voor de bestaande fade-up. */
  --motion-fast: 120ms ease-out;
  --motion-base: 160ms ease-out;
  --motion-enter: 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
  --motion-pulse: 2s;
  --spring: cubic-bezier(0.2, 0.7, 0.3, 1);

  /* ---- maatvoering ---- */
  --nav-h: 72px;
  /* Hoogte van de vaste spelleiding-/startbalk (.lead-bar op het jagerscherm,
     .lobby-action-bar in de lobby) — zie beide regels verderop in dit bestand.
     Versie 1.12.0: was 60px vast. Met 8px padding boven en onder hield een knop daar 44px over,
     onder het raakvlak-minimum — en na de nieuwe, grotere knoptypografie pasten de twee knoppen
     van de spelleider er niet meer naast elkaar in: ze schoven over elkaar heen, waardoor de
     "Beëindig"-knop de pauzeknop onklikbaar maakte. Gevonden door de UI-smoke-test. Nu afgeleid
     van --tap, dus de balk groeit mee met Nachtjachts 56px. */
  --lead-bar-h: calc(var(--tap) + 16px);
  /* Raakvlakhoogte. Nachtjacht zet 'm op 56px: in het donker, met handschoenen aan en al
     rennend, is 48px te klein. */
  --tap: 48px;

  /* ---- Klopjacht-palet (pakket: data-app="klopjacht") ---- */
  --accent: #e01b24; /* --c-accent */
  --accent-2: #ff6a1a; /* --c-accent2 */
  --accent-strong: #b31119;
  --accent-alt: #3ddc84; /* --c-safe */
  --accent-alt-2: #5ce59c;

  --bg: #0b0b0d; /* --c-bg */
  --bg-2: #101013;
  --surface: #141417; /* --c-surface */
  --surface-2: #1b1b20;
  --ink: #f4f4f5; /* --c-text */
  --muted: #a5a5b0; /* --c-text-muted */
  --faint: #7b7b87; /* --c-text-faint */
  --line: #232329; /* --c-border */
  --line-strong: #2e2e36; /* --c-line */
  --link: var(--accent-2);
  --on-accent: #ffffff; /* --c-on-accent */

  --ok-bg: rgba(61, 220, 132, 0.14);
  --ok-bd: rgba(61, 220, 132, 0.34);
  --ok-tx: #7fe8ab;
  --warn: #ffc53d; /* --c-warn */
  --warn-bg: rgba(255, 197, 61, 0.13);
  --warn-bd: rgba(255, 197, 61, 0.32);
  --warn-tx: #ffd875;
  --danger: #e01b24;
  --danger-bg: rgba(224, 27, 36, 0.16);
  --danger-bd: rgba(224, 27, 36, 0.4);
  --danger-tx: #ff9a9f;

  --field: #1b1b20;
  --field-bd: #2e2e36;
  --nav: rgba(11, 11, 13, 0.86);
  /* Panelen dragen hun begrenzing nu met een rand, niet met een schaduw. Wat overblijft is
     bedoeld voor wat écht boven de pagina zweeft: overlays, de duo-sheet, het menu. */
  --shadow: 0 1px 0 rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 2px 8px rgba(0, 0, 0, 0.5), 0 22px 50px rgba(0, 0, 0, 0.55);

  /* Chip "DE JAGERS" op de landing — eigen tint, los van --warn. */
  --chip-hunter-bg: rgba(255, 106, 26, 0.16);
  --chip-hunter-tx: #ffa470;

  /* De jagerskleur op de kaart en in de legenda. Bij Klopjacht en Smokkeljacht is dat het
     accent zelf; Nachtjacht zet 'm op koraal (zie daar). */
  --hunter: var(--accent);

  font-family: var(--font-body);
}

/* ---- Nachtjacht — nachtjacht.com (pakket: data-app="nachtjacht") ----
   Het merk zit als data-brand op <html> (src/views/layout.tsx). IJsblauw op nachtblauw, jagers
   in koraal. Specificiteit (0,2,0) wint van :root hierboven.

   Felheidsregel uit het pakket: maximaal 12% van het scherm mag lichter zijn dan #1E2839, en
   geen witte vlakken — die verblinden de speler en verraden zijn positie in het donker. Praktisch
   betekent dat: gevulde accentvlakken alleen op de primaire actie (zie de .btn-uitzondering in de
   componentlaag), verder rand en tekst. */
:root[data-brand="nachtjacht"] {
  --accent: #7fd4ff; /* --c-accent */
  --accent-2: #b98cff; /* --c-accent2 */
  --accent-strong: #4fbef2;
  --accent-alt: #3ddc84;
  --accent-alt-2: #5ce59c;

  --bg: #04070f;
  --bg-2: #070c16;
  --surface: #0a0f1a;
  --surface-2: #101724;
  --ink: #c7d4e4;
  --muted: #9faec2;
  --faint: #7c8798;
  --line: #1a2233;
  --line-strong: #1e2839;
  --link: var(--accent);
  --on-accent: #04070f;

  --ok-bg: rgba(61, 220, 132, 0.13);
  --ok-bd: rgba(61, 220, 132, 0.3);
  --ok-tx: #7fe8ab;
  --warn: #f5c542;
  --warn-bg: rgba(245, 197, 66, 0.12);
  --warn-bd: rgba(245, 197, 66, 0.3);
  --warn-tx: #f7d98a;
  --danger: #ff5470;
  --danger-bg: rgba(255, 84, 112, 0.15);
  --danger-bd: rgba(255, 84, 112, 0.38);
  --danger-tx: #ffa3b3;

  --field: #101724;
  --field-bd: #1e2839;
  --nav: rgba(4, 7, 15, 0.88);

  --chip-hunter-bg: rgba(255, 84, 112, 0.16);
  --chip-hunter-tx: #ffa3b3;

  /* --c-hunter: de jagers zijn hier koraalrood, niet de accentkleur — anders zijn ze op de
     kaart niet van je eigen positie te onderscheiden. */
  --hunter: #ff5470;

  --tap: 56px;
}

/* ---- Smokkeljacht — smokkeljacht.com (pakket: data-app="smokkeljacht") ----
   Limoen op nachtgroen, met goud als tweede accent. Sinds v1.12.0 dezelfde typografie als de
   andere twee: de piratenletter (Pirata One + IM Fell English SC) is vervallen met de overhaul,
   dus --font-display-weight blijft hier gewoon op 800 staan. */
:root[data-brand="smokkeljacht"] {
  --accent: #8be04a; /* --c-accent */
  --accent-2: #f5c542; /* --c-accent2 */
  --accent-strong: #6fc334;
  --accent-alt: #f5c542;
  --accent-alt-2: #f8d668;

  --bg: #06100d;
  --bg-2: #091511;
  --surface: #0e1512;
  --surface-2: #131e19;
  --ink: #f4f4f5;
  --muted: #a8b8af;
  --faint: #7e9186;
  --line: #1e2a23;
  --line-strong: #22302a;
  --link: var(--accent-2);
  --on-accent: #04201a;

  --ok-bg: rgba(139, 224, 74, 0.13);
  --ok-bd: rgba(139, 224, 74, 0.32);
  --ok-tx: #b3ec86;
  --warn: #f5c542;
  --warn-bg: rgba(245, 197, 66, 0.13);
  --warn-bd: rgba(245, 197, 66, 0.32);
  --warn-tx: #f7d98a;
  --danger: #e8452f; /* --c-seized */
  --danger-bg: rgba(232, 69, 47, 0.16);
  --danger-bd: rgba(232, 69, 47, 0.4);
  --danger-tx: #f5a394;

  --field: #131e19;
  --field-bd: #22302a;
  --nav: rgba(6, 16, 13, 0.88);

  --chip-hunter-bg: rgba(46, 125, 255, 0.18);
  --chip-hunter-tx: #8ab6ff;

  /* --c-douane uit het designpakket: de douane vaart onder blauw in dit merk. De douanezones en
     de ingenomen-lading-kleur (--c-seized) die het pakket hier ook noemt, horen bij F4
     (smokkel-douane) uit docs/TOEKOMST-SCHERMEN.md — die komen er samen met dat scherm bij,
     zodat er nu geen tokens staan die nergens op slaan. */
  --hunter: #2e7dff;
}

/* ---- Voortvluchtig — voortvluchtig.com (vierde merk, versie 1.16.0/1.17.0) ----
   Versie 1.17.0: het echte designpakket verving de plaatshouderidentiteit van 1.16.0 (signaalgeel
   op asfaltdonker, verzonnen bij gebrek aan een pakket-export). Dit blok komt nu rechtstreeks uit
   tokens.css, [data-app="voortvluchtig"] uit dat pakket — violet op een bijna zwarte, paarse
   achtergrond, met magenta als tweede accent.

   Kleurregel uit het pakket: magenta (--c-accent2 / --accent-2, ook --hunter/--danger hieronder)
   betekent in dit merk UITSLUITEND "iemand kan jou nu zien" — de uitzending, de jagerstip op de
   kaart, het gevaar. Gebruik die kleur nergens anders, anders verliest het signaal zijn betekenis.

   Het pakket definieert óók een cyaan --route (#2FD3FF, pakketnaam --c-route): "altijd een route
   of spoor, nooit een status". Komt sinds versie 1.19.0 (Premiejacht: de schermen) echt in beeld
   — het spoor van de laatste drie uitzendingen op de jagerskaart (public/js/hunter.js) — dus het
   "geen tokens die nergens op slaan"-principe blijft overeind (vergelijk de F4-tokens die bij
   Smokkeljacht hierboven nog wél wachten op hun scherm).

   manifest.voortvluchtig.json in het pakket zet theme_color op het accent (#A94BFF) — als enige
   van de vier pakketmanifests; klopjacht/nachtjacht/smokkeljacht gebruiken daar hun achtergrond.
   We volgen hier de familieconventie (theme = achtergrond, zie src/lib/brand.ts), dus themeColor
   blijft #0a0611 en niet het accentpaars. */
:root[data-brand="voortvluchtig"] {
  --accent: #a94bff;
  --accent-2: #ff3d9a;
  --accent-strong: #8a2fe0;
  --accent-alt: #3ddc84;
  --accent-alt-2: #5ce59c;

  --bg: #0a0611;
  --bg-2: #0e0919;
  --surface: #150c22;
  --surface-2: #1b1130;
  --ink: #efe8f7;
  --muted: #b0a3c2;
  --faint: #857794;
  --line: #291838;
  --line-strong: #35204a;
  --link: var(--accent);
  --on-accent: #0a0611;

  --ok-bg: rgba(61, 220, 132, 0.13);
  --ok-bd: rgba(61, 220, 132, 0.32);
  --ok-tx: #7fe8ab;
  --warn: #ffc53d;
  --warn-bg: rgba(255, 197, 61, 0.13);
  --warn-bd: rgba(255, 197, 61, 0.32);
  --warn-tx: #ffd875;
  --danger: #ff3d9a;
  --danger-bg: rgba(255, 61, 154, 0.15);
  --danger-bd: rgba(255, 61, 154, 0.38);
  --danger-tx: #ffa3cd;

  --field: #1b1130;
  --field-bd: #35204a;
  --nav: rgba(10, 6, 17, 0.88);

  --chip-hunter-bg: rgba(255, 61, 154, 0.16);
  --chip-hunter-tx: #ffa3cd;

  /* V1-spec: jagers in magenta op de kaart — dezelfde kleur als --danger, want een jager in
     beeld ís hier het gevaar (zie de kleurregel hierboven). */
  --hunter: #ff3d9a;

  /* Versie 1.19.0 (Premiejacht: de schermen): het spoor van de laatste drie uitzendingen op de
     jagerskaart — nooit voor iets anders (de kleurregel hierboven). */
  --route: #2fd3ff;
}

/* Bugfix (versie 1.11.1, punt 10): op 390px plakte de titel van een "Zo werkt het"-kaart
   NAAST de tekst (h3 links smal, p rechts) i.p.v. gestapeld. Oorzaak: DIT IS GEEN
   smokkeljacht-specifieke bug — `.feature-card` heeft ÉÉN generieke mobiele regel
   (`@media (max-width: 860px) { .feature-card { flex-direction: row; ... } }`, verderop in
   dit bestand bij "Zo werkt het") die drie PLATTE flex-kinderen (het icoon, de <h3> en de <p>
   — zie src/views/home.tsx) gewoon alle drie naast elkaar zet. Zonder een wrapper-element om
   h3+p heen (die zou de HTML van ALLE merken wijzigen, dus ook Klopjacht — dat mag niet, zie
   het eindrapport van v1.11.1) verdeelt flexbox de resterende breedte dan op basis van
   content-breedte, en de langere Klopjacht-titels ("1. Maak een spel, deel de PIN") wonnen
   tot nu toe geen aandacht omdat op klopjacht.com/nachtjacht.com ditzelfde defect gewoon ook
   optreedt — bevestigd door dezelfde 390px-render lokaal te vergelijken. De opdracht voor deze
   ronde is uitdrukkelijk "Klopjacht blijft byte-voor-byte identiek", dus in plaats van de
   gedeelde regel te repareren (wat Klopjacht ZOU verbeteren maar ook zou WIJZIGEN) krijgt
   alleen smokkeljacht hier een override: CSS Grid op dezelfde drie platte kinderen, zonder
   de HTML aan te raken — hogere specificiteit (0,2,0 i.p.v. 0,1,0) wint automatisch van de
   generieke regel, ongeacht bestandsvolgorde. Nachtjacht krijgt in deze ronde bewust alleen de
   nieuwe hero-art (zie punt 7) — dit hoort daar dus niet bij, en het bestaande gedrag blijft
   voor dat merk exact zoals het was. */
@media (max-width: 860px) {
  [data-brand="smokkeljacht"] .feature-card {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 14px;
    row-gap: 4px;
    align-items: start;
  }
  [data-brand="smokkeljacht"] .feature-card .feature-icon {
    grid-column: 1;
    grid-row: 1 / span 2;
  }
  [data-brand="smokkeljacht"] .feature-card h3 {
    grid-column: 2;
    grid-row: 1;
  }
  [data-brand="smokkeljacht"] .feature-card p {
    grid-column: 2;
    grid-row: 2;
  }
}

/* Versie 1.12.0: `*` selecteert GEEN pseudo-elementen, dus deze regel liet ::before/::after
   ongemoeid. Dat viel niet op zolang er nauwelijks animatie was; sinds de motionlaag zit juist
   daar het meeste — de radarringen, de ping op een drop-marker, de halo om de eigen positie en
   de noodmarker zijn allemaal pseudo-elementen. Gemeten op de landing met
   `prefers-reduced-motion: reduce`: vier animaties liepen gewoon door. Nu alle drie de
   selectors, zodat "beweging uit" ook echt alles betekent. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

/* Bugfix C3 (versie 1.4.0, ontdekt via het nieuwe #version-banner): een `hidden`-attribuut
 * verliest van élke class die zelf `display` zet (bv. .banner-paused/.banner-zone-info met
 * `display:flex`) — de UA-regel `[hidden] { display:none }` en zo'n class-regel hebben
 * DEZELFDE specificiteit, en de auteursstijl wint dan altijd van de UA-stijl, ongeacht welke
 * van de twee in de HTML/CSS het eerst komt. Zonder deze regel bleven .banner-paused/
 * .banner-zone-info-elementen met `hidden` dus gewoon zichtbaar (en innamen ze layoutruimte) —
 * dat trof niet alleen het nieuwe version-banner, maar ook het al langer bestaande
 * #paused-banner. `!important` hier is bedoeld: dit MOET boven elke class-`display` staan. */
/* Geen enkele afbeelding mag breder worden dan zijn container. Deze regel ontbrak — geen enkele
   <img> in de app had een bovengrens. Dat is precies waardoor het merklogo in de hero kon
   ontsporen: zijn breedte hing aan een percentage dat niet altijd oplost (zie .hero-logo
   verderop), en waar het niet oplost valt een <img> terug op zijn intrinsieke breedte. Zonder
   max-width remt dan niets dat af. Eén regel maakt dat voor élke afbeelding onmogelijk, wat er
   verderop ook misgaat. */
img {
  max-width: 100%;
}

[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
}

/* Vloeiend scrollen naar het joinformulier (#meedoen, "Ik heb een PIN") — alleen als de
   bezoeker geen reduced-motion heeft gevraagd; de algemene animatie/transitie-uitzondering
   hierboven raakt scroll-behavior niet, dus dit is een losse, expliciete opt-in. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  /* Het pakket: body nooit onder 16px — dit wordt buiten gelezen, vaak lopend. */
  font-size: var(--size-body);
  line-height: 1.5;
  min-height: 100vh;
  /* Nederlands maakt lange samenstellingen, en die staan hier bovendien in een condensed
     hoofdletterkop. "VERWERKINGSVERANTWOORDELIJKE" op /privacy paste niet in een scherm van
     320px en duwde de hele pagina 35px breder dan het toestel. Deze regel breekt zo'n woord af
     zodra het op geen enkele manier op zijn eigen regel past — hij doet dus niets zolang tekst
     gewoon past, en kan alleen maar een horizontale overloop voorkomen. Elementen met
     `white-space: nowrap` blijven ongemoeid: daar mag sowieso niet worden afgebroken. */
  overflow-wrap: break-word;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---- typografie (v1.12.0) ----
   Koppen: Saira Condensed, schuin en in kapitalen. Saira Condensed heeft geen echte italic;
   de browser synthetiseert de schuinstelling, precies zoals de mockups 'm tonen. De
   font-family/-weight/-tracking van individuele koppen staat verderop per component; hier
   staat wat ze allemaal delen, zodat één regel de hele app omzet. */
h1,
h2,
h3,
.display-font,
.hero-title,
.section-heading,
.cheat-copy h2,
.cta-band-copy h2,
.kant-card h3,
.verhaal-titel,
.brand-word,
.feature-card h3,
.timeline-step h3,
.faq-teaser-card h3,
.replay-point h3 {
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 800);
  font-style: italic;
  text-transform: uppercase;
  letter-spacing: var(--font-display-tracking, 0.01em);
  line-height: 1.02;
}

/* Labels: Barlow Condensed 700, wijd gespatieerd, in kapitalen. Dit is de tweede stem van de
   huisstijl — alles wat een waarde aankondigt in plaats van hem te zijn. */
.label,
.clock-label,
.hero-badge,
.kant-tag,
.badge,
.hunter-drop-label,
.stat-tile-label,
.phone-visual-label,
.cheat-chip,
.map-legend-row,
.replay-stat-label,
.verhaal-label,
.duo-deck-more,
.bar-col-label {
  font-family: var(--font-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
}

/* HUD-cijfers: wat je rennend moet kunnen lezen. Het pakket: nooit onder 28px. */
.big-clock,
.hunter-drop-value,
.pin,
.recovery-code,
.stat-tile-value,
.replay-stat-value {
  font-family: var(--font-display);
  font-weight: 900;
  font-style: normal;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.icon {
  width: 1em;
  height: 1em;
  flex: none;
}

/* ===========================================================================
   MOTIONLAAG (versie 1.12.0)
   ===========================================================================
   Het designpakket schrijft voor: 160ms ease-out, pulse 2s, en ALLEEN transform en opacity
   animeren. Dat laatste is hier geen stijlregel maar een accu-regel. Dit spel draait een hele
   avond buiten op één telefoonlading — de app heeft daar een eigen GPS-scheduler en een
   schermdimmer voor. Een animatie op `box-shadow`, `filter`, `width` of `background-position`
   laat de browser elk frame opnieuw tekenen; transform en opacity gaan puur over de
   compositor en kosten vrijwel niets. Waar hieronder een oude box-shadow-puls stond, is die
   dus herschreven naar een ring-pseudo-element op transform/opacity.

   Tweede accu-regel: niets draait onnodig door. `body[data-motion-paused="1"]` (gezet door
   public/js/dimmer.js zodra het scherm gedimd is of het tabblad naar de achtergrond gaat) zet
   elke lopende animatie op pauze. Zie onderaan dit blok.

   Alles staat uit onder `prefers-reduced-motion` — dat regelt het globale
   `* { animation: none }`-blok bovenaan dit bestand; waar het wegvallen van de animatie
   informatie zou kosten, staat hieronder een statische terugval. */

@keyframes radar-sweep {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* Uitdijende ring. Gebruikt door de radar, de dropping op de jagerskaart en de eigen-positie-
   halo — één keyframe, drie plekken. */
@keyframes radar-ping {
  0% {
    transform: scale(0.35);
    opacity: 0.85;
  }
  70% {
    opacity: 0.12;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}

/* Rustige aandachtspuls voor een HUD-cijfer dat urgent wordt. Bewust klein: op 3.4rem is een
   schaalsprong van 4% al duidelijk zichtbaar, en meer zou de cijfers onleesbaar maken terwijl
   je rent. */
@keyframes hud-pulse {
  0%,
  100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.04);
    opacity: 0.82;
  }
}

@keyframes melding-in {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes overlay-in {
  from {
    opacity: 0;
    transform: scale(1.04);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ---- de radar ----
   Het kernbeeld van het designpakket: een draaiende sweep met uitdijende ringen. Eén component,
   drie plekken (landing-hero, de wachtstand in de lobby, en de "volgende drop"-tegel op het
   gejaagde-scherm). Formaat via --radar-size, kleur volgt --accent.

   De sweep is een conic-gradient op een eigen laag die roteert; de ringen zijn twee
   pseudo-elementen met dezelfde radar-ping-keyframe en een versprongen delay, zodat er altijd
   één ring onderweg is. */
.radar {
  position: relative;
  width: var(--radar-size, 180px);
  height: var(--radar-size, 180px);
  flex: none;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  overflow: hidden;
  isolation: isolate;
}
.radar::before,
.radar::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  animation: radar-ping 3.2s cubic-bezier(0.1, 0.6, 0.3, 1) infinite;
}
.radar::after {
  animation-delay: 1.6s;
}
.radar-sweep {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(
    from 0deg,
    transparent 0deg,
    transparent 250deg,
    color-mix(in srgb, var(--accent) 6%, transparent) 300deg,
    color-mix(in srgb, var(--accent) 34%, transparent) 358deg,
    color-mix(in srgb, var(--accent) 60%, transparent) 360deg
  );
  animation: radar-sweep 4s linear infinite;
}
/* Vaste ringen + kruisdraad, zodat de radar ook stilstaand (reduced-motion) een beeld is en
   geen lege cirkel. */
.radar-grid {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle, transparent 0 32%, color-mix(in srgb, var(--accent) 20%, transparent) 32% calc(32% + 1px), transparent calc(32% + 1px)),
    radial-gradient(circle, transparent 0 64%, color-mix(in srgb, var(--accent) 14%, transparent) 64% calc(64% + 1px), transparent calc(64% + 1px)),
    linear-gradient(to right, transparent calc(50% - 0.5px), color-mix(in srgb, var(--accent) 12%, transparent) calc(50% - 0.5px) calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    linear-gradient(to bottom, transparent calc(50% - 0.5px), color-mix(in srgb, var(--accent) 12%, transparent) calc(50% - 0.5px) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}
/* Het "contact" in het midden: waar de radar naar kijkt. */
.radar-blip {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 70%, transparent);
}

/* ---- urgentie op een HUD-cijfer ----
   Gezet door public/js/runner.js en public/js/hunter.js in de laatste seconden vóór een drop.
   Niet met een timer in CSS: het moment komt van de servertijd, niet van de animatie. */
.big-clock.urgent,
.hunter-drop-value.urgent {
  color: var(--accent);
  animation: hud-pulse var(--motion-pulse) ease-in-out infinite;
}

/* ---- meldingen ---- */
.card.banner-ended,
.card.banner-penalty,
.card.banner-zone-info,
.warning-box {
  animation: melding-in var(--motion-base) both;
}

/* De wachtstand in de lobby: radar met de uitleg eronder. */
.wachtstand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 12px 0 4px;
  text-align: center;
}

/* ---- accu: alles stil zodra het scherm gedimd is of het tabblad weg is ---- */
body[data-motion-paused="1"] *,
body[data-motion-paused="1"] *::before,
body[data-motion-paused="1"] *::after {
  animation-play-state: paused !important;
}

/* ---- layout ---- */

.page {
  max-width: 480px;
  margin: 0 auto;
  padding: 24px 16px 64px;
}

/* ---- contentpagina's op een breed scherm (versie 1.13.1) ----

   `.page` hierboven is de telefoonwrapper van de SPELSCHERMEN, en 480px is daar de bedoeling:
   die worden buiten op een telefoon gespeeld. De contentpagina's — spelregels, organiseren,
   faq, privacy, voorwaarden, bug melden — hergebruikten diezelfde wrapper. Op een laptop stond
   daardoor elke tekstpagina als een kolom van 480px midden in een scherm van 1440px, met de
   navigatiebalk er in volle breedte (72rem) overheen. Dat las als "mobiele weergave op
   desktop", en dat was het feitelijk ook.

   Dit stond er al vanaf het eerste commit; de designoverhaul (1.12.0) maakte het alleen
   zichtbaar, doordat de landing wél een echte desktopbreedte kreeg en het verschil ernaast
   opviel.

   72rem is niet willekeurig: dat is exact `.container-wide`, de container van de nav en de
   footer. Daarmee liggen de linkerrand van het logo, van de kop en van de kaarten op één lijn.
   Elke andere breedte oogt als een uitlijnfout. */
@media (min-width: 861px) {
  .page-content {
    max-width: 72rem;
    padding-left: 24px;
    padding-right: 24px;
  }

  /* Maar een alinea van 1150px breed is ~140 tekens per regel, en dat leest niet: je oog
     verliest bij het terugspringen de regel. Lopende tekst houdt daarom een eigen maat, terwijl
     de tabellen en de instellingenrijen de volle breedte wél mogen gebruiken — die worden juist
     leesbaarder naarmate er meer ruimte is. */
  .page-content p,
  .page-content .content-lede,
  .page-content ol.ordered-list li,
  .page-content .faq-item p {
    max-width: 78ch;
  }

  /* De term/omschrijving-rijen (`ul.list`) staan op `space-between`. Op 480px zet dat de twee
     netjes tegenover elkaar; op 1150px dreven ze uit elkaar — "Te voet" links en de
     omschrijving 700px verderop, met niets ertussen om het oog langs te leiden. Een vaste
     termkolom houdt het paar bij elkaar en laat de omschrijving links uitlijnen. */
  .page-content ul.list li {
    justify-content: flex-start;
    gap: 28px;
  }
  .page-content ul.list li > span:first-child {
    flex: 0 0 15rem;
  }
  .page-content ul.list li > span:last-child {
    text-align: left;
  }
}

/* ---- de lobby op een breed scherm (versie 1.14.0) ----
   Dit is het andere scherm waar de spelleider aan tafel zit: voor de start, terwijl de groep
   binnendruppelt. Bij een schoolklas zijn dat dertig namen plus de duo-indeling, en die stonden
   op een laptop onder elkaar in een kolom van 480px — je scrollde langs je eigen groep.
   Twee kolommen, met `align-items: start` zodat een korte kaart naast een lange niet meerekt.
   De kop en de lead-zin lopen over de volle breedte, die horen boven het geheel. */
@media (min-width: 861px) {
  .page-lobby {
    max-width: 64rem;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    gap: 16px;
  }
  .page-lobby > h1,
  .page-lobby > .lead {
    grid-column: 1 / -1;
    margin-bottom: 0;
  }
  /* De duo-lijst is de kaart waar het bij een grote groep om draait; die mag beide kolommen. */
  .page-lobby > #teams-card {
    grid-column: 1 / -1;
  }
}

/* Bevinding 5 (reisreview 2026-08-31, ronde 5, B11): ruimte voor .lobby-action-bar (vast
   onderin) — zonder deze extra padding zou de laatste kaart erachter kunnen wegvallen. */
.page-has-action-bar {
  padding-bottom: calc(64px + var(--lead-bar-h) + env(safe-area-inset-bottom));
}

/* Signature entrance: staggered fade-up op de directe kinderen van .page (zowel op de
   gewone SSR-pagina's als binnen de feed-tab van het jager-scherm, die dezelfde klasse
   hergebruikt). Uitgezet op het gejaagde-scherm (data-motion="sober", zie onderaan). */
@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.page > * {
  animation: fadeUp 0.42s both;
}
.page > *:nth-child(2) {
  animation-delay: 0.04s;
}
.page > *:nth-child(3) {
  animation-delay: 0.08s;
}
.page > *:nth-child(4) {
  animation-delay: 0.12s;
}
.page > *:nth-child(5) {
  animation-delay: 0.16s;
}
.page > *:nth-child(6) {
  animation-delay: 0.2s;
}

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

h2 {
  font-size: 1.4rem;
  margin: 0 0 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  /* `anywhere` en niet `break-word`, en dat verschil is hier precies de bug. Deze kop is een
     flexcontainer (voor het schuine streepje hieronder), dus de tekst zit in een anonieme
     flexitem met `min-width: auto` — oftewel: minstens zo breed als het langste woord. Alleen
     `anywhere` telt mee in die min-content-berekening; `break-word` niet. Zonder dit maakte
     "VERWERKINGSVERANTWOORDELIJKE" op /privacy de pagina 355px breed op een scherm van 320. */
  overflow-wrap: anywhere;
}
/* Het schuine streepje voor een sectiekop — dezelfde helling als de knoppen, zodat de
   vormtaal ook in de kleine dingen doorklinkt (v1.12.0: was een afgeronde verloopbalk). */
h2::before {
  content: "";
  width: 5px;
  height: 0.94em;
  background: var(--accent);
  clip-path: polygon(3px 0, 100% 0, calc(100% - 3px) 100%, 0 100%);
  flex: 0 0 auto;
}

h3 {
  font-size: 1.1rem;
  margin: 0 0 8px;
}

p.lead {
  color: var(--muted);
  margin-top: 0;
}

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

/* Bevinding 6 (reisreview 2026-08-31, ronde 5, B11): de tikbare revanche-link (src/views/
   end.tsx + public/js/rematch-link.js) — de tekst IS de volledige URL (incl. "https://", zie
   die kop), dus word-break op smalle schermen om overloop naast de "Kopieer link"-knop te
   voorkomen (.row hierboven wrapt de twee al, maar een enkel lang woord zou zelf nog kunnen
   overlopen zonder deze regel). */
.rematch-link {
  font-weight: 600;
  text-decoration: underline;
  word-break: break-all;
}

/* ---- kaarten ---- */

/* Panelen dragen hun begrenzing met een rand van 1px en 4px radius — geen zachte schaduw en
   geen hover-lift meer (v1.12.0). Dat was de oude huisstijl; de nieuwe vormtaal is vlak en
   hoekig, en op een spelscherm dat je met natte vingers in het donker bedient voegt een
   zwevende kaart niets toe. */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 16px;
  transition: border-color var(--motion-base);
}
.card:hover {
  border-color: var(--line-strong);
}

/* ---- formulieren ---- */

label {
  display: block;
  font-weight: 640;
  font-size: 0.9rem;
  margin: 14px 0 6px;
}

input,
select,
textarea {
  display: block;
  width: 100%;
  padding: 11px 13px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--field-bd);
  background: var(--field);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  transition: border-color 0.15s, box-shadow 0.15s;
}
input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent);
}

.hint {
  color: var(--muted);
  font-size: 0.82rem;
  margin-top: 4px;
}

/* ---- checkbox/range (spelgebied-kaartkiezer, versie 0.7.0) ---- */
/* De algemene input/select/textarea-regel hierboven (display:block, width:100%, veldrand)
   past niet bij een checkbox of slider — hier expliciet overschreven. */
input[type="checkbox"] {
  display: inline-block;
  width: auto;
  padding: 0;
  border: none;
  background: none;
  accent-color: var(--accent);
}
input[type="range"] {
  padding: 0;
  border: none;
  background: transparent;
  accent-color: var(--accent);
}
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Honeypot voor /bug-melden (versie 0.6.0, src/views/bugreport.tsx): van het canvas geschoven
   i.p.v. display:none — sommige spam-bots negeren display:none/visibility:hidden-velden juist
   omdat die de meestgebruikte anti-spamtruc zijn. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---- knoppen (v1.12.0: schuin, niet rond) ----
   Het pakket schrijft `clip-path: var(--skew)` voor op primaire knoppen. Dat kan hier NIET
   rechtstreeks op de knop: clip-path clipt álles wat het element tekent, inclusief de
   `:focus-visible`-outline — een toetsenbordgebruiker zou dan niet meer zien waar hij staat.

   Daarom draagt een pseudo-element de schuine vulling en blijft de knop zelf ongeclipt:
     ::before  de gevulde (of, bij .secondary, de rand-)vorm
     ::after   bij .secondary de binnenvulling, 2px ingezet — samen een schuine rand van 2px
   `z-index: 0` + `position: relative` maakt van de knop een eigen stapelcontext, zodat de
   negatieve z-index van de pseudo's achter de tekst blijft en niet achter de pagina verdwijnt.

   Hoogte minstens --tap (48px, op Nachtjacht 56px): dit wordt rennend en met koude vingers
   aangetikt. */
button,
.btn {
  position: relative;
  z-index: 0;
  /* Houdt de lichtveeg (het ::after verderop) binnen de knop; hij loopt van links buiten het
     kader naar rechts erbuiten. Klipt de eigen focus-outline NIET — overflow snijdt alleen
     nakomelingen weg, niet de outline van het element zelf. */
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: none;
  border-radius: 0;
  padding: 12px 22px;
  min-height: var(--tap);
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 800;
  font-style: italic;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  cursor: pointer;
  background: none;
  color: var(--on-accent);
  margin-top: 16px;
  transition: transform var(--motion-fast), color var(--motion-base);
  text-decoration: none;
}
button::before,
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background: var(--accent);
  clip-path: var(--skew);
  transition: background var(--motion-base);
}
button:hover::before,
.btn:hover::before {
  background: var(--accent-strong);
}
button:active,
.btn:active {
  transform: scale(0.97);
}

/* Secundair: schuine rand, geen vlak. Op Nachtjacht is dit de standaardknop — de
   felheidsregel staat daar maar één gevuld accentvlak per scherm toe. */
button.secondary,
.btn.secondary,
.btn.btn-outline {
  color: var(--accent);
}
button.secondary::before,
.btn.secondary::before,
.btn.btn-outline::before {
  background: var(--accent);
}
button.secondary::after,
.btn.secondary::after,
.btn.btn-outline::after {
  content: "";
  position: absolute;
  inset: 2px;
  z-index: -1;
  background: var(--surface);
  clip-path: var(--skew);
  transition: background var(--motion-base);
}
button.secondary:hover::before,
.btn.secondary:hover::before,
.btn.btn-outline:hover::before {
  background: var(--accent);
}
button.secondary:hover::after,
.btn.secondary:hover::after,
.btn.btn-outline:hover::after {
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
}

button.danger::before {
  background: var(--danger);
}
button.danger:hover::before {
  background: color-mix(in srgb, var(--danger) 82%, black);
}

button:disabled,
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}
button:disabled::before,
.btn:disabled::before {
  background: var(--line-strong);
}

/* ---- knoppen die GEEN knop-vlak zijn ----
   Een `<button>` is in deze app niet altijd een actie met een vlak eromheen: er zijn
   icoontoggles, tabbladen, een sleepgreep, een kaart-volgknop, de "laatste gebeurtenis"-balk en
   de aantikbare duo-rijen. Die krijgen hun eigen vorm verderop in dit bestand, dus hier worden
   de schuine pseudo-vullingen en de knop-typografie weer uitgezet — anders krijgt elke
   icoontoggle een rood schuin vlak achter zich (de negatieve z-index van ::before schildert
   binnen de stapelcontext ÓVER de eigen achtergrond van het element heen).
   Bij het toevoegen van zo'n knop: zet 'm in deze lijst. */
.icon-toggle-btn,
.tab-btn,
.signalen-hint-close,
.map-follow-btn,
.last-event-bar,
.duo-deck-grip,
.duo-row,
.stijlkiezer button {
  min-height: 0;
  font-family: var(--font-body);
  font-style: normal;
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
}
.icon-toggle-btn::before,
.icon-toggle-btn::after,
.tab-btn::before,
.tab-btn::after,
.signalen-hint-close::before,
.signalen-hint-close::after,
.map-follow-btn::before,
.map-follow-btn::after,
.last-event-bar::before,
.last-event-bar::after,
.duo-deck-grip::before,
.duo-deck-grip::after,
.duo-row::before,
.duo-row::after,
.stijlkiezer button::before,
.stijlkiezer button::after {
  content: none;
}

/* De sluitknop van het duo-paneel is wél een echte secundaire knop, maar klein — de
   --tap-minimumhoogte hoort hier niet, hij zit al binnen een paneel dat je bewust opent. */
.duo-sheet-close {
  min-height: 0;
  font-size: 0.85rem;
}

.pin {
  font-size: 2.6rem;
  letter-spacing: 0.14em;
  text-align: center;
  padding: 16px 14px;
  border: 1px dashed var(--accent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--accent) 8%, var(--surface));
  color: var(--accent);
  margin: 12px 0;
}

/* Bevinding 3 (reisreview 2026-08-31, ronde 5, B11): de herstelcode in de lobby (src/views/
   lobby.tsx) — zelfde grootte/opzet als .pin hierboven (dit is de tegenhanger die je nog
   moet opschrijven, in plaats van de code die je net kreeg voorgelezen), maar met de
   waarschuwingskleuren (--warn-*, zelfde tokens als .warning-box) i.p.v. --accent: dit is geen
   uitnodiging om te delen zoals de PIN, maar iets om apart te bewaren. */
.recovery-code {
  font-size: 2.4rem;
  letter-spacing: 0.14em;
  text-align: center;
  padding: 16px 14px;
  border: 1px dashed var(--warn-bd);
  background: var(--warn-bg);
  color: var(--warn-tx);
  border-radius: var(--radius);
  margin: 12px 0;
}

.status-msg {
  font-size: 0.9rem;
  min-height: 1.2em;
  margin-top: 8px;
}
.status-msg.error {
  color: var(--danger-tx);
}
.status-msg.ok {
  color: var(--ok-tx);
}

.big-clock {
  font-size: 3.4rem;
  text-align: center;
  margin: 8px 0;
}

/* Bevinding 1 (reisreview 2026-08-31, ronde 6, B2): public/js/runner.js's renderClocks() zet
   deze klasse op #next-drop-clock zodra estimateNextDropAtLocal() moest doortellen op hele
   intervallen (verbinding weg voorbij het laatst bekende next_drop_at) — zelfde regel en zelfde
   gedempte kleur als hunter.js's .hunter-drop-value.estimate hierboven, nooit verwarren met een
   servergegarandeerde aftelling (B2). */
.big-clock.estimate {
  color: var(--muted);
}

.clock-label {
  text-align: center;
  color: var(--faint);
  font-size: var(--size-label);
}

/* Derde variant (versie 1.11.0, smokkel-modus): vrachtbrief-voortgang op het gejaagde-scherm
   (src/views/runner.tsx) — een rij genummerde ruiten, "gelost" ingekleurd door
   public/js/runner.js zodra ownCheckpoints.gelost meegroeit. */
.vrachtbrief-row {
  display: flex;
  justify-content: center;
  gap: 18px;
  margin: 14px 0 4px;
}
.vrachtbrief-seq {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  transform: rotate(45deg);
  border: 2px dashed var(--field-bd);
  border-radius: 4px;
  color: var(--muted);
  font-weight: 800;
  font-size: 0.85rem;
  flex: none;
}
.vrachtbrief-seq > * {
  transform: rotate(-45deg);
}
.vrachtbrief-seq.gelost {
  border-style: solid;
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}

ul.list {
  list-style: none;
  margin: 0;
  padding: 0;
}
ul.list li {
  padding: 11px 0;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  gap: 8px;
  align-items: center;
}
ul.list li:last-child {
  border-bottom: none;
}
/* Het label (links, met stip+icoon) blijft op één regel; alleen de waarde rechts mag
   afbreken — anders knipt "Scherm aan" lelijk af op smalle schermen (runner-status-lijst). */
ul.list li > span:first-child {
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
}
ul.list li > span:last-child {
  text-align: right;
  /* Een flex-item mag standaard niet kleiner worden dan zijn min-content, en min-content van een
     tekst is het langste ondeelbare woord. Op /organiseren staat "batterijspaarstand/
     energiebesparingsmodus" — 309px aan één stuk — naast een label van 198px in een rij van
     316px. Het item weigerde dus te krimpen en maakte de héle pagina 553px breed op een scherm
     van 390. Deze twee regels samen lossen dat op: min-width laat het item wél krimpen,
     overflow-wrap laat zo'n woord afbreken in plaats van de rij open te duwen. Geldt bewust voor
     élke ul.list, ook de lijsten die de client vult (duonamen, spelersnamen) — daar kan
     morgen net zo goed een onafbreekbaar lang woord in staan. */
  min-width: 0;
  overflow-wrap: break-word;
}

/* F4-fix (adversariële toets 2026-08-31, S8): "Per duo" op het eindscherm (src/views/end.tsx) —
   een lang smokkel-badgelabel duwde deze rij horizontaal uit beeld op 390px (gemeten scrollWidth
   473 vs 390). Oorzaak: naam + badge(s) zitten samen in de first-child-span hierboven, en die
   staat op `white-space: nowrap` — prima voor een los label, maar een span met een lange badge
   erin kan dan niet naar een tweede regel uitwijken. Deze specifiekere regel (hogere
   specificiteit dan de generieke rul hierboven dankzij .outcome-row) zet dat terug naar normale
   wrapping, alleen voor deze rij — de rest van .list (bv. de runner-statuslijst) blijft
   ongemoeid. */
ul.list li.outcome-row > span:first-child {
  white-space: normal;
  flex-wrap: wrap;
  row-gap: 4px;
}
ul.list li.outcome-row .outcome-name {
  white-space: nowrap;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  /* Hoekig, niet rond (v1.12.0) — een badge is een stempel, geen pil. */
  border-radius: 2px;
  font-size: 0.72rem;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  color: var(--muted);
}
.badge.active {
  background: color-mix(in srgb, var(--accent-alt) 18%, transparent);
  border-color: var(--accent-alt);
  color: var(--accent-alt);
}
.badge.caught {
  background: color-mix(in srgb, var(--danger) 18%, transparent);
  border-color: var(--danger);
  color: var(--danger-tx);
}
.badge.escaped {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--muted);
}
.badge.warn {
  background: var(--warn-bg);
  color: var(--warn-tx);
  border: 1px solid var(--warn-bd);
}

.dot {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--muted);
  margin-right: 6px;
  flex: none;
}
.dot.ok {
  background: var(--accent-alt);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-alt) 22%, transparent);
}
.dot.bad {
  background: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 22%, transparent);
}
.dot.warn {
  background: #d8940a;
  box-shadow: 0 0 0 3px color-mix(in srgb, #d8940a 22%, transparent);
}

/* Alle meldingen delen sinds v1.12.0 één vorm: een dikke accentrand LINKS, een vlakke
   achtergrond en tekst op leesmaat. De kleur zegt hoe erg het is, de rand zegt dat het een
   melding is. Dat vervangt de vier verschillende omrande bannerstijlen die hier stonden. */
.warning-box {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--warn-bg);
  border: 1px solid var(--warn-bd);
  border-left: 3px solid var(--warn);
  color: var(--warn-tx);
  border-radius: var(--radius);
  padding: 14px 16px;
  font-size: 0.95rem;
  margin-bottom: 16px;
}
.warning-box .icon {
  width: 20px;
  height: 20px;
  margin-top: 1px;
}

.row {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

/* Kaartkiezer voor het spelgebied op /new (versie 0.7.0) — een gewoon (niet-fullscreen)
   kaartje in de flow van het formulier, in tegenstelling tot de fullscreen jagerskaart. */
.zone-map {
  height: 320px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  margin: 10px 0;
  background: var(--bg-2);
}
.zone-radius-value {
  text-align: right;
  font-weight: 650;
  margin-top: -2px;
}

.muted {
  color: var(--muted);
}

.settings-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 16px;
}
.settings-grid dt {
  color: var(--muted);
  font-size: 0.82rem;
  grid-column: 1;
}
.settings-grid dd {
  margin: 0;
  grid-column: 2;
  text-align: right;
  font-weight: 650;
}

.banner-ended {
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent-alt);
  background: color-mix(in srgb, var(--accent-alt) 10%, var(--surface));
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Strafminuten-melding op het gejaagde-scherm (batch 3, PLAN.md punt 2). Hergebruikt voor de
   "rand haalt je in"-waarschuwing en de "buiten het spelgebied"-banner (versie 0.7.0) —
   bewust dezelfde stijl, het zijn allemaal urgente, actie-vragende meldingen.
   Bevinding 5 (reisreview 2026-08-31, ronde 6, B2): border van 2px naar 3px en de rand in
   `--warn-tx` (de donkere waarschuwingstekstkleur) i.p.v. het bleke `--warn-bd` — zie de
   toelichting bij .banner-zone-info hieronder: dit was de ZACHTERE van de twee stijlen, terwijl
   dit juist de banner is die om een actie vraagt. Getest op een screenshot met alle drie de
   banners tegelijk (390×844): vóór deze fix trok de louter informatieve krimpbanner (feller
   accent-oranje randje, roze-getinte achtergrond, bijna-zwarte tekst) duidelijk meer aandacht
   dan deze — nu is het andersom. `--warn-bg`/`--warn-tx` blijven ongewijzigd (die tokens worden
   op 26 andere plekken in dit bestand hergebruikt voor zachtere, niet-actionable meldingen). */
.banner-penalty {
  border: 1px solid var(--warn-bd);
  border-left: 3px solid var(--warn);
  background: var(--warn-bg);
  color: var(--warn-tx);
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Zesde variant (versie 1.22.0, batch 6): de finale-banner — "het net sluit zich". Urgent, net
   als .banner-penalty hierboven (dezelfde --warn-tokens), maar bewust GEEN actie-oproep (er is
   niets te "verhelpen") — vandaar geen felheidsverschil met .banner-penalty nodig, de tekst zelf
   ("Het net sluit zich — fase T-X") maakt het onderscheid al duidelijk. */
.banner-finale {
  border: 1px solid var(--warn-bd);
  border-left: 3px solid var(--warn);
  background: var(--warn-bg);
  color: var(--warn-tx);
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 650;
}

/* Neutrale, niet-urgente spelgebied-mededeling (krimp begonnen/mijlpaal, versie 0.7.0) —
   geen straf, dus bewust GEEN waarschuwingskleur; het merk-accent past beter.
   Bevinding 5 (reisreview 2026-08-31, ronde 6, B2): "de informatieve krimpbanner schreeuwt
   harder dan de banner die om actie vraagt" — deze klasse had een dikkere, feller verzadigde
   rand (2px effen `--accent`) en een hogere-contrast tekstkleur (`--ink`, bijna zwart) dan
   .banner-penalty hierboven (dat een bleke `--warn-bd`-rand en gedempte `--warn-tx`-tekst had) —
   precies omgekeerd aan de bedoelde rangorde ("neutraal, niet-urgent" hoorde de RUSTIGSTE van de
   twee te zijn). Rand nu dun en neutraal (`--line`, dezelfde subtiele scheidingskleur als de
   rest van de kaarten) i.p.v. een verzadigde merkkleur, en de achtergrond bijna kleurloos (2%
   i.p.v. 10% accent-tint) — de tekst blijft `--ink` voor leesbaarheid, maar zonder felle
   rand/achtergrond eromheen leest dit nu duidelijk kalmer dan .banner-penalty. */
.banner-zone-info {
  border: 1px solid var(--line);
  border-left: 3px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 2%, var(--surface));
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ---- versie 0.9.0: pauzeren/hervatten (onderdeel B2) ---- */

/* Onmiskenbare pauze-banner op het gejaagde-scherm — bewust sterker dan .banner-penalty/
   .banner-zone-info hierboven: dit is geen individuele waarschuwing maar een spelbrede status
   die je nooit mag missen.

   Versie 1.12.0: dit was `background: var(--ink)` — een bijna-wit vlak over de volle breedte.
   Dat was in de oude, deels lichte huisstijl nog te verdedigen, maar niet meer nu alle drie de
   merken donker zijn, en op Nachtjacht verbiedt de felheidsregel het ronduit: geen witte
   vlakken, want die verblinden de speler en verraden zijn positie in het donker. De urgentie
   komt nu van een volle accentrand en de displayletter, niet van helderheid. */
.banner-paused {
  border: 1px solid var(--accent);
  border-left: 5px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--surface));
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 800;
  text-transform: uppercase;
  font-size: 1.2rem;
}

/* Zelfde banner op het jager-scherm (.hunter-app is geen .page/.card-layout) — vast onder de
   header, altijd zichtbaar ongeacht welke tab (Kaart/Feed) actief is. Zelfde omslag als
   .banner-paused hierboven: geen bijna-wit vlak meer. */
.paused-banner {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 16px;
  background: color-mix(in srgb, var(--accent) 16%, var(--surface));
  color: var(--ink);
  border-bottom: 2px solid var(--accent);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 800;
  text-transform: uppercase;
  font-size: 1.05rem;
  z-index: 20;
}

/* ---- versie 1.4.0: versie-handshake (bugfix C3) ----
   Subtiele, NIET-blokkerende hint zodra de server een nieuwere APP_VERSION meldt dan waarmee
   deze pagina ooit geladen is — bewust geen waarschuwingskleur (.banner-penalty) en bewust
   geen auto-reload: alleen een zachte aansporing om zelf te verversen, nooit midden in een
   spel afgedwongen. Alleen op het jager-scherm (.hunter-app, geen .page/.card-layout): vast
   onder de header, net als .paused-banner hierboven. Het gejaagde-scherm hergebruikt
   hiervoor gewoon .card.banner-zone-info (al precies dezelfde neutrale bedoeling). */
.version-banner {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 16px;
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  color: var(--ink);
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
}


/* ---------- HUD-band op het gejaagde-scherm (versie 1.12.0) ----------
   De twee cijfers die je rennend moet kunnen lezen, altijd in beeld. Sticky in plaats van
   fixed: de band schuift mee tot bovenaan en blijft daar dan hangen — zo botst hij niet met de
   safe-area-inset of de adresbalk van de browser, en houdt de rest van de pagina zijn normale
   scrollgedrag. */
.hud {
  position: sticky;
  top: 0;
  z-index: 30;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  margin: -24px -16px 18px;
  padding-top: env(safe-area-inset-top);
  background: var(--line);
  border-bottom: 1px solid var(--line-strong);
}
.hud-tile {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  padding: 14px 12px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 104px;
}
.hud-tile .big-clock {
  margin: 0;
  /* Iets kleiner dan de losstaande .big-clock: twee tegels naast elkaar op 390px moeten allebei
     een klok van 1:29:37 kunnen dragen zonder af te breken. Ondergrens is --size-hud (30px) —
     het pakket: cijfers die je rennend moet lezen nooit onder 28px. */
  font-size: clamp(var(--size-hud), 1.2rem + 3.4vw, 2.6rem);
  position: relative;
  z-index: 1;
}
.hud-tile .clock-label {
  position: relative;
  z-index: 1;
}
/* De radar zit ACHTER de dropklok — dat is de tegel die de spanning draagt. */
.hud-radar {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 0;
  pointer-events: none;
  opacity: 0.4;
  border: 0;
}

/* ---------- N4: de offline-melding ----------
   Eigen variant naast .banner-penalty (geel, "let op") en .banner-zone-info (accent, neutraal):
   dit is rood, want zonder verbinding werkt het spel niet meer zoals het hoort. */
.banner-offline {
  border: 1px solid var(--danger-bd);
  border-left: 3px solid var(--danger);
  background: var(--danger-bg);
  color: var(--danger-tx);
  display: flex;
  align-items: center;
  gap: 10px;
  animation: melding-in var(--motion-base) both;
}

/* ---------- N5: "Je bent gepakt", fullscreen ---------- */
.gepakt-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(24px + env(safe-area-inset-top)) 20px calc(24px + env(safe-area-inset-bottom));
  background: linear-gradient(160deg, color-mix(in srgb, var(--danger) 34%, var(--bg)), var(--bg) 72%);
  animation: overlay-in var(--motion-enter) both;
}
/* Ontsnapt is dezelfde overname, andere kleur — het is even goed nieuws als het andere slecht is. */
.gepakt-overlay.is-ontsnapt {
  background: linear-gradient(160deg, color-mix(in srgb, var(--accent-alt) 30%, var(--bg)), var(--bg) 72%);
}
.gepakt-inner {
  width: 100%;
  max-width: 420px;
}
.gepakt-tag {
  display: inline-block;
  font-family: var(--font-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-badge);
  font-size: 0.7rem;
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 14%, transparent);
  padding: 5px 14px 5px 11px;
  clip-path: polygon(0 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
}
.gepakt-titel {
  font-size: clamp(2.6rem, 1.6rem + 5vw, 3.6rem);
  margin: 14px 0 12px;
}
/* h2 krijgt elders een decoratief accentstreepje (h2::before) voor de kaartkoppen; op een
   fullscreen-overname hoort dat niet — daar is de kop het hele scherm. */
.gepakt-titel::before {
  content: none;
}
.gepakt-lead {
  color: var(--ink);
  font-size: 1.05rem;
  margin: 0 0 22px;
  opacity: 0.86;
}
.gepakt-tegels {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 26px;
}
.gepakt-tegel {
  background: color-mix(in srgb, var(--bg) 55%, transparent);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.gepakt-tegel-waarde {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.8rem;
  line-height: 1.1;
  color: var(--ink);
  /* Een lange duonaam mag de tegel niet uit elkaar duwen. */
  overflow-wrap: anywhere;
}


/* ---------- A3 · Nachtregels & veiligheid (versie 1.12.0) ----------
   Verplicht scherm vóór de start, als overlay over lobby/gejaagde/jager. Zie
   src/views/nachtregels.tsx.

   Niet scrollbaar: dat is een regel uit het designpakket, geen toeval. Passen de regels er niet
   op, dan zijn het er te veel en leest niemand ze. De maten hieronder zijn daarop gekozen — vier
   regels van twee tekstregels passen op een scherm van 640px hoog. */
.regels-overlay {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(20px + env(safe-area-inset-top)) 18px calc(20px + env(safe-area-inset-bottom));
  background: var(--bg);
  overflow: hidden;
  animation: overlay-in var(--motion-enter) both;
}
.regels-inner {
  width: 100%;
  max-width: 460px;
}
.regels-tag {
  display: inline-block;
  font-family: var(--font-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-badge);
  font-size: 0.68rem;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  padding: 5px 14px 5px 11px;
  clip-path: polygon(0 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
}
.regels-titel {
  font-size: clamp(2.2rem, 1.5rem + 3.6vw, 3rem);
  margin: 12px 0 8px;
}
.regels-titel::before {
  content: none;
}
.regels-lead {
  color: var(--muted);
  margin: 0 0 18px;
}
.regels-lijst {
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.regels-regel {
  display: flex;
  gap: 11px;
  background: var(--surface);
  border: 1px solid var(--line);
  /* De accentrand links draagt de ernst — zelfde patroon als de meldingen elders. */
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 12px 14px;
  font-size: 0.98rem;
  line-height: 1.4;
}
.regels-regel.ernst-let-op {
  border-left-color: var(--warn);
}
.regels-regel.ernst-nood {
  border-left-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 8%, var(--surface));
}
.regels-nr {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.05rem;
  line-height: 1.35;
  color: var(--muted);
  flex: none;
}
.regels-regel.ernst-nood .regels-nr {
  color: var(--danger);
}
.regels-vink {
  display: flex;
  align-items: center;
  gap: 11px;
  margin: 0 0 16px;
  padding: 13px 14px;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  /* Het hele blok is aantikbaar (het is een <label>) en minstens --tap hoog — een los vinkje van
     16px is in het donker met koude vingers niet te raken. */
  min-height: var(--tap);
  cursor: pointer;
  font-weight: 600;
}
.regels-vink input[type='checkbox'] {
  width: 22px;
  height: 22px;
  flex: none;
}
#regels-start {
  width: 100%;
  margin-top: 0;
}

/* ---- versie 0.9.0: geluid/tril-toggle (onderdeel A) ---- */

.screen-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.screen-title-row h1 {
  margin-bottom: 6px; /* iets krapper dan de standaard h1-marge, past beter naast de knop */
}

.icon-toggle-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  margin: 0;
  /* Versie 1.12.0: was een cirkel. De vormtaal kent geen cirkels; dit is een vierkant met
     dezelfde 4px-hoek als de panelen. Ook meteen op --tap gebracht — een 42px-doel is te
     klein voor een scherm dat je rennend bedient. */
  border-radius: var(--radius);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  color: var(--muted);
}
.icon-toggle-btn:hover {
  box-shadow: none;
  filter: none;
  color: var(--ink);
}
.icon-toggle-btn[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent);
}
.hunter-header .icon-toggle-btn {
  width: 36px;
  height: 36px;
}

/* ---- Ronde 5 (v1.1.0): automatische schermdimming (onderdeel B) ----
   Bijna-zwarte overlay bovenop de HELE pagina (dus ook boven de fullscreen jagerskaart en de
   frosted tabbar) na inactiviteit — een web-overlay kan de hardware-helderheid niet écht
   verlagen, maar scheelt wél stroom op OLED. Standaard onzichtbaar (opacity 0, geen
   pointer-events) i.p.v. een `hidden`-toggle: zo blijft in-/uitfaden één simpele
   class-toggle, zonder een setTimeout die moet wachten op het einde van de transitie. */
#dim-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: rgba(0, 0, 0, 0.92);
  color: rgba(255, 255, 255, 0.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s ease;
}
#dim-overlay.no-motion {
  transition: none;
}
#dim-overlay.visible {
  opacity: 1;
}
.dim-overlay-hint {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 640;
  letter-spacing: 0.01em;
}
.dim-overlay-clock {
  margin: 0;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  opacity: 0.75;
}

/* ---- versie 0.9.0: spelleider-gereedschap — speler verwijderen (onderdeel B1) ---- */

.member-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.member-row button {
  margin: 0;
  padding: 5px 10px;
  font-size: 0.78rem;
  box-shadow: none;
}

/* ---- meldingen-/installatiekaart (jager-scherm, batch 3) ---- */

.push-status-row,
.install-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  color: var(--muted);
  font-size: 0.88rem;
}
.install-note {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 8px 0 0;
  line-height: 1.5;
}

/* ---- Jager-scherm: fullscreen kaart + frosted bottom-tabbar ---- */

.hunter-app {
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}

.hunter-header {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: calc(env(safe-area-inset-top) + 10px) 16px 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.02), 0 6px 20px rgba(28, 20, 10, 0.05);
  z-index: 20;
}

/* Versie 1.8.0: één held-cijfer (de drop-aftelling) + twee bijzaken + de knoppen. De spelnaam
   stond hier met een ellipsis meestal tot niets gereduceerd en is naar de Meer-tab verhuisd. */
.hunter-header-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.hunter-drop {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
  min-width: 0;
}

.hunter-drop-label {
  font-size: 0.66rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.hunter-drop-value {
  font-size: 2rem;
  font-weight: 800;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
/* Bevinding 7 (reisreview 2026-08-31, ronde 3, B2/B6): public/js/hunter.js's renderHeader() zet
   deze klasse zodra de aftelling geen door de server bevestigd moment meer toont, maar op hele
   intervallen doortelt vanaf het laatst bekende next_drop_at (estimateNextDropAtLocal). Gedempte
   kleur i.p.v. de gewone (opvallende) accentkleur — nooit verwarren met een servergegarandeerde
   aftelling (B2), zonder terug te vallen op de dode "--:--" die er zonder deze fix stond. */
.hunter-drop-value.estimate {
  color: var(--muted);
}

.hunter-header-side {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.hunter-mini {
  font-size: 0.78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hunter-header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}
/* Bugfix S8, versie 1.8.0: het verbindingslampje is hier een kale stip — geen pil meer bovenop
   de kaart (en dus ook geen botsing meer met de Leaflet-zoomknoppen linksboven). */
.hunter-header-actions .dot {
  margin-right: 0;
}

/* Loopt het drop-interval af: "bijna" aflezen zonder cijfers te ontcijferen terwijl je rent. */
.hunter-progress {
  height: 3px;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
}
.hunter-progress-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width 0.9s linear;
}

/* Alleen zichtbaar als de verbinding ECHT weg is (setConnectionDot('bad', …)). */
.connection-warning {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 0.8rem;
  color: var(--danger-tx);
  background: var(--danger-bg);
  border: 1px solid var(--danger-bd);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
}
.connection-warning[hidden] {
  display: none;
}
.connection-warning .icon {
  width: 16px;
  height: 16px;
  flex: none;
}

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

/* Bevinding 5 (reisreview 2026-08-31, ronde 5, B11): de vaste spelleiding-balk op het
   jagerscherm (src/views/hunter.tsx, alleen aanwezig als canManage) — pauzeren/stoppen stonden
   hiervoor in een gewone kaart op de Meer-tab, dus twee tikken plus scrollen voor de twee
   dingen die een spelleider tijdens het spel het vaakst nodig heeft. Zelfde `position:fixed`-
   aanpak als nav.tabbar hieronder, alleen ÉÉN band hoger (bottom = de hoogte van de tabbar)
   zodat hij ongeacht welke tab open staat in beeld blijft, zonder de tabbar zelf te overlappen. */
.lead-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--nav-h) + env(safe-area-inset-bottom));
  z-index: 29;
  height: var(--lead-bar-h);
  display: flex;
  gap: 8px;
  padding: 8px 12px;
  background: var(--surface);
  border-top: 1px solid var(--line);
  box-shadow: 0 -2px 10px rgba(28, 20, 10, 0.08);
}
.lead-bar form {
  flex: 1;
  /* Zonder min-width:0 krimpt een flex-item niet onder zijn eigen inhoud: de knoptekst duwde de
     balk dan open in plaats van zichzelf af te breken. */
  min-width: 0;
  margin: 0;
}
.lead-bar button {
  width: 100%;
  height: 100%;
  /* De balk is zelf al op --tap gedimensioneerd; een tweede minimum hier zou de knop juist weer
     uit de balk duwen. */
  min-height: 0;
  margin: 0;
  padding: 0 10px;
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

/* Bevinding 5 (reisreview 2026-08-31, ronde 5, B11): de vaste "Spel starten"-balk in de lobby
   (src/views/lobby.tsx) — geen tabbar hier om onder te schuiven, dus gewoon op bottom:0, in
   dezelfde stijl als .lead-bar hierboven. */
.lobby-action-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 29;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
  background: var(--nav);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-top: 1px solid var(--line);
}
.lobby-action-bar form {
  margin: 0;
  max-width: 480px;
  margin: 0 auto;
}
.lobby-action-bar button {
  width: 100%;
  margin: 0;
  font-size: 1.05rem;
}

/* Frosted bottom tab bar (skill webapp-house-style § "non-negotiables" nr. 8). */
nav.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  height: calc(var(--nav-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--nav);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-top: 1px solid var(--line);
  display: flex;
}

.tab-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: transparent;
  color: var(--muted);
  border: none;
  border-radius: 0;
  margin: 0;
  padding: 8px;
  font: inherit;
  font-size: 0.68rem;
  font-weight: 640;
  box-shadow: none;
  transition: color 0.15s, transform 0.12s;
}
.tab-btn .icon {
  width: 24px;
  height: 24px;
  transition: transform 0.16s;
}
.tab-btn:hover {
  box-shadow: none;
  filter: none;
}
.tab-btn[aria-selected="true"] {
  color: var(--accent);
}
.tab-btn[aria-selected="true"] .icon {
  transform: translateY(-1px) scale(1.06);
}
.tab-btn:active {
  transform: scale(0.94);
}

.tab-panel {
  flex: 1;
  min-height: 0;
  position: relative;
  overflow: hidden;
}
.tab-panel[hidden] {
  display: none;
}

#tab-feed.tab-panel,
#tab-meer.tab-panel {
  overflow-y: auto;
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom));
}

/* Bevinding 5 (reisreview 2026-08-31, ronde 5, B11): extra bodemruimte zodra .lead-bar er is
   (body[data-can-manage="1"], zelfde attribuut als hunter.js al leest) — anders zou de balk het
   onderste stuk van elke tab (incl. de OSM-attributie op de kaart) overlappen. Hogere
   specificiteit dan de twee kale regels hierboven/hieronder wint dit vanzelf, ongeacht volgorde. */
body[data-can-manage='1'] #tab-feed.tab-panel,
body[data-can-manage='1'] #tab-meer.tab-panel,
body[data-can-manage='1'] #tab-map.tab-panel {
  padding-bottom: calc(var(--nav-h) + var(--lead-bar-h) + env(safe-area-inset-bottom));
}

/* ---- spelleider op tablet/laptop (versie 1.14.0) ----

   Het jagerscherm is een telefoonpatroon: 100dvh met drie tabs waarvan er één tegelijk zichtbaar
   is. Dat klopt voor een jager die buiten met één hand zijn telefoon vasthoudt. Het klopt NIET
   voor de spelleider — een docent met dertig kinderen zit aan tafel met een tablet of laptop en
   moet juist alles tegelijk zien: waar de duo's zijn, wat er net gebeurde, en wie hij zo nodig
   moet verwijderen of pauzeren. Die wisselde tot nu toe de hele avond heen en weer tussen drie
   tabs op een scherm waarvan tweederde leeg was.

   Vandaar deze indeling, en de twee voorwaarden erop:

   - `min-width: 1000px` — tablet in liggende stand en elke laptop; een telefoon en een tablet
     rechtop houden precies wat ze hadden.
   - `body[data-can-manage="1"]` — alleen wie het spel beheert. Een gewone jager is buiten aan
     het rennen; die heeft aan een breed scherm niets, en krijgt dus ook geen ander scherm.

   .hunter-panes en .hunter-rail staan buiten deze media query op `display: contents`. Daardoor
   bestaan ze op een telefoon effectief niet: de drie panelen blijven directe flex-kinderen van
   .hunter-app, precies zoals vóór deze versie. Geen enkel telefoongedrag verandert. */
.hunter-panes,
.hunter-rail {
  display: contents;
}

@media (min-width: 1000px) {
  body[data-can-manage='1'] .hunter-panes {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(340px, 30%);
    flex: 1;
    min-height: 0;
    gap: 1px;
    background: var(--line); /* de 1px gap wordt zo de scheidingslijn tussen kaart en rail */
  }
  body[data-can-manage='1'] .hunter-rail {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow-y: auto;
    background: var(--bg);
  }
  /* De panelen staan hier naast elkaar in plaats van gestapeld, dus geen van drieën mag nog
     `flex: 1` op de hoogte van .hunter-app claimen. hunter.js laat `hidden` op een breed
     spelleidersscherm bewust weg (zie setActiveTab daar) — deze regels gaan over de maatvoering,
     niet over zichtbaarheid. */
  body[data-can-manage='1'] .hunter-panes > #tab-map {
    flex: none;
    min-height: 0;
  }
  body[data-can-manage='1'] .hunter-rail > .tab-panel {
    flex: none;
    overflow: visible;
    padding-bottom: 0;
  }
  /* De rail scrollt zélf niet: dan zou de feed simpelweg onder de onderrand verdwijnen (en
     halverwege een zin worden afgeknipt, wat de eerste opzet ook deed). In plaats daarvan is het
     een vaste kolom met twee vakken die elk hun eigen scroll hebben — de feed bovenaan, want dat
     is wat een spelleider continu in de gaten houdt, en het beheerblok eronder. */
  body[data-can-manage='1'] .hunter-rail {
    overflow: hidden;
  }
  body[data-can-manage='1'] .hunter-rail > #tab-feed {
    flex: 0 0 45%;
    min-height: 14rem;
    overflow-y: auto;
    border-bottom: 1px solid var(--line);
  }
  body[data-can-manage='1'] .hunter-rail > #tab-meer {
    flex: 1 1 auto;
    overflow-y: auto;
  }
  /* De tabbalk heeft niets meer te wisselen: alle drie de panelen staan in beeld. */
  body[data-can-manage='1'] .tabbar {
    display: none;
  }
  /* De .page binnen feed/"Meer" is de telefoonkolom van 480px — in een rail van ~380px hoort
     die gewoon de volle breedte te nemen. */
  body[data-can-manage='1'] .hunter-rail .page {
    max-width: none;
  }
  /* Zonder tabbalk valt de bodemruimte die daarvoor gereserveerd was weg. */
  body[data-can-manage='1'] #tab-map.tab-panel,
  body[data-can-manage='1'] #tab-feed.tab-panel,
  body[data-can-manage='1'] #tab-meer.tab-panel {
    padding-bottom: 0;
  }

  /* "Spel pauzeren" en "Spel stoppen" gelden het hele spel, niet de kaart — dus blijft de balk
     over de volle breedte staan, zoals op een telefoon. De ruimte ervoor wordt hier gereserveerd
     op de panelen-container als geheel; deed je dat alleen op de kaartkolom, dan bleef er onder
     het duo-dek een strook dode ruimte staan terwijl de rail gewoon doorliep. Zonder tabbalk zakt
     de balk naar de onderrand. */
  body[data-can-manage='1'] .hunter-panes {
    padding-bottom: calc(var(--lead-bar-h) + env(safe-area-inset-bottom));
  }
  body[data-can-manage='1'] .lead-bar {
    bottom: env(safe-area-inset-bottom);
  }
}


/* ---- versie 1.8.0: de Kaart-tab is een GRID van drie rijen ----
   Was: #map absoluut op inset:0 met vier overlays erbovenop, elk met een eigen
   bottom/z-index-rekensom. Gevolg 1: de onderste ~106 px kaart (tabbar + safe-area) was
   permanent onzichtbaar, inclusief de verplichte OSM-attributie. Gevolg 2: elk nieuw element
   moest weer in die rekensom passen — de gebeurtenisbalk van 1.7.0 kreeg geen `position` mee en
   werd daardoor door de (gepositioneerde) kaart overschilderd, dus was hij nooit zichtbaar.
   Nu zijn de gebeurtenisbalk en het duo-dek echte rijen; alleen kleine chips liggen nog over de
   kaart. */
#tab-map.tab-panel {
  display: grid;
  grid-template-rows: 1fr auto auto;
  /* Bugfix B1 (visuele controle, versie 1.8.0): zonder een expliciete kolom kreeg de enige
     (impliciete) kolom een `auto`-breedte — en die wordt bepaald door het MAX-CONTENT van de
     breedste rij, gemeten VOORDAT enige flex-shrink toeslaat. Eén duo-regel met een lange naam
     én twee badges (bv. NOOD + "+5 min") kon zo, ongekrompen gemeten, breder zijn dan het
     scherm — waardoor #tab-map (en daarmee de hele kaarttab) 100px buiten beeld liep en
     `.tab-panel`'s `overflow:hidden` dat gewoon onbereikbaar afsneed. `minmax(0,1fr)` zet een
     harde bovengrens (de beschikbare breedte) op de kolom, ongeacht content. */
  grid-template-columns: minmax(0, 1fr);
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom));
}
/* Bugfix B1 (vervolg): de grid-track-limiet hierboven is niet genoeg — een grid-ITEM heeft
   zelf ook een default `min-width:auto`, wat neerkomt op zijn eigen content-based minimum
   (net als bij flex-items). Zonder deze reset zou #map/.last-event-bar/.duo-deck alsnog buiten
   hun (nu wél beperkte) kolom willen groeien. Met min-width:0 mogen ze net zo smal worden als
   de kolom, en doen hun EIGEN kinderen (zoals .duo-row-name's ellipsis) het krimpwerk. */
#map,
.last-event-bar,
.duo-deck {
  min-width: 0;
}
/* Nodig omdat de regel hierboven een id-selector heeft: `.tab-panel[hidden] { display:none }`
   zou anders verliezen van `#tab-map.tab-panel { display:grid }` en de Kaart-tab zou zichtbaar
   blijven op de Feed-/Meer-tab. */
#tab-map.tab-panel[hidden] {
  display: none;
}

#map {
  position: relative;
  min-height: 0;
  background: var(--bg-2);
  /* Sluit Leaflets eigen z-index-schaal (panes 200-700, controls 800-1000) op in dit element,
     zodat hij niet meer meedoet met de z-index van de kopbalk/tabbar/het duoblad daarbuiten.
     Zonder deze isolatie hangt de schilderorde af van welk voorouderelement toevallig een
     stacking context maakt — precies het soort onzichtbare afhankelijkheid waardoor de
     gebeurtenisbalk van 1.7.0 nooit in beeld kwam. */
  isolation: isolate;
}

/* Verschijnt UITSLUITEND tijdens een krimp (renderZoneChip in public/js/hunter.js) — een
   stilstaande cirkel is zelfverklarend en verdient geen permanent element.
   Bugfix B2 (visuele controle, versie 1.8.0): stond linksonder, waar Leaflets eigen
   attributievermelding (rechtsonder-anchor, maar breed genoeg om de hele onderrand te raken)
   'm overlapte. Rechtsboven staat sinds 1.8.0 vrij — de kaartlegenda die daar stond is met de
   herindeling van dit scherm verdwenen (zie de kop van hunter.tsx), en de Leaflet-zoomknoppen
   zitten linksBOVEN, dus rechtsboven botst de chip nergens mee. */
.zone-chip {
  position: absolute;
  right: 8px;
  top: 8px;
  /* Binnen #map: boven Leaflets kaart-/markerlagen (400-700), onder zijn bedieningselementen. */
  z-index: 900;
  background: var(--surface);
  border: 1px solid var(--accent);
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  font-size: 0.78rem;
  font-weight: 650;
  box-shadow: var(--shadow);
}
.zone-chip[hidden] {
  display: none;
}

/* Bevinding 1 (reisreview 2026-08-31, ronde 3, B6): verschijnt UITSLUITEND zodra de jager zelf
   gepand/gezoomd heeft (public/js/hunter.js's breakFollow/updateFollowUI) — zolang de kaart
   vanzelf meebeweegt met nieuwe drops is er niets te doen en dus geen knop nodig. Onderin,
   gecentreerd: boven Leaflets duim-bereikbare zoomknoppen (linksboven) en niet in de weg van de
   attributievermelding (rechtsonder, smal genoeg om dit te missen). Ingevulde accentkleur i.p.v.
   de neutrale .zone-chip-stijl — dit IS een knop om op te tikken, geen informatieregel. */
.map-follow-btn {
  position: absolute;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  z-index: 900;
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--accent);
  color: #fff;
  border: 0;
  border-radius: 999px;
  padding: 9px 16px;
  font-size: 0.82rem;
  font-weight: 700;
  box-shadow: var(--shadow);
}
.map-follow-btn .icon {
  width: 16px;
  height: 16px;
}
.map-follow-btn[hidden] {
  display: none;
}

/* ---- het duo-dek (versie 1.8.0) ----
   Was een horizontaal scrollende rij pillen: bij 260-320 px per pil zag je op een 390 px-scherm
   één duo tegelijk, terwijl een spel tot 20 duo's toestaat. Nu één regel per duo, gesorteerd op
   urgentie (nood bovenaan, daarna oplopend op afstand) — "welk duo is het dichtstbij" is dan
   gewoon de bovenste regel. */
.duo-deck {
  position: relative;
  z-index: 1;
  background: var(--surface);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
}

.duo-deck-grip {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin: 0;
  padding: 6px 0 4px;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.duo-deck-grip:hover {
  box-shadow: none;
  filter: none;
}
.duo-deck-grip-bar {
  display: block;
  width: 36px;
  height: 4px;
  border-radius: 999px;
  background: var(--line-strong);
}

.duo-deck-rows {
  display: flex;
  flex-direction: column;
  /* ~3 regels; de rest is bereikbaar door te scrollen, of door de grijper naar "full" te tikken
     hieronder. Wie puur wil navigeren klapt het dek in met de grijper (stand bewaard in
     localStorage). */
  max-height: 108px;
  overflow-y: auto;
}
.duo-deck.collapsed .duo-deck-rows {
  max-height: 36px;
}
/* Bevinding 4 (reisreview 2026-08-31, ronde 3, B6): derde grijperstand — "normaal" (108px, ~3
   regels) had geen weg naar méér dan scrollen binnen dat strookje, en niets op het scherm zei
   dat dat kon. `dvh` (met `vh`-fallback voor oudere WebKit) i.p.v. een vaste pixelwaarde: het
   dek moet met de ECHTE, dynamische viewporthoogte meeschalen (adresbalk in/uit) net als
   `.hunter-app` hierboven al met `100dvh` doet — anders zou "60%" op een telefoon met een
   ingeklapte adresbalk ineens een ander deel van het scherm betekenen dan waarop getest is. */
.duo-deck.full .duo-deck-rows {
  max-height: 60vh;
  max-height: 60dvh;
}

/* Bugfix B3 (visuele controle, versie 1.8.0): drie regels zonder enige aanwijzing dat er meer
   waren — spellen mogen tot 20 duo's hebben. public/js/hunter.js's updateDuoDeckMoreHint() telt
   na elke render hoeveel rijen buiten het zichtbare deel van .duo-deck-rows vallen (via
   offsetTop, dus ongeacht DOM-volgorde — de duo's worden gesorteerd met CSS `order`) en vult dit
   regeltje. Bewust een rustig, klein bijschrift i.p.v. een schaduw/gradient-overlay over de
   kaart: minder afleiding op een scherm waar je toch al op moet letten. */
.duo-deck-more {
  margin: 0;
  padding: 4px 14px 7px;
  border-top: 1px solid var(--line);
  text-align: center;
  font-size: 0.72rem;
  font-weight: 650;
  color: var(--muted);
  /* Bijvangst (visuele hercontrole, versie 1.8.0), vangnet voor de regel hieronder: op de
     allereerste render (vóór hunter.js' eerste updateDuoDeckMoreHint() draait) staat hier nog
     geen tekst in — min-height reserveert dan dezelfde hoogte als mét tekst (line-height +
     padding + border-top), zodat er nooit een moment is zonder gereserveerde ruimte. */
  min-height: calc(1.08rem + 12px);
}
/* Bijvangst (visuele hercontrole, versie 1.8.0): dit regeltje verscheen/verdween met het
   `hidden`-attribuut (updateDuoDeckMoreHint() in hunter.js), dus het duo-dek kromp ~29px in
   zodra alle duo's zichtbaar waren (bv. na scrollen naar beneden in het dek) — precies genoeg om
   de kaart/inhoud eronder te laten verspringen, midden in het scrollgebaar. De ruimte blijft nu
   ALTIJD gereserveerd: verborgen betekent hier onzichtbaar (visibility), niet weg (display).
   Overschrijft de globale `[hidden] { display: none !important }` (bugfix C3 hierboven) met
   gelijke !important maar een hogere specificiteit (klasse + attribuut), dus dit wint ongeacht
   bronvolgorde. */
.duo-deck-more[hidden] {
  display: block !important;
  visibility: hidden;
}

.duo-deck-empty {
  margin: 0;
  padding: 8px 14px 12px;
  font-size: 0.85rem;
}

.duo-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 0;
  padding: 7px 14px;
  background: transparent;
  color: var(--ink);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  font: inherit;
  font-size: 0.84rem;
  text-align: left;
  flex: none;
}
.duo-row + .duo-row {
  border-top: 1px solid var(--line);
}
.duo-row:hover {
  box-shadow: none;
  filter: none;
}
.duo-row:active {
  background: var(--surface-2);
}
.duo-row.nood {
  background: var(--danger-bg);
}

.duo-row-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex: none;
  border: 1px solid rgba(0, 0, 0, 0.25);
}

/* Bugfix B4 (visuele hercontrole, versie 1.8.0): de naam is de IDENTITEIT van het duo en mag
   nooit tot 0px krimpen. Vóór deze fix stonden .duo-row-drop/.duo-row-dist op `flex: none` en
   mocht .duo-row-badges wrappen — daarmee was de naam het ENIGE element dat nog kon krimpen, en
   bij twee badges (de NOOD-regel, de "+5 min"-regel, de "gepakt"-regel) kromp hij tot precies
   0,0px. Nu krijgt de naam de krimpvolgorde die bij zijn belang past: hij krimpt als LAATSTE (en
   dan met een leesbaar minimum + beletselteken), de afstand (die ook op het duoblad staat) wijkt
   als EERSTE. */
.duo-row-name {
  font-weight: 650;
  flex: 1 1 auto;
  min-width: 4.5em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Bevinding 6 (reisreview 2026-08-31, ronde 3, B6): kreeg er een positie-leeftijd-staartje bij
   ("drop 6s geleden — positie ±47s oud", public/js/hunter.js's positionAgeSuffix) — die tekst
   kan een stuk langer worden dan het oude "drop 23s geleden". `flex: none` (nooit krimpen) was
   met de kortere tekst van vóór deze fix onschuldig; nu kan dit element zelf ook de reden worden
   dat de rij te breed wordt. `flex: 0 1 auto` (mag krimpen, groeit niet vanzelf) + ellipsis geeft
   hem dezelfde soort nette afkap als .duo-row-dist hieronder — de naam (die WEL een harde
   min-width-vloer heeft) blijft zo als laatste over voordat er iets echt onleesbaar wordt. */
.duo-row-drop {
  flex: 0 1 auto;
  min-width: 3em;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: 0.78rem;
}
/* Bevinding 3 (reisreview 2026-08-31, ronde 4, B6): een geflagde drop (onbetrouwbaar en/of oud
   — public/js/hunter.js's positionAgeSuffix) mag er in het dek niet hetzelfde uitzien als een
   schone. Zelfde `--warn-tx` als .badge.warn, zodat "geflagd" overal in de app dezelfde kleur
   draagt. */
.duo-row-drop.flagged {
  color: var(--warn-tx);
  font-weight: 650;
}

/* B4: eerste kandidaat om te krimpen (was `flex: none`) — de afstand staat ook op het duoblad
   (één tik verderop), dus die mag hier het veld ruimen vóór de naam ook maar een pixel inlevert.
   Bevinding 3 (reisreview 2026-08-31, ronde 3, B6): met twee badges ernaast (bv. "+10 min" +
   "stil 2m geleden") kromp dit element tot breedte 0 — de tekst ("5,3 km ↗") bleef dan zonder
   `white-space: nowrap` gewoon dóórstaan, alleen AFGEBROKEN over drie regels binnen die 0px
   (hoogte 56px, zichtbare breedte 0px: onleesbaar, niet weg). `min-width: 0` liet dus precies
   het VERKEERDE gebeuren: de bedoelde "wijkt als eerste"-fix (B4 hierboven) werkte wél voor de
   BREEDTE, maar zonder nowrap week de tekst in hoogte i.p.v. te verdwijnen achter een ellipsis.
   nowrap + ellipsis maakt "krimpt" weer letterlijk "kapt af", nooit "wordt onzichtbaar op drie
   regels". */
.duo-row-dist {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-align: right;
  color: var(--ink);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
}

/* Bugfix B1 (vervolg) + B4: stond op `flex-wrap: wrap` zodat twee badges niet breder werden dan
   hun kolom — maar een wrappende regel maakte de RIJ hoger, en gaf de naam zo nog minder ruimte.
   B4: terug naar `flex: none` (niet meer krimpen/wrappen); .duo-row-dist hierboven is nu het
   element dat wijkt, dus de badges hoeven zelf niet meer in te schikken. */
.duo-row-badges {
  display: flex;
  flex-wrap: nowrap;
  justify-content: flex-end;
  gap: 4px;
  flex: none;
}
.duo-row-badges .badge {
  font-size: 0.68rem;
  padding: 1px 7px;
}

/* ---- het duoblad (versie 1.8.0) ----
   "Gepakt" is voor een gewone jager onomkeerbaar en stond permanent op duimhoogte, pal boven de
   kaart waar je tijdens het rennen op pant en zoomt. Alle duo-acties zitten daarom achter één
   tik op de duoregel. */
.duo-sheet {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.duo-sheet[hidden] {
  display: none;
}

.duo-sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
}

.duo-sheet-panel {
  position: relative;
  background: var(--surface);
  border-top-left-radius: var(--radius-lg);
  border-top-right-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 14px 16px calc(16px + env(safe-area-inset-bottom));
  max-height: 72vh;
  overflow-y: auto;
}

.duo-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.duo-sheet-head strong {
  font-size: 1.1rem;
}
.duo-sheet-close {
  margin: 0;
  padding: 6px 12px;
  font-size: 0.8rem;
  box-shadow: none;
}

.duo-sheet-line {
  margin: 0 0 4px;
  font-size: 0.86rem;
}

.duo-sheet-subtitle {
  margin: 14px 0 6px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.duo-sheet-drops {
  max-height: 30vh;
  overflow-y: auto;
}

.duo-sheet-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}
.duo-sheet-actions button {
  margin: 0;
  flex: 1 1 46%;
}

/* .duo-pill-dot is het overblijfsel van de oude duo-strip: de gekleurde stip wordt GEDEELD met
   het nabespeelscherm (.replay-team-card-head / .replay-share-card-head in src/views/end.tsx),
   dus die blijft — de pil eromheen is met de strip verdwenen. Het duo-dek heeft zijn eigen
   .duo-row-dot. */
.duo-pill-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex: none;
  border: 1px solid rgba(0, 0, 0, 0.25);
}

/* Versie 1.8.0: de kaartlegenda rechtsboven op het jagersscherm is WEG — het duo-dek toont
   dezelfde kleurstip + duonaam, en bij 5+ duo's dekte de legenda precies dat deel van de kaart
   af waar die duo's stonden. Deze twee klassen BLIJVEN: ze worden gedeeld met de statische
   legenda van het nabespeelscherm (.map-legend-static / #replay-map-legend in end.tsx). */
.map-legend-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  white-space: nowrap;
}

.map-legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}
/* Legenda-stip voor het spelgebied: gestippelde rand, subtiele vulling — zelfde taal als de
   cirkel op de kaart zelf (buildZoneCircleOptions in public/js/hunter.js). */
.map-legend-dot.zone {
  border-radius: 3px;
  border: 2px dashed #e8590c;
  background: rgba(232, 89, 12, 0.12);
}

/* Genummerde drop-marker (L.divIcon-html) */
.drop-marker {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid white;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  color: white;
  font-weight: 700;
  font-size: 0.72rem;
  display: flex;
  align-items: center;
  justify-content: center;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
}

.drop-marker.flagged {
  border-style: dashed;
  border-color: #fff3;
  opacity: 0.65;
}

/* De nieuwste drop van een duo pingt. Versie 1.12.0: dit was een animatie op `box-shadow`, wat
   de browser elk frame opnieuw laat tekenen — op een kaart met tien markers en een jager die
   rondpant is dat merkbaar. Nu twee ringen die schalen en vervagen (compositor), met een
   versprongen delay zodat er altijd één onderweg is. Zelfde beeld, veel goedkoper.

   .drop-marker-wrap is de divIcon-className van Leaflet (public/js/hunter.js' buildDropIcon);
   .latest staat alleen op de nieuwste marker per duo. */
.drop-marker-wrap.latest .drop-marker-icon::before,
.drop-marker-wrap.latest .drop-marker-icon::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  border: 2px solid currentColor;
  color: inherit;
  pointer-events: none;
  animation: radar-ping 2s cubic-bezier(0.1, 0.6, 0.3, 1) infinite;
}
.drop-marker-wrap.latest .drop-marker-icon::after {
  animation-delay: 1s;
}
/* De ringen erven hun kleur van de duokleur die hunter.js op .drop-marker zet; het wrap-element
   zelf heeft die niet, dus hier expliciet doorgeven via het icoon. */
.drop-marker-wrap.latest .drop-marker-icon {
  color: #fff;
}

@media (prefers-reduced-motion: reduce) {
  .drop-marker-wrap.latest .drop-marker {
    outline: 3px solid currentColor;
    outline-offset: 2px;
  }
}

/* Bevinding 5 (reisreview 2026-08-31, ronde 3, B6): wrapt .drop-marker zodat .drop-marker-name
   (alleen op de nieuwste marker per duo, zie buildDropIcon in public/js/hunter.js) er absoluut
   onder gepositioneerd kan worden zonder de 26×26 iconSize/iconAnchor van de marker zelf te
   verstoren — .drop-marker-name draagt zelf niet bij aan de doosgrootte (position:absolute), dus
   dit element blijft voor Leaflet nog steeds precies 26×26. */
.drop-marker-icon {
  position: relative;
  width: 26px;
  height: 26px;
}
.drop-marker-name {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 3px;
  white-space: nowrap;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

/* Derde variant (versie 1.11.0, smokkel-modus): loswal-marker (L.divIcon-html) — een 45°-
   gedraaid vierkant (een "ruit") in de teamkleur, zodat een wal nooit met een ronde drop-marker
   verward wordt. Gelost = gedempt/gevuld met een vinkje (.reached); nog niet gelost = een
   opening in de teamkleur met het volgnummer. */
.checkpoint-marker {
  width: 22px;
  height: 22px;
  transform: rotate(45deg);
  border: 3px solid;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}
.checkpoint-marker > span {
  transform: rotate(-45deg);
  font-weight: 700;
  font-size: 0.68rem;
}
.checkpoint-marker.reached {
  opacity: 0.55;
}

/* Mede-jagers op de kaart. Versie 1.12.0: was donkergrijs met een witte rand — op een donkere
   kaart nauwelijks te onderscheiden, en die witte rand is op Nachtjacht sowieso verboden. Nu de
   --hunter-token: bij Klopjacht het accent, bij Nachtjacht koraal (anders zijn de jagers niet van
   je eigen positie te onderscheiden) en bij Smokkeljacht douaneblauw. */
.hunter-marker {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--hunter);
  border: 2px solid var(--bg);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  transition: opacity 0.6s ease;
}

.hunter-marker.stale {
  opacity: 0.3;
}

/* Bevinding 1 (reisreview 2026-08-31, ronde 5, B11): de SOS-marker (public/js/hunter.js's
   buildNoodIcon/rebuildNoodMarkers) — bewust NIET te verwarren met een genummerde .drop-marker
   (rood i.p.v. de teamkleur, groter, een 🆘 in plaats van een cijfer) en met een doorlopende
   puls: dit is een van de weinige dingen op de kaart die een jager NOOIT mag missen, dus die
   pulseert altijd, niet alleen "de nieuwste" (vergelijk .drop-marker-wrap.latest hierboven, dat
   stopt zodra er een nieuwere drop bijkomt). Vaste kleur (geen thema-variabele), zelfde
   overweging als .hunter-marker/.own-position-marker hierboven: dit staat op de Leaflet-kaart. */
.nood-marker {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--danger);
  border: 3px solid white;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  position: relative;
}

/* Versie 1.12.0: ook deze puls ging van box-shadow naar een ring op transform/opacity. Sneller
   dan de dropping (1.4s i.p.v. 2s) — dit is een noodmelding, die hoort onrustiger te zijn. */
.nood-marker::before {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 2px solid var(--danger);
  pointer-events: none;
  animation: radar-ping 1.4s cubic-bezier(0.1, 0.6, 0.3, 1) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .nood-marker {
    animation: none;
    outline: 3px solid var(--danger);
  }
}

/* Eigen positie op de jagerskaart (v0.5.1, veldtest-fix 1): wit gevuld met donkere rand +
   middenstip — bewust het OMGEKEERDE kleurenschema van .hunter-marker hierboven (donker
   gevuld), zodat de eigen marker nooit met een mede-jager te verwarren is. Vaste kleuren
   (geen thema-variabelen), net als de andere kaartmarkers: dit staat op de Leaflet-kaart,
   niet in de gewone UI. */
/* De eigen positie van de jager. Versie 1.12.0: het witte bolletje met de donkere kern is
   omgedraaid naar de accentkleur op een donkere kern — een wit punt was in de nieuwe, altijd
   donkere kaart het felste ding op het scherm, en op Nachtjacht verboden. De halo eromheen
   pulseert rustig mee (het pakket: "eigen positie als punt met halo"). */
.own-position-marker {
  position: relative;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent);
  border: 3px solid var(--bg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.own-position-marker::after {
  content: '';
  position: absolute;
  inset: -7px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  pointer-events: none;
  animation: radar-ping var(--motion-pulse) cubic-bezier(0.1, 0.6, 0.3, 1) infinite;
}

.leaflet-popup-content {
  font-size: 0.85rem;
  line-height: 1.4;
}

/* Bugfix B2 (visuele controle, versie 1.8.0): Leaflets eigen attributievermelding en
   zoomknoppen komen met een hardgecodeerde WITTE achtergrond (public/vendor/leaflet/leaflet.css)
   — dat was het felste vlak van het hele scherm, in een app die juist een eigen schermdimmer
   heeft. Versie 1.12.0: stond in een `prefers-color-scheme: dark`-blok, want Klopjacht had toen
   nog een lichte modus. Nu zijn alle drie de merken altijd donker (color-scheme: dark op :root),
   dus deze regels gelden onvoorwaardelijk — met de mediaquery eromheen zou een toestel in
   lichte modus die witte vlakken alsnog terugkrijgen. */
.leaflet-container .leaflet-control-attribution {
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  color: var(--muted);
}
.leaflet-container .leaflet-control-attribution a {
  color: var(--ink);
}
.leaflet-bar a {
  background-color: var(--surface);
  color: var(--ink);
  border-bottom-color: var(--line);
}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
  background-color: var(--surface-2);
}
.leaflet-bar a.leaflet-disabled {
  background-color: var(--surface-2);
  color: var(--muted);
}

/* ---------- Nabespeel-scherm / replaykaart (versie 0.8.0) ---------- */
/* Gewoon (niet-fullscreen) kaartje in de flow van het eindscherm — zelfde opzet als
   .zone-map op /new, maar hoger (per-duo routes + vangkruisen willen meer ruimte dan een
   los middelpunt+straal-kaartje). */
.replay-map {
  height: 420px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  margin: 10px 0;
  background: var(--bg-2);
}

/* Donkere basemap. OpenStreetMap levert lichte tegels; het designpakket vraagt een donkere kaart
   zonder felle vlakken, en op Nachtjacht verbiedt de felheidsregel ze ronduit — een fullscreen
   witte kaart verblindt de speler en verraadt zijn positie in het donker.

   Versie 1.12.0, twee wijzigingen. (1) Dit stond in een `prefers-color-scheme: dark`-blok uit de
   tijd dat Klopjacht licht was; nu onvoorwaardelijk, want alle drie de merken zijn altijd donker.
   (2) Het hing aan `.replay-tiles` (de className die public/js/end.js op zijn tileLayer zet), dus
   het gold ALLEEN voor de nabespeelkaart — de fullscreen jagerskaart (#map, geen className, zie
   public/js/hunter.js) en het zonekaartje op /new bleven helwit. Nu op de tegel-pane zelf, dus
   elke Leaflet-kaart in deze app krijgt 'm.

   Op de pane en niet op de losse tegels: dat is één composited laag in plaats van een filter per
   tegel-<img>. Markers en overlays zitten in eigen panes (.leaflet-marker-pane e.d.) en blijven
   dus onaangeroerd — alleen de kaartondergrond wordt donker. */
.leaflet-tile-pane {
  filter: grayscale(0.4) saturate(0.9) brightness(0.52) contrast(1.05);
}

.map-legend-static {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  font-size: 0.82rem;
  color: var(--muted);
  margin-bottom: 12px;
}
.map-legend-static .map-legend-row {
  padding: 0;
}

/* Vangkruis (afgeleid van de laatste drop van een gepakt duo, src/lib/replay.ts's
   deriveCaughtPoint) — bewust duidelijk anders dan een genummerde .drop-marker: rood, groter,
   een kruis in plaats van een cijfer. */
.catch-marker {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--danger);
  border: 2px solid white;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  color: white;
  font-weight: 800;
  font-size: 0.95rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Statistieken per duo (nabespeel-scherm, src/lib/replay.ts's TeamReplayStats). */
.replay-team-card {
  border-top: 1px solid var(--line);
  padding: 14px 0;
}
.replay-team-card:first-child {
  border-top: none;
  padding-top: 4px;
}
.replay-team-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.replay-stats-row {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
}
.replay-stat {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.replay-stat-value {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: var(--font-display-weight, 800);
}
.replay-stat-label {
  font-size: 0.74rem;
  color: var(--muted);
  font-weight: 600;
}
/* Zevende variant (versie 1.23.0, batch 7, extractie-eindscherm): de tijdlijn van gespeelde
   misleidingskaarten + gebruikte intel-acties onder de statistiekenrij van een duo (src/views/
   end.tsx's buildTijdlijn) — ul.list levert de rijstijl al, alleen wat lucht erboven nodig. */
.replay-tijdlijn {
  margin-top: 10px;
}

/* "Deel als video"-kaarten per duo — runners zien hun eigen duo vooraan + gemarkeerd
   (src/routes/pages.tsx zet het eigen team vooraan in replay.teams). */
.replay-share-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 12px 0;
}
.replay-share-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}
.replay-share-card.own {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 20%, transparent);
}
.replay-share-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ---------- Gejaagde-scherm: functioneel-sober (PLAN.md § "House style") ---------- */

.status-note {
  font-size: 0.78rem;
  color: var(--muted);
  margin-top: -8px;
  margin-bottom: 16px;
}

/* Geen speelse animaties tijdens het spel: geen fade-up, geen hover-lift. Hoog contrast
   voor de klok blijft van de gewone .big-clock/.card-regels hierboven komen — die zijn al
   fors genoeg (font-weight 800, grote maat) om ook in fel zonlicht leesbaar te zijn. */
body[data-motion="sober"] .page > * {
  animation: none;
}
body[data-motion="sober"] .card {
  transition: none;
}
body[data-motion="sober"] .card:hover {
  transform: none;
  box-shadow: var(--shadow);
}

/* ---------- Beheerstatistieken (versie 0.9.0, onderdeel C): eenvoudige CSS-staafdiagrammen,
   géén charting-library ---------- */

.stat-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 12px;
  margin-bottom: 4px;
}
.stat-tile {
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  text-align: center;
}
.stat-tile-value {
  font-size: 1.5rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.stat-tile-label {
  font-size: 0.72rem;
  color: var(--muted);
  margin-top: 2px;
}

.bar-chart {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 130px;
  overflow-x: auto;
  padding: 8px 4px 0;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
}
.bar-col {
  flex: none;
  width: 30px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
}
.bar-col-bar {
  width: 100%;
  min-height: 2px;
  border-radius: 4px 4px 0 0;
  background: linear-gradient(180deg, var(--accent-2), var(--accent));
}
.bar-col-label {
  margin-top: 6px;
  font-size: 0.6rem;
  color: var(--muted);
  white-space: nowrap;
}

/* ---------- Contentpagina's: strafmodus-/instellingentabellen (batch 4, hergebruikt
   in batch 5 op /spelregels en /organiseren) ---------- */

/* Genummerde stappenlijst met leestekst (batch 5) — visueel in lijn met ul.list hieronder,
   maar met echte <ol>-nummering i.p.v. losse kop/waarde-kolommen. */
.ordered-list {
  margin: 0;
  padding-left: 22px;
}
.ordered-list li {
  padding: 9px 0 9px 4px;
  border-bottom: 1px solid var(--line);
  line-height: 1.5;
}
.ordered-list li:last-child {
  border-bottom: none;
}

.table-scroll {
  overflow-x: auto;
}
.mode-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.mode-table th,
.mode-table td {
  text-align: left;
  padding: 10px 8px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.mode-table th {
  color: var(--muted);
  font-weight: 650;
  font-size: 0.76rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.mode-table tr:last-child td {
  border-bottom: none;
}

/* Gereserveerde advertentieruimte (batch 4, punt 4) — leeg totdat Auto ads daadwerkelijk
   iets plaatst; alleen aanwezig in de HTML als ADSENSE_CLIENT geldig is (src/views/adslot.tsx). */
.ad-slot {
  min-height: 100px;
  border-radius: var(--radius);
  border: 1px dashed var(--line-strong);
  margin-bottom: 16px;
}

/* ======================================================================================
   Batch 5 — landing (herontwerp) + de drie contentpagina's (/spelregels, /organiseren,
   /faq). Ontwerpbron: de door de gebruiker goedgekeurde mockups (Main.dc.html op 1440px,
   Mobiel.dc.html op 390px) — kleuren/radius/schaduw komen daar 1-op-1 uit de bestaande
   huisstijl-tokens hierboven (--accent, --bg, --ink, --shadow(-lg), --radius(-lg) matchen
   exact de mockup-waarden), dus geen nieuwe kleurenset nodig. Eén breakpoint (860px) zet
   de grids/nav om van desktop- naar mobiel-gedrag; ertussenin schalen vloeiende clamp()-
   maten mee zodat tussenliggende breedtes (bv. tablet) niet breken. De secties "Kies je
   kant", de tijdlijn en de FAQ-teaser stonden niet op het mobiele artboard (dat toont een
   ingekorte selectie secties) — hun mobiele opmaak hieronder is geïnterpoleerd in dezelfde
   kaartstijl/maatvoering als de secties die wél op het mobiele artboard staan. ---------- */

.marketing {
  color: var(--ink);
}

.container-wide {
  max-width: 72rem; /* 1152px, exact de mockup-containerbreedte */
  margin: 0 auto;
  padding: 0 24px;
  box-sizing: border-box;
}
@media (max-width: 860px) {
  .container-wide {
    padding: 0 18px; /* mockup-mobiel: 18px zijkant i.p.v. 24px */
  }
}

/* Let op voor alle regels hieronder die vaak SAMEN met .container-wide op hetzelfde element
   staan (bv. class="section-marketing container-wide"): gebruik nooit de `padding`-shorthand
   met 0 voor links/rechts — dat zou (zelfde specificiteit, latere regel wint) de
   zijmarge van .container-wide hierboven ongedaan maken. Alleen padding-top/-bottom hier. */

/* .only-desktop/.only-mobile zelf staan bewust HELEMAAL ONDERAAN dit bestand, niet hier —
   zie de toelichting daar: als utility-klasse moeten ze na alles staan zodat ze bij gelijke
   specificiteit altijd winnen van een component-regel als "nav-links { display: flex }". */

.display-font {
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 800);
  letter-spacing: var(--font-display-tracking, -0.02em);
}

/* ---- nav ---- */

.site-nav {
  padding-top: 24px;
}
.site-nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  color: inherit;
  text-decoration: none;
}
.brand-mark {
  width: 42px;
  height: 42px;
  border-radius: 0;
  clip-path: polygon(7px 0, 100% 0, calc(100% - 7px) 100%, 0 100%);
  background: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.brand-mark .icon {
  width: 24px;
  height: 24px;
  color: white;
}
.brand-word {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: var(--font-display-weight, 800);
  letter-spacing: var(--font-display-tracking, -0.02em);
}
/* .nav-links/.nav-cta hieronder verbergen zichzelf bewust via hun EIGEN regel in de
   media-query verderop, in plaats van de gedeelde .only-desktop-utility te dragen: die
   utility zet ook `display` (inline/none), en zou bij gelijke specificiteit met déze
   `display: flex`-regel een bronvolgorde-wedstrijd worden — welke van de twee wint hangt dan
   af van waar de utility toevallig in dit bestand staat, en repareert het ene breekpunt
   alleen door het andere kapot te maken (precies dit gebeurde eerder — zie de toelichting
   bij .only-desktop onderaan dit bestand). Een eigen, specifieke regel voorkomt de race. */
.nav-links {
  display: flex;
  align-items: center;
  gap: 28px;
}
.nav-links a {
  font-size: 1rem;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
}
.nav-links a:hover,
.nav-links a[aria-current="page"] {
  color: var(--ink);
}
.nav-cta {
  margin-top: 0 !important;
}
.nav-menu-toggle {
  display: none;
}

@media (max-width: 860px) {
  .brand-mark {
    width: 34px;
    height: 34px;
  }
  .brand-mark .icon {
    width: 19px;
    height: 19px;
  }
  .brand-word {
    font-size: 1.19rem;
  }
  .nav-links,
  .nav-cta {
    display: none;
  }
  .nav-menu-toggle {
    display: block;
    position: relative;
  }
  .nav-menu-toggle > summary {
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--ink);
  }
  .nav-menu-toggle > summary::-webkit-details-marker {
    display: none;
  }
  .nav-menu-toggle > summary .icon {
    width: 24px;
    height: 24px;
  }
  .nav-menu-panel {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    z-index: 40;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 220px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: 10px;
  }
  .nav-menu-panel a {
    display: block;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--ink);
    text-decoration: none;
  }
  .nav-menu-panel a:hover {
    background: var(--bg-2);
  }
  .nav-menu-panel .btn {
    margin: 6px 4px 2px;
  }
}

/* ---- hero ---- */

.hero-marketing {
  padding-top: clamp(40px, 3vw + 24px, 72px);
  padding-bottom: 48px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 56px;
  align-items: center;
}
.hero-copy {
  display: flex;
  flex-direction: column;
  gap: 22px;
}
/* Versie 1.12.0: was een groene ronde pil. De vormtaal kent geen pillen meer — dit is een
   schuin gesneden strook in de tweede accentkleur, dezelfde helling als de knoppen. */
.hero-badge {
  display: inline-flex;
  align-self: flex-start;
  background: color-mix(in srgb, var(--accent-2) 16%, var(--surface));
  color: var(--accent-2);
  border: 0;
  border-left: 3px solid var(--accent-2);
  font-size: 0.8rem;
  padding: 7px 16px 7px 13px;
  border-radius: 0;
  clip-path: polygon(0 0, 100% 0, calc(100% - 9px) 100%, 0 100%);
}
.hero-title {
  font-family: var(--font-display);
  font-size: clamp(2.875rem, 2.1rem + 2.6vw, 4.625rem);
  font-weight: var(--font-display-weight, 800);
  line-height: 1.03;
  letter-spacing: var(--font-display-tracking, -0.03em);
  margin: 0;
}
.hero-lead {
  font-size: clamp(1.03rem, 0.95rem + 0.3vw, 1.25rem);
  line-height: 1.55;
  color: var(--muted);
  max-width: 42ch;
  margin: 0;
}
.hero-ctas {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.hero-ctas .btn {
  margin-top: 0;
  font-size: 1.05rem;
  padding: 16px 30px;
}
/* Versie 1.12.0: de losse rand + achtergrond die hier stond tekende een RECHTHOEK om de
   schuine knop heen — twee vormen over elkaar. De rand komt nu van de ::before/::after-
   pseudo's bij de knopregels bovenaan dit bestand; hier blijft alleen de maatvoering over. */
.btn.btn-outline {
  background: none;
  border: 0;
}
.hero-checks {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  color: var(--muted);
  font-size: 0.94rem;
  font-weight: 600;
}
.hero-checks span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.hero-checks .icon {
  width: 17px;
  height: 17px;
  color: var(--accent-alt);
  stroke-width: 2.75;
}

/* Versie 1.12.0: de hero draagt nu het merklogo met de radar erachter (zie src/views/home.tsx).
   Geen paneel eromheen meer — het logo heeft zijn eigen vorm en achtergrond, een kader eromheen
   zou er een plaatje-in-een-kaartje van maken. */
.hero-art {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 320px;
}
.hero-radar {
  position: absolute;
  /* Achter het logo, en niet aantikbaar — het is decor. */
  z-index: 0;
  pointer-events: none;
  opacity: 0.55;
  /* De size-prop uit src/views/radar.tsx is de bovengrens; op een smal scherm krimpt hij mee
     in plaats van buiten de hero te steken.
     Stond op 104vw, en dat is letterlijk brééder dan het scherm: op een telefoon van 390px werd
     de radar 406px en kon de landing 8px horizontaal scrollen — precies het "de site past niet"-
     gevoel waar dit bestand op 1.15.1 voor is nagelopen. 86vw houdt hem binnen de rand en laat
     nog steeds een ruime ring om het logo (dat op mobiel 58vw is). */
  width: min(var(--radar-size), 86vw);
  height: auto;
  aspect-ratio: 1;
}
.hero-logo {
  position: relative;
  z-index: 1;
  /* Was `min(320px, 72%)`. Dat percentage moet worden afgeleid van de gridcel van .hero-art, en
     die staat op `place-items: center` — daardoor voegt de cel zich naar zijn inhoud in plaats
     van de volle breedte te nemen. Maar die inhoud IS dit logo: het percentage hangt dus van zijn
     eigen uitkomst af. Zo'n circulaire maat is onderbepaald, en engines lossen 'm verschillend
     op; waar hij helemaal niet oplost valt een <img> terug op zijn intrinsieke breedte — hier de
     640px die als attribuut in src/views/home.tsx staat.
     Gemeld vanaf een iPhone, mét screenshot: het logo veel te groot. Hoe groot precies is hier
     niet vastgesteld — Chromium mat vóór én na deze wijziging 56-58% en vertoonde het probleem
     dus niet, en WebKit staat niet in deze omgeving. Wat wél vaststaat is de constructie
     hierboven, en die is nu weg: `vw` hangt nergens van af en lost altijd op, met een max-width
     eronder als vangnet. */
  width: min(320px, 34vw);
  max-width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-frame);
}

@media (max-width: 860px) {
  .hero-marketing {
    grid-template-columns: 1fr;
    gap: 26px;
    padding-top: 8px;
  }
  .hero-ctas {
    flex-direction: column;
    align-items: stretch;
  }
  .hero-ctas .btn {
    text-align: center;
  }
  .hero-checks {
    justify-content: center;
    gap: 14px;
    font-size: 0.85rem;
  }
  .hero-art {
    min-height: 260px;
  }
  .hero-logo {
    /* Zelfde reden als hierboven: vw in plaats van een percentage op een cel zonder vaste maat. */
    width: min(240px, 58vw);
  }
}

/* ---- sectiekop ---- */

.section-marketing {
  padding-top: 40px;
  padding-bottom: 40px;
}
/* h2 krijgt elders (in .card, zie de basisregels bovenaan dit bestand) een decoratief
   accentstreepje via h2::before — bedoeld voor de kaart-koppen van de bestaande schermen.
   De grote display-koppen hier (sectiekoppen + de twee banden) horen dat NIET te hebben —
   in de mockups staat gewoon platte, vette Bricolage Grotesque-tekst zonder streepje. */
.section-heading,
.cheat-copy h2,
.cta-band-copy h2 {
  display: block;
}
.section-heading::before,
.cheat-copy h2::before,
.cta-band-copy h2::before {
  content: none;
}

.section-heading {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.5rem + 1vw, 2.375rem);
  font-weight: var(--font-display-weight, 800);
  letter-spacing: var(--font-display-tracking, -0.02em);
  margin: 0 0 26px;
}

/* ---- Zo werkt het ---- */

.feature-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}
.feature-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 26px;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 12px;
  transition: transform 0.16s var(--spring), box-shadow 0.16s;
}
.feature-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.feature-icon {
  width: 46px;
  height: 46px;
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.feature-icon.tint-warm {
  background: var(--chip-hunter-bg);
  color: var(--accent-strong);
}
.feature-icon.tint-green {
  background: var(--ok-bg);
  color: var(--ok-tx);
}
.feature-icon .icon {
  width: 24px;
  height: 24px;
}
.feature-card h3 {
  font-size: 1.25rem;
  font-weight: 800;
  margin: 0;
}
.feature-card p {
  font-size: 1rem;
  line-height: 1.5;
  color: var(--muted);
  margin: 0;
}

@media (max-width: 860px) {
  .feature-grid {
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .feature-card {
    flex-direction: row;
    align-items: flex-start;
    gap: 14px;
    padding: 16px 18px;
    border-radius: var(--radius);
  }
  .feature-icon {
    width: 30px;
    height: 30px;
    border-radius: 999px;
    font-weight: 800;
    font-size: 1rem;
  }
  .feature-icon .icon {
    width: 16px;
    height: 16px;
  }
  .feature-card h3 {
    font-size: 1.06rem;
  }
  .feature-card p {
    font-size: 0.94rem;
  }
}

/* ---- Kies je kant ---- */

.kant-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}
.kant-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px;
  display: flex;
  gap: 22px;
  align-items: center;
}
.kant-copy {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}
/* Versie 1.12.0: was een ronde pil, net als .hero-badge. Zelfde schuine snede als de knoppen. */
.kant-tag {
  display: inline-flex;
  align-self: flex-start;
  font-size: 0.72rem;
  padding: 5px 15px 5px 12px;
  border-radius: 0;
  clip-path: polygon(0 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
}
.kant-tag.runners {
  background: var(--ok-bg);
  color: var(--ok-tx);
}
.kant-tag.hunters {
  background: var(--chip-hunter-bg);
  color: var(--chip-hunter-tx);
}
.kant-card h3 {
  font-family: var(--font-display);
  font-size: 1.625rem;
  font-weight: var(--font-display-weight, 800);
  letter-spacing: var(--font-display-tracking, -0.01em);
  margin: 0;
}
.kant-card p {
  font-size: 1rem;
  line-height: 1.55;
  color: var(--muted);
  margin: 0;
}

/* Derde variant (versie 1.11.0, alleen Smokkeljacht): het verhaal van de smokkelnacht als
   perkament-kaart tussen de hero en "Zo werkt het" (zie Main.dc.html). Vaste perkamentkleuren
   i.p.v. tokens — dit vlak hoort ook in het donkere thema licht en "oud papier" te blijven
   ogen, dat is precies het contrast-effect uit het ontwerp. */
.verhaal-kaart {
  background: #e8d9b0;
  border: 1.5px solid #c9a227;
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 34px 38px;
  color: #3b3222;
}
.verhaal-label {
  font-family: var(--font-label, inherit);
  font-size: 0.85rem;
  letter-spacing: 0.18em;
  color: #8a6f2e;
  margin-bottom: 8px;
}
.verhaal-titel {
  font-family: var(--font-display);
  font-weight: var(--font-display-weight, 800);
  letter-spacing: var(--font-display-tracking, -0.02em);
  font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem);
  color: #2a2418;
  margin: 0 0 14px;
}
.verhaal-kaart p {
  margin: 0 0 12px;
  font-size: 1.05rem;
  line-height: 1.6;
}
.verhaal-kaart p:last-child {
  margin-bottom: 0;
}
.verhaal-kaart em {
  font-style: italic;
}
/* Vierde merk (versie 1.17.0): de perkament-kaart hierboven ís het smokkelverhaal — "oud
   papier" hoort bij dát merk en zijn vaste kleuren blijven daar bewust staan. Voortvluchtig
   kreeg met het designpakket ook een verhaal-sectie, maar een crème vlak is op het violette
   merk het felste blok van de hele pagina (gezien op de 1280px-screenshot van de eerste
   render). Hier wordt het een donker opsporingsdossier op de eigen tokens, met een violette
   accentrand links. Specificiteit (0,2,0) wint van de regels hierboven, ongeacht
   bestandsvolgorde — zelfde patroon als de smokkel-feature-card-override bovenin dit bestand. */
[data-brand="voortvluchtig"] .verhaal-kaart {
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent);
  box-shadow: var(--shadow);
  color: var(--ink);
}
[data-brand="voortvluchtig"] .verhaal-label {
  color: var(--accent);
}
[data-brand="voortvluchtig"] .verhaal-titel {
  color: var(--ink);
}

/* Versie 1.12.0: was 150px vast. De kaartvisual is sindsdien een echte kaart met een raster,
   het spelgebied en drie genummerde drops — op 150px werd dat een postzegel waarin je niets
   herkent. Nu meegroeiend met de kaart, met een bovengrens zodat de tekst ernaast leesbaar
   blijft. */
.kant-visual {
  width: clamp(150px, 34%, 230px);
  flex: none;
}
.kant-visual svg {
  display: block;
  width: 100%;
  height: auto;
}
/* Versie 1.12.0: een echt toestel in plaats van een grijs blokje met een cijfer. Droeg ook nog
   vaste hexkleuren uit de lichte huisstijl (#1c1917, #a8a29b, #ffffff, #d6d2cb) — die klopten na
   de omslag naar donker nergens meer. Nu tokens, een frame met notch, en dezelfde twee HUD-tegels
   als het echte gejaagde-scherm. */
.phone-visual {
  position: relative;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-frame);
  padding: 20px 10px 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
  box-shadow: var(--shadow-lg);
}
.phone-notch {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  width: 44px;
  height: 4px;
  border-radius: 999px;
  background: var(--line-strong);
}
.phone-hud {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  width: 100%;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.phone-hud-tile {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  padding: 10px 6px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}
.phone-radar {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  opacity: 0.34;
  border: 0;
  pointer-events: none;
}
.phone-visual-label {
  position: relative;
  z-index: 1;
  font-size: 0.56rem;
  color: var(--faint);
}
.phone-visual-clock {
  position: relative;
  z-index: 1;
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 900;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.phone-visual-rows {
  display: flex;
  flex-direction: column;
  gap: 5px;
  width: 100%;
  padding: 0 6px 2px;
}
.phone-visual-row {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.7rem;
  color: var(--muted);
  font-weight: 600;
}
.phone-visual-row .dot {
  margin-right: 0;
  width: 7px;
  height: 7px;
}
.map-visual {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px;
  overflow: hidden;
}

@media (max-width: 860px) {
  .kant-grid {
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .kant-card {
    flex-direction: column;
    align-items: stretch;
    padding: 22px;
    border-radius: var(--radius);
  }
  .kant-visual {
    width: 130px;
    align-self: center;
  }
}

/* ---- Een spel in vijf bedrijven (Ronde 5, v1.1.0: Krimp toegevoegd tussen Drops/Finale) ---- */

.timeline {
  position: relative;
}
.timeline-line {
  position: absolute;
  left: 70px;
  right: 70px;
  top: 20px;
  height: 2px;
  background: var(--line-strong);
}
.timeline-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 20px;
}
.timeline-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
}
.timeline-dot {
  width: 42px;
  height: 42px;
  border-radius: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 6px var(--bg);
  flex: none;
}
.timeline-dot .icon {
  width: 19px;
  height: 19px;
  color: white;
}
.timeline-dot.tone-green {
  background: var(--accent-alt);
}
/* Versie 1.12.0: was `background: var(--ink)` — een wit vlak, uit de tijd dat Klopjacht licht
   was. In een donkere app is dat het felste puntje van de hele tijdlijn, precies op de twee
   stappen die het minst belangrijk zijn. Nu een lege ring: aanwezig, niet schreeuwend. */
.timeline-dot.tone-dark {
  background: var(--surface-2);
  border: 2px solid var(--line-strong);
  color: var(--muted);
}
.timeline-dot.tone-accent {
  background: var(--accent);
}
.timeline-step h3 {
  font-size: 1.125rem;
  font-weight: 800;
  margin: 0;
}
.timeline-step p {
  font-size: 0.9375rem;
  color: var(--muted);
  line-height: 1.45;
  margin: 0;
}

@media (max-width: 860px) {
  .timeline-line {
    display: none;
  }
  .timeline-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 22px 16px;
  }
}

/* ---- Anti-valsspeel (donker) — bewust een vaste donkere kaart, ongeacht licht/donker
   thema van de site zelf: precies zoals in beide mockups, geen theme-afhankelijke tokens. */

.cheat-band {
  background: #1c1917;
  border-radius: var(--radius-lg);
  padding: 44px 48px;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 40px;
  align-items: center;
}
.cheat-copy {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.cheat-copy h2 {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 1.4rem + 1.3vw, 2.5rem);
  font-weight: var(--font-display-weight, 800);
  letter-spacing: var(--font-display-tracking, -0.02em);
  color: #ffffff;
  line-height: 1.12;
  margin: 0;
}
.cheat-copy h2 span {
  color: var(--accent-2);
}
.cheat-copy p {
  font-size: 1.0625rem;
  line-height: 1.6;
  color: #d6d2cb;
  margin: 0;
}
.cheat-copy p strong {
  color: #ffffff;
}
.cheat-chips {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.cheat-chip {
  border: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent-2);
  color: var(--ink);
  border-radius: 0;
  padding: 8px 16px;
  font-size: 0.78rem;
}
.cheat-feed {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.cheat-notice {
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  padding: 16px 18px;
  display: flex;
  align-items: center;
  gap: 14px;
}
.cheat-notice .icon {
  width: 24px;
  height: 24px;
  flex: none;
}
.cheat-notice .icon.tone-warn {
  color: var(--accent-2);
}
.cheat-notice .icon.tone-ok {
  color: var(--accent-alt-2);
}
.cheat-notice p {
  font-size: 0.9375rem;
  color: #ffffff;
  line-height: 1.4;
  margin: 0;
}

@media (max-width: 860px) {
  .cheat-band {
    grid-template-columns: 1fr;
    padding: 24px 22px;
    gap: 20px;
    border-radius: var(--radius);
  }
  .cheat-copy p {
    font-size: 0.9375rem;
  }
  .cheat-feed {
    gap: 10px;
  }
}

/* ---- FAQ-teaser ---- */

.faq-teaser-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}
.faq-teaser-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 24px 26px;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 10px;
  transition: transform 0.16s var(--spring), box-shadow 0.16s;
}
.faq-teaser-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
}
.faq-teaser-card h3 {
  font-size: 1.125rem;
  font-weight: 800;
  line-height: 1.3;
  margin: 0;
}
.faq-teaser-card p {
  font-size: 0.97rem;
  line-height: 1.55;
  color: var(--muted);
  margin: 0;
}
.faq-teaser-more {
  display: flex;
  justify-content: center;
  margin-top: 20px;
}

@media (max-width: 860px) {
  .faq-teaser-grid {
    grid-template-columns: 1fr;
    gap: 14px;
  }
}

/* ---- Na afloop deel je de jacht (versie 1.0.0) ---- */

.replay-band {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 30px 34px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 30px;
  align-items: center;
}
.replay-band-lede {
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--muted);
  margin: 0;
}
.replay-band-points {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.replay-point {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}
.replay-point h3 {
  font-size: 1.0625rem;
  font-weight: 800;
  margin: 0 0 2px;
}
.replay-point p {
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--muted);
  margin: 0;
}

@media (max-width: 860px) {
  .replay-band {
    grid-template-columns: 1fr;
    padding: 22px 20px;
    gap: 20px;
    border-radius: var(--radius);
  }
}

/* ---- Het net sluit zich — letterlijk (Ronde 5, v1.1.0, onderdeel C: spelgebied/krimp als
   spanningselement) — hergebruikt de .replay-band-grid hierboven, alleen deze ene visual
   erbij. ---- */
.zone-visual {
  width: 132px;
  margin: 0 auto 18px;
  color: var(--accent);
}
.zone-visual svg {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- slot-CTA ---- */

.cta-band {
  background: var(--accent);
  border-radius: var(--radius-lg);
  padding: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 30px;
}
.cta-band-copy {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.cta-band-copy h2 {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.45rem + 1.3vw, 2.5rem);
  font-weight: var(--font-display-weight, 800);
  letter-spacing: var(--font-display-tracking, -0.02em);
  color: #ffffff;
  margin: 0;
}
.cta-band-copy p {
  font-size: 1.125rem;
  color: #ffe3d1;
  margin: 0;
}
.cta-band .btn {
  margin-top: 0;
  flex: none;
  white-space: nowrap;
  background: #ffffff;
  color: var(--accent-strong);
  box-shadow: 0 2px 6px rgba(28, 20, 10, 0.15);
  font-size: 1.1875rem;
  padding: 18px 34px;
}
.cta-band .btn:hover {
  filter: none;
  box-shadow: 0 4px 12px rgba(28, 20, 10, 0.22);
}

@media (max-width: 860px) {
  .cta-band {
    flex-direction: column;
    text-align: center;
    padding: 26px 22px;
    border-radius: var(--radius);
    gap: 14px;
  }
  .cta-band-copy h2 {
    font-size: 1.75rem;
  }
  .cta-band .btn {
    width: 100%;
    text-align: center;
  }
}

/* ---- "Heb je al een PIN?"-sectie (batch 5, punt 1): draagt het bestaande joinformulier
   (zelfde ids als voorheen, public/js/home.js blijft ongewijzigd werken) in de nieuwe
   landingsopmaak — niet in de mockups getekend maar functioneel verplicht. */

.join-section {
  max-width: 30rem;
  margin: 0 auto;
  scroll-margin-top: 24px;
}
.join-section .card {
  text-align: left;
}
.join-section-lede {
  text-align: center;
  color: var(--muted);
  margin: -8px 0 20px;
}

/* ---- footer ---- */

.site-footer-full {
  padding-top: 40px;
  padding-bottom: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
.footer-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--muted);
  font-size: 0.9375rem;
}
.footer-brand .brand-mark {
  width: 30px;
  height: 30px;
}
.footer-brand .brand-mark .icon {
  width: 17px;
  height: 17px;
}
.footer-links {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  font-size: 0.9375rem;
  font-weight: 600;
}
.footer-links a {
  color: var(--muted);
  text-decoration: none;
}
.footer-links a:hover {
  color: var(--ink);
}

@media (max-width: 860px) {
  .site-footer-full {
    flex-direction: column-reverse;
    text-align: center;
    justify-content: center;
    padding-top: 30px;
    padding-bottom: 36px;
  }
  .footer-links {
    justify-content: center;
    gap: 14px;
    font-size: 0.84rem;
  }
  .footer-brand {
    font-size: 0.78rem;
  }
}

/* ---- contentpagina's (/spelregels, /organiseren, /faq): zelfde nav/footer, verder de
   bestaande .page/.card-taal voor de leestekst — hier alleen kleine aanvullingen. ---- */

.content-lede {
  color: var(--muted);
  font-size: 1.05rem;
  margin: 0 0 20px;
}
.faq-item summary {
  cursor: pointer;
  font-weight: 700;
  list-style: none;
}
.faq-item summary::-webkit-details-marker {
  display: none;
}
.faq-item summary::after {
  content: "+";
  float: right;
  color: var(--muted);
  font-weight: 400;
}
.faq-item[open] summary::after {
  content: "−";
}
.faq-item p {
  margin: 10px 0 0;
  color: var(--muted);
  line-height: 1.55;
}
.faq-item + .faq-item {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

/* ---- utility-klassen: bewust helemaal onderaan (batch 5) ----
   .only-desktop/.only-mobile zijn puur voor kale <span>-tekstwissels zonder eigen
   `display`-regel (zie de footer-links in src/views/sitefooter.tsx) — daar is de plek in
   het bestand onschadelijk. Zet deze utility NOOIT samen met een klasse die zelf ook
   `display` zet (zoals .nav-links, die `display: flex` nodig heeft): bij gelijke
   specificiteit wint dan simpelweg wie het laatst in dit bestand staat, en dat repareert
   het ene breekpunt alleen door het andere kapot te maken — precies wat er hier eerder
   gebeurde (zie het commentaar bij .nav-links hierboven). Zo'n element krijgt in plaats
   daarvan zijn eigen, specifieke show/hide-regel in zijn media-query. */
.only-desktop {
  display: inline;
}
.only-mobile {
  display: none;
}
@media (max-width: 860px) {
  .only-desktop {
    display: none;
  }
  .only-mobile {
    display: inline;
  }
}

/* ---- versie 1.7.0 (veldtest), afgemaakt in 1.8.0: jagerscherm overzichtelijker ----
   Terugkoppeling uit het veld: "het is vooral kaart". De gebeurtenisbalk hieronder is een rij
   van het #tab-map-grid geworden; in 1.7.0 had hij geen `position` en geen `z-index` en
   schilderde de (absolute) kaart er domweg overheen, waardoor er op de Kaart-tab niets van te
   zien was. Twee tokens klopten toen ook niet: `--border` en `--text` bestaan niet in dit
   project (het zijn `--line` en `--ink`), en een onopgeloste var() maakt de hele declaratie
   ongeldig — de scheidingslijn viel dus stil weg en de tekstkleur erfde. */
.last-event-bar {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  margin: 0;
  padding: 9px 14px;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  background: var(--surface);
  color: var(--ink);
  box-shadow: none;
  font: inherit;
  font-size: 0.86rem;
  text-align: left;
  cursor: pointer;
}
.last-event-bar:hover {
  box-shadow: none;
  filter: none;
}
.last-event-bar:active {
  background: var(--surface-2);
}
/* Nood mag je nooit missen doordat je net op de kaart keek. */
.last-event-bar.nood {
  background: var(--danger-bg);
  color: var(--danger-tx);
}
.last-event-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.last-event-more {
  flex-shrink: 0;
  color: var(--accent);
  font-weight: 650;
}

/* Ongelezen-teller op de Feed-tabknop (versie 1.8.0): niets op de Kaart-tab vertelde dat er
   nieuwe gebeurtenissen waren, dus opende je de Feed op goed geluk — of nooit. */
.tab-icon-wrap {
  position: relative;
  display: inline-flex;
}
.tab-badge {
  position: absolute;
  top: -4px;
  right: -8px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 0.6rem;
  font-weight: 800;
  line-height: 16px;
  text-align: center;
}
.tab-badge[hidden] {
  display: none;
}
.tab-badge.nood {
  background: var(--danger);
}

/* Eerlijke uitleg onder de geluid/tril-knop (iPhone kan niet trillen) — compact, niet
   alarmerend, en alleen zichtbaar als public/js/signalen.js 'm invult. Bugfix B6 (versie
   1.8.0): wegklikbaar (public/js/signalen.js's #signalen-hint-close), dus nu een rijtje van
   tekst + kruisje in plaats van kale tekst. */
.signalen-hint {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin: 6px 0 0;
  font-size: 0.78rem;
  line-height: 1.35;
  opacity: 0.85;
}
.signalen-hint-close {
  flex: none;
  margin: -2px -4px 0 0;
  padding: 0 4px;
  background: none;
  border: none;
  box-shadow: none;
  color: inherit;
  font-size: 1.05rem;
  line-height: 1.3;
}
.signalen-hint-close:hover {
  box-shadow: none;
  filter: none;
  color: var(--ink);
}

/* ---- versie 1.7.0: uitklapbare locatie-uitleg (Safari/Chrome) ---- */
.locatie-hulp {
  margin-top: 10px;
  font-size: 0.85rem;
}
.locatie-hulp summary {
  cursor: pointer;
  font-weight: 650;
}
.locatie-hulp ol {
  margin: 8px 0 0;
  padding-left: 20px;
  line-height: 1.5;
}
.locatie-hulp li + li {
  margin-top: 4px;
}
.locatie-hulp .locatie-hulp-kop {
  margin: 10px 0 2px;
  font-weight: 650;
}

/* Stijlkiezer op het eindscherm (versie 1.9.0) — twee keuzes voor het deelfilmpje.
   Bewust GEEN <select> of instellingenscherm: de keuze gaat over wat je nú gaat versturen, dus
   hij staat vlak boven de knop en toont van allebei meteen waar je voor kiest. aria-pressed
   draagt de staat (end.js zet 'm), zodat het ook zonder de kleuren te zien duidelijk is. */
.stijlkiezer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: 10px;
  margin: 14px 0 4px;
}
.stijlkiezer button {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
  text-align: left;
  padding: 12px 14px;
  min-height: 44px; /* raakdoel */
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: inherit;
  cursor: pointer;
}
.stijlkiezer button strong {
  font-family: var(--font-display);
  font-size: 1rem;
}
.stijlkiezer button span {
  font-size: 0.82rem;
  color: var(--muted);
  line-height: 1.35;
}
.stijlkiezer button[aria-pressed='true'] {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.stijlkiezer button[aria-pressed='true'] strong::after {
  content: ' ✓';
  color: var(--accent);
}

/* ===========================================================================
   LANDING: BEELD EN BEWEGING (versie 1.12.0, tweede ronde)
   ===========================================================================
   Op de spelschermen geldt een harde accu-regel: alleen transform en opacity (zie de motionlaag
   hierboven). Op de landing is die reden er niet — daar is iemand een minuut, niet een avond —
   dus staat hier ook een enkele `stroke-dashoffset` waar dat de juiste tekening geeft. Alles wat
   doorloopt blijft klein van oppervlak, en alles staat uit onder `prefers-reduced-motion`.

   Scroll-reveal (`.reveal`) heeft één belangrijke eigenschap: de verborgen begintoestand geldt
   pas als public/js/home.js `data-reveal="on"` op <html> zet. Zonder JS — of met reduced-motion,
   want dan zet home.js het bewust niet aan — is de pagina gewoon zichtbaar. Een landingspagina
   die leeg blijft omdat een script niet laadde, is erger dan een landingspagina zonder animatie.
   --------------------------------------------------------------------------- */

html[data-reveal="on"] .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity 560ms var(--spring),
    transform 560ms var(--spring);
  transition-delay: var(--reveal-delay, 0ms);
}
html[data-reveal="on"] .reveal.is-in {
  opacity: 1;
  transform: none;
}

/* ---- de nav verdicht zodra je scrollt ---- */
.site-nav {
  transition:
    background-color var(--motion-base),
    border-color var(--motion-base);
  border-bottom: 1px solid transparent;
}
body[data-scrolled='1'] .site-nav {
  background: var(--nav);
  border-bottom-color: var(--line);
  backdrop-filter: saturate(160%) blur(14px);
  -webkit-backdrop-filter: saturate(160%) blur(14px);
}

/* ---- lichtveeg over de primaire knop ----
   Eén schuine streep die er bij hover overheen trekt. Puur transform op een pseudo-element binnen
   de al bestaande clip van de knop, dus hij blijft netjes binnen de schuine vorm.

   De :not()-keten is lang, en dat moet: elk `:not(.x)` telt mee voor de specificiteit, dus deze
   selector (0,9,1) wint van de `content: none`-uitzonderingen die verderop in dit bestand voor de
   icoon-, tab- en rijknoppen staan (0,1,0). Zonder de uitsluiting hier zou de veeg daar dus
   gewoon terugkomen — een lichtstreep over een tabblad of een duo-rij. Deze lijst hoort
   één-op-één gelijk te blijven aan die uitzonderingslijst; komt er een knop bij, dan in allebei. */
.btn:not(.secondary):not(.btn-outline):not(.icon-toggle-btn):not(.tab-btn):not(.signalen-hint-close):not(.map-follow-btn):not(.last-event-bar):not(.duo-deck-grip):not(.duo-row)::after,
button:not(.secondary):not(.btn-outline):not(.icon-toggle-btn):not(.tab-btn):not(.signalen-hint-close):not(.map-follow-btn):not(.last-event-bar):not(.duo-deck-grip):not(.duo-row)::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 40%;
  z-index: -1;
  background: linear-gradient(100deg, transparent, color-mix(in srgb, var(--on-accent) 26%, transparent), transparent);
  clip-path: var(--skew);
  transform: translateX(0);
  opacity: 0;
  transition: none;
  /* Cruciaal, en niet vanzelfsprekend: de veeg begint op `left: -60%`, dus hij steekt links
     BUITEN de knop uit. Zonder pointer-events:none vangt dat onzichtbare vlak de klikken op van
     wat daar toevallig naast staat — in de spelleider-balk maakte de veeg van "Spel stoppen" de
     pauzeknop ernaast onklikbaar. Gevonden door de UI-smoke-test, niet met het oog: het ding is
     op dat moment volledig doorzichtig. */
  pointer-events: none;
}
.btn:not(.secondary):not(.btn-outline):not(.icon-toggle-btn):not(.tab-btn):not(.signalen-hint-close):not(.map-follow-btn):not(.last-event-bar):not(.duo-deck-grip):not(.duo-row):hover::after,
button:not(.secondary):not(.btn-outline):not(.icon-toggle-btn):not(.tab-btn):not(.signalen-hint-close):not(.map-follow-btn):not(.last-event-bar):not(.duo-deck-grip):not(.duo-row):hover::after {
  opacity: 1;
  transform: translateX(420%);
  transition:
    transform 620ms var(--spring),
    opacity 120ms linear;
}
/* De secundaire knop heeft zijn ::after al als binnenvulling (zie de knopregels bovenaan dit
   bestand) — vandaar de :not() hierboven. Zonder die uitsluiting zou `width: 40%` uit de veeg
   die binnenvulling tot 40% terugbrengen: `inset: 2px` zet wél top/right/bottom/left maar geen
   width, dus dat ene longhand lekte erdoorheen en holde de knop uit.
   Een van die fouten die je pas ziet als je er met een liniaal naast gaat staan. */

/* ---- kaarten: hoekige lift ---- */
.feature-card,
.faq-teaser-card,
.kant-card {
  transition:
    transform var(--motion-base),
    border-color var(--motion-base);
}
.feature-card:hover,
.faq-teaser-card:hover,
.kant-card:hover {
  transform: translateY(-3px);
  border-color: var(--accent);
}

/* ===========================================================================
   De jagerskaart (src/views/landing-art.ts' HUNTER_MAP_ART)
   =========================================================================== */
.map-visual svg {
  display: block;
  width: 100%;
  height: auto;
}
.mv-bg {
  fill: var(--bg-2);
}
.mv-streets path {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 5;
  stroke-linecap: round;
}
.mv-zone {
  fill: color-mix(in srgb, var(--accent) 3%, transparent);
  stroke: color-mix(in srgb, var(--accent) 38%, transparent);
  stroke-width: 1.5;
  stroke-dasharray: 5 6;
}
.mv-zone-inner {
  fill: none;
  stroke: color-mix(in srgb, var(--accent) 22%, transparent);
  stroke-dasharray: 3 7;
}
.mv-route {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 4 6;
  opacity: 0.75;
}
/* De jagers zijn open ringen met een kern — onmiskenbaar iets anders dan een gevulde, genummerde
   drop. Bij Klopjacht is --hunter gelijk aan het accent, dus zonder verschil in VORM zou je op
   deze kaart niet zien wie waar staat. */
.mv-hunter-ring {
  fill: color-mix(in srgb, var(--bg) 70%, transparent);
  stroke: var(--hunter);
  stroke-width: 2;
}
.mv-hunter-core {
  fill: var(--hunter);
}
.mv-drop-dot {
  fill: var(--accent);
}
.mv-drop-nr {
  fill: var(--on-accent);
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 11px;
}
.mv-ping {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  opacity: 0;
}

/* De animatie start pas als de kaart in beeld is (.is-in wordt door home.js gezet), zodat je hem
   ook echt ziet gebeuren in plaats van hem te missen tijdens het scrollen. */
.map-visual .mv-route {
  stroke-dasharray: 160;
  stroke-dashoffset: 160;
}
.map-visual.is-in .mv-route {
  animation: mv-draw 900ms ease-out 160ms forwards;
}
@keyframes mv-draw {
  to {
    stroke-dashoffset: 0;
  }
}

.map-visual .mv-drop {
  /* Zonder fill-box schaalt een SVG-groep om de oorsprong van het hele viewBox. */
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(0.2);
  opacity: 0;
}
.map-visual.is-in .mv-drop {
  animation: mv-land 420ms var(--spring) forwards;
  animation-delay: calc(var(--mv-i) * 240ms + 420ms);
}
@keyframes mv-land {
  60% {
    transform: scale(1.12);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.map-visual.is-in .mv-ping {
  transform-box: fill-box;
  transform-origin: center;
  animation: radar-ping 2s cubic-bezier(0.1, 0.6, 0.3, 1) 1.3s infinite;
}

.map-visual .mv-hunter {
  opacity: 0;
}
.map-visual.is-in .mv-hunter {
  animation: mv-fade 500ms ease-out 1.1s forwards;
}
@keyframes mv-fade {
  to {
    opacity: 1;
  }
}

/* ===========================================================================
   Het krimpende spelgebied (ZONE_RINGS_ART)
   =========================================================================== */
.zv-ring {
  fill: none;
  stroke: var(--accent);
  transform-box: fill-box;
  transform-origin: center;
}
.zv-ring-outer {
  stroke-width: 2;
  opacity: 0.22;
}
.zv-ring-mid {
  stroke-width: 2;
  stroke-dasharray: 7 7;
  opacity: 0.5;
}
.zv-ring-inner {
  stroke-width: 2.5;
  opacity: 0.9;
}
.zv-core {
  fill: var(--accent);
}
.zv-runner {
  fill: var(--ink);
  transform-box: fill-box;
  transform-origin: center;
}

/* De buitenring trekt samen en begint opnieuw — het beeld van de sectie ernaast. De gejaagde
   loopt ondertussen zijn rondje en wordt zo ingehaald. */
.zone-visual.is-in .zv-ring-outer {
  animation: zv-krimp 6s ease-in-out infinite;
}
@keyframes zv-krimp {
  0%,
  12% {
    transform: scale(1);
    opacity: 0.22;
  }
  70%,
  100% {
    transform: scale(0.42);
    opacity: 0.62;
  }
}
.zone-visual.is-in .zv-runner {
  animation: zv-loop 6s linear infinite;
}
@keyframes zv-loop {
  from {
    transform: rotate(0deg) translateX(56px) rotate(0deg);
  }
  to {
    transform: rotate(360deg) translateX(56px) rotate(-360deg);
  }
}

/* ===========================================================================
   PREMIEJACHT — DE SCHERMEN (versie 1.19.0, batch 3 van docs/PLAN-VOORTVLUCHTIG.md)
   ===========================================================================
   V1 (kaartshell, beide rollen), V2 (uitzending-overlay, fullscreen), V3 (veilig-huis-paneel),
   N5-uitbreiding (rolwissel) en het eindscherm-scorebord. Alleen mode='premie' rendert deze
   klassen (src/views/runner.tsx/hunter.tsx/end.tsx) — klassiek/smokkel raken geen van deze
   regels. Motion: uitsluitend transform/opacity, zoals de rest van dit bestand. */

/* ---------- V1: de voortvluchtige-kaartshell (runner.tsx) ---------- */
/* Vervangt vanaf hier de kale premie-hud-card van batch 2: een eigen HUD-band (hergebruikt
   .hud/.hud-tile — hetzelfde vaste patroon als het klassieke gejaagde-scherm) boven een
   ingebedde (niet-fullscreen) kaart, met de primaire-actiezone eronder. */
.premie-tile-pot {
  align-items: stretch;
  text-align: center;
}
.premie-permin-line {
  margin: 0;
  font-size: 0.72rem;
  color: var(--muted);
  position: relative;
  z-index: 1;
}
/* Violet-naar-magenta balk onder de pot (het pakket: "een violet-naar-magenta balk"). Transform
   i.p.v. width, zoals de motionlaag hierboven voorschrijft. */
.premie-bar {
  width: 100%;
  height: 4px;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
  margin-top: 4px;
  position: relative;
  z-index: 1;
}
/* Geen literaire 0-100%-waarde bestaat voor een onbegrensd oplopende pot — de balk is hier een
   levend beeld ("loopt door"), geen voortgangsmeter: altijd vol, met een trage ademende puls
   zolang de pot groeit (opacity, geen box-shadow). */
.premie-bar-fill {
  display: block;
  height: 100%;
  width: 100%;
  transform-origin: left center;
  transform: scaleX(1);
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  animation: premie-bar-pulse 2.4s ease-in-out infinite;
}
@keyframes premie-bar-pulse {
  0%,
  100% {
    opacity: 0.75;
  }
  50% {
    opacity: 1;
  }
}
/* Bevroren (in een veilig huis, PLAN-punt 6): het bedrag dooft, de balk stopt zichtbaar
   ("stilstaande balk") en een slotje verschijnt — de "ruil" (tijd tegen premie) in één oogopslag. */
.premie-tile-pot.is-frozen .big-clock {
  color: var(--muted);
}
.premie-tile-pot.is-frozen .premie-bar-fill {
  animation: none;
  opacity: 1;
  background: var(--faint);
}
.premie-frozen-lock {
  display: none;
  align-items: center;
  gap: 4px;
  margin-top: 4px;
  font-size: 0.72rem;
  color: var(--warn-tx);
  position: relative;
  z-index: 1;
}
.premie-frozen-lock .icon {
  width: 13px;
  height: 13px;
}
.premie-tile-pot.is-frozen .premie-frozen-lock {
  display: inline-flex;
}
/* Signaaltegel: magenta zodra het signaal < 30s weg is — de kleurregel geldt hier onverkort
   ("iemand kan jou nu zien" is BINNENKORT waar). */
.premie-tile-signal .big-clock.urgent {
  color: var(--accent-2);
}

/* Magenta schermrand-gloed die aanzwelt zodra het signaal nadert (PLAN-punt 2) — een vast
   overlay-element, transform+opacity geanimeerd (nooit box-shadow, zie de motionlaag). Ligt
   ACHTER de HUD/kaart (lage z-index, pointer-events uit) zodat hij nooit iets blokkeert. */
.premie-edge-glow {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse at center, transparent 55%, color-mix(in srgb, var(--accent-2) 55%, transparent) 150%);
}
.premie-edge-glow.is-active {
  animation: premie-glow-swell 1.6s ease-in-out infinite;
}
@keyframes premie-glow-swell {
  0%,
  100% {
    opacity: 0.28;
    transform: scale(1);
  }
  50% {
    opacity: 0.62;
    transform: scale(1.015);
  }
}

/* De ingebedde V1-kaart — dezelfde donkere tegelbehandeling als de jagerskaart (.leaflet-tile-
   pane hierboven geldt voor elke Leaflet-instantie in de app), maar in een kaart, niet
   fullscreen: dit scherm draagt meer dan alleen de kaart (SOS, herstelcode, installatie, …). */
.premie-map-card {
  height: 46vh;
  min-height: 260px;
  max-height: 420px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
  margin: 10px 0;
  background: var(--bg-2);
  position: relative;
}
.premie-map-card #premie-map {
  width: 100%;
  height: 100%;
  position: relative;
  min-height: 0;
  /* Zelfde isolatie als hunter.tsx's #map hierboven — sluit Leaflets eigen z-index-schaal
     (panes 200-700, bedieningselementen 800-1000) op in dit element, anders schilderen de
     Leaflet-zoomknoppen zich BOVEN de V2-fullscreen-overlay (z-index 95 hieronder < 1000). */
  isolation: isolate;
}

/* De "primaire actiezone" onderin (PLAN-brief punt 2): geen knop — er is niets om in of uit een
   veilig huis te tikken, de motor detecteert dat zelf op het locatiepad. Dit is de bestaande
   .card-lijn met de actuele veilig-huis-status; V3 (hieronder) vervangt de inhoud zodra de
   speler er echt in staat. */
.premie-actiezone {
  border: 1px solid var(--line-strong);
}
.premie-actiezone .clock-label {
  display: block;
  margin-bottom: 6px;
}

/* ---------- V2: "Je wordt uitgezonden" — fullscreen, beide rollen ---------- */
/* Bewust GEEN overlay-in-animatie (die faded in): dit scherm valt er in ÉÉN keer overheen, het
   pakket is expliciet ("geen fade-in"). De styling is verder een eigen, compactere zusje van
   .gepakt-overlay: fullscreen magenta-op-zwart in plaats van rood-op-zwart. */
.uitzending-overlay {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: calc(24px + env(safe-area-inset-top)) 20px calc(24px + env(safe-area-inset-bottom));
  background: linear-gradient(165deg, color-mix(in srgb, var(--accent-2) 40%, black), black 78%);
}
.uitzending-inner {
  width: 100%;
  max-width: 420px;
  text-align: left;
}
.uitzending-badge {
  display: inline-block;
  font-family: var(--font-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-badge);
  font-size: 0.7rem;
  color: var(--accent-2);
  border: 1px solid color-mix(in srgb, var(--accent-2) 55%, transparent);
  padding: 4px 12px;
  border-radius: var(--radius-sm);
}
.uitzending-titel {
  font-size: clamp(2.4rem, 1.5rem + 4.6vw, 3.2rem);
  color: #fff;
  margin: 14px 0 10px;
}
.uitzending-lead {
  margin: 0 0 18px;
  color: color-mix(in srgb, #fff 82%, var(--accent-2));
}
.uitzending-cijferbox {
  border: 1px solid color-mix(in srgb, var(--accent-2) 45%, transparent);
  border-radius: var(--radius);
  padding: 16px;
  text-align: center;
  margin-bottom: 14px;
}
.uitzending-cijfer {
  font-family: var(--font-display);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  font-size: 76px;
  line-height: 1;
  color: #fff;
  display: inline-block;
  /* Laatste 5s: transform:scale() per seconde, gezet vanuit public/js/runner.js — nooit CSS-
     getimed, precies zoals de rest van dit spel op servertijd rekent. */
  transition: transform 160ms ease-out;
}
.uitzending-tegels {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 20px;
}
.uitzending-tegel {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.03);
}
.uitzending-tegel .big-clock {
  font-size: clamp(var(--size-hud), 1rem + 2.6vw, 1.8rem);
  color: #fff;
}
/* Geen knop voor de voortvluchtige — een gestreepte, niet-klikbare balk. Statisch (geen
   animatie): het is geen voortgang, het is een grens die je niet zelf kunt openen. */
.uitzending-streep {
  border: 1px dashed color-mix(in srgb, var(--accent-2) 55%, transparent);
  border-radius: var(--radius-sm);
  padding: 12px;
  text-align: center;
  font-family: var(--font-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  font-size: 0.74rem;
  color: color-mix(in srgb, #fff 70%, var(--accent-2));
  background: repeating-linear-gradient(
    135deg,
    rgba(255, 61, 154, 0.08),
    rgba(255, 61, 154, 0.08) 10px,
    transparent 10px,
    transparent 20px
  );
}
/* Jager-variant: compacter, één knop ("de jager mag wegtikken", PLAN-brief punt 3). */
.uitzending-overlay.is-jager .uitzending-inner {
  max-width: 380px;
}
.uitzending-overlay.is-jager .uitzending-cijferbox {
  display: none;
}
.uitzending-overlay.is-jager .uitzending-tegels {
  margin-bottom: 22px;
}

/* ---------- Live-marker op de jagerskaart tijdens het venster (V2) ---------- */
/* Fel, pulserend magenta — duidelijk anders dan de kalme .drop-marker: dit is een LEVEND punt
   dat meebeweegt, geen vastgelegd spoorpunt. */
.live-marker {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent-2);
  border: 3px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
  position: relative;
}
.live-marker::before,
.live-marker::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 2px solid var(--accent-2);
  pointer-events: none;
  animation: radar-ping 1.3s cubic-bezier(0.1, 0.6, 0.3, 1) infinite;
}
.live-marker::after {
  animation-delay: 0.65s;
}
@media (prefers-reduced-motion: reduce) {
  .live-marker {
    outline: 3px solid var(--accent-2);
  }
}

/* ---------- Spoor: laatste drie uitzendpunten (jagerskaart) ---------- */
/* De genummerde markers hergebruiken .drop-marker/.drop-marker-wrap ONGEWIJZIGD (public/js/
   hunter.js geeft ze de --route-kleur mee i.p.v. de teamkleur); dit hier is alleen de lijn
   ertussen. Leaflet tekent een polyline als SVG-<path> met een vast attribuutkleur (hunter.js
   geeft de hex direct mee, net als de bestaande zone-cirkel) — .spoor-lijn bestaat voor het
   dashArray/de dikte, niet voor de kleur. */
.spoor-lijn {
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- Grijze veilig-huis-ruit (beide kaarten) ---------- */
/* Locatie, geen status ("wáár, niet óf iemand er zit") — daarom neutraal grijs, nooit een
   merkkleur. Zelfde ruit-vorm als .checkpoint-marker, eigen kleur. */
.safehouse-marker {
  width: 22px;
  height: 22px;
  transform: rotate(45deg);
  border: 3px solid var(--faint);
  border-radius: 4px;
  background: color-mix(in srgb, var(--faint) 22%, var(--surface));
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}

/* ---------- V3: het veilig-huis-paneel (runner.tsx) ---------- */
.veilighuis-panel {
  border: 1px solid var(--ok-bd);
  position: relative;
  border-radius: var(--radius-sm);
}
.veilighuis-status-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.veilighuis-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent-alt);
  flex: none;
  position: relative;
}
.veilighuis-dot::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  border: 1.5px solid var(--accent-alt);
  animation: radar-ping 2s cubic-bezier(0.1, 0.6, 0.3, 1) infinite;
}
.veilighuis-bar {
  width: 100%;
  height: 6px;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
  margin: 8px 0 4px;
}
.veilighuis-bar-fill {
  display: block;
  height: 100%;
  width: 100%;
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform 0.9s linear;
  background: var(--accent-alt);
}
/* De ruil: tijdwinst tegenover bevroren premie — altijd zichtbaar, nooit achter een infoknop
   (PLAN-brief punt 5). */
.veilighuis-ruil {
  border: 1px solid var(--warn-bd);
  border-left: 3px solid var(--warn);
  background: var(--warn-bg);
  color: var(--warn-tx);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: 0.85rem;
  margin: 10px 0;
}
.veilighuis-jagers-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.82rem;
  color: var(--muted);
}
/* Voltooid: groene flash — één keer (public/js/runner.js zet/verwijdert de klasse zelf).
   UITSLUITEND transform/opacity (de motionlaag hierboven): geen background-color-animatie op
   het paneel zelf, maar een eigen overlay-pseudo-element (::after) die vervaagt — dezelfde
   truc als .premie-edge-glow eerder in dit blok. */
.veilighuis-panel.is-flash {
  animation: veilighuis-flash-scale 900ms ease-out 1;
}
.veilighuis-panel.is-flash::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--accent-alt);
  pointer-events: none;
  animation: veilighuis-flash-fade 900ms ease-out 1;
}
@keyframes veilighuis-flash-scale {
  0% {
    transform: scale(1);
  }
  40% {
    transform: scale(1.01);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes veilighuis-flash-fade {
  0% {
    opacity: 0.32;
  }
  100% {
    opacity: 0;
  }
}
/* Marge overschreden (>20 m): 5s waarschuwing vóór de voortgang vervalt. */
.veilighuis-margin-warning {
  border: 1px solid var(--danger-bd);
  border-left: 3px solid var(--danger);
  background: var(--danger-bg);
  color: var(--danger-tx);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: 0.85rem;
  margin-top: 10px;
}

/* ---------- Jager-HUD: premie-tegel (hunter.tsx) ---------- */
/* Vervangt de kale #premie-pot-line (batch 2) met een echte tegel — zelfde taal als .hud-tile,
   maar in de kopbalk-breedte i.p.v. de volle .hud-band (de jagerskop is fullscreen-tabs, geen
   page-scroll, zie DESIGN-OVERHAUL.md §3). "Volgende uitzending" (tegel 2 uit de PLAN-brief)
   bestond al als het grote #next-drop-clock-cijfer in de kopbalk zelf (batch 2) — dit hier is
   het bijbehorende zusje voor de pot, niet een tweede exemplaar van dezelfde aftelling. */
.hunter-premie-tiles {
  padding: 0 16px 10px;
}
.hunter-premie-tile {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.hunter-premie-tile .hunter-drop-value,
.hunter-premie-tile .big-clock {
  font-size: clamp(var(--size-hud), 1rem + 2vw, 1.7rem);
}

/* ---------- N5-uitbreiding: rolwissel (beide rollen) ---------- */
/* Hergebruikt .gepakt-overlay/.gepakt-inner/.gepakt-tegels/.gepakt-tegel (runner.tsx al, hunter.tsx
   krijgt er sinds deze versie een eigen exemplaar bij) — hier alleen de premie-specifieke
   varianten: 600ms i.p.v. de standaard 300ms (PLAN-brief: "600 ms animatie"), en de
   vanger-kant in violet (jij bent nu voortvluchtig) i.p.v. het rood/groen van klassiek/smokkel. */
.gepakt-overlay.rolwissel {
  animation-duration: 600ms;
}
.gepakt-overlay.is-vanger {
  background: linear-gradient(160deg, color-mix(in srgb, var(--accent) 34%, var(--bg)), var(--bg) 72%);
}

/* ---------- Eindscherm: premie-scorebord (end.tsx) ---------- */
.scorebord-winner {
  font-size: clamp(1.8rem, 1.2rem + 3vw, 2.6rem);
  margin: 4px 0 2px;
}
.scorebord-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.scorebord-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.scorebord-row:last-child {
  border-bottom: 0;
}
.scorebord-rank {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.1rem;
  color: var(--muted);
  width: 1.6em;
  text-align: right;
  flex: none;
}
.scorebord-row:first-child .scorebord-rank {
  color: var(--accent-2);
}
.scorebord-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.scorebord-row.is-self .scorebord-name {
  color: var(--accent);
  font-weight: 700;
}
.scorebord-jij {
  font-size: 0.72rem;
  color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.04em;
}
.scorebord-score {
  font-family: var(--font-display);
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  font-size: 1.05rem;
}

/* ===========================================================================
   EXTRACTIE — DE SCHERMEN (versie 1.20.0, batch 4B van docs/PLAN-VOORTVLUCHTIG.md)
   ===========================================================================
   /new's moduskeuze+missiebouwer, de missielijst/het extractiepaneel op het gejaagde-scherm, het
   leeftijdslabel/de missieteller/de GM-markers op het jagerscherm, de eindscherm-badge en (batch
   7, versie 1.23.0) de replaykaart/-tijdlijn. Alleen mode='extractie' rendert deze klassen —
   klassiek/smokkel/premie raken geen van deze regels. Kleuren uitsluitend via tokens (behalve de
   vier Leaflet-inline-stijlen in public/js/new.js/hunter.js/end.js — Leaflets SVG-renderer kan
   geen CSS-custom-property lezen, zie hun eigen kopjes). Motion: uitsluitend transform/opacity. */

/* ---------- /new: moduskeuze-radio's (versie 1.23.0: altijd een echte keuze op voortvluchtig.com) ---------- */
/* De radio zelf is onzichtbaar maar wél gefocust/bedienbaar (toetsenbord, screenreader) — de
   rand-highlight volgt puur CSS via de aangrenzende-sibling-selector hieronder, geen JS nodig
   voor de visuele kant. */
.mode-kaart {
  display: block;
  flex: 1;
  cursor: pointer;
  position: relative;
}
.mode-kaart input[type='radio'] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.mode-kaart-inner {
  margin: 0;
  border: 2px solid var(--line-strong);
}
.mode-kaart input:checked + .mode-kaart-inner {
  border-color: var(--accent);
}
.mode-kaart input:focus-visible + .mode-kaart-inner {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---------- /new: missiebouwer ---------- */
.missiebouwer-row {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px;
  margin: 12px 0;
  background: var(--surface);
}
.missiebouwer-row textarea {
  resize: vertical;
}

/* ---------- /new: opdrachtenblok (zesde variant, versie 1.21.0, batch 5, F1) ----------
   Een <details class="card"> — native inklapblok, standaard dicht, geen JS/CSP nodig (zelfde
   idee als .locatie-hulp/.faq-item elders in dit bestand). Scoped op het id (niet een generieke
   `.card > summary`-regel): er bestaat geen ander <details class="card"> op dit moment, maar een
   toekomstige zou deze stijl anders ongevraagd meekrijgen. */
#opdrachten-card > summary {
  cursor: pointer;
  font-weight: 700;
  font-size: 1.15rem;
  list-style: none;
}
#opdrachten-card > summary::-webkit-details-marker {
  display: none;
}
#opdrachten-card > summary::after {
  content: "+";
  float: right;
  color: var(--muted);
  font-weight: 400;
}
#opdrachten-card[open] > summary::after {
  content: "−";
}

/* ---------- gejaagde-scherm: missielijst ---------- */
/* Client-gebouwd door public/js/runner.js (buildMissieRow/updateMissieRow) — elke rij is een
   tikbare kop (.missie-row-btn) die een detailpaneel in-/uitklapt, zelfde "kop + inklapbaar
   detail"-idee als de duo-sheet op het jagerscherm, maar dan INLINE i.p.v. een los blad (deze
   lijst is nooit meer dan 3-8 regels, een sheet zou hier overkill zijn). */
#missies-list .missie-row {
  border-bottom: 1px solid var(--line);
  padding: 0;
}
#missies-list .missie-row:last-child {
  border-bottom: 0;
}
.missie-row-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 0;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.missie-icoon {
  font-size: 1.1rem;
  flex: none;
  width: 1.4em;
  text-align: center;
}
.missie-titel {
  flex: 1;
  min-width: 0;
}
.missie-status {
  flex: none;
  font-size: 0.78rem;
  color: var(--muted);
}
.missie-status.is-voltooid {
  color: var(--ok-tx);
  font-weight: 650;
}
.missie-detail {
  padding: 0 0 14px calc(1.4em + 10px);
}
.missie-omschrijving {
  color: var(--muted);
  margin: 0 0 10px;
}
.missie-code-form {
  display: flex;
  gap: 8px;
}
.missie-code-form input {
  flex: 1;
  min-width: 0;
}
.missie-code-status {
  margin-top: 8px;
}

/* ---------- gejaagde-scherm: kaartenstrip (zesde variant, versie 1.22.0, batch 6) ---------- */
/* Client-gebouwd door public/js/runner.js (buildKaartRow/updateKaartRow) — een rij per soort:
   icoon + titel/uitleg/resterend-aantal + een "Speel"-knop (data-confirm/confirmed()). Geen
   inklapbaar detail nodig (in tegenstelling tot .missie-row hierboven) — de uitleg past altijd
   op één regel, en er is niets in te vullen. */
#kaarten-list .kaart-row {
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid var(--line);
  padding: 10px 0;
}
#kaarten-list .kaart-row:last-child {
  border-bottom: 0;
}
.kaart-icoon {
  font-size: 1.1rem;
  flex: none;
  width: 1.4em;
  text-align: center;
}
.kaart-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.kaart-titel {
  font-weight: 650;
}
.kaart-uitleg {
  color: var(--muted);
  font-size: 0.82rem;
}
.kaart-status {
  color: var(--muted);
  font-size: 0.78rem;
}
.kaart-row button[data-kaart-speel] {
  flex: none;
}

/* ---------- gejaagde-scherm: extractiepaneel ---------- */
/* GEEN kaart (het huisprincipe) — #extractie-kompas hergebruikt gewoon .big-clock/.clock-label
   (zelfde visuele taal als het smokkel-kompas/de klassieke HUD hierboven), dus verder weinig
   eigen CSS nodig. */
#extractie-vergrendeld-tekst {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ---------- jagerscherm: signaalvertraging-chip ---------- */
/* Permanent (anders dan .zone-chip, die alleen tijdens een krimp verschijnt) — linksonder, waar
   niets anders op de kaart staat (.map-follow-btn is onderin-gecentreerd, de Leaflet-attributie
   rechtsonder, de zoomknoppen linksboven). */
.hunt-delay-chip {
  position: absolute;
  left: 8px;
  bottom: 8px;
  z-index: 900;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  color: var(--muted);
  border-radius: var(--radius-sm);
  padding: 5px 9px;
  font-size: 0.72rem;
  box-shadow: var(--shadow);
}

/* ---------- jagerscherm: missie-/extractiemarkers (lead-only) ---------- */
/* Vierkant, violet (--accent) — hoort bij GEEN specifiek duo (een missie is gedeeld), dus geen
   teamkleur zoals buildDropIcon/buildCheckpointIcon. */
.missie-marker {
  width: 24px;
  height: 24px;
  border-radius: 5px;
  background: var(--accent);
  color: var(--on-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 700;
  box-shadow: var(--shadow);
}
.missie-marker-wrap {
  background: none;
  border: none;
}
/* Cyaan (--route) — een routedoel, geen "iemand ziet je nu"-signaal (V2-kleurregel), zelfde
   accent als het spoor op de premie-jagerskaart. */
.extractie-marker {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--surface);
  border: 2px solid var(--route);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  box-shadow: var(--shadow);
}
.extractie-marker-wrap {
  background: none;
  border: none;
}

/* Zevende variant (versie 1.23.0, batch 7): legenda-stippen voor dezelfde markers op de
   REPLAYKAART (public/js/end.js) — .missie-marker/.extractie-marker hierboven zijn de
   Leaflet-inline-divIcon's zelf, deze twee zijn puur de kleine bolletjes in de legenda-tekst
   ernaast (zelfde patroon als .map-legend-dot.zone). */
.map-legend-dot.missie {
  border-radius: 3px;
  background: var(--accent);
}
.map-legend-dot.extractie {
  border-radius: 999px;
  border: 2px solid var(--route);
  background: var(--surface);
}

/* ---------- eindscherm-badge ---------- */
/* Zelfde gevaar-tint als .badge.caught — 'gemist' is net zo goed een verlies, geen half succes
   (zie public/js/runner.js's showGepaktOverlay-kop). */
.badge.gemist {
  background: color-mix(in srgb, var(--danger) 18%, transparent);
  border-color: var(--danger);
  color: var(--danger-tx);
}
/* Positieve badge (nu ook herbruikbaar voor "extractie vrij" in het duo-dek) — dezelfde
   --ok-tokens als .status-msg.ok hierboven, tot deze versie nog niet als badge-variant bestond. */
.badge.ok {
  background: var(--ok-bg);
  border-color: var(--ok-bd);
  color: var(--ok-tx);
}
