/* Phase 17 [LOKA-02] [D-13]: bare hex/px swept to --loka-* tokens. */
/*
 * frontend/world/style.css -- World UX Shell base layout.
 * Reseeded from haebit_vision_prototyping/prototype_v2/style.css on 2026-04-24
 * per D-33 Composite Layout Contract. Line-range comments below map to prototype.
 *
 * Shadow-DOM components own their own per-component CSS inside their JS module;
 * this file covers light-DOM layers only: #phone-shell, #boot, .world, .ambient-life,
 * .volley, .scene-fade, .tc-layer container, sprite-template-related rules.
 * Phase 18.1: ceremony and wrap-up overlay rules deleted; owned by <fof-transition>.
 *
 * Tokens are sourced from /shared-static/design-tokens-app.css (already defines the full
 * prototype :root palette: --cream, --cream-dark, --cream-dim, --ink, --ink-soft,
 * --border, --accent, --accent-deep, --indigo, --indigo-deep, --gold, --gold-soft,
 * --gold-deep, --shadow, --red, --red-soft, --phone-w, --void). No token duplication
 * here.
 */

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

/* Volley-local helper -- preserved from prior shipped shell. */
.hidden { display: none !important; }

/* =========================================================================
 * Locale-aware typography (Spec: [I18N-01] [I18N-02])
 *
 * `Press Start 2P` (the pixel display font used across chrome) has no
 * Hangul glyphs, so Korean text under it falls back to a system font and
 * is illegible at <12px. When the active language is Korean (set on
 * <html lang="..."> by window.i18n.setLocale), swap chrome typography to
 * `Noto Sans KR` (already loaded via Google Fonts) and lift any tiny
 * font-size to a Korean-legible floor.
 *
 * Best-practice CJK readability floors (compact UI):
 *   - body / label / button: >= 12px
 *   - chip / micro-caption: >= 11px
 *   - title / display: existing sizes are fine, only swap family
 *
 * Shadow-DOM components mirror this with `:host-context([lang="ko"])` rules.
 * ========================================================================= */

html[lang="ko"] {
  font-family: "Noto Sans KR", "Nanum Gothic Coding", sans-serif;
}

/* Default body font for the lobby is preserved (DM Sans / etc.); per-element
 * Press Start 2P swaps are below — narrow override, won't bleed into the
 * pixel-aesthetic display headings where Press Start 2P still resolves
 * naturally because the headlines are English-only or use display weight. */

html[lang="ko"] #desktop-note,
html[lang="ko"] .scene-card-title,
html[lang="ko"] .pixel-label,
html[lang="ko"] button.pixel-button {
  font-family: "Noto Sans KR", "Nanum Gothic Coding", sans-serif;
  letter-spacing: 0;
}
html[lang="ko"] #desktop-note { font-size: 12px; }

/* Exit-confirm modal — top-level (no shadow DOM). */
html[lang="ko"] .exit-confirm-header,
html[lang="ko"] .exit-confirm-actions button {
  font-family: "Noto Sans KR", sans-serif;
  letter-spacing: 0;
}
html[lang="ko"] .exit-confirm-header { font-size: 13px; }
html[lang="ko"] .exit-confirm-actions button { font-size: 13px; padding: 8px 18px; }

/* Prototype lines 28-44 (body globals + pixelated image rendering + button reset) --
   most of this is already in /shared-static/design-tokens-app.css; keep the button reset
   local so non-component buttons (welcome-start, #travel-pill, etc.) inherit the
   prototype's clean-slate style. */
button {
  font-family: inherit;
  cursor: pointer;
  background: none;
  border: none;
  color: inherit;
}

/* Prototype lines 54-69 -- desktop note chip for wide viewports. */
#desktop-note {
  display: none;
  position: fixed;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  font-family: "Press Start 2P", monospace;
  font-size: 8px;
  letter-spacing: 2px;
  color: #7a7585; /* alpha variant of indigo-space -- no loka token for this purple-grey */
  padding: 8px 12px;
  border: 1px solid #332d44; /* alpha variant of loka-indigo-soft -- no token */
  background: #120c1a; /* dark indigo -- no loka token for this deep shade */
  z-index: 1000;
  white-space: nowrap;
}
@media (min-width: 520px) { #desktop-note { display: block; } }

/* Prototype lines 71-87 -- #app container becomes #phone-shell in Volley
   (Volley-local wrapper -- not in prototype). */
#phone-shell {
  position: relative;
  width: 100vw;
  height: 100dvh;
  max-width: var(--phone-w);
  margin: 0 auto;
  overflow: hidden;
  background: var(--loka-indigo-deep);
}
@media (min-width: 520px) {
  #phone-shell {
    height: min(100dvh, 900px);
    margin-top: max(0px, calc((100dvh - 900px) / 2));
    border: 2px solid #3a3248; /* phone-shell viewport border: dark indigo-purple -- no loka token */
    box-shadow: 0 0 80px rgba(0, 0, 0, 0.7);
  }
}

/* Volley-local pre-auth welcome (not in prototype). */
#welcome-screen {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 24px;
  background: var(--loka-indigo-deep);
  color: var(--loka-cream);
  z-index: 150;
}
#welcome-screen h1 {
  font-family: "Press Start 2P", monospace;
  font-size: 22px;
  letter-spacing: 2px;
  color: var(--loka-cream);
}
#welcome-screen input {
  width: 100%;
  padding: 12px;
  background: var(--loka-cream);
  color: var(--loka-ink);
  border: 3px solid var(--loka-rule-dark);
  font-family: "Nanum Gothic Coding", monospace;
  font-size: 15px;
}
#welcome-screen button {
  padding: 10px 20px;
  background: var(--loka-terracotta);
  color: var(--loka-cream);
  border: 3px solid var(--loka-rule-dark);
  box-shadow: 2px 2px 0 var(--loka-rule-dark);
  font-family: "Press Start 2P", monospace;
  font-size: 9px;
  letter-spacing: 2px;
  cursor: pointer;
}
#welcome-screen button:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--loka-rule-dark);
}

#world-root {
  position: absolute;
  inset: 0;
}

/* ============================================================
   Prototype lines 89-110 -- WORLD stage + backdrop + tint
============================================================ */
.world {
  position: absolute; inset: 0;
  overflow: hidden;
}
.world-bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: opacity 0.55s ease, filter 0.55s ease;
  /* Plan 17.2-02: image-rendering: pixelated required per slice lobby.css:41 [UI-LOBBY-02]. */
  image-rendering: pixelated;
  z-index: 1;
}
.world-tint {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 2;
  transition: background 0.55s ease, opacity 0.55s ease;
  background: rgba(0,0,0,0);
}
.world.tod-evening .world-tint { background: rgba(40, 30, 90, 0.22); }
.world.tod-morning .world-bg   { filter: brightness(1.02) saturate(1.03); }
.world.tod-evening .world-bg   { filter: brightness(0.82) saturate(1.08) hue-rotate(-10deg); }

/* in-task blur per D-22. Applies to light-DOM layers only. */
.world.in-task .world-bg { filter: blur(12px) brightness(0.65); }

/* ============================================================
   Prototype lines 112-202 -- TOP CHROME (.hud + .hud-btn)
   06-13 rewrites world-hud shadow-DOM to host these same selectors.
   Leaving light-DOM copies in here documents the visual contract.
============================================================ */
.hud {
  position: absolute;
  top: 12px; left: 12px; right: 12px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  z-index: 60;
  pointer-events: none;
}
.hud > * { pointer-events: auto; }
.hud-left  { display: flex; flex-direction: column; gap: 6px; }
.hud-right { display: flex; gap: 6px; }

/* PR-2 (2026-05-06) defensive belt-and-braces for the FoF chat-thread
   overlay. The layer is position:fixed; inset:0 and would otherwise
   absorb every tap on the world frame underneath if it ever lingered
   on the DOM after a visit closes. Pair it with the unmount-removes-
   the-layer behaviour in app.js so a future drift in either path
   doesn't re-introduce the Lobby-tap blocker. */
#fof-thread-layer { pointer-events: none; }
#fof-thread-layer > * { pointer-events: auto; }

