/* B277 / base.css — html/body resets, .screen/.intro, .app/.topbar,
   hero/mascots/wordmark. Split from style.css 2026-05-24 (~400 LOC).
   Loaded AFTER tokens.css; before components.css → modals.css →
   screens.css → game.css → decorative.css → legacy-pantsy-7-8.css */


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

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-body);
  color: var(--text-on-deep);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  /* B406 FIX 1 (2026-05-29): kill the iOS Safari long-press
     copy/define/share callout app-wide. Pairs with the existing
     `-webkit-tap-highlight-color: transparent` (line 7) for the
     full "this is not a web page" feel. `touch-callout` only — NO
     global `user-select: none`, because room codes / player names
     must stay selectable (the app has intentionally never globally
     suppressed selection). The .cat-pill declaration at style.css
     remains in place as a harmless precedent. */
  -webkit-touch-callout: none;
  /* 2026-05-23 (Stian directive — no zoom, no alignment shift):
     `touch-action: manipulation` disables double-tap-to-zoom AND
     ditches the ~300ms click delay iOS Safari adds to wait for a
     potential second tap. Pinch-zoom and pan still allowed because
     they live at the gesture level, not touch-action; the viewport
     meta + JS gesturestart listener handle those. `pan-y` would
     also kill horizontal scroll but our share-card carousel needs
     it; `manipulation` is the right cut. */
  touch-action: manipulation;
  /* Text-size auto-bumps on iOS Safari when content reads as "too
     long" — disable so our font tokens land at their intended size. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
/* D (Bølge 1.7 / 2026-05-07): full-height background fix. Earlier the gradient
   sat on body with min-height: 100dvh, which on iOS Safari (URL bar visible)
   shrinks to less than the visible viewport — the area behind the URL bar
   showed only solid html bg-color (no gradient), so a tone discontinuity
   appeared at the top/bottom edges when scrolling.
   Fix: gradient on html (covers full viewport, including under URL bar +
   safe-area + bounce-scroll). body becomes transparent, so the gradient is
   what paints. body keeps 100dvh for content-flow sizing.
   GAME SHOW bakgrunn — radial gradient fra varm aubergine på toppen til
   near-black i bunnen. */
