/* styles/base.css
   :root tokens, reset, scene-crossfade pattern, typography.
   Every other stylesheet depends on the variables defined here. */

:root {
  /* CAFÉ painterly placeholder — swap for raster image when ready */
  --cafe-image: linear-gradient(
    135deg,
    #3a2a1e 0%, #5a4030 25%, #8a6a4a 50%,
    #c89868 70%, #e8c890 90%, #f8e8c0 100%
  );

  /* Brand + Intent palette */
  --adyen-green:  #0abf53;   /* used only when Adyen actively helps */
  --intent-glow:  #f8e8c0;   /* the hopeful Intent's gold */
  --packet-glow:  #b8e0ff;   /* the post-Gateway packet-blue */
  --celestial:    #e8d8ff;   /* the Narrator's pale violet */
  --surface-bg:   #050810;   /* the void inside the system */

  /* Typography */
  --serif: 'Georgia', 'Iowan Old Style', serif;
  --mono:  'JetBrains Mono', 'Menlo', 'Consolas', monospace;

  /* Timing tokens — see 05_visual_audio_direction.md */
  --fade-default: 800ms;
  --fade-slow:    1600ms;

  /* ----- NARRATIVE SAFE ZONES -----
     Each biome keeps interactive content INSIDE the central rectangle
     so prose never collides with gameplay. A biome may opt into a
     reserved zone for visual content (e.g. the Foundry's conveyor in
     the top zone) only when the voice that owns that zone doesn't
     speak during that biome. See feedback_voices_typography.md. */
  --narrator-zone: 18vh;     /* top reserved for Narrator */
  --system-zone:   18vh;     /* bottom reserved for System voice */
  --voice-gutter:  24vw;     /* each side reserved for inner voices */

  /* ----- INTENT CAST-SHADOW (GDD §05 — data-trail signature) -----
     Every payment-card representation casts a soft downward shadow
     that grows as the Ledger's trust accumulates. The hook
     `body[data-trust-level]` is set in episode.js from ledger.trust.
     Per-biome cards consume this via `var(--intent-trust-shadow)` in
     their box-shadow comma list. The GDD specifies this shadow as
     the player's "data shadow" — European framing (NOT a credit
     score; a data trail you cast longer the more you exist). */
  --intent-trust-shadow: 0 0 0 transparent;

  /* ----- Z-INDEX SCALE -----
     Named tokens for the project's persistent chrome layers. Biome-
     internal z-indexes (1–10) stay as scoped literals — these tokens
     are only for cross-cutting concerns. Skip-link sits above the
     audio prompt (which sits above modals which sit above the HUD). */
  --z-hud:         30;       /* vitality HUD, journey stepper, wayfinding */
  --z-overlay:     40;       /* audio prompt */
  --z-modal:       60;       /* dossier, settings */
  --z-scenario:    50;       /* scenario select (between HUD and modal) */
  --z-skip-link: 1000;       /* must always be reachable first */
}

/* Utility: wrap a biome's interactive elements to auto-respect the
   central safe rectangle. Use as an absolutely-positioned container. */
.biome-stage {
  position: absolute;
  top:    var(--narrator-zone);
  bottom: var(--system-zone);
  left:   0;
  right:  0;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  width: 100%; height: 100%;
  overflow: hidden;
  /* Tinted near-black — never pure #000 (research lesson P3.1, dark-UI
     fatigue). Tinted slightly toward warm-neutral so it harmonises with
     biome backgrounds whose deepest stops are #0a0604, #0a0e16, etc. */
  background: #08070a;
  color: #f8f0e0;
  font-family: var(--serif);
}

#stage {
  position: relative;
  width: 100vw; height: 100vh;
  overflow: hidden;
  display: block;       /* it's a <main> now — restore block flow */
}

/* When the engine/orientation.js scaler is active (viewport smaller than
   the 1920×1080 design), the stage takes its native design size and the
   transform handles the visual fit. `body` gets `data-scaled="1"` from
   the JS; the transform itself lives inline on #stage. */
body[data-scaled="1"] #stage {
  width:  1920px;
  height: 1080px;
}
/* Hide the now-overflowing stage on the body itself when scaled, since
   the transform translates the stage to centre it and any overflow is
   intentional empty space (not content to scroll). */
body[data-scaled="1"] {
  overflow: hidden;
}

/* Skip-link — invisible until keyboard focus reveals it. */
.skip-link {
  position: fixed;
  top: -100px;
  left: 1vw;
  z-index: var(--z-skip-link);
  padding: 8px 16px;
  background: rgba(248, 232, 192, 0.98);
  color: #1a0e0a;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: 2px;
  transition: top 200ms ease;
}
.skip-link:focus-visible {
  top: 1vh;
  outline: 2px solid #1a0e0a;
  outline-offset: 2px;
}