.hud-btn {
  background: rgba(26, 16, 24, 0.78);
  border: 2px solid var(--loka-cream);
  box-shadow: 2px 2px 0 var(--loka-shadow-hard);
  padding: 6px 8px;
  font-family: "Press Start 2P", monospace;
  color: var(--loka-cream);
  font-size: 8px;
  letter-spacing: 1.5px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.hud-btn:active { transform: translate(2px, 2px); box-shadow: 0 0 0 var(--loka-shadow-hard); }
.hud-btn .tod-icon { font-size: 14px; color: var(--loka-gold); line-height: 1; }
.world.tod-evening .tod-icon { color: #c8c0ff; /* moon icon: lavender-blue -- no loka token for cool evening tone */ }

/* ============================================================
   Prototype lines 204-363 -- AMBIENT LIFE
============================================================ */
.ambient-life {
  position: absolute; inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 18;
}

/* Morning bird */
.ambient-bird {
  position: absolute;
  top: 14%;
  left: -10%;
  width: 22px; height: 10px;
  animation: bird-drift 18s linear infinite;
  display: none;
}
.world.tod-morning .ambient-bird { display: block; }
.ambient-bird .wing {
  position: absolute;
  top: 0;
  width: 11px; height: 5px;
  border-top: 3px solid rgba(20, 14, 8, 0.9);
  border-radius: 80% 80% 0 0 / 100% 100% 0 0;
  transform-origin: bottom center;
  animation: wing-flap 0.38s ease-in-out infinite;
}
.ambient-bird .wing.l { left: 0; }
.ambient-bird .wing.r { right: 0; transform: scaleX(-1); }
@keyframes wing-flap {
  0%, 100% { transform: rotate(-12deg); }
  50%      { transform: rotate(14deg); }
}
@keyframes bird-drift {
  0%   { transform: translate(0, 0); }
  45%  { transform: translate(55vw, -6px); }
  100% { transform: translate(120vw, 4px); }
}

/* Fireflies (evening) */
.ambient-firefly {
  position: absolute;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: #fff6c0; /* firefly glow: warm yellow-cream -- no loka token for this ambient light */
  display: none;
  box-shadow:
    0 0 6px 3px rgba(255, 230, 140, 1),
    0 0 18px 8px rgba(240, 184, 74, 0.85),
    0 0 32px 14px rgba(240, 184, 74, 0.35);
}
.world.tod-evening .ambient-firefly { display: block; }
.ambient-firefly.f1 { top: 46%; left: 18%; animation: firefly-drift-a 6.5s ease-in-out infinite; }
.ambient-firefly.f2 { top: 58%; left: 72%; animation: firefly-drift-b 7.8s ease-in-out infinite; animation-delay: 1.4s; }
.ambient-firefly.f3 { top: 40%; left: 54%; animation: firefly-drift-a 8.2s ease-in-out infinite; animation-delay: 2.1s; }
.ambient-firefly.f4 { top: 64%; left: 34%; animation: firefly-drift-b 6.0s ease-in-out infinite; animation-delay: 0.8s; }
@keyframes firefly-drift-a {
  0%, 100% { transform: translate(0, 0);       opacity: 0.85; }
  25%      { transform: translate(18px, -10px); opacity: 1; }
  55%      { transform: translate(-12px, -4px); opacity: 0.85; }
  80%      { transform: translate(6px, 8px);    opacity: 1; }
}
@keyframes firefly-drift-b {
  0%, 100% { transform: translate(0, 0);       opacity: 0.85; }
  30%      { transform: translate(-22px, 6px);  opacity: 1; }
  60%      { transform: translate(10px, -12px); opacity: 0.9; }
}

/* Lantern (evening dominant, morning dim) */
.ambient-lantern {
  position: absolute;
  right: 12%;
  top: 22%;
  width: 22px;
  height: 42px;
  pointer-events: none;
  transform-origin: 50% 0%;
  animation: lantern-sway 4.6s ease-in-out infinite;
  z-index: 19;
}
.ambient-lantern.l2 { right: auto; left: 16%; top: 30%; animation-delay: 1.3s; }
.world.tod-evening .ambient-lantern { opacity: 1; }
.world.tod-morning .ambient-lantern { opacity: 0.35; }
.lantern-rope { position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 2px; height: 14px; background: #1a1018; /* intrinsic ambient decorative: near-black rope color -- no loka token */ }
.lantern-body { position: absolute; top: 14px; left: 50%; transform: translateX(-50%); width: 18px; height: 20px; background: var(--loka-ember); border: 2px solid #1a1018; box-shadow: inset 0 0 0 2px #d9a24d; /* #1a1018 lantern-rope intrinsic dark; #d9a24d approx loka-gold-deep */ }
.lantern-glow {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  width: 34px; height: 34px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,214,130,0.6), rgba(255,214,130,0) 70%);
  filter: blur(1px);
  animation: lantern-flicker 2.3s ease-in-out infinite;
}
.world.tod-morning .lantern-glow { opacity: 0; }
@keyframes lantern-sway    { 0%, 100% { transform: rotate(-4deg); } 50% { transform: rotate(4deg); } }
@keyframes lantern-flicker { 0%, 100% { opacity: 0.85; } 40% { opacity: 1; } 70% { opacity: 0.7; } }

/* Cherry petals (morning only) */
.ambient-petal {
  position: absolute;
  width: 5px; height: 5px;
  background: var(--loka-petal);
  border: 1px solid #c77c92; /* alpha variant of --loka-petal; darker petal border -- no token */
  display: none;
  animation: petal-fall 9s linear infinite;
}
.world.tod-morning .ambient-petal { display: block; }
.ambient-petal.p1 { left: 18%; top: -10px; animation-delay: 0s; }
.ambient-petal.p2 { left: 58%; top: -10px; animation-delay: 2.3s; background: #efa4b8; /* alpha variant of --loka-petal; no token for this lighter shade */ }
.ambient-petal.p3 { left: 82%; top: -10px; animation-delay: 4.1s; }
@keyframes petal-fall {
  0%   { transform: translate(0, 0) rotate(0deg); opacity: 0; }
  8%   { opacity: 0.9; }
  50%  { transform: translate(-18px, 50vh) rotate(180deg); opacity: 0.9; }
  100% { transform: translate(14px, 110vh) rotate(420deg); opacity: 0; }
}

/* Walkers -- front-row (.near) only per v5 PM decision. */
.walker {
  position: absolute;
  left: -48px;
  bottom: 8%;
  width: 26px;
  height: 40px;
  filter: drop-shadow(1px 1px 0 rgba(0,0,0,0.38));
  animation: walker-stride 13s linear infinite;
  pointer-events: none;
  z-index: 20;
}
.walker svg { width: 100%; height: 100%; animation: walker-bob 0.44s steps(2) infinite; }
.walker.w2 { animation-delay: 4.5s; }
.walker.w3 { animation-delay: 9.2s; }
@keyframes walker-stride { 0% { left: -48px; } 100% { left: 108%; } }
@keyframes walker-bob    { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1px); } }
.world.tod-evening .walker { filter: drop-shadow(1px 1px 0 rgba(0,0,0,0.65)) brightness(0.78); }

/* Pigeon peck loop (near the storefront) */
.pigeon {
  position: absolute;
  left: 28%;
  bottom: 6%;
  width: 20px;
  height: 14px;
  z-index: 21;
  animation: pigeon-hop 5.3s ease-in-out infinite;
  pointer-events: none;
  filter: drop-shadow(1px 1px 0 rgba(0,0,0,0.4));
}
.pigeon svg { width: 100%; height: 100%; animation: pigeon-peck 1.5s ease-in-out infinite; transform-origin: 50% 100%; }
@keyframes pigeon-hop {
  0%   { left: 28%; }
  20%  { left: 29%; transform: translateY(-3px); }
  25%  { left: 29%; transform: translateY(0); }
  55%  { left: 31%; transform: translateY(-2px); }
  60%  { left: 31%; transform: translateY(0); }
  100% { left: 33%; }
}
@keyframes pigeon-peck {
  0%, 40%, 100% { transform: rotate(0); }
  20%  { transform: rotate(28deg); }
  60%  { transform: rotate(22deg); }
}

/* ============================================================
   Prototype lines 365-425 -- CHARACTER SPRITES on street
============================================================ */
.char-stage {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 30;
}

/* Spec: [WORLD-EXIT-01] (Phase 11 SC6). Mid-task exit confirmation modal.
   Inline shell HTML; toggled via [hidden]. Sits above task-layer (z-index
   200) so it overlays the encounter while the user decides. CANCEL keeps
   realtime alive; EXIT proceeds with back_to_lobby (bridge marks the
   execution as interrupted, NOT abandoned). */
.exit-confirm {
  position: absolute;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
}
.exit-confirm[hidden] { display: none; }
.exit-confirm-scrim {
  position: absolute;
  inset: 0;
  background: rgba(26, 16, 24, 0.72);
}
.exit-confirm-panel {
  position: relative;
  background: var(--loka-cream);
  border: 3px solid var(--loka-rule-dark);
  box-shadow: 4px 4px 0 var(--loka-rule-dark);
  width: min(86%, 320px);
  display: flex;
  flex-direction: column;
}
.exit-confirm-header {
  background: var(--loka-rule-dark);
  color: var(--loka-cream);
  font-family: 'Press Start 2P', monospace;
  font-size: 10px;
  letter-spacing: 2px;
  padding: 10px 14px;
}
.exit-confirm-body {
  padding: 16px 18px 6px;
}
.exit-confirm-line {
  font-family: 'Nanum Gothic Coding', monospace;
  font-size: 13px;
  color: var(--loka-ink);
  font-weight: 700;
  margin: 0 0 6px;
  line-height: 1.4;
}
.exit-confirm-sub {
  font-family: 'VT323', monospace;
  font-size: 14px;
  color: var(--loka-ink-soft);
  margin: 0;
  line-height: 1.4;
}
.exit-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 14px 14px;
}
.exit-confirm-actions button {
  font-family: 'Press Start 2P', monospace;
  font-size: 8px;
  letter-spacing: 2px;
  padding: 8px 14px;
  border: 3px solid var(--loka-rule-dark);
  cursor: pointer;
}
.exit-confirm-actions button:active {
  transform: translate(2px, 2px);
  box-shadow: 0 0 0 var(--loka-rule-dark);
}
.exit-confirm-cancel {
  background: transparent;
  color: var(--loka-rule-dark);
  box-shadow: 2px 2px 0 var(--loka-rule-dark);
}
.exit-confirm-exit {
  background: var(--loka-terracotta);
  color: var(--loka-cream);
  box-shadow: 2px 2px 0 var(--loka-rule-dark);
}

/* Spec: [WORLD-EMPTY-01] (Phase 11 SC3). In-scene placeholder rendered
   when ContentService composes the current scene to zero presented
   characters/instances. Sits inside the world view (above the backdrop,
   below the HUD), dimmed cream pill so it reads as ambient signage
   rather than a modal CTA. The scene-change-modal owns the actionable
   "what next" prompt per [WORLD-TRAVEL-02]. */
.scene-empty {
  position: absolute;
  left: 50%;
  top: 48%;
  transform: translate(-50%, -50%);
  z-index: 40;
  pointer-events: none;
  background: rgba(26, 16, 24, 0.78);
  color: var(--loka-cream);
  border: 2px solid var(--loka-cream);
  box-shadow: 2px 2px 0 var(--loka-shadow-hard);
  padding: 10px 16px;
  font-family: "Press Start 2P", monospace;
  font-size: 9px;
  letter-spacing: 2px;
  text-align: center;
  max-width: 80%;
}
.scene-empty[hidden] { display: none; }
.scene-empty-line { margin: 0; line-height: 1.4; text-transform: uppercase; font-family: var(--loka-font-pixel); font-size: 9px; }
.char {
  position: absolute;
  pointer-events: auto;
  cursor: pointer;
  filter: drop-shadow(2px 2px 0 rgba(0,0,0,0.45));
  animation: char-idle-bob 2.8s ease-in-out infinite;
  transition: transform 0.15s ease;
}
.char svg { display: block; }
.char:active { transform: translateY(1px); }
@keyframes char-idle-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
.char .char-name {
  position: absolute;
  left: 50%;
  top: -26px;
  transform: translateX(-50%);
  font-family: "Press Start 2P", monospace;
  /* Phase 17 post-UAT legibility floor: 7px was below DS §5 pixel-face
     range (8-11px) and barely visible on mobile retina backdrops. */
  font-size: 9px;
  letter-spacing: 1px;
  color: var(--loka-cream);
  background: rgba(26, 16, 24, 0.82);
  border: 2px solid var(--loka-cream);
  padding: 3px 5px 2px;
  white-space: nowrap;
  box-shadow: 2px 2px 0 var(--loka-shadow-hard);
}
.char .char-pip {
  position: absolute;
  right: -10px;
  top: -6px;
  min-width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--loka-gold);
  color: var(--loka-rule-dark);
  border: 2px solid var(--loka-rule-dark);
  font-family: "Press Start 2P", monospace;
  font-size: 8px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 2px 2px 0 var(--loka-shadow-hard);
  animation: pip-pulse 1.6s ease-in-out infinite;
}
@keyframes pip-pulse {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(240,184,74,0.6), 2px 2px 0 var(--loka-shadow-hard); }
  50%      { transform: scale(1.08); box-shadow: 0 0 10px 4px rgba(240,184,74,0.45), 2px 2px 0 var(--loka-shadow-hard); }
}
.char .char-glow {
  position: absolute;
  inset: -10px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255,214,130,0.35), rgba(255,214,130,0) 70%);
  z-index: -1;
  animation: char-glow 3.2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes char-glow { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }

/* ============================================================
   Prototype lines 483-504 -- TOAST (top-center).
   Volley-local id is #toast-layer (prototype uses #toast); selector
   keeps the prototype .toast class so either id works visually.
============================================================ */
/* The Volley pattern is rolling-toast: showToast() appends a .world-toast
   child for each message, and the child animates its own opacity 0->1 via
   the toast-appear keyframe below.

   The prototype's single-toast .on toggle (opacity 0 on layer, opacity 1 on
   .toast.on) is incompatible with rolling-toast — opacity 0 on the parent
   multiplies children to invisible regardless of the child's own opacity.
   Volley's showToast never set .on, so toasts were silently invisible since
   they were introduced. Caught when Phase 18's "Profile saved" toast never
   appeared during plan 18-16 smoke gate.

   Fix: the layer is a positioning + stacking wrapper only. No paint, no
   border, no opacity gate. Visibility lives entirely on the .world-toast
   child. */
.toast {
  position: absolute;
  left: 50%;
  top: 86px;
  transform: translate(-50%, 0);
  font-family: "Nanum Gothic Coding", monospace;
  /* Was hardcoded 80; switched to the --z-toast token so the Plan 18-04
     bump (now 150, above --z-overlay) actually applies. The hardcoded value
     defeated the token bump and kept toasts hidden behind Settings/Dashboard. */
  z-index: var(--z-toast, 150);
  pointer-events: none;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
}

/* Volley-local toast child -- shipped showToast appends .world-toast rows
   to #toast-layer. Preserves the rolling-toast pattern until Phase 7 wires
   the prototype's single-toast behavior. */
.world-toast {
  background: var(--overlay-glass);
  color: var(--loka-cream);
  padding: 10px 14px;
  border: 2px solid var(--loka-cream);
  font-family: "Nanum Gothic Coding", monospace;
  font-size: 13px;
  opacity: 0;
  animation: toast-appear 260ms ease forwards;
  max-width: 320px;
}
@keyframes toast-appear {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ============================================================
   Prototype lines 506-533 -- BOOT SPLASH (D-30)
============================================================ */
#boot {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at center, #1a1028 0%, #09060f 78%); /* alpha variant pair: #1a1028 no loka token; #09060f = --void */
  display: flex; align-items: center; justify-content: center;
  z-index: 200;
  transition: opacity 0.45s ease;
}
#boot.off { opacity: 0; pointer-events: none; }
#boot .boot-title {
  font-family: "Press Start 2P", monospace;
  font-size: clamp(22px, 9vw, 34px);
  color: var(--loka-cream);
  letter-spacing: 4px;
  line-height: 1.3;
  text-align: center;
  text-shadow: 3px 3px 0 var(--loka-terracotta-deep), 6px 6px 0 rgba(0,0,0,0.5);
}
#boot .boot-sub {
  font-family: "Press Start 2P", monospace;
  font-size: 10px;
  color: var(--loka-gold);
  letter-spacing: 3px;
  margin-top: 14px;
  text-align: center;
  animation: blink 1.3s steps(2) infinite;
}
@keyframes blink { 50% { opacity: 0.3; } }

/* ============================================================
   WORLD-LOCATING overlay -- bridges the gap between world-root
   reveal and the first backdrop image load. Replaces the prior
   hardcoded SCENE-001 default that flashed through as a wrong
   scene under the correct HUD. Dismissed once on first scene
   image load; never re-shown (scene-change ritual lives in
   #scene-fade and is independent).
============================================================ */
.world-locating {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at center, #1a1028 0%, #09060f 78%); /* alpha variant pair: #1a1028 no loka token; #09060f = --void */
  display: flex; align-items: center; justify-content: center;
  z-index: 175;
  opacity: 1;
  transition: opacity 0.45s ease;
  pointer-events: none;
}
.world-locating.off { opacity: 0; }
.world-locating-inner { text-align: center; padding: 0 18px; }
.world-locating-title {
  font-family: "Press Start 2P", monospace;
  font-size: clamp(16px, 6vw, 22px);
  color: var(--loka-cream);
  letter-spacing: 3px;
  line-height: 1.3;
  text-shadow: 2px 2px 0 var(--loka-terracotta-deep), 4px 4px 0 rgba(0,0,0,0.5);
  animation: blink 1.3s steps(2) infinite;
}
.world-locating-sub {
  font-family: "Nanum Gothic Coding", monospace;
  font-size: 12px;
  color: var(--loka-gold);
  letter-spacing: 2px;
  margin-top: 12px;
}

/* ============================================================
   Prototype lines 552-560 -- SCRIM (shared by overlay panels).
============================================================ */
.scrim {
  position: absolute; inset: 0;
  background: rgba(8, 4, 16, 0.55);
  z-index: 90;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease;
}
.scrim.on { opacity: 1; pointer-events: auto; }