html {
  background-color: var(--surface-deep);
  /* B402 (2026-05-27): switched radial → linear vertical gradient.
     The radial ellipse at 50% 0% used `farthest-corner` sizing, so the
     bottom EDGE (not corners) was at an intermediate color brighter than
     var(--surface-deep). Where that intermediate edge met the iOS Safari
     safe-area bg (painted from bg-color = var(--surface-deep) solid), a
     visible band appeared at the bottom of the visible viewport. Linear
     vertical gradient guarantees the bottom edge is exactly at the 100%
     stop (var(--surface-deep)), matching bg-color → no band.
     Design intent ("varm aubergine på toppen til near-black i bunnen")
     is top-to-bottom, which linear expresses more honestly than radial.
     Cross-screen fix — every screen that relies on the html bg benefits. */
  background-image: linear-gradient(180deg, #3A0E2A 0%, var(--ink) 50%, var(--surface-deep) 100%);
  background-attachment: fixed;
  min-height: 100%;
}
body {
  background: transparent;
  /* B405-fix3 (2026-05-29): REMOVED `height:100% + overflow:hidden`. That
     was the bug Stian's screen-recording exposed — a global scroll-lock I
     added to kill bounce. On iOS, `overflow:hidden` + `height:100%` on body
     interferes with `position:fixed` viewport anchoring, so EVERY fixed
     shell (.app, .sheet-overlay, .wt-overlay) came up short at the bottom →
     the dead band on home, lobby, profile sheet AND the demo at once.
     It's also unnecessary: `.app` is now position:fixed (no in-flow body
     content to scroll), and bounce is already handled by the pre-existing
     `overscroll-behavior-y: none` below + `#screen { overscroll-behavior:
     contain }`. body reverts to its original content-flow sizing. */
  min-height: 100vh;  /* fallback for browsers without dvh */
  min-height: 100dvh; /* B405-fix7: browser/mobile-Safari uses dvh so the shell
                         tracks the URL bar and never overshoots → not scrollable
                         when content fits. PWA standalone overrides this to
                         100vh below (static — no iOS dvh first-paint settle). */
}

/* B176 B3 + B4 (2026-05-13): block rubber-band scroll bleed-through behind
   modals (B3) AND pull-to-refresh during gameplay (B4). The original
   `overscroll-behavior-y: contain` on the shell stopped the at-edge
   gesture from propagating to the browser chrome (no PTR reload, no
   chained scroll into the previous page) but STILL showed iOS Safari's
   own visual rubber-band on the page itself — so a screen whose content
   fit the viewport could still be pulled down/up and snap back.
   2026-05-23 (Stian directive — "when everything on one screen can fit
   on that screen, can we make it non-scrollable? both in normal mode
   and in PWA?"): stepped up from `contain` to `none`. `none` keeps the
   PTR + chained-scroll blocks of `contain` AND additionally suppresses
   the visual rubber-band, so a fitting screen feels pinned like a
   native app. When content DOES overflow (long lobby, profile sheet),
   normal scrolling still works — `overscroll-behavior` only controls
   boundary behavior, not whether the element scrolls.
   The modal-internal `overscroll-behavior: contain` on `.onboard-overlay`
   lives inside the canonical block at line ~2190 to avoid splitting the rule. */
html, body {
  overscroll-behavior-y: none;
}

/* GAME SHOW typografi — display på alt stort, Inter Tight på body. */
.brand,
.hero h1,
h2,
.code-display,
.reveal-card .word,
.recap-card .player-name,
.timer span:not(.label) {
  font-family: var(--font-display);
  letter-spacing: var(--track-tightest) /* was -0.035em — B321 §7A */;
  font-weight: var(--fw-bold);
}

/* Mono brukes for 4-tegns rom-koder + technical readouts */
.code-display,
input.code-input {
  font-family: var(--font-mono);
  letter-spacing: var(--track-caps-wide);
  font-weight: var(--fw-bold);
}

.app {
  max-width: 480px;
  margin: 0 auto;
  /* B405-fix5 (2026-05-29): growable `min-height` shell — fills the viewport
     when content fits (margin-top:auto footer pins to the bottom) and grows
     past it when content overflows (the DOCUMENT scrolls). No JS, no fixed
     positioning, no overflow lock — all of those were failed detours this day.
     The unit is `vh`, NOT `dvh`: on iOS PWA `dvh` resolves SHORT on the very
     first paint and only corrects after a reflow, so the footer rendered ~one
     home-indicator high and snapped down only when a modal forced a relayout
     (the "open/close the QR and it pins" symptom). `vh` is the static
     large-viewport unit = the full screen in standalone, stable from paint #1,
     so the footer is pinned with no reflow and no visible jump. (Prior detours:
     `--app-vh = innerHeight` pinned a too-short height → dead band; a post-mount
     JS reflow settled it but a frame late → visible jump.) */
  min-height: 100vh;  /* fallback for browsers without dvh */
  min-height: 100dvh; /* B405-fix7: browser/mobile-Safari uses dvh so the shell
                         tracks the URL bar and never overshoots → not scrollable
                         when content fits. PWA standalone overrides this to
                         100vh below (static — no iOS dvh first-paint settle). */
  display: flex;
  flex-direction: column;
  /* env(safe-area-inset-*) handles the iPhone notch / dynamic island (top)
     and the home-indicator (bottom). */
  padding:
    calc(env(safe-area-inset-top, 0px) + var(--space-16))
    max(var(--space-12), env(safe-area-inset-left))
    var(--space-12)
    max(var(--space-12), env(safe-area-inset-right));
}
/* B405-fix4 (2026-05-29): PWA bottom margin = the same 12px as the sides,
   measured ABOVE the home-indicator safe area (env-bottom clears the pill,
   +12 gives the matching breathing room). Browser mode keeps the base 12px
   (env-bottom = 0 there). */
@media (display-mode: standalone) {
  /* B405-fix7 (2026-05-29): PWA uses static 100vh (= full screen, no URL bar)
     — avoids the iOS dvh first-paint settle bug that left the footer unpinned
     until a reflow. Browser keeps the base 100dvh (tracks the URL bar, no
     overshoot/scroll). */
  body, .app {
    min-height: 100vh;
  }
  .app {
    padding-bottom: calc(env(safe-area-inset-bottom) + var(--space-12));
  }
}

/* B405-fix6 (2026-05-29, Stian): screens that END in a quiet secondary CTA
   (home "Rules ↘", lobby footer trio, round/game-end links) drop the 12px
   bottom breathing — the secondary CTA pins flush to the safe edge. Scoped
   via :has() so it hits ONLY those screens; every screen ending in a primary
   CTA keeps the 12px bottom = sides. Browser: 0. PWA: env() only (clears the
   home-indicator pill, no extra 12). iOS Safari 15.4+ :has() (already relied
   on by body:has(.sheet-overlay) elsewhere). */
.app:has(.lobby-text-action, .result-skip-cta, .result-end-game-link) {
  /* B405-fix8 (2026-05-29, Stian): SYMMETRY. The space above the secondary CTA
     (join-row → link box) measures 20px; the box centres its text (≈9px each
     side). So we set the below-gap to 20px too → the text reads 29px above /
     29px below, balanced. This is a deliberate ~14px under-shoot of iOS's
     conservative 34px env safe-area (the asymmetry source) — 20px still clears
     the home-indicator pill (~13px from the edge), so the quiet link isn't
     under the line. Fixed 20px applies in both PWA and browser (env-relative
     isn't needed; the pill position is constant). NOTE: the box's lower edge
     now sits inside the home-indicator gesture band — if taps near the very
     bottom of "Regler" ever feel deferred, bump to var(--space-24). */
  padding-bottom: var(--space-20);
}

/* Større padding på iPad og oppover */
@media (min-width: 600px) {
  .app { padding-left: var(--space-20); padding-right: var(--space-20); }
}

/* ---------------- Top bar — GAME SHOW ---------------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-8);
  font-size: var(--fs-base);
  color: color-mix(in srgb, var(--surface-card) 70%, transparent);
  /* B230 (2026-05-17): the global AppBar is position:static by default,
     so the Pekekaos strobe overlay (body.peke-kaos-active::before —
     position:fixed; z-index:0; opaque radial-gradient covering inset:0)
     painted ABOVE it per CSS paint order, hiding the "Pantsy" wordmark +
     profile button on the Point-at-Pantsy screen. position:relative
     creates the stacking context so z-index applies; z-index:10 matches
     the design-pack AppBar (.p9-appbar) and sits above the strobe (z:0)
     while staying below modals/sheets (z:320/350) so they still cover
     the bar correctly. The L13412 "below the topbar's z-index" comment
     finally has its referenced z-index. */
  position: relative;
  z-index: 10;
}
.topbar-right { display: flex; align-items: center; gap: var(--gap-sm); }
.topbar .brand {
  display: flex;
  align-items: center;
  gap: var(--gap-xs);
  /* Display-font på wordmark — Bricolage Grotesque, ikke samme som body */
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  letter-spacing: var(--track-tight);
  color: var(--text-on-deep);
  font-size: var(--fs-md);
}
.topbar .brand .flame {
  display: inline-block;
  filter: drop-shadow(0 0 8px var(--primary-glow));
}
/* Brand-pantsy span gets specifikt display-font weight i game show — */
.topbar .brand .brand-pantsy {
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
}
.lang-toggle, .icon-btn {
  background: color-mix(in srgb, var(--overlay-white) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--overlay-white) 15%, transparent);
  color: white;
  border-radius: var(--radius-pill);
  padding: var(--space-6) var(--space-12);
  font-size: var(--fs-sm);
  cursor: pointer;
}
.icon-btn { padding: var(--space-6) var(--space-10); font-size: var(--fs-base); }
.icon-btn.muted { opacity: 0.5; }