/* ---------- AUDIO PROMPT (one-time hint) ----------
   Appears bottom-center 1.2s after page load IF the browser keeps the
   AudioContext suspended. Vanishes on any pointerdown / keydown. The
   game still works fine without sound — this is a non-blocking nudge. */
#audio-prompt {
  position: fixed;
  left: 50%;
  bottom: 6vh;
  transform: translateX(-50%) translateY(8px);
  z-index: var(--z-overlay);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 18px;
  background: rgba(10, 14, 22, 0.92);
  border: 1px solid rgba(184, 224, 255, 0.4);
  border-radius: 2px;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(184, 224, 255, 0.85);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  opacity: 0;
  transition: opacity 600ms ease, transform 600ms ease;
}
#audio-prompt.visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
#audio-prompt .ap-glyph {
  color: rgba(184, 224, 255, 0.95);
  font-size: 1rem;
  letter-spacing: 0;
}
@media (prefers-reduced-motion: reduce) {
  #audio-prompt { transition-duration: 200ms; }
}

/* The .scene crossfade pattern — every biome's root container is .scene */
.scene {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity var(--fade-default) ease;
  pointer-events: none;
}
.scene.active {
  opacity: 1;
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  .scene { transition-duration: 200ms; }
}

/* ---------- KEYBOARD FOCUS (project-wide) ----------
   :focus-visible only (not :focus), so mouse users never see a ring.
   Double-stack — white outline + soft dark halo — so it reads on any
   biome background. Per-element overrides can adjust offset for cases
   where the default 2px collides with a tight layout. */

button:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible,
.data-tile:focus-visible,
.slot-target:focus-visible,
.evidence-card:focus-visible,
.tx-btn:focus-visible,
.posture-btn:focus-visible,
.capture-choice:focus-visible,
.continue-gate:focus-visible,
.scenario-card:focus-visible,
#dossier-close:focus-visible,
.js-stop:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.92);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.55);
}

/* Tile and slot are getting promoted from div to button — neutralise
   default button chrome so existing styling still reads. */
.data-tile, .slot-target {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  color: inherit;
}

/* ---------- HOVER-AFFORDANCE HINTS (T128) ----------
   Small italic hint placed inside Foundry / Customs / Court to surface
   the tooltip pattern. Players miss hoverable details without a prompt.
   Shared base; each biome's CSS overrides the colour for palette match. */
.biome-hover-hint {
  font-family: var(--serif);
  font-style: italic;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  text-align: center;
  margin: 8px 0 4px 0;
  opacity: 0;
  transition: opacity 1200ms ease;
  pointer-events: none;
}
.scene.active .biome-hover-hint { opacity: 0.85; }

/* Per-biome palette overrides. */
#foundry-hover-hint {
  position: absolute;
  left: 50%;
  bottom: 16%;
  transform: translateX(-50%);
  z-index: 4;
  color: rgba(200, 152, 88, 0.85);
  text-shadow: 0 0 8px rgba(200, 152, 88, 0.25);
}
.customs-hover-hint {
  color: rgba(184, 212, 232, 0.78);
  margin-top: 6px;
}
.court-hover-hint {
  color: rgba(255, 220, 160, 0.75);
  margin-bottom: 10px;
}

/* =====================================================================
   THE PAYMENT CARD — shared visual identity for the protagonist.
   ---------------------------------------------------------------------
   Every in-scene representation of the Intent (Tolls card, Waiting
   card, Tide card) PLUS the HUD's mini-card glyph all share the same
   blue-gradient + chip-glow identity. They stay different SIZES per
   biome (the card is small on the Tolls salt-flat, larger in the
   intimate Waiting Room) but the visual SIGNATURE is consistent so
   the player recognises the same protagonist crossing scenes.

   Damage state is carried on the BODY (`data-payment-condition`) so
   any card in any scene reflects accumulated wear — the Waiting card
   shows worn-state desaturation if the player got there after Tolls,
   and the Tide card dissolves looking battered if a chargeback hit.
   ===================================================================== */

.payment-card {
  position: relative;
  background:
    linear-gradient(135deg,
      rgba(184, 224, 255, 0.55) 0%,
      rgba(120, 180, 220, 0.40) 100%);
  border: 1px solid rgba(184, 224, 255, 0.85);
  border-radius: 2px;
  box-shadow:
    0 0 8px 1px rgba(184, 224, 255, 0.40),
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    var(--intent-trust-shadow);
  /* Filter/clip-path transition smoothly so condition shifts feel like
     accumulated wear, not a state flip. */
  transition: filter 800ms ease, box-shadow 800ms ease, clip-path 800ms ease;
}

/* Per-trust-level cast-shadow lengths. Levels are set on `body` from
   ledger.trust in episode.js. The shadow grows downward with each
   level, reaching ~40px offset at trust >= 60 (3+ clean runs). */