/* ============================================================
   Prototype lines 1927-1967 -- SCENE-FADE RITUAL (D-30 sibling)
============================================================ */
.scene-fade {
  position: absolute;
  inset: 0;
  z-index: 180;
  background: rgba(8, 4, 16, 0);
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.45s ease;
}
.scene-fade.on {
  background: rgba(8, 4, 16, 0.88);
  pointer-events: auto;
}
.scene-fade-bubble {
  max-width: 300px;
  padding: 14px 16px;
  background: var(--loka-cream);
  border: 3px solid var(--loka-rule-dark);
  box-shadow: 4px 4px 0 var(--loka-rule-dark);
  font-family: "Nanum Gothic Coding", monospace;
  font-size: 14px;
  line-height: 1.45;
  color: var(--loka-ink);
  text-align: center;
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 0.35s ease, transform 0.35s ease;
}
.scene-fade.on .scene-fade-bubble { opacity: 1; transform: scale(1); }
.scene-fade-bubble::before {
  content: "VOLLEY";
  display: block;
  font-family: "Press Start 2P", monospace;
  font-size: 7px;
  letter-spacing: 2px;
  color: var(--loka-terracotta-deep);
  margin-bottom: 6px;
}

/* Layer that hosts the verdict screen. Sits above task + tutor layers.
   Phase 18.1: the paper-note ceremony and wrap-up overlay rules were
   deleted; the <fof-transition> web component owns its own CSS. */
.tc-layer {
  position: absolute; inset: 0;
  z-index: 110;
  pointer-events: none;
  display: none;
}
.tc-layer.on { display: block; pointer-events: auto; }
/* Phase 18.1: fof-transition[open] inside the container makes it visible.
   Mirrors the .on class but is driven by the component's own [open] attribute. */
.tc-layer:has(fof-transition[open]) { display: block; pointer-events: auto; }
/* Phase 13 [TBLT-BRIDGE-13] dim transition: legacy .tc-dimming class from
   the old tc-layer ceremony. Retained in case any code path still uses it;
   Phase 18.1 replaced the ceremony with <fof-transition> which owns its own
   dim/hide lifecycle. This rule is harmless if unused. */
.tc-layer.tc-dimming {
  opacity: 0;
  transition: opacity 1s ease;
  pointer-events: none;
}
/* Paper-note ceremony and wrap-up overlay rules deleted in Phase 18.1 Plan 04.
   The <fof-transition> web component owns its own shadow-DOM CSS.
   See frontend/components/fof-transition.css. */

/* ============================================================
   In-task coordinator (UX-11 / D-22). world-app toggles .in-task on
   #world-root (legacy) AND on #world (prototype-aligned) to centralize
   HUD / character-stage / ambient hiding while the encounter screen is
   active. Each Web Component also honors data-in-task for robustness.
============================================================ */
#world-root.in-task #character-stage,
#world-root.in-task .ambient-life,
#world-root.in-task world-hud,
.world.in-task .char-stage,
.world.in-task .ambient-life,
.world.in-task world-hud {
  display: none;
}

/* ----- Level-select welcome-flow form (Plan 07-05 port from frontend/style.css) ----- */
/* Mirrors the v1 frontend/style.css block 1:1 so the form looks identical under
   both flag-on (world shell) and flag-off (v1 shell) servings. The level-select
   welcome surface is a *welcome-flow* concern, not a world-UX concern, so it does
   not pull on the prototype-pixel-art tokens; it uses the lobby palette already
   exported by /shared-static/design-tokens-app.css. */
.welcome-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  max-width: 360px;
  margin: 24px auto;
  padding: 24px;
  background: var(--loka-cream);
  border-radius: 12px;
  font-family: var(--font-main, system-ui, -apple-system, sans-serif);
}

.welcome-label {
  color: var(--loka-ink);
}

.welcome-tagline {
  color: var(--loka-ink-soft);
  font-size: 14px;
  line-height: 1.4;
}

.level-select-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(3, auto);
  gap: 16px;
  width: 100%;
}

.level-select-tile {
  min-height: 64px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  text-align: left;
  cursor: pointer;
  background: #ffffff; /* level-select tile: white bg per Phase 7 form aesthetic -- no loka token */
  border: 1.5px solid var(--loka-rule-dark, rgba(43, 39, 96, 0.15));
  border-radius: 12px;
  font-family: var(--font-main, system-ui, -apple-system, sans-serif);
  transition: background 0.18s ease, border-color 0.18s ease,
              box-shadow 0.18s ease, transform 0.1s ease, opacity 0.18s ease;
}
.level-select-tile:hover { background: var(--loka-cream); }
.level-select-tile:active { transform: scale(0.98); }
.level-select-tile:focus-visible {
  outline: 2px solid rgba(217, 162, 77, 0.5);
  outline-offset: 2px;
}
.level-select-tile[aria-checked="true"] {
  border-color: var(--loka-terracotta);
  box-shadow: 0 0 0 1px var(--loka-terracotta);
}
.level-select-tile-band {
  font-size: 18px;
  font-weight: 400;
  line-height: 1.2;
  color: var(--loka-ink);
  letter-spacing: 0.02em;
}
.level-select-tile-label {
  font-size: 13px;
  font-weight: 400;
  line-height: 1.3;
  color: var(--loka-ink-soft);
}
.level-select-tile-cue {
  font-size: 13px;
  font-weight: 300;
  line-height: 1.4;
  color: var(--loka-ink-soft);
  margin-top: 4px;
}
@media (max-width: 360px) {
  .level-select-tile-cue { font-size: 12px; }
}

.welcome-btn {
  padding: 14px 20px;
  border-radius: 12px;
  border: 1.5px solid var(--loka-rule-dark, rgba(43, 39, 96, 0.15));
  background: #ffffff; /* welcome-btn: white bg per Phase 7 form aesthetic -- no loka token */
  color: var(--loka-ink);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  font-family: var(--font-main, system-ui, -apple-system, sans-serif);
}
.welcome-btn-primary {
  background: var(--loka-terracotta);
  color: var(--loka-ink);
  border-color: var(--loka-terracotta);
}
.welcome-btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: none;
}
.welcome-btn-back {
  background: transparent;
  border-color: transparent;
  color: var(--loka-ink-soft);
}

.welcome-error {
  color: var(--loka-ember);
  font-size: 13px;
  font-weight: 400;
  line-height: 1.4;
  width: 100%;
  text-align: center;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .level-select-tile,
  .level-select-tile:active {
    transition: none;
    transform: none;
  }
}

/* QUICK-260427-GLP-01: mobile-safari scroll-lock fix for level-select.
   The Phase 7 #levelSelectForm content (~750-900px on 375x667) exceeds
   the viewport on small phones, but #phone-shell has overflow:hidden so
   C1/C2 tiles + Continue button were unreachable. Scope: only relax
   overflow when the form is the visible surface (welcome-screen and
   world-root continue to clip as before). */
#phone-shell:has(> #levelSelectForm:not([style*="display: none"])) {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
#levelSelectForm {
  /* Internal breathing room so the heading is not glued to the top edge
     and the Back button is not glued to the bottom edge after scroll. */
  padding-block: 24px;
}
@supports not selector(:has(*)) {
  /* Fallback for engines lacking :has() (very old iOS Safari < 15.4):
     unconditionally allow #phone-shell to scroll. The welcome / world
     flows are short and unaffected. */
  #phone-shell { overflow-y: auto; -webkit-overflow-scrolling: touch; }
}

/* Wrap-up overlay rules deleted in Phase 18.1 Plan 04.
   The <fof-transition> pre-roll state (ft-screen--pre-roll) replaces
   the wrap-up overlay. See frontend/components/fof-transition.css. */

/* =================================================================
   PHASE 15.1 ONBOARDING SHELL (ported from frontend/style.css:1268..2530)
   Boot loader (z 250) | Splash (z 200) | Auth overlay (z 220)
   Onboarding stage (composite) | Cinematic fade (z 180) | Verify banner (z 300)
   Tokens reference /shared-static/design-tokens-app.css. Volley-local tokens
   (--volley-hat, --volley-face) are scoped to :root in this block.
   .hidden duplicate skipped (already defined at frontend/world/style.css:21).
   ================================================================= */
/* =================================================================
   PHASE 15 ONBOARDING SHELL
   Boot Loader + Splash + Auth Modal + Cinematic Fade + Verify Banner
   Volley-local color tokens: --volley-hat, --volley-face
   All colors reference design-tokens.css (D-03 mandate).
   ================================================================= */

/* Volley-specific color tokens.
 * These are intentionally separate from design-tokens.css because they
 * describe character-specific tones, not reusable UI surface colors. */
:root {
    /* Character-specific app-local tokens: intentionally NOT in --loka-* namespace */
    --volley-hat: #d9a24d; /* app-local: Volley hat tint */
    --volley-face: #f7e6c4; /* app-local: Volley face tint */
}


/* -- BOOT LOADER ----------------------------------------------- */
/* z-index 250. Full-bleed boot screen while /auth/me resolves. */

.boot-loader {
    position: fixed;
    inset: 0;
    z-index: 250;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--loka-indigo-deep);
    opacity: 1;
    transition: opacity 0.4s ease;
}

.boot-loader.fade-out {
    opacity: 0;
    pointer-events: none;
}

/* "HAEBIT-DONG" title in Press Start 2P */
.boot-loader .bl-title {
    font-family: 'Press Start 2P', monospace;
    font-size: 22px;
    letter-spacing: 3px;
    color: var(--loka-cream);
    text-shadow: 2px 2px 0 var(--loka-terracotta-deep);
    animation: caret-blink 1.4s steps(2) infinite;
    user-select: none;
}

/* Blinking caret animation for boot title */
@keyframes caret-blink {
    0%, 100% { opacity: 1; }
    50%       { opacity: 0; }
}

/* -- SPLASH ---------------------------------------------------- */
/* Plan 17.2-08 DEFECT-1: rewritten against #tpl-splash slice contract.
   z-index 200. Indigo full-bleed backdrop (no scene image) + stars + skyline
   + moon + wordmark + Lo bubble + cream pill CTAs. */