/* Profil-knapp øverst til høyre — match Claude Designs AppBar-avatar.
   Subtle dark-transparent bg + 1px hvit border + cream-tekst (ikke solid fire).
   Tidligere var den var(--primary) som var altfor påtrengende. */
.profile-btn {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  background: var(--surface-hi);
  border: 1px solid color-mix(in srgb, var(--overlay-white) 8%, transparent);
  color: var(--text-on-deep);
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-sm);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  letter-spacing: var(--track-none);
  padding: 0;
  transition: transform var(--dur-fast) var(--ease-default), background var(--dur-fast) var(--ease-default);
  box-shadow: none;
}
@media (hover: hover) {
  .profile-btn:hover { background: color-mix(in srgb, var(--overlay-white) 10%, transparent); }
}
.profile-btn:active { transform: scale(0.96); }

/* ---------------- Screens ----------------
   `#screen` (i index.html) er parent for alle render()-cycle-screens.
   Den må ha flex:1 og display:flex slik at child .screen får full høyde
   og kan plassere bottom-CTA pinnet via flex-spacer eller margin-top:auto. */
#screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* B405-fix4 (2026-05-29): REVERTED — removed the `overflow-y: auto` scroll
     container I added in B405. The B402 model scrolls at the DOCUMENT level
     (.app min-height:100dvh grows past the viewport when content overflows),
     not inside #screen. Making #screen its own scroller is what stopped the
     document model from working. Back to the proven passthrough. */
}
.screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--gap-base);
}
/* PRINSIPP 1: bottom-CTA er ALLTID pinnet til bunnen av skjermen.
   Helper-class `.pin-bottom` på en CTA pusher den ned via margin-top:auto.
   PRINSIPP 2: hovedkortet på 03/04/05/06/07 har flex:1 så det vokser til
   å fylle all plass mellom topp-eyebrow og bottom-CTA — se .reveal-card,
   .handover-card, .clue-floor-card og .pre-reveal-card. */