body[data-trust-level="1"] {
  --intent-trust-shadow: 0 12px 8px  -2px rgba(232, 216, 255, 0.20);
}
body[data-trust-level="2"] {
  --intent-trust-shadow: 0 24px 12px -4px rgba(232, 216, 255, 0.26);
}
body[data-trust-level="3"] {
  --intent-trust-shadow: 0 40px 16px -6px rgba(232, 216, 255, 0.32);
}

/* ----- DAMAGE CONDITIONS -----
   Selector reaches every payment-card variant in the codebase via
   :is() — keeps the rule list flat and lets new cards join by adding
   class="payment-card" without revisiting this file. */

body[data-payment-condition="pristine"]
  :is(.payment-card, .tolls-intent-card, #waiting-intent, #tide-intent-card) {
  filter: saturate(1) brightness(1);
}

body[data-payment-condition="scuffed"]
  :is(.payment-card, .tolls-intent-card, #waiting-intent, #tide-intent-card) {
  filter: saturate(0.85) brightness(0.96);
  box-shadow:
    0 0 6px 1px rgba(184, 224, 255, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    var(--intent-trust-shadow);
}

/* WORN — one diagonal "scratch" via inset box-shadow + meaningful
   saturation drop. The box-shadow approach lets us add a visible mark
   without replacing the gradient background or fighting existing
   ::before/::after chip + stripe pseudos on per-biome cards. */
body[data-payment-condition="worn"]
  :is(.payment-card, .tolls-intent-card, #waiting-intent, #tide-intent-card) {
  filter: saturate(0.65) brightness(0.92);
  box-shadow:
    0 0 4px 0 rgba(184, 224, 255, 0.15),
    inset  6px  6px 0 -5px rgba(0, 0, 0, 0.35),
    inset -8px -8px 0 -7px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    var(--intent-trust-shadow);
}

/* BATTERED — heavier desaturation, dimmer glow, two inset scratches,
   and a clipped bottom-right corner notch (~6px) so the silhouette
   itself reads as damaged. No tilt (would push the design cartoonish
   per the impeccable BAN guidelines on bounce/cartoon motion). */
body[data-payment-condition="battered"]
  :is(.payment-card, .tolls-intent-card, #waiting-intent, #tide-intent-card) {
  filter: saturate(0.35) brightness(0.78);
  box-shadow:
    0 0 2px 0 rgba(184, 224, 255, 0.08),
    inset  10px  10px 0 -9px rgba(0, 0, 0, 0.45),
    inset -12px -12px 0 -11px rgba(0, 0, 0, 0.40),
    inset 0 1px 0 rgba(255, 255, 255, 0.06),
    var(--intent-trust-shadow);
  clip-path: polygon(
    0 0, 100% 0, 100% calc(100% - 6px),
    calc(100% - 6px) 100%, 0 100%
  );
}

@media (prefers-reduced-motion: reduce) {
  .payment-card { transition-duration: 200ms; }
}

/* ---------- FIRST-ENCOUNTER HINTS ----------
   Small inline mechanics-teach callouts fired ONCE per biome (gated by
   the Ledger's firstEncountered flags). Auto-dismiss after ~4.5s or
   first click. Subordinate to the biome's prose hierarchy — small mono,
   warm-amber, low-alpha so it reads as a hint, not a demand. */

.first-encounter-hint {
  position: absolute;
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: lowercase;
  color: rgba(255, 220, 160, 0.78);
  background: rgba(20, 14, 8, 0.85);
  border: 1px solid rgba(200, 152, 88, 0.35);
  border-radius: 2px;
  padding: 6px 14px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 500ms ease, transform 500ms ease;
  z-index: 18;
  white-space: nowrap;
  text-shadow: 0 0 8px rgba(255, 168, 100, 0.25);
}
.first-encounter-hint.fe-below { top: 100%; margin-top: 8px; }
.first-encounter-hint.fe-above { bottom: 100%; margin-bottom: 8px;
  transform: translateX(-50%) translateY(-4px); }
.first-encounter-hint.visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .first-encounter-hint { transition-duration: 200ms; }
}

/* =====================================================================
   GLOBAL MOBILE TOKENS (C7)
   ---------------------------------------------------------------------
   At 360px viewport the default voice-gutter (24vw = 86px each side)
   crushes any central-content row to 188px. The fix is to shrink the
   gutter to a thin strip — the inner voices use absolute positioning
   with pointer-events:none and z-index:22, so they continue to render
   in their margins even when central content overlaps them visually
   (they're translucent ambient text, not interactive UI). Each biome
   below picks up the wider central rectangle automatically.

   Reduced narrator + system zones too — at 18vh each on a 600px-tall
   phone, the central interactive area would be only 360px tall.
   ===================================================================== */
@media (max-width: 520px) {
  :root {
    --voice-gutter:   4vw;
    --narrator-zone: 10vh;
    --system-zone:   12vh;
  }
}