.splash {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: var(--loka-indigo-deep);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Star field: 24 i-elements with absolute positions, gold pixel blink */
.lk-stars {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 1;
}

.lk-stars i {
    position: absolute;
    display: block;
    background: var(--loka-gold-soft);
    border-radius: 0; /* pixel-honest: square pixels */
    animation: lk-star-blink 3.2s ease-in-out infinite;
}

@keyframes lk-star-blink {
    0%, 100% { opacity: 0.25; }
    50%       { opacity: 1; }
}

/* Skyline silhouette: thin band at horizon (~70% height), indigo-soft */
.lk-skyline {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 34%;
    background: var(--loka-indigo-soft);
    clip-path: polygon(
        0% 60%, 3% 60%, 3% 20%, 6% 20%, 6% 60%,
        10% 60%, 10% 35%, 13% 35%, 13% 60%,
        17% 60%, 17% 10%, 20% 10%, 20% 60%,
        25% 60%, 25% 40%, 28% 40%, 28% 60%,
        33% 60%, 33% 25%, 37% 25%, 37% 60%,
        42% 60%, 42% 45%, 46% 45%, 46% 60%,
        50% 60%, 50% 15%, 54% 15%, 54% 60%,
        59% 60%, 59% 38%, 63% 38%, 63% 60%,
        67% 60%, 67% 28%, 70% 28%, 70% 60%,
        75% 60%, 75% 42%, 78% 42%, 78% 60%,
        83% 60%, 83% 18%, 87% 18%, 87% 60%,
        92% 60%, 92% 50%, 96% 50%, 96% 60%,
        100% 60%, 100% 100%, 0% 100%
    );
    pointer-events: none;
    z-index: 2;
}

/* Moon pixel art: positioned top-right per slice line 96 */
.lk-moon-pos {
    position: absolute;
    right: 44px;
    top: 86px;
    z-index: 3;
    pointer-events: none;
}

/* Inner content column: flex column anchored bottom per slice .lk-inner */
.lk-inner {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 4;
    display: flex;
    flex-direction: column;
    padding: 0 20px 24px;
    max-width: var(--phone-w, 430px);
    margin: 0 auto;
    width: 100%;
}

/* PUBLIC BETA eyebrow: pixel uppercase, amber/gold per slice line 106-113 */
.lk-eyebrow {
    font-family: var(--loka-font-pixel);
    font-size: 7px;
    letter-spacing: 0.18em;
    color: var(--loka-gold-soft);
    text-transform: uppercase;
    margin-bottom: 14px;
}

/* Pixel wordmark: hydrated by ds-lo-renderer.js */
.lk-wordmark {
    line-height: 0;
    margin-bottom: 20px;
}

/* Serif italic tagline: <em>Step into</em> the language. */
.lk-tag {
    font-family: var(--loka-font-serif);
    font-size: clamp(26px, 7vw, 32px);
    font-weight: 400;
    line-height: 1.1;
    color: var(--loka-cream);
    margin-bottom: 8px;
}

.lk-tag em {
    font-style: italic;
    color: var(--loka-gold);
    font-weight: 400;
}

.lk-tag span {
    color: var(--loka-cream);
}

/* Place lede: serif, cream, smaller */
.lk-lede {
    font-family: var(--loka-font-serif);
    font-style: normal;
    font-size: 14px;
    line-height: 1.4;
    color: var(--loka-cream-2);
    margin-bottom: 18px;
}

/* Lo speaking container: positions bubble left + Lo right */
.lk-mid {
    margin-bottom: 18px;
}

.lk-lo-speaking {
    display: flex;
    align-items: flex-end;
    gap: 10px;
}

/* Lo bubble: cream paper, pixel-border, LO tab + body + tail */
.lk-bubble {
    flex: 1;
    position: relative;
    background: var(--loka-cream);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: 3px 3px 0 var(--loka-rule-dark);
    padding: 8px 10px 8px 10px;
}

/* LO tab: terracotta corner stamp, ink pixel text.
   Phase 20 A11Y-02: fg swapped from --loka-cream to --loka-ink to clear
   WCAG AA contrast (cream-on-terracotta = 2.8; ink-on-terracotta >= 6.0).
   Keeps the terracotta brand surface intact; text reads pixel-sharp ink. */
.lk-bubble-tag {
    position: absolute;
    top: -1px;
    left: -1px;
    background: var(--loka-terracotta);
    color: var(--loka-ink);
    font-family: var(--loka-font-pixel);
    font-size: 7px;
    letter-spacing: 0.10em;
    padding: 2px 5px 2px 4px;
    line-height: 1;
    border-right: 2px solid var(--loka-rule-dark);
    border-bottom: 2px solid var(--loka-rule-dark);
}

.lk-bubble-body {
    font-family: var(--loka-font-serif);
    font-style: italic;
    font-size: 14px;
    line-height: 1.35;
    color: var(--loka-ink);
    padding-top: 14px; /* clear the LO tab */
}

/* Pixel tail: points right toward Lo, slice-authoritative position */
.lk-bubble-tail {
    position: absolute;
    right: -8px;
    bottom: 12px;
    width: 6px;
    height: 4px;
    background: var(--loka-cream);
    border-top: 2px solid var(--loka-rule-dark);
    border-right: 2px solid var(--loka-rule-dark);
    box-shadow: 3px 0 0 0 var(--loka-rule-dark);
}

/* Lo host: fixed-size slot for the data-lo mount */
.lk-lo-host {
    flex-shrink: 0;
    width: 56px;
}

/* CTA stack: cream pill buttons in a flex column */
.lk-foot {
    /* bottom area */
}

.lk-stack {
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: 100%;
}

/* Auth action buttons (.loka-btn base + .lk-auth variant) */
.lk-auth {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    padding: 13px 16px;
    font-family: var(--loka-font-pixel);
    font-size: 9px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--loka-ink);
    background: var(--loka-cream);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: 3px 3px 0 var(--loka-rule-dark);
    cursor: pointer;
    user-select: none;
    transition: transform var(--motion-press), box-shadow var(--motion-press);
    min-height: 44px;
    text-align: center;
    border-radius: 0;
}

.lk-auth:active {
    transform: translate(2px, 2px);
    box-shadow: none;
}

.lk-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

/* Already a resident line */
.lk-resident {
    font-family: 'DM Sans', system-ui, sans-serif;
    font-size: 14px;
    color: var(--loka-cream-2);
    text-align: center;
}

.lk-resident a {
    color: var(--loka-gold-soft);
    text-decoration: underline;
    cursor: pointer;
    font-family: var(--loka-font-pixel);
    font-size: 8px;
    letter-spacing: 0.12em;
}

/* Star blink / bubble-pop kept for backward compat with any remaining vol-bubble usage */
@keyframes volley-peek {
    0%, 100% { transform: translateY(0) rotate(-3deg); }
    50%       { transform: translateY(-6px) rotate(3deg); }
}

@keyframes bubble-pop {
    0%, 30%, 100% { opacity: 0; }
    45%, 80%      { opacity: 1; }
}

/* -- AUTH OVERLAY ---------------------------------------------- */
/* Phase 17.2 Plan-05: rebuilt against slice CSS (tpl-auth-{signup,signin,forgot,sent}).
   Spec: [UI-AUTH-02] Zones A-G / [UI-AUTH-03] per-mode visibility / [UI-AUTH-07] Forbidden.
   DSX-07: --modal-scrim-strong (0.78 alpha -- stronger than lobby's 0.55).
   DSX-08: --loka-track-pixel-sm/md/lg for three pixel-face tracking tiers.
   DSX-09: --loka-border-1-dashed on .auth-confirmation (wait-state visual signature).
   z-index managed by var(--z-overlay) from ds-tokens.css (DSX-02 promoted). */

/* Zone A -- scrim (full-frame, sits over splash backdrop) */
.auth-overlay {
    position: fixed;
    inset: 0;
    /* Auth overlay must sit ABOVE splash (z-index 200) and boot loader (200).
       220 is a slice-authoritative surface-composition literal per [UI-AUTH-02] z-stack.
       --z-overlay (100) is for in-world modals; auth lives in the Welcome chrome region
       above the splash surface, not in-world. */
    z-index: 220;
    background: var(--modal-scrim-strong); /* DSX-07: rgba(8,4,16,0.78) -- auth needs stronger scrim than lobby */
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.auth-overlay.hidden {
    display: none !important;
}

/* Zone B -- cream paper card ([UI-AUTH-02]) */
.auth-card {
    position: relative;
    width: 100%;
    max-width: 360px;
    background: var(--loka-cream);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-xl); /* 6px 6px 0 --loka-shadow-hard */
    border-radius: 0; /* [UI-AUTH-07]: pixel-honest, no rounded corners */
    padding: 24px 22px 22px;
}

/* Zone C -- close affordance ([UI-AUTH-02]) */
.auth-close {
    position: absolute;
    top: 8px;
    right: 12px;
    width: 28px;
    height: 28px;
    background: transparent;
    border: 0;
    border-radius: 0;
    font-family: var(--loka-font-mono);
    font-size: 22px;
    line-height: 1;
    color: var(--loka-ink-mute);
    cursor: pointer;
    padding: 0;
}

.auth-close:hover {
    color: var(--loka-ink);
}

/* Zone D -- card head: eyebrow + title + sub ([UI-AUTH-02]) */

/* Eyebrow: place-voice pixel uppercase, ink, 0.12em tracking (DSX-08).
   Spec: [UI-AUTH-01] / [UI-PRIN-25] place-voice register.
   Phase 20 A11Y-02: fg --loka-terracotta-deep (#a85a28) -> --loka-ink to
   clear WCAG AA on the --loka-cream-2 auth-card surface
   (terracotta-deep-on-cream-2 = 3.96 fail; ink-on-cream-2 >= 11.6 pass). */
.auth-eyebrow {
    font-family: var(--loka-font-pixel);
    font-size: var(--loka-text-pixel-sm); /* 9px */
    letter-spacing: var(--loka-track-pixel-sm); /* DSX-08: 0.12em */
    color: var(--loka-ink);
    text-transform: uppercase;
    margin-bottom: 10px;
    padding-right: 32px; /* room for close button */
}

/* Title: Instrument Serif italic -- place-voice 3rd-person about Lo ([UI-AUTH-01]).
   Spec: [UI-PRIN-25] / [UI-AUTH-07] "no Lo voice in titles that name Lo as I". */
.auth-title {
    font-family: var(--loka-font-serif);
    font-style: italic;
    font-size: 26px;
    line-height: 1.1;
    color: var(--loka-ink);
    margin: 0 0 6px;
    font-weight: normal;
}

/* Sub: serif non-italic, ink-soft, place-voice */
.auth-sub {
    font-family: var(--loka-font-serif);
    font-style: normal;
    font-size: 14px;
    line-height: 1.35;
    color: var(--loka-ink-soft);
    margin: 0 0 16px;
}

/* Zone E -- OAuth row + divider */

.auth-oauth-row {
    margin-bottom: 12px;
}

.auth-oauth-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    justify-content: center;
    width: 100%;
    /* DEFECT-2: full cream-warm pill per slice tpl-auth-signup; cream-2 was too muted */
    background: var(--loka-cream);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-md); /* 3px 3px 0 */
    border-radius: 0;
    padding: 10px 14px;
    font-family: var(--loka-font-pixel);
    font-size: var(--loka-text-pixel-sm); /* 9px */
    letter-spacing: var(--loka-track-pixel-md); /* DSX-08: 0.16em -- OAuth label tier */
    text-transform: uppercase;
    color: var(--loka-ink);
    cursor: pointer;
    min-height: 44px;
    transition: transform var(--motion-press), box-shadow var(--motion-press);
}

.auth-oauth-btn:active {
    transform: translate(2px, 2px);
    box-shadow: var(--loka-card-shadow-sm);
}

/* OR WITH EMAIL divider (mono, 0.20em = --loka-track-mono)
   Phase 20 A11Y-02: fg --loka-ink-mute (#876b5d) -> --loka-ink-soft
   (#5a3f34) for WCAG AA contrast on cream-2 (ink-mute = 4.27 fail). */
.auth-divider {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 16px 0;
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs); /* 11px */
    letter-spacing: var(--loka-track-mono); /* 0.20em */
    color: var(--loka-ink-soft);
    text-transform: uppercase;
}

.auth-divider::before,
.auth-divider::after {
    content: '';
    flex: 1;
    border-top: 1px dashed var(--loka-rule);
}

/* Zone E -- field group */

.auth-field {
    margin-bottom: 12px;
}

/* Phase 20 A11Y-02: fg --loka-ink-mute (#876b5d) -> --loka-ink to clear
   WCAG AA contrast on cream-2 (ink-mute-on-cream-2 = 4.27 fail; ink = pass). */
.auth-label {
    display: block;
    font-family: var(--loka-font-pixel);
    font-size: var(--loka-text-pixel-sm); /* 9px */
    letter-spacing: var(--loka-track-pixel-md); /* DSX-08: 0.16em -- field label tier */
    color: var(--loka-ink);
    text-transform: uppercase;
    margin-bottom: 4px;
}

.auth-input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    background: var(--loka-cream-2);
    border: 2px solid var(--loka-rule-dark);
    border-radius: 0;
    padding: 8px 10px;
    font-family: var(--loka-font-mono);
    font-size: 14px;
    color: var(--loka-ink);
    min-height: 44px;
    transition: border-color 0.15s ease;
}

.auth-input::placeholder {
    color: var(--loka-ink-mute);
}

.auth-input:focus {
    outline: 2px solid var(--loka-gold);
    outline-offset: 0;
}

.auth-input.invalid,
.auth-input:invalid:not(:placeholder-shown) {
    border-color: var(--loka-ember); /* alert-only per DS [UI-AUTH-07] */
}

/* Zone E -- forgot-link (signin mode only, mono terracotta-deep) */
.auth-forgot-link {
    display: inline-block;
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs); /* 11px */
    color: var(--loka-terracotta-deep);
    text-decoration: underline;
    margin-bottom: 12px;
    margin-top: -4px;
}

.auth-forgot-link:hover {
    color: var(--loka-terracotta);
}

/* Zone E -- inline ember error (T-15-07-02: textContent only, never innerHTML) */
.auth-error {
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs); /* 11px */
    color: var(--loka-ember);
    margin-bottom: 10px;
    line-height: 1.4;
}

/* Zone E -- ToS row (Spec: [LEGAL-02]) */
.auth-tos-row {
    margin: 0 0 12px;
}

.auth-tos-label {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs); /* 11px */
    color: var(--loka-ink-soft);
    line-height: 1.4;
    cursor: pointer;
}

.auth-tos-label input[type="checkbox"] {
    margin-top: 2px;
    flex-shrink: 0;
}

/* Phase 20 A11Y-02: fg --loka-terracotta-deep (#a85a28) -> --loka-ink for
   WCAG AA contrast (terracotta-deep-on-cream-2 = 3.96 fail). Underline
   preserved as the link affordance. */
.auth-tos-label a {
    color: var(--loka-ink);
    text-decoration: underline;
}

/* Zone F (submit / [UI-AUTH-02]) */
.auth-submit {
    display: block;
    width: 100%;
    background: var(--loka-terracotta);
    color: var(--loka-cream);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-lg); /* 4px 4px 0 */
    border-radius: 0;
    padding: 14px 12px;
    font-family: var(--loka-font-pixel);
    font-size: var(--loka-text-pixel-lg); /* 11px */
    letter-spacing: var(--loka-track-pixel-lg); /* DSX-08: 0.18em -- submit tier */
    text-transform: uppercase;
    cursor: pointer;
    min-height: 44px;
    transition: transform var(--motion-press), box-shadow var(--motion-press);
    margin-top: 4px;
    margin-bottom: 12px;
}

.auth-submit:active {
    transform: translate(2px, 2px);
    box-shadow: var(--loka-card-shadow-sm);
}