.screen > .pin-bottom,
.screen .pin-bottom {
  margin-top: auto;
}
.screen > .btn:last-child {
  /* Knapper som direkte siste-barn av .screen får automatisk pin-bottom
     hvis det ikke allerede er en flex:1-search før dem. Dette gjelder
     reveal-screen, clue-screen, etc. */
  flex-shrink: 0;
}
/* slideIn-animasjonen er beholdt men bare anvendt på .screen.intro,
   som settes KUN ved fase-skifte (ikke per re-render). Tidligere fikk
   .screen alltid animation, som re-startet på hver state-oppdatering
   og skapte synlig "blink-in" på iOS Safari. */
/* 2026-05-23: `both` fill-mode added below. Without it the new screen's
   first paint uses base styles (opacity 1) BEFORE the animation kicks
   in at its `from` state (opacity 0), which iOS Safari can render as a
   1-frame flash of the new content at full opacity before the fade-in
   plays — Stian's "glimpse of an old screen" report on cue→discuss.
   `both` applies `from` immediately on mount and holds `to` after the
   animation ends, so the only visible motion is the intended slideIn.
   will-change hints the compositor to keep opacity + transform on the
   GPU for the duration. */
.screen.intro {
  animation: slideIn .28s cubic-bezier(.32, .72, .32, 1) both;
  will-change: opacity, transform;
}
/* 2026-05-23 (Stian persisted cue→clue glitch — fifth attempt):
   .screen-freezing freezes every running CSS animation on the leaving
   screen the instant the user taps Got it / I'm ready to bluff. Pauses
   the reveal-card's infinite-loop animations (revealFlameFlicker,
   revealPulse, pantsy-name-flicker, smarty-glow) so when
   replaceChildren swaps the DOM 100-300ms later, there's no mid-cycle
   animation state for iOS Safari's compositor to paint as a ghost
   frame between the old + new screen. The class is added by tapReady
   in render.js (line ~2990). The new screen mounts via replaceChildren
   which removes the leaving screen entirely — the paused state never
   re-appears. */
.screen.screen-freezing,
.screen.screen-freezing * {
  animation-play-state: paused !important;
}
@keyframes slideIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---------------- Hero (home) — GAME SHOW ----------------
   Ny mascot-sone: SVG-flamme + SVG-jeans side-ved-side, ingen emoji.
   "Pants on Fire" wordmark i Bricolage med per-bokstav drop-in. */
.hero {
  text-align: center;
  /* Komprimert padding så hjem-skjermen passer uten scroll på mindre telefoner.
     Tidligere 24x16x20 → 8x16x4. */
  padding: var(--space-8) var(--space-16) var(--space-4);
}
.hero .logo {
  font-size: var(--fs-mega);
  line-height: var(--lh-flat);
  filter: drop-shadow(0 6px 20px var(--primary-glow));
  animation: flicker 2s ease-in-out infinite alternate;
}
.hero .logo-img {
  display: block;
  margin: 0 auto;
  width: 96px;
  height: 96px;
  border-radius: 22px;
  animation: none;
  filter: drop-shadow(0 8px 24px var(--primary-glow));
  -webkit-user-select: none;
  user-select: none;
}
@keyframes flicker {
  0%   { transform: rotate(-2deg) scale(1); }
  50%  { transform: rotate(2deg) scale(1.04); }
  100% { transform: rotate(-1deg) scale(1); }
}