.auth-submit:disabled {
    /* DEFECT-2 follow-up: disabled state stays in the terracotta family so the
       button doesn't read as cream-on-cream (which diverged from the slice contract).
       Dimmed via color-mix against indigo-deep for state legibility while keeping
       the same accent identity as the active state. */
    background: color-mix(in oklab, var(--loka-terracotta) 55%, var(--loka-indigo-deep) 45%);
    color: color-mix(in oklab, var(--loka-cream) 75%, transparent);
    box-shadow: var(--loka-card-shadow-sm);
    cursor: not-allowed;
}

/* Zone F -- confirmation block (sent mode; DSX-09 dashed border = wait-state signature)
   Spec: [UI-AUTH-03] data-mode="sent" body / [UI-AUTH-07] DSX-09 annotation. */
.auth-confirmation {
    padding: 12px 14px;
    background: var(--loka-cream-2);
    border: var(--loka-border-1-dashed); /* DSX-09: 1px dashed --loka-rule-dark */
    border-radius: 0;
    margin-bottom: 12px;
}

.auth-confirmation-title {
    font-family: var(--loka-font-serif);
    font-style: italic;
    font-size: 15px;
    color: var(--loka-ink-soft);
    line-height: 1.4;
    margin-bottom: 6px;
}

.auth-confirmation-body {
    font-family: var(--loka-font-serif);
    font-style: normal;
    font-size: 14px;
    color: var(--loka-ink-mute);
    line-height: 1.4;
}

/* Zone G -- toggle row
   Phase 20 A11Y-02: fg --loka-ink-mute (#876b5d) -> --loka-ink-soft (#5a3f34)
   and link fg --loka-terracotta-deep (#a85a28) -> --loka-ink for WCAG AA
   (ink-mute / terracotta-deep on cream-2 = 4.27 / 3.96 fail). */
.auth-toggle-row {
    margin-top: 14px;
    text-align: center;
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs); /* 11px */
    color: var(--loka-ink-soft);
}

.auth-toggle-row a {
    color: var(--loka-ink);
    text-decoration: underline;
    cursor: pointer;
}

/* -- Per-mode visibility rules ([UI-AUTH-03]) -- */

/* signup only: OAuth row + divider + ToS row */
.auth-card:not([data-mode="signup"]) .auth-oauth-row,
.auth-card:not([data-mode="signup"]) .auth-divider { display: none; }

/* ToS: signup shows, signin/forgot/sent hide */
.auth-card[data-mode="signin"] #authTosRow,
.auth-card[data-mode="forgot"] #authTosRow,
.auth-card[data-mode="sent"] #authTosRow { display: none; }
.auth-card[data-mode="signup"] #authTosRow { display: block; }

/* Password field: hidden in forgot + sent */
.auth-card[data-mode="forgot"] #authPasswordField,
.auth-card[data-mode="sent"] #authPasswordField { display: none; }

/* Forgot-link: signin only */
.auth-card:not([data-mode="signin"]) .auth-forgot-link { display: none; }

/* sent mode: hide submit + form fields; show confirmation block */
.auth-card[data-mode="sent"] .auth-submit { display: none; }
.auth-card[data-mode="sent"] .auth-field { display: none; }
.auth-card[data-mode="sent"] .auth-error { display: none; }
/* Confirmation is hidden by default via [hidden] attr; shown via JS in sent mode. */

/* -- ONBOARDING STAGE ------------------------------------------ */
/* Empty skeleton -- 15-08 populates content. */

.onboarding-stage {
    position: fixed;
    inset: 0;
    z-index: 10;
    background: var(--void);
    overflow: hidden;
}

/* -- CINEMATIC FADE -------------------------------------------- */
/* z-index 180. "ENTERING HAEBIT-DONG" overlay between phases. */

.cin-fade {
    position: fixed;
    inset: 0;
    z-index: 180;
    background: var(--void); /* --void (#09060f) */
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.6s ease;
}

.cin-fade.on {
    opacity: 1;
}

.cin-text {
    font-family: 'Press Start 2P', monospace;
    font-size: 10px;
    letter-spacing: 3px;
    color: var(--loka-gold);
    text-align: center;
    user-select: none;
}

.cin-text .cin-sub {
    display: block;
    font-family: 'Press Start 2P', monospace;
    font-size: 7px;
    letter-spacing: 2px;
    color: var(--loka-cream-2);
    margin-top: 10px;
}

/* -- VERIFICATION BANNER --------------------------------------- */
/* z-index 300. Fixed top bar for unverified users.
   Plan 17.2-08 DEFECT-3/DEFECT-7: banner is suppressed (display:none) during
   pre-lobby surfaces so it does not contaminate splash / auth / onboarding / tour.
   Class-driven CSS — DOM testids are preserved (volley-verify-banner, volley-verify-dismiss).
   Body classes are set by app.js _routeToPhase lifecycle:
     welcome-active  : splash shown
     auth-modal-open : auth overlay open
     onboarding-active: onboarding flow mounted
   All three removed when lobby lands (#worldRoot visible). */
body.welcome-active .verify-banner,
body.auth-modal-open .verify-banner,
body.onboarding-active .verify-banner {
    display: none !important;
}

/* DEFECT-2 follow-up: while the auth modal is open, hide the splash "Already a
   resident? LOG IN" link (#splashLoginLink) — at scrim opacity 0.78 the link
   was still faintly visible underneath the modal, creating a duplicate Log-in
   affordance below the in-card .auth-toggle-row. Splash itself stays mounted
   (markup intact, testids preserved); only the bottom login-prompt row hides. */
body.auth-modal-open #splashLoginLink,
body.auth-modal-open .lk-resident {
    visibility: hidden;
}

.verify-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 300;
    background: var(--loka-indigo);
    border-bottom: 1px solid var(--loka-rule-dark);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 0 16px;
    min-height: 44px;
    transition: opacity 0.3s ease;
}

.verify-banner-text {
    font-family: 'DM Sans', system-ui, sans-serif;
    font-size: 14px;
    font-weight: 400;
    color: var(--loka-cream);
    line-height: 1.4;
    flex: 1;
}

.verify-banner-dismiss {
    font-family: 'DM Sans', system-ui, sans-serif;
    font-size: 16px;
    color: var(--loka-cream-2);
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 4px 6px;
    flex-shrink: 0;
    min-width: 32px;
    min-height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.verify-banner-dismiss:hover {
    color: var(--loka-cream);
}

/* -- LO BUBBLE TAB --------------------------------------------- */
/* Plan 17.2-08 DEFECT-3: LO tab stamp — present on every Lo bubble across the app
   (splash .lk-bubble, lobby murmur shadow-DOM, onboarding .ob-bubble, tour .ob-tour-bubble).
   Ink text on terracotta corner, pixel font, positioned at top-left of parent bubble.
   Parent bubble must have position: relative for top-left offset to resolve.
   Phase 20 A11Y-02: fg --loka-cream -> --loka-ink for WCAG AA contrast
   (cream-on-terracotta = 2.8; ink-on-terracotta >= 6.0). */

.lo-bubble-tab {
    position: absolute;
    top: -1px;
    left: -1px;
    background: var(--loka-terracotta);
    color: var(--loka-ink);
    font-family: var(--loka-font-pixel);
    font-size: 7px;
    letter-spacing: 0.10em;
    padding: 2px 5px 2px 4px;
    line-height: 1;
    border-right: 2px solid var(--loka-rule-dark);
    border-bottom: 2px solid var(--loka-rule-dark);
    z-index: 1;
    user-select: none;
}

/* Ensure ob-bubble body text clears the LO tab (tab is ~20px tall at top-left corner).
   Add top-padding so text doesn't hide under the tab when present. */
.ob-bubble .lo-bubble-tab ~ .vb-text,
.ob-bubble .lo-bubble-tab ~ .vb-typing {
    /* leave room so the first line isn't hidden under the tab */
    display: block;
}

/* Add padding-top to ob-bubble when it has a LO tab, so text clears the corner stamp.
   LO tab is ~18px tall; bubble default padding-top is 14px (not enough). */
.ob-bubble:has(.lo-bubble-tab) {
    padding-top: 24px;
}

/* -- VOLLEY BUBBLE (shared base) ------------------------------- */
/* Used in splash and in-scene during onboarding (15-08 extends). */

.vol-bubble {
    position: absolute;
    z-index: 50;
    background: var(--loka-cream);
    border: 3px solid var(--loka-rule-dark);
    box-shadow: 4px 4px 0 var(--loka-rule-dark);
    padding: 12px 14px 11px;
    max-width: 280px;
    font-family: 'Nanum Gothic Coding', monospace;
    font-size: 14px;
    font-weight: 700;
    line-height: 1.5;
    color: var(--loka-ink);
}

/* VOLLEY label inside bubble */
.vol-bubble .vb-label {
    display: block;
    font-family: 'Press Start 2P', monospace;
    font-size: 7px;
    letter-spacing: 1.6px;
    color: var(--loka-terracotta-deep);
    margin-bottom: 5px;
}

/* Typing caret */
.vol-bubble .vb-typing {
    display: inline-block;
    width: 7px;
    height: 1em;
    background: var(--loka-terracotta-deep);
    animation: caret-blink 0.7s steps(2) infinite;
    vertical-align: text-bottom;
    margin-left: 1px;
}

/* -- REDUCED MOTION -------------------------------------------- */
/* Must be at the end of the file per plan task specification. */

@media (prefers-reduced-motion: reduce) {
    .boot-loader,
    .splash,
    .auth-overlay,
    .auth-card,
    .cin-fade,
    .splash-bubble,
    .vol-bubble {
        transition: none !important;
        animation: none !important;
    }
    .boot-loader .bl-title {
        animation: none !important;
    }
    .splash-bubble {
        opacity: 1 !important;
    }
    .cin-fade {
        transition: none !important;
    }
    .verify-banner {
        transition: none !important;
    }
    .auth-submit,
    .auth-oauth-btn,
    .splash-btn {
        transition: none !important;
    }
}

/* ==========================================================================
   Phase 17.2 Plan-04 -- Onboarding Stage (interview steps 0..6)
   Rebuilt against prototype/slices/loka-onboarding-step/Loka Screens.html
   (D-01 strict pixel-parity). Tour-stop steps 7..9 owned by Plan-06.
   Spec: [UI-OB-01..07] [ONBOARD-01] [ONBOARD-02] [ONBOARD-05..07]
   DSX: DSX-05 (data-scale=4 interview hero), DSX-06 (--motion-screen-fade 280ms)
   ========================================================================== */

/* --- Onboarding stage container ------------------------------------------ */

.onboarding-stage {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

/* --- Interview mode: uniform cream daylight background [UI-OB-02] Background
   Steps 0..6 use .ob-question on the stage (added by onboarding-flow.js).
   No per-step scene backdrop for interview steps per [UI-OB-07] Forbidden.
   Paper-grain stipple matches prototype/slices/loka-onboarding-step/Loka Screens.html
   .phone .ob-question CSS block (lines 414-422). Ambient timings are slice-internal
   literals per [UI-OB-07] DSX-extension flags. ----------------------------*/

.onboarding-stage.ob-question {
    background: var(--loka-cream);
    color: var(--loka-ink);
    background-image:
        radial-gradient(circle at 20% 20%, color-mix(in oklab, var(--loka-ink) 3.5%, transparent) 0 1px, transparent 2px),
        radial-gradient(circle at 70% 60%, color-mix(in oklab, var(--loka-ink) 3.0%, transparent) 0 1px, transparent 2px),
        radial-gradient(circle at 40% 85%, color-mix(in oklab, var(--loka-ink) 2.5%, transparent) 0 1px, transparent 2px);
    background-size: 7px 7px, 11px 11px, 13px 13px;
}

/* Hide scene layers when in interview mode -- they carry no image for steps 0..6 */
.onboarding-stage.ob-question .ob-layer-a,
.onboarding-stage.ob-question .ob-layer-b,
.onboarding-stage.ob-question .ob-stage-tint,
.onboarding-stage.ob-question .ob-stage-vignette,
.onboarding-stage.ob-question .ob-amb,
.onboarding-stage.ob-question .ob-scene-flash {
    display: none;
}

/* --- Interview inner layout: flex column [UI-OB-02] Zones A/B/C ---------- */

.ob-q-inner {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    padding: 30px 22px 22px;
    overflow: hidden;
}

/* Zone A: Eyebrow chip "FROM LO · <CONTEXT>" [UI-OB-02] Zone A
   --loka-font-mono, --loka-text-xs (11px), tracking 0.30em, --loka-terracotta-deep.
   Uppercase per slice (loka-onboarding-step/Loka Screens.html .ob-q-eyebrow). */
.ob-q-eyebrow {
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs);
    letter-spacing: 0.30em;
    color: var(--loka-terracotta-deep);
    text-transform: uppercase;
    margin-bottom: 18px;
    flex: none;
}

/* Zone B: Stage (Lo + narration bubble) [UI-OB-02] Zone B */
.ob-q-stage {
    flex: 1;
    position: relative;
    min-height: 200px;
}

/* Lo hero mount inside the stage: center-bottom [UI-PRIN-09] interview exception */
.ob-q-lo-mount {
    position: absolute;
    left: 50%;
    bottom: 6px;
    transform: translateX(-50%);
    z-index: 4;
}

/* Tour-stop mode (Plan-06): reposition Lo to bottom-right corner (scale 3 narrator tier).
   .ob-tour-mode is added to #onboardingStage by _renderIntroPanel for steps 7..9.
   Matches slice .lo-corner positioning: absolute bottom-right per [UI-TOUR-02] Zone F. */
.onboarding-stage.ob-tour-mode .ob-q-lo-mount {
    left: auto;
    right: 12px;
    bottom: 12px;
    transform: none;
    z-index: 8;
}

/* Tour mode: hide the interview inner layout (eyebrow, stage, pips, panel)
   since the tour overlay (.ob-tour-inner injected by Plan-06) replaces them. */
.onboarding-stage.ob-tour-mode .ob-q-eyebrow,
.onboarding-stage.ob-tour-mode .ob-q-stage,
.onboarding-stage.ob-tour-mode .ob-bubble,
.onboarding-stage.ob-tour-mode .ob-panel {
    visibility: hidden;
}

/* Tour overlay: .ob-tour-inner injected directly into #onboardingStage for steps 7..9.
   z-index 5 puts it above scene layers (1-4) but below the Lo mount (8) and
   scene-flash (6). Pointer-events enabled for actions row interaction. */
.onboarding-stage.ob-tour-mode .ob-tour-inner {
    z-index: 5;
}

/* --- Scene crossfade layers (tour-stop steps 7..9) ----------------------- */
/* Two layers ping-pong between each other on scene change.
   The incoming layer fades in (.visible), the outgoing fades out (.visible removed).
   Ken Burns kb-drift applied to visible layer for gentle scale drift.
   DSX-06: inter-step screen-fade uses var(--motion-screen-fade) (280ms ease).
   These layers are hidden in interview mode (.ob-question) per Plan-04 rule above. */

.ob-layer-a,
.ob-layer-b {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    opacity: 0;
    transition: opacity var(--motion-screen-fade);
    will-change: opacity;
}

.ob-layer-a {
    z-index: 1;
}

.ob-layer-b {
    z-index: 2;
}

.ob-layer-a.visible,
.ob-layer-b.visible {
    opacity: 1;
    animation: kb-drift 18s ease-in-out infinite alternate;
}

/* --- Stage tint + vignette ----------------------------------------------- */

.ob-stage-tint {
    position: absolute;
    inset: 0;
    z-index: 2;
    background: linear-gradient(
        180deg,
        color-mix(in oklab, var(--loka-indigo-deep) 0%, transparent) 0%,
        color-mix(in oklab, var(--loka-indigo-deep) 30%, transparent) 60%,
        color-mix(in oklab, var(--loka-indigo-deep) 55%, transparent) 100%
    );
    pointer-events: none;
}

.ob-stage-vignette {
    position: absolute;
    inset: 0;
    z-index: 3;
    background: radial-gradient(
        ellipse at center,
        transparent 60%,
        color-mix(in oklab, var(--loka-indigo-deep) 50%, transparent) 100%
    );
    pointer-events: none;
}

/* --- Ambient overlays (petals + bird) ------------------------------------- */

.ob-amb {
    position: absolute;
    inset: 0;
    z-index: 4;
    pointer-events: none;
    overflow: hidden;
}

.amb-petal {
    position: absolute;
    top: -10px;
    width: 5px;
    height: 5px;
    background: var(--loka-petal);
    border-radius: 2px;
    animation: petal-fall 9s linear infinite;
}

.amb-petal:nth-child(2) {
    animation-delay: 3s;
}

.amb-petal:nth-child(3) {
    animation-delay: 6s;
}

.amb-bird {
    position: absolute;
    left: -30px;
    top: 18%;
    width: 22px;
    height: 10px;
    background: var(--loka-ink);
    border-radius: 50% 50% 0 0;
    animation:
        wing-flap 0.38s ease-in-out infinite,
        bird-drift 22s linear infinite;
}

/* --- Scene flash ---------------------------------------------------------- */

.ob-scene-flash {
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
    border-radius: inherit;
}

.ob-scene-flash.on {
    animation: flash-pulse 1.1s ease-out forwards;
}

/* --- Progress pips -------------------------------------------------------- */
/* Absolute position for tour-stop mode (Plan-06); interview mode overrides
   below via .ob-q-inner context so pips flow in the flex column. */

.ob-progress {
    position: absolute;
    top: 14px;
    left: 14px;
    right: 14px;
    z-index: 65;
    display: flex;
    gap: 4px;
    pointer-events: none;
}

/* In interview mode (inside .ob-q-inner), progress pips are a flex item,
   not an absolute overlay — they sit between .ob-q-stage and .ob-panel. */
.ob-q-inner .ob-progress {
    position: static;
    margin-top: 12px;
}

.ob-pip {
    flex: 1;
    height: 4px;
    background: color-mix(in oklab, var(--loka-cream) 18%, transparent);
    border: 1px solid color-mix(in oklab, var(--loka-rule-dark) 50%, transparent);
    border-radius: 2px;
}

.ob-pip.done {
    background: var(--loka-gold);
    border-color: var(--loka-rule-dark);
}

.ob-pip.active {
    background: var(--loka-terracotta);
    border-color: var(--loka-rule-dark);
}

/* --- Narration bubble [UI-OB-02] Zone B ---------------------------------- */
/* Narration tier per slice-DS CLAUDE.md §6.5: cream paper, 2px ink border,
   hand-shadow, --loka-font-serif italic, --loka-text-md (19px), tail down toward Lo.
   NO LO stamp (that is Feedback tier, FoF surface only per [UI-OB-07] Forbidden).
   Positioned absolute within .ob-q-stage; left:50% + translateX(-50%) center-aligns.
   Slice reference: .phone .ob-bubble (Loka Screens.html lines 448-476). */
.ob-bubble {
    position: absolute;
    left: 50%;
    bottom: 104px;
    width: 78%;
    max-width: 320px;
    z-index: 5;
    background: var(--loka-cream-2);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-md);
    padding: 14px 18px 16px;
    font-family: var(--loka-font-serif);
    font-style: italic;
    font-size: 19px;
    line-height: 1.3;
    color: var(--loka-ink);
    transform: translateX(-50%);
    opacity: 0;
    transition: opacity var(--motion-screen-fade);
}

.ob-bubble:not(.hidden) {
    opacity: 1;
}

.ob-bubble .vb-text {
    display: block;
}

.ob-bubble .vb-typing {
    display: inline-block;
    width: 7px;
    height: 1em;
    background: var(--loka-terracotta-deep);
    vertical-align: text-bottom;
    margin-left: 2px;
    animation: caret-blink 0.7s steps(2) infinite;
}

/* Bubble tail pointing DOWN toward Lo (::after pseudo-element border-triangle).
   Slice reference: .phone .ob-bubble::after (Loka Screens.html lines 467-477). */
.ob-bubble::after {
    content: "";
    position: absolute;
    bottom: -10px;
    left: 50%;
    transform: translateX(-50%);
    width: 0;
    height: 0;
    border: 8px solid transparent;
    border-top-color: var(--loka-rule-dark);
    border-bottom: 0;
}

/* --- Answer panel: cream paper card [UI-OB-02] Zone C -------------------- */
/* Replaces old dark-gradient .answer-panel with slice cream paper card.
   Positioned by .ob-q-inner flex column (margin-top: 18px below .ob-q-stage).
   Slice reference: .phone .ob-panel (Loka Screens.html lines 478-487).
   --loka-cream-2 surface, 2px ink border, --loka-card-shadow-lg hand-shadow. */
.ob-panel {
    margin-top: 18px;
    background: var(--loka-cream-2);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-lg);
    padding: 16px;
    overflow-y: auto;
    flex: 0 0 auto;
    max-height: 45%;
    opacity: 0;
    transition: opacity var(--motion-screen-fade);
}

.ob-panel:not(.hidden) {
    opacity: 1;
}

/* --- Answer panel inner elements ----------------------------------------- */

/* Prompt label: pixel font, ink, uppercase per [UI-OB-06] copy contract.
   Slice reference: .phone .ob-prompt (Loka Screens.html lines 488-495).
   Phase 20 A11Y-02: fg --loka-terracotta-deep (#a85a28) -> --loka-ink to
   clear WCAG AA on the --loka-cream-2 panel surface
   (terracotta-deep-on-cream-2 = 3.96 fail; ink-on-cream-2 >= 11.6 pass). */
.ob-prompt,
.ap-prompt {
    font-family: var(--loka-font-pixel);
    font-size: 9px;
    letter-spacing: 0.16em;
    color: var(--loka-ink);
    text-transform: uppercase;
    margin-bottom: 12px;
}

/* Hint: serif italic, ink-soft per [UI-OB-06] copy contract.
   Slice reference: .phone .ob-hint (Loka Screens.html lines 507-513). */
.ob-hint,
.ap-hint {
    font-family: var(--loka-font-serif);
    font-size: 14px;
    color: var(--loka-ink-soft);
    margin-top: 8px;
    font-style: italic;
}

.ob-actions,
.ap-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 14px;
}

/* --- Name input: [UI-INV-29] step-text-field ----------------------------- */
/* --loka-font-serif italic 22px, cream surface, 2px ink border, no radius.
   Slice reference: .phone .ob-input (Loka Screens.html lines 496-506).
   T-15-08-01: value rendered via textContent in onboarding-flow.js -- no innerHTML. */
.ob-input,
.ap-input {
    width: 100%;
    box-sizing: border-box;
    font-family: var(--loka-font-serif);
    font-size: 22px;
    font-style: italic;
    color: var(--loka-ink);
    background: var(--loka-cream);
    border: 2px solid var(--loka-rule-dark);
    caret-color: var(--loka-terracotta-deep);
    padding: 12px 14px;
    outline: none;
    border-radius: 0;
}

.ob-input::placeholder,
.ap-input::placeholder {
    color: var(--loka-ink-mute);
}

/* --- Chip grid: [UI-INV-30] chip-grid -------------------------------------- */
/* 2-col grid for Language / Home language steps.
   Slice reference: .phone .ob-grid (Loka Screens.html lines 514-519). */
.ob-grid,
.ap-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-bottom: 10px;
}

/* Wrap grid for Why / Topics steps.
   Slice reference: .phone .ob-chips-wrap (Loka Screens.html lines 520-524). */
.ob-chips-wrap,
.ap-chips-wrap {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 10px;
}

/* --- Chips: [UI-INV-30] chip-grid element -------------------------------- */
/* --loka-font-pixel 9px, cream surface, 2px ink border, --loka-card-shadow-sm.
   Slice reference: .phone .ob-chip (Loka Screens.html lines 525-537). */
.ob-chip,
.ap-chip {
    font-family: var(--loka-font-pixel);
    font-size: 9px;
    letter-spacing: 0.1em;
    background: var(--loka-cream);
    color: var(--loka-ink);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-sm);
    padding: 10px 12px;
    cursor: pointer;
    text-transform: uppercase;
    text-align: center;
    border-radius: 0;
    transition: transform var(--motion-press), box-shadow var(--motion-press);
}

.ob-chip:active,
.ap-chip:active {
    transform: translate(2px, 2px);
    box-shadow: none;
}

/* Selected: terracotta background + ink label.
   Slice reference: .phone .ob-chip.selected (Loka Screens.html line 538-541).
   Phase 20 A11Y-02: fg --loka-cream -> --loka-ink for WCAG AA contrast
   (cream-on-terracotta = 2.8 fail; ink-on-terracotta >= 6.0 pass).
   Mirrors the matching fix on .ob-cta / .lk-bubble-tag / .lo-bubble-tab. */
.ob-chip.selected,
.ap-chip.selected {
    background: var(--loka-terracotta);
    color: var(--loka-ink);
}

/* Disabled: 40% opacity, not interactive.
   Slice reference: .phone .ob-chip.disabled (Loka Screens.html line 542). */
.ob-chip.disabled,
.ap-chip.disabled {
    opacity: 0.4;
    cursor: not-allowed;
    pointer-events: none;
}

/* L2 chip native-script sub-span (Korean in --loka-font-l2-ko).
   Slice reference: .phone .ob-chip-l2 .ko (Loka Screens.html line 543). */
.ap-chip .ko {
    font-family: var(--loka-font-l2-ko);
    font-size: 11px;
}

/* --- CTA button: [UI-OB-02] Zone D --loka-btn --primary ------------------- */
/* --loka-terracotta surface, --loka-ink label, --loka-font-pixel 11px.
   Slice reference: .phone .ob-cta (Loka Screens.html lines 606-618).
   Phase 20 A11Y-02: fg --loka-cream -> --loka-ink for WCAG AA contrast
   (cream-on-terracotta = 2.8 fail; ink-on-terracotta >= 6.0 pass).
   Mirrors the matching fix on .lk-bubble-tag / .lo-bubble-tab. */
.ob-cta,
.ap-cta {
    margin-left: auto;
    font-family: var(--loka-font-pixel);
    font-size: 11px;
    letter-spacing: 0.18em;
    background: var(--loka-terracotta);
    color: var(--loka-ink);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-lg);
    padding: 12px 16px;
    cursor: pointer;
    text-transform: uppercase;
    border-radius: 0;
    transition: transform var(--motion-press), box-shadow var(--motion-press);
}

.ob-cta:active,
.ap-cta:active {
    transform: translate(2px, 2px);
    box-shadow: none;
}