/* GAME SHOW mascot composer — flame venstre, jeans høyre, baseline-aligned
   D-001, D-007 (Pantsy-8 user-decision): hero illustration was visually smaller than
   design's commanding mid-screen presence. Bumped height 130→170 — gives the SVG
   children room to render at their natural larger size (which is set via attributes
   on each SVG element directly to avoid clashing with the strut/rock animations). */
.hero-mascot {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 0;
  height: 170px;
  margin: var(--space-4) auto var(--space-8);
  position: relative;
  margin-left: -8px;
}
.hero-mascot .mascot-flame {
  transform-origin: 50% 100%;
  animation: heroFlameStrut 1.6s ease-in-out infinite;
  margin-right: -18px;
  margin-bottom: var(--space-4);
}
.hero-mascot .mascot-flame svg {
  display: block;
  filter: drop-shadow(0 0 14px var(--primary));
  transform-origin: 50% 100%;
  animation: heroFlameBlaze 0.6s ease-in-out infinite;
}
.hero-mascot .mascot-jeans {
  transform-origin: 50% 100%;
  animation: heroJeansRock 2.4s ease-in-out infinite;
  filter: drop-shadow(0 6px 12px color-mix(in srgb, var(--shadow-black) 40%, transparent));
}
.hero-mascot .mascot-jeans svg { display: block; }
@keyframes heroFlameStrut {
  0%, 100% { transform: translateY(0) rotate(-3deg); }
  50%      { transform: translateY(-6px) rotate(4deg); }
}
@keyframes heroFlameBlaze {
  0%, 100% { transform: scaleY(1) scaleX(1); opacity: 1; }
  33%      { transform: scaleY(1.08) scaleX(0.96); opacity: 0.92; }
  66%      { transform: scaleY(0.95) scaleX(1.04); opacity: 1; }
}
@keyframes heroJeansRock {
  0%, 100% { transform: rotate(-3deg); }
  50%      { transform: rotate(3deg); }
}

/* GAME SHOW per-letter drop-in for "Pants on Fire" */
.hero h1 {
  /* Tighter margin over og under wordmark — komprimerer hele hero-zonen */
  margin: 2px 0 2px;
  font-family: var(--font-display);
  font-weight: var(--fw-bold);
  font-size: calc(clamp(38px, 12vw, 48px) * var(--text-scale));
  letter-spacing: var(--track-tightest) /* was -0.04em — B321 §7A drift-elim */;
  line-height: var(--lh-ultra);
  color: var(--text-on-deep);
}
.hero h1 .letter {
  display: inline-block;
  white-space: pre;
  animation: letterDrop 0.5s cubic-bezier(.2, .9, .3, 1.2) both;
}
.hero h1 .letter.fire-pulse {
  animation: letterDrop 0.5s cubic-bezier(.2, .9, .3, 1.2) both,
             letterFirePulse 6s ease-in-out infinite;
  animation-delay: 0s, 1.6s;
}
@keyframes letterDrop {
  0%   { transform: translateY(-14px); opacity: 0; }
  100% { transform: translateY(0);     opacity: 1; }
}
@keyframes letterFirePulse {
  0%, 92%, 100% { color: var(--text-on-deep); text-shadow: none; }
  94% { color: var(--primary);   text-shadow: 0 0 14px color-mix(in srgb, var(--primary) 55%, transparent); }
  96% { color: var(--gold);      text-shadow: 0 0 18px color-mix(in srgb, var(--gold) 65%, transparent); }
  98% { color: var(--primary);   text-shadow: 0 0 14px color-mix(in srgb, var(--primary) 55%, transparent); }
}

.hero p.tagline {
  /* D-002 (Pantsy-8 Batch 22): tagline "A bluffing game. One liar, everyone else hunts."
     wraps to 2 lines in app at 14px / 280px max-width; design fits it on 1 line.
     Reduce font 14→12.5 and bump max-width 280→340 so the full tagline fits horizontally. */
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin: var(--space-6) auto 0;
  max-width: 340px;
  line-height: var(--lh-cozy) /* was 1.35 — B318 §5 kollaps */;
}