/* Disabled CTA: --loka-cream-3 surface, --loka-ink-mute label, --loka-card-shadow-sm.
   Slice reference: .phone .ob-cta.disabled (Loka Screens.html lines 619-623). */
.ob-cta.disabled,
.ob-cta:disabled,
.ap-cta.disabled,
.ap-cta:disabled {
    background: var(--loka-cream-3);
    color: var(--loka-ink-mute);
    box-shadow: var(--loka-card-shadow-sm);
    pointer-events: none;
    cursor: default;
}

/* --- Back affordance: [UI-INV-32] step-back (FU-onb-back resolved Plan-04) */
/* --loka-btn --ghost semantics: transparent surface, 2px dashed --loka-rule-dark border,
   ink label, --loka-font-pixel uppercase. Present steps 1..6; absent on Greet (step 0).
   Slice reference: .phone .ob-skip (Loka Screens.html lines 595-605) adapted for back. */
.ob-back,
.ap-back {
    font-family: var(--loka-font-pixel);
    font-size: 11px;
    letter-spacing: 0.16em;
    color: var(--loka-ink);
    background: transparent;
    border: 2px dashed var(--loka-rule-dark);
    padding: 10px 14px;
    cursor: pointer;
    text-transform: uppercase;
    border-radius: 0;
    transition: transform var(--motion-press);
}

.ob-back:active,
.ap-back:active {
    transform: translate(1px, 1px);
}

/* --- Skip button (tour steps 7..8 SKIP TOUR, owned by Plan-06) ----------- */
.ap-skip {
    font-family: var(--loka-font-pixel);
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0.16em;
    color: var(--loka-ink-mute);
    background: transparent;
    border: 0;
    cursor: pointer;
    padding: 8px 4px;
    border-radius: 0;
}

.ap-skip:active {
    color: var(--loka-ink);
}

/* --- CEFR tile grid: [UI-INV-31] cefr-tile-grid -------------------------- */
/* 2-col 4-tile grid for Starting point step (A1/A2/B1/B2).
   Slice reference: .phone .cefr-grid / .cefr-tile (Loka Screens.html lines 545-590).
   --loka-cream surface, 2px ink border, selected = --loka-terracotta with cream text. */

.cefr-grid,
.ap-cefr-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    margin-bottom: 10px;
}

.cefr-tile,
.ap-cefr-grid .level-select-tile {
    background: var(--loka-cream);
    border: 2px solid var(--loka-rule-dark);
    box-shadow: var(--loka-card-shadow-sm);
    padding: 10px 12px;
    cursor: pointer;
    text-align: left;
    border-radius: 0;
    transition: transform var(--motion-press), box-shadow var(--motion-press);
}

.cefr-tile:active,
.ap-cefr-grid .level-select-tile:active {
    transform: translate(2px, 2px);
    box-shadow: none;
}

.cefr-tile.selected,
.ap-cefr-grid .level-select-tile[aria-checked="true"],
.ap-cefr-grid .level-select-tile.selected {
    background: var(--loka-terracotta);
    color: var(--loka-cream);
}

/* .cefr-band: pixel band label (A1, A2, B1, B2)
   Slice reference: .phone .cefr-band (Loka Screens.html lines 566-573).
   Phase 20 A11Y-02: idle fg --loka-terracotta-deep (#a85a28) -> --loka-ink
   for WCAG AA on cream (terracotta-deep-on-cream = 4.41 fail; ink = pass).
   Selected fg --loka-cream -> --loka-ink for cream-on-terracotta = 2.8 fail. */
.cefr-band,
.ap-cefr-grid .level-select-tile .level-select-tile-band {
    display: block;
    font-family: var(--loka-font-pixel);
    font-size: 11px;
    color: var(--loka-ink);
    letter-spacing: 0.08em;
    margin-bottom: 2px;
}

.cefr-tile.selected .cefr-band,
.ap-cefr-grid .level-select-tile[aria-checked="true"] .level-select-tile-band,
.ap-cefr-grid .level-select-tile.selected .level-select-tile-band {
    color: var(--loka-ink);
}

/* .cefr-label: mono label (Beginner, Elementary…)
   Slice reference: .phone .cefr-label (Loka Screens.html lines 574-582).
   Phase 20 A11Y-02: idle fg --loka-ink-mute (#876b5d) -> --loka-ink-soft
   (#5a3f34) for WCAG AA on cream (ink-mute-on-cream = 4.27 fail).
   Selected fg --loka-cream -> --loka-ink for cream-on-terracotta = 2.8 fail. */
.cefr-label,
.ap-cefr-grid .level-select-tile .level-select-tile-label {
    display: block;
    font-family: var(--loka-font-mono);
    font-size: 10px;
    letter-spacing: 0.16em;
    color: var(--loka-ink-soft);
    text-transform: uppercase;
    margin-bottom: 6px;
}

.cefr-tile.selected .cefr-label,
.ap-cefr-grid .level-select-tile[aria-checked="true"] .level-select-tile-label,
.ap-cefr-grid .level-select-tile.selected .level-select-tile-label {
    color: var(--loka-ink);
}

/* .cefr-cue: serif italic cue sentence
   Slice reference: .phone .cefr-cue (Loka Screens.html lines 583-590).
   Phase 20 A11Y-02 (CR-01 follow-up): selected-state cream-on-terracotta
   measures ~2.6:1 (ink-soft #5a3f34 on terracotta #d07a3f also fails).
   Override to --loka-ink for WCAG 2.1 AA 1.4.3 pass on --loka-terracotta. */
.cefr-cue,
.ap-cefr-grid .level-select-tile .level-select-tile-cue {
    display: block;
    font-family: var(--loka-font-serif);
    font-style: italic;
    font-size: 13px;
    line-height: 1.25;
    color: var(--loka-ink-soft);
}

.cefr-tile.selected .cefr-cue,
.ap-cefr-grid .level-select-tile[aria-checked="true"] .level-select-tile-cue,
.ap-cefr-grid .level-select-tile.selected .level-select-tile-cue {
    color: var(--loka-ink);
}

/* Submit error: --loka-ember tone, mono 11px, inline below panel content
   Per [UI-OB-03] States § error + [UI-OB-07] Forbidden § no flash-banner error.
   T-15-08-02: text rendered via textContent in onboarding-flow.js _showSubmitError(). */
.ob-submit-error {
    font-family: var(--loka-font-mono);
    font-size: 11px;
    color: var(--loka-ember);
    margin-top: 8px;
}

/* --- Keyframes ------------------------------------------------------------ */

@keyframes petal-fall {
    0%   { top: -10px; transform: rotate(0deg);   opacity: 0.8; }
    50%  { transform: rotate(180deg); opacity: 1; }
    100% { top: 110%;  transform: rotate(360deg); opacity: 0; }
}

@keyframes bird-drift {
    0%   { left: -30px; }
    100% { left: 110%; }
}

@keyframes wing-flap {
    0%,  100% { transform: scaleY(1); }
    50%        { transform: scaleY(0.4); }
}

@keyframes flash-pulse {
    0%   { background: radial-gradient(circle, rgba(255, 235, 180, 0) 0%, transparent 100%); }
    50%  { background: radial-gradient(circle, rgba(255, 235, 180, 0.7) 0%, transparent 80%); }
    100% { background: radial-gradient(circle, rgba(255, 235, 180, 0) 0%, transparent 100%); }
}

@keyframes kb-drift {
    0%   { transform: scale(1.04) translate(-1.5%, 0.8%); }
    100% { transform: scale(1.10) translate(1.8%, -1.4%); }
}

/* --- Reduced-motion overrides (onboarding-specific) ---------------------- */
/* Extends the Phase 15 reduced-motion block at top of this section. */

@media (prefers-reduced-motion: reduce) {
    .ob-layer-a,
    .ob-layer-b {
        transition: none !important;
        animation: none !important;
    }
    .ob-layer-a.visible,
    .ob-layer-b.visible {
        animation: none !important;
    }
    .ob-amb {
        display: none !important;
    }
    .ob-bubble,
    .ob-panel {
        transition: none !important;
        animation: none !important;
    }
    .ob-bubble:not(.hidden) {
        transition: none !important;
    }
    .ob-panel:not(.hidden) {
        transition: none !important;
    }
    .vb-typing {
        display: none !important;
    }
    .ob-chip,
    .ob-cta,
    .ap-chip,
    .ap-cta,
    .cefr-tile,
    .ap-cefr-grid .level-select-tile {
        transition: none !important;
    }
}

/* ============================================================
   Plan 17.2-06: Tour-stop slice CSS contract (.ob-tour-*)
   ============================================================
   Rewritten against prototype/slices/loka-onboarding-step/Loka Screens.html
   <style> block lines 628-718 (tpl-onb-7/8/9).

   DSX tokens consumed:
     DSX-10: --loka-scene-tint + --loka-scene-vignette (Plan-01 minted)
     DSX-11: --loka-text-sm (16px, Plan-01 minted) for Narration-tier bubble
     DSX-06: --motion-screen-fade (280ms ease, Plan-01 minted) — already wired
             on .ob-layer-a / .ob-layer-b crossfade; tour-stops share the same
             layer machinery so no new CSS needed.

   Spec: [UI-TOUR-02] zones A-F, [UI-INV-34..38]
   ============================================================ */

/* ── Tour root shell ─────────────────────────────────────────
   Matches slice CSS line 628-631. Indigo base shows through any
   letterbox gap or during scene-loading state per [UI-TOUR-03]. */
.ob-tour {
    background: var(--loka-indigo-deep);
    color: var(--loka-cream);
    position: absolute;
    inset: 0;
    overflow: hidden;
}

/* ── Zone A: scene PNG (full-bleed background) ───────────────
   Matches slice CSS lines 633-638.
   image-rendering: pixelated is mandatory per slice README §11
   — never interpolate pixel-art scenes. */
.ob-tour-bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    image-rendering: pixelated;
    z-index: 1;
}

/* ── Zone A: indigo tint gradient ────────────────────────────
   Matches slice CSS lines 639-643.
   DSX-10 resolution: --loka-scene-tint minted by Plan-01
   (color-mix derivation of --loka-indigo-deep at 5%/55% stops). */
.ob-tour-tint {
    position: absolute;
    inset: 0;
    background: var(--loka-scene-tint);
    z-index: 2;
    pointer-events: none;
}

/* ── Zone A: inset vignette (edge fall-off) ──────────────────
   Matches slice CSS lines 644-648.
   DSX-10 resolution: --loka-scene-vignette minted by Plan-01
   (color-mix derivation of --loka-indigo-deep at 50%, 120px spread). */
.ob-tour-vignette {
    position: absolute;
    inset: 0;
    box-shadow: var(--loka-scene-vignette);
    z-index: 3;
    pointer-events: none;
}

/* ── Zone B: content layer (flex column) ─────────────────────
   Matches slice CSS lines 649-654.
   36px top pad: no exact DS token (between --loka-space-6 28px and next);
   22px = --loka-space-5 for side + bottom. */
.ob-tour-inner {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    padding: 36px 22px 22px;
    z-index: 4;
}

/* ── Zone C: narration bubble (Narration tier) ───────────────
   Matches slice CSS lines 655-702.
   DSX-11 resolution: font-size uses --loka-text-sm (16px, Plan-01 minted).
   Anchored bottom-right above Lo's corner per [UI-TOUR-02] Zone C.
   No terracotta rule, no LO stamp (those are FoF tier only). */
.ob-tour-bubble {
    position: absolute;
    right: 22px;
    bottom: 132px;
    max-width: 286px;
    background: var(--loka-cream);
    color: var(--loka-ink);
    border: var(--loka-border-2);
    box-shadow: var(--loka-card-shadow-lg);
    padding: 12px 14px 13px;
    font-family: var(--loka-font-serif);
    font-style: italic;
    font-size: var(--loka-text-sm); /* DSX-11: 16px narration-tier */
    line-height: 1.3;
    border-radius: 0; /* [UI-TOUR-07] no rounded corners */
    z-index: 7;
}

/* Tail pointing DOWN toward Lo's corner (slice CSS lines 675-685) */
.ob-tour-bubble::after {
    content: "";
    position: absolute;
    right: 22px;
    bottom: -10px;
    width: 0;
    height: 0;
    border: 8px solid transparent;
    border-top-color: var(--loka-rule-dark);
    border-bottom: 0;
}

/* Inner tail — cream fill masking border tip (slice CSS lines 686-697) */
.ob-tour-bubble::before {
    content: "";
    position: absolute;
    right: 24px;
    bottom: -6px;
    width: 0;
    height: 0;
    border: 6px solid transparent;
    border-top-color: var(--loka-cream);
    border-bottom: 0;
    z-index: 1;
}

/* Korean character-name spans inside narration copy (slice CSS lines 698-702).
   Breaks out of serif italic into L2 face per [UI-PRIN-30] two-font-slot. */
.ob-tour-bubble .ko {
    font-family: var(--loka-font-l2-ko);
    font-style: normal;
    font-weight: 500;
}

/* Flex spacer pushing meta + actions to bottom of .ob-tour-inner */
.ob-tour-spacer {
    flex: 1;
}

/* ── Zone D: meta line (system-voice place label) ────────────
   Matches slice CSS lines 704-712.
   No "FROM LO ·" prefix — place-label form per [UI-TOUR-02] Zone D.
   Cream at 92% alpha using color-mix per DSX-16 pattern. */
.ob-tour-meta {
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs); /* 11px */
    letter-spacing: var(--loka-track-eye); /* 0.30em — matches token exactly */
    color: color-mix(in oklab, var(--loka-cream) 92%, transparent);
    text-transform: uppercase;
    text-shadow: 1px 1px 0 var(--loka-indigo-deep); /* legibility over scene PNG */
    margin-bottom: 14px;
}

/* ── Zone E: actions row ─────────────────────────────────────
   Matches slice CSS lines 713-717.
   padding-right: 80px clears Lo's bottom-right corner sprite. */
.ob-tour-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-right: 80px;
}

/* Skip tour ghost link (stops 7+8; absent on stop 9 per [UI-TOUR-07]).
   [UI-INV-38]: <tour-skip-link> ghost mono primitive.
   Cream at 70% alpha using color-mix per DSX-16 pattern. */
.ob-tour-actions .ob-skip,
.ob-tour-actions .ap-skip {
    font-family: var(--loka-font-mono);
    font-size: var(--loka-text-xs);
    color: color-mix(in oklab, var(--loka-cream) 70%, transparent);
    text-decoration: underline;
    background: transparent;
    border: 0;
    padding: 8px 4px;
    cursor: pointer;
    text-transform: uppercase;
}

/* LO tab inside tour bubble — matches interview + lobby treatment (DEFECT-5c).
   Phase 20 A11Y-02: ink-on-terracotta corner stamp (was cream-on-terracotta;
   cleared WCAG AA per the base .lo-bubble-tab rule above).
   Bubble needs extra top padding to clear tab. */
.ob-tour-bubble .lo-bubble-tab {
    position: absolute;
    top: -1px;
    left: -1px;
    background: var(--loka-terracotta);
    color: var(--loka-ink);
    font-family: var(--loka-font-pixel);
    font-size: 7px;
    letter-spacing: 0.10em;
    padding: 2px 5px 2px 4px;
    line-height: 1;
    border-right: 2px solid var(--loka-rule-dark);
    border-bottom: 2px solid var(--loka-rule-dark);
    z-index: 1;
    user-select: none;
}
/* When the LO tab is present, the bubble needs extra top padding to clear it.
   DO NOT add position:relative here -- the base .ob-tour-bubble rule sets
   position:absolute (right:22px / bottom:132px) to anchor bottom-right above
   Lo's corner per [UI-TOUR-02] Zone C. Overriding to relative drops the
   bubble back into the .ob-tour-inner flex column at the top of the frame
   and turns right/bottom into relative offsets (the Phase 17.2-08 regression
   captured in Image #1 of the post-merge UAT). The .lo-bubble-tab anchors
   correctly to an absolute parent (absolute is also a positioning context). */
.ob-tour-bubble:has(.lo-bubble-tab) {
    padding-top: 24px;
}

/* Lo mount inside tour overlay — bottom-right corner, narrator tier (scale 3).
   DEFECT-5a: Lo visible on every tour-stop (was missing entirely because the
   existing .ob-q-lo-mount repositioning was dead code — visibility:hidden inherits
   from .ob-tour-mode .ob-q-stage).
   .lo-mount--tour is the positioning wrapper around the [data-lo] sprite; the
   wrapper carries the absolute anchor because ds-lo-renderer.js sets
   `style.position = 'relative'` on the sprite element itself (ds-lo-renderer.js:159),
   which would otherwise override any CSS positioning. Z-index 9 sits above the
   tour overlay (.ob-tour-inner z=4) so Lo stacks on top of the narration bubble
   tail and the actions row. */
.lo-mount--tour {
    position: absolute;
    right: 12px;
    bottom: 12px;
    z-index: 9;
    pointer-events: none;
}

/* Hide progress bar during tour-stop mode — overlaps tour overlay (DEFECT-5d).
   The absolute-positioned .ob-progress (z-index 65) stacks above .ob-tour-inner (z-index 5)
   and collides with the top of the tour overlay. Hide it in tour mode; pips are
   redundant for tour narration steps. */
.onboarding-stage.ob-tour-mode .ob-progress {
    display: none;
}

/* ── Reduced-motion override for tour-stop bubble entrance ── */
@media (prefers-reduced-motion: reduce) {
    .ob-tour-bubble {
        transition: none !important;
        animation: none !important;
    }
}

/* ============================================================
   Plan 17.2-02: Lobby slice CSS contract
   ============================================================
   Mirrors prototype/slices/loka-lobby/lobby.css for the static visual
   contract. Every rgba() literal from the slice is replaced with a
   color-mix() or --loka-* token (using --loka-scene-tint / --loka-scene-vignette
   from Plan-01 DSX-10 resolution). Documented exceptions carry
   "/* slice raw *\/" inline comments per 17.2-PATTERNS.md.

   [UI-LOBBY-02] Composition — Zones A, B, C.
   Spec source: docs/ui/lobby.md + prototype/slices/loka-lobby/lobby.css.
   ============================================================ */

/* ── Lobby container ─────────────────────────────────────────
   Maps to .world in production (.lobby in slice).
   Background provides fallback behind the scene asset. */
.lobby,
.world {
  background: var(--loka-indigo-deep);
}

/* ── Background scene asset ─────────────────────────────────
   .lobby__bg is a slice-native class; .world-bg is the production
   img element counterpart. Both get image-rendering: pixelated
   per slice HANDOFF §2 + lobby.css:41. */
.lobby__bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  z-index: 1;
}

/* ── Atmospheric tint (top-bottom indigo gradient — depth cue) ─
   DSX-10 resolution: --loka-scene-tint minted by Plan-01.
   Matches lobby.css:46-55 (source rgba values now token-derived). */
.lobby__tint {
  position: absolute;
  inset: 0;
  background: var(--loka-scene-tint);
  pointer-events: none;
  z-index: 2;
}

/* ── Vignette (inset indigo shadow — edge fall-off) ─────────
   DSX-10 resolution: --loka-scene-vignette minted by Plan-01.
   Matches lobby.css:57-62. */
.lobby__vignette {
  position: absolute;
  inset: 0;
  box-shadow: var(--loka-scene-vignette);
  pointer-events: none;
  z-index: 2;
}

/* ── Inner container (z=3; carries top bar + stage + bubble + Lo) ─
   position: absolute inset: 0; flex-column; padding per
   --loka-space-5 (22px). Mirrors lobby.css:64-71. */
.lobby__inner {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: var(--loka-space-5); /* 22px — lobby.css:69 */
  z-index: 3;
}

/* ── Top bar: wordmark + place/time meta ─────────────────── */
.lobby__bar {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Meta line — place · time. Uses --loka-font-mono + --loka-text-xs +
   --loka-track-mono (0.20em). Color is --loka-cream at 92% alpha via
   color-mix(). Matches lobby.css:83-91. */
.lobby__meta {
  margin-left: auto;
  font-family: var(--loka-font-mono);
  font-size: var(--loka-text-xs);
  letter-spacing: var(--loka-track-mono);
  color: color-mix(in oklab, var(--loka-cream) 92%, transparent);
  text-transform: uppercase;
  text-shadow: 1px 1px 0 var(--loka-indigo-deep);
}

/* ── Stage: character + overlays fill the middle band ───── */
.lobby__stage {
  flex: 1;
  position: relative;
}

/* ── Character sprite wrapper (slice §4 / lobby.css:119-137) ─
   .loka-char--minjun modifier sets per-character offsets.
   Positioned absolute via bottom + left from modifier.
   .loka-char values map to slice-default scale 3 / charX 40% / charY 14%. */
.loka-char {
  --char-scale: 3;
  --char-x: 40%;
  --char-y: 14%;
  position: absolute;
  left: var(--char-x);
  bottom: var(--char-y);
  width: calc(22px * var(--char-scale));
  display: flex;
  flex-direction: column-reverse; /* name above sprite */
  align-items: center;
  image-rendering: pixelated;
  pointer-events: none;
}

.loka-char--minjun {
  --char-scale: 3;
  --char-x: 40%;
  --char-y: 14%;
}

/* Sprite shell: animates per data-anim. */
.loka-char__sprite {
  width: 100%;
  display: block;
  position: relative;
  z-index: 2;
  animation: loka-char-idle 1.6s steps(2, end) infinite;
  transform-origin: 50% 100%;
}
.loka-char__svg,
.loka-char__sprite > svg,
.loka-char__sprite > img {
  display: block;
  width: 100%;
  height: auto;
  image-rendering: pixelated;
}

/* Idle bob — 2px, pixel-honest. */
@keyframes loka-char-idle {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-2px); }
}

[data-anim="listening"] .loka-char__sprite { animation-duration: 1.2s; }
[data-anim="talking"]   .loka-char__sprite { animation-duration: 0.8s; }

/* Shadow halo — radial ground wash, counter-bobs. */
.loka-char__shadow {
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 70%;
  height: 8px;
  transform: translateX(-50%);
  background: radial-gradient(
    ellipse at center,
    color-mix(in oklab, var(--loka-indigo-deep) 55%, transparent) 0%,
    color-mix(in oklab, var(--loka-indigo-deep) 32%, transparent) 45%,
    transparent 75%
  );
  z-index: 1;
  pointer-events: none;
  animation: loka-char-shadow 1.6s steps(2, end) infinite;
}
@keyframes loka-char-shadow {
  0%, 100% { transform: translateX(-50%) scaleX(1.00); opacity: 0.95; }
  50%       { transform: translateX(-50%) scaleX(0.92); opacity: 0.80; }
}

/* Character name plate — Korean names use --loka-font-l2-ko exclusively.
   Per slice HANDOFF §6: Instrument Serif italic is FORBIDDEN here (Lo's voice). */
.loka-char__name {
  margin-bottom: 6px;
  font-family: var(--loka-font-l2-ko);
  font-size: 13px; /* slice raw — lobby.css:210; no matching DS token */
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--loka-cream);
  text-shadow: 1px 1px 0 var(--loka-indigo-deep);
  line-height: 1;
  white-space: nowrap;
  text-align: center;
}

/* ── Lo's corner (reserved bottom-right real estate) ─────
   .loka-lo-corner is assigned to the .lo-mount--lobby div in index.html.
   position: absolute applies when inside .lobby__inner (.lo-mount provides
   position: fixed for the full-viewport case; the slice uses absolute
   within the inner container). The lo-mount CSS owns the actual position
   for the production shell. This block adds the focus-visible ring and
   interactive cursor per slice HANDOFF §7 + lobby.css:224-235.
   14px corner inset — slice raw; reconciliation deferred per [UI-LOBBY-07]. */
.loka-lo-corner {
  cursor: pointer;
  outline: none;
  /* pointer-events: auto overrides .lo-mount's pointer-events: none so
     the Lo corner tap for bubble re-summon actually fires. */
  pointer-events: auto;
}
.loka-lo-corner:focus-visible {
  /* Pixel-honest focus ring — no rounded corners. 2px gold per slice lobby.css:233. */
  box-shadow: 0 0 0 2px var(--loka-gold);
}

/* ============================================================
   End Plan 17.2-02: Lobby slice CSS contract
   ============================================================ */

/* === Loka Lo mount (Phase 17 D-02 + D-03; viewport-anchored post-UAT) ===
 * Lo home zone: bottom-right of the viewport, always. Spec:
 * loka-design-system/CLAUDE.md §8 "Same place every screen."
 *
 * Uses position: fixed so Lo anchors to the viewport regardless of which
 * shell container hosts the [data-lo] placeholder. Necessary because the
 * onboarding stage nests Lo inside #obVolleyMount (top 50% only) -- without
 * fixed positioning, Lo's absolute bottom/right resolves to the middle of
 * the screen on onboarding instead of the bottom-right corner.
 *
 * Paired with ds-lo-renderer.js conditional-position fix: the renderer no
 * longer clobbers this position via inline `style.position = 'relative'`. */
.lo-mount {
  position: fixed;
  bottom: var(--loka-space-4);
  right: var(--loka-space-4);
  z-index: var(--z-volley, 65);
  pointer-events: none;
}
/* Splash: lift above boot/splash bg, stay below auth overlay (z-overlay=100) */
.lo-mount--splash { z-index: var(--z-boot, 200); }
/* Lobby: anchor Lo to the phone-frame's bottom-right, not the viewport.
   Post-17.2 UAT regression: with position:fixed inherited from .lo-mount,
   Lo tracked the viewport's right edge -- on a viewport wider than
   --phone-w (the centered phone-shell), Lo escaped the shell to the right
   and fell partially off the visible scene. Override to position:absolute
   so right/bottom resolve against .world (which is absolute inset:0 inside
   the centered #phone-shell). Onboarding still inherits position:fixed
   from .lo-mount above -- that case needs viewport anchoring because the
   onboarding stage nests Lo inside a top-50% container. */
.lo-mount--lobby  {
  position: absolute;
  z-index: var(--z-volley, 65);
}
/* Onboarding: inherits fixed bottom-right from .lo-mount above. */
.lo-mount--onboarding { /* z-index inherited */ }
