/* Elixir Hour — global styles. Palette per GDD §12 (roles fixed). */
:root{
  --sage:#9CAF88; --sage-deep:#6B7F5C; --sage-pale:#C7D3B5;
  --elixir:#7A3FA0; --glow:#B26AE0;
  --wood-dark:#3A2A20; --wood:#6E4B2A; --amber:#E8B770;
  --night:#1C1B3A; --cream:#F4ECDD; --charcoal:#241F1B;
  --blue:#2E6FD6; --red:#C8392F;          /* accents only — never dominant */
  --gold:#c9a55a; --ink:#4a3a28; --ink-soft:#7a6748;
}
*{box-sizing:border-box; margin:0; padding:0;}
html,body{height:100%;}
body{background:#141019; font-family:'Nunito',sans-serif; overflow:hidden;}

/* The stage: everything is laid out in a fixed 1280×720 design space
   (the mockup's reference resolution) and the whole stage scales to fit
   the window — see src/stage.js. */
#viewport{position:fixed; inset:0;}
#stage{position:absolute; left:0; top:0; width:1280px; height:720px; overflow:hidden;
  background:var(--night); transform-origin:0 0; user-select:none;}
.screen{position:absolute; inset:0; display:none;}
/* §11 scene transitions (round 8/9): a layout change fades the new screen in
   (never a hard pop); within a layout, expression/character swaps cross-fade via
   the .scene-layer / .duo-cut opacity transitions. */
.screen.active{display:block; animation:screenfade .26s ease;}
@keyframes screenfade{from{opacity:0;} to{opacity:1;}}
/* prefers-reduced-motion: drop the layout drift; the opacity fades stay (that IS
   the reduced-motion fallback the GDD asks for) but instant enough to be calm. */
@media (prefers-reduced-motion: reduce){
  .screen.active{animation:none;}
  .scene-layer, .duo-cut{transition-duration:.06s;}
}
/* R13: layout-change DIP — a warm near-dark camera cut between serve/duo/group.
   Sits above the scene + HUD (z12) but below the Tome/overlays (z30+). Fades IN
   fast (200ms) to cover the old layout, the screen swaps under it, then OUT (250ms)
   to reveal the new one. While dipping the screenfade is suppressed so the dip is
   the only reveal (no double animation). */
#dip{position:absolute; inset:0; z-index:20; background:#160d07; opacity:0;
  pointer-events:none; transition:opacity .25s ease-out;}
#dip.on{opacity:1; transition:opacity .2s ease-in;}
#stage.dipping .screen.active{animation:none;}

/* buttons (mockup style) */
.btn{font-family:'Baloo 2',cursive; font-size:20px; border:none; border-radius:12px; padding:11px 1.4em;
  cursor:pointer; letter-spacing:.05em; line-height:1.2;
  background:linear-gradient(180deg,#fdf6e8,var(--cream)); color:var(--charcoal); box-shadow:0 4px 0 #b7a888;}
.btn.primary{background:linear-gradient(180deg,var(--glow),var(--elixir)); color:#fff; box-shadow:0 4px 0 #572c73;}
.btn.sage{background:linear-gradient(180deg,var(--sage-pale),var(--sage)); color:var(--charcoal); box-shadow:0 4px 0 var(--sage-deep);}
.btn:active{transform:translateY(2px); box-shadow:none;}
.btn:disabled{opacity:.45; cursor:default;}
.btn:disabled:active{transform:none; box-shadow:0 4px 0 #b7a888;}
.btn.small{font-size:15px; padding:8px 26px;}
/* round 11: a long title label splits into a main line + a smaller sub-line so no
   word orphans onto a line of its own (§11). */
.btn.two-line{display:flex; flex-direction:column; align-items:center; gap:1px; line-height:1.05; padding-top:8px; padding-bottom:9px;}
.btn-main{font-family:'Baloo 2',cursive; font-size:20px;}
.btn-sub{font-family:'Nunito',sans-serif; font-size:12.5px; font-weight:700; opacity:.85; letter-spacing:.01em; text-transform:none;}

/* ---------- TITLE / LOADING ---------- */
#title-bg{width:100%; height:100%; object-fit:cover; filter:brightness(.85);}
#fx{position:absolute; inset:0; width:100%; height:100%; pointer-events:none;}
#title-logo{position:absolute; top:9%; left:50%; transform:translateX(-50%); width:44%;
  filter:drop-shadow(0 0 22px rgba(178,106,224,.35));}  /* soft Elixir glow re-added over the cut-out */
#title-menu{position:absolute; bottom:11%; left:50%; transform:translateX(-50%);
  display:flex; flex-direction:column; gap:12px; width:300px;}
/* visible build stamp (bottom-right corner) — recessive, but readable so Tessa
   can confirm which build loaded and copy it into a bug report. Bumped with the
   ?v= cache token (see strings.js BUILD). */
#build-stamp{position:absolute; right:10px; bottom:7px; z-index:8;
  font-family:'Nunito',sans-serif; font-size:10px; letter-spacing:.04em;
  color:rgba(244,236,221,.34); text-shadow:0 1px 2px rgba(0,0,0,.55);
  pointer-events:none; user-select:text;}

/* ---------- HERALD INTERSTITIAL ---------- */
/* §11 (round 11): the Herald reads as the day's paper lying on the counter — the
   dim wood backdrop (lantern pool at top) → the 3D brown-paper render → the article
   over its blank lower area. The dark gradient is retired; the paper stays brightest. */
#herald-wrap{position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  background:#0a0810 url("../assets/scenes/Herald Backdrop (counter top-down).webp?v=31") center/cover no-repeat;}
#broadsheet{width:54%; max-width:780px; aspect-ratio:2048/1329; overflow:hidden;
  background:url("../assets/ui/Arena Herald Paper (brown).webp?v=31") center/100% 100% no-repeat;
  /* padding % is relative to the PARENT width (the stage), so 15% ≈ the paper's
     embossed-masthead depth — drops the article into the blank parchment below it.
     The parchment render is asymmetric, so the sides aren't equal: a touch more on
     the left centres the body text on the paper's flat area against the masthead (R12). */
  padding:15% 12.4% 2.5% 13.6%; color:#3a2a1c; box-shadow:none; border:none; border-radius:0;
  transform:rotate(-0.8deg); cursor:pointer;}
#herald-masthead{display:none;}   /* the paper art carries the embossed masthead */
#herald-ed{text-align:center; font-family:'Cormorant Garamond',serif; font-weight:600; font-size:15px;
  color:#5a4632; margin-top:0;}
#herald-head{font-family:'Baloo 2',cursive; font-size:20px; margin:7px 0 7px; text-align:center; color:#3a2a1c;}
#herald-story{font-size:14.5px; line-height:1.48; font-weight:600; color:#4a3826;}
.cont{margin-top:13px; text-align:center; font-family:'Baloo 2',cursive; color:#7a3fa0; font-size:14px;}

/* ---------- SERVE SCREEN — pre-baked hand-grounded scenes (GDD §12, round 9) ----
   The whole screen is one image: character grounded on the counter, brew wall,
   window — Tessa composited it by hand. Two stacked #ms scene layers cross-fade on
   an expression swap; the backgrounds are pixel-identical so the bar never moves,
   only the character dissolves. No cutout / anchor / shadow layers any more. The
   per-Night light shift is a subtle whole-scene filter (the character is baked in,
   so it warms/cools with the room — which is what a bar does at night). */
#ms{position:absolute; inset:0; overflow:hidden;}
.scene-layer{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  object-position:center; opacity:0; transition:opacity .25s ease-in-out;}
#stage.night2 #ms{filter:brightness(.97) saturate(1.02) hue-rotate(-4deg);}
#stage.night3 #ms{filter:brightness(.93) saturate(1.05) hue-rotate(-8deg);}
#serve-bubble{left:2%; width:36%; display:none;}
#serve-bubble.on{display:block;}
#serve-bubble:after{left:52%;}   /* tail points down at the cast anchor */
#serve-bubble.notail:after{display:none;}
#serve-bubble .line em{color:var(--ink-soft); font-style:italic; font-weight:600;}

/* Sage's own home (Fix 4): bottom-LEFT, sage-tinted cream, NO tail — the keeper
   speaks from behind the counter, not from above the customer. Shares the
   .bubble look; overrides fill/border to the sage tint. Sits in the same
   bottom-left column as the choice stack; the two are never on at once
   (dialogue mode is line XOR choice), so no z-fighting. */
#sage-bubble{left:2%; bottom:4%; top:auto; width:36%; z-index:7; display:none;
  background:#E9EFDC; border:2px solid #c6d2b4;}
#sage-bubble.on{display:block;}
#sage-bubble:after{display:none;}                 /* never a tail toward the customer */
#sage-bubble .speaker{color:var(--sage-deep);}    /* deep sage #6B7F5C label */
#sage-bubble .line em{color:var(--ink-soft); font-style:italic; font-weight:600;}

/* tone-shaping choices (GDD §14.4) — anchored bottom-LEFT, stacked up from
   the bottom (the mixing panel owns bottom-right), so the block never covers
   the character's face; overlapping the lower body/counter is fine */
#serve-choices{position:absolute; left:2%; bottom:4%; width:36%; z-index:7; display:none;
  flex-direction:column; gap:8px; justify-content:flex-end;}
#serve-choices.on{display:flex;}
.choice-prompt{font-family:'Baloo 2',cursive; font-size:12.5px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--cream); text-shadow:0 2px 4px rgba(0,0,0,.6); padding-left:4px;}
/* §11 (round 10): Sage is asking, so her prompts wear her colour — sage fill like
   her bubbles, dark-sage outline, a slight lift on hover (they read cream before,
   as if someone else were asking). */
.choice-btn{font-family:'Nunito',sans-serif; font-weight:600; font-size:13.5px; text-align:left;
  background:linear-gradient(180deg,var(--sage-pale),var(--sage)); color:var(--charcoal);
  border:2.5px solid var(--sage-deep); border-radius:12px;
  padding:8px 12px; cursor:pointer; box-shadow:0 3px 0 var(--sage-deep);
  transition:transform .12s ease, filter .12s ease, box-shadow .12s ease;}
/* round 11: hover FILLS dark sage with light text (not outline-only) — the state
   is unmistakable. */
.choice-btn:hover{background:var(--sage-deep); color:var(--creamHi, #FBF4E4); border-color:#55684a;
  transform:translateY(-2px); box-shadow:0 5px 0 #4a5c40;}
.choice-btn:active{transform:translateY(2px); box-shadow:none;}
body.portrait #serve-choices{left:4%; top:auto; bottom:37.5%; width:78%;}  /* above the stacked panel */

/* dialogue bubble (shared look, from the mockup) */
.bubble{position:absolute; background:var(--cream); border-radius:14px; padding:7px 14px 9px; z-index:5;
  filter:drop-shadow(0 4px 6px rgba(0,0,0,.4));}
/* tail overlaps 2px UP into the body (bottom:-9 not -11) so body + tail read as
   ONE cream silhouette — the drop-shadow on .bubble covers the outer edge only,
   with no dark seam where the triangle meets the body (Fix 9). */
.bubble:after{content:""; position:absolute; bottom:-9px; left:46%; border:8px solid transparent;
  border-top-color:var(--cream); border-width:11px 9px 0;}
.bubble .speaker{font-family:'Baloo 2',cursive; font-size:12px; letter-spacing:.06em; color:var(--sage-deep); text-transform:uppercase;}
.bubble .line{font-size:13.5px; line-height:1.35; color:var(--charcoal); font-weight:600; margin-top:3px;}

/* ---------- MIXING PANEL (Mixing UI Panel Spec, ported from the mockup) ---------- */
#panel-frame{font-size:13px; position:absolute; right:1.2%; bottom:1.6%; left:40.6%; height:41%; z-index:6;
  background:linear-gradient(160deg,#4a3527,#3A2A20 60%,#2e2118); border:3px solid #241a12; border-radius:18px;
  box-shadow:0 10px 30px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.08); padding:14px;
  display:flex; flex-direction:column; gap:9px; transition:all .45s ease;}
/* the three columns share the top row; the dose slider gets its OWN reserved
   row below (Fix 10) so its live "{n} drops" label never overlaps the mixer slots */
.panel-cols{flex:1; display:flex; gap:14px; min-height:0;}
/* Order Strip (§7) — a brown bar at the top of the panel that, during a brew
   beat, carries the customer's order in their own words; hidden while the panel
   is locked (outside a brew beat). */
/* Layering (§7): the strip is an explicit LOW layer (z-index:1) so the mixer
   dropdown/controls always sit on top; its text is pointer-events:none so it can
   never block a click even where the open picker visually overlaps it. */
#order-strip{display:none; align-items:center; gap:10px; position:relative; z-index:1;
  background:linear-gradient(180deg,#241a12,#1b130c); border:2px solid #3a2a1c; border-radius:10px;
  padding:5px 8px 5px 12px; box-shadow:inset 0 1px 0 rgba(255,255,255,.05);}
#panel-frame.brew-open #order-strip{display:flex;}
#panel-frame.brew-open #order-strip.order-empty{display:none;}   /* empty text (between beats / cleared on pour) → no blank bar */
#order-strip.has-more{cursor:pointer;}                            /* a trimmed line is tappable for the full request */
#order-text{flex:1; min-width:0; color:#f2e7cf; font-family:'Nunito',sans-serif; font-weight:600;
  font-size:12px; font-style:italic; line-height:1.28; overflow:hidden; pointer-events:none;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;}
/* §7 tap-for-full popover: grows UPWARD out of the panel over the scene, so it
   never reflows the mixing panel (a reflow would re-open the Pour-overflow P0).
   z-index above the mixer picker so a stale open picker can't cover the request. */
#order-full{display:none; position:absolute; left:0; right:0; bottom:calc(100% + 8px);
  max-height:44vh; overflow-y:auto; z-index:12; cursor:pointer;
  background:linear-gradient(180deg,#241a12,#1b130c); border:2px solid #3a2a1c; border-radius:12px;
  padding:12px 14px; color:#f2e7cf; font-family:'Nunito',sans-serif; font-weight:600; font-size:13px;
  font-style:italic; line-height:1.42; box-shadow:0 -8px 26px rgba(0,0,0,.55);}
#panel-frame.order-open #order-full{display:block;}
#panel-info{flex:0 0 auto; width:24px; height:24px; border-radius:50%;
  background:var(--gold); color:#241a12; font-family:'Baloo 2',cursive; font-weight:800;
  text-align:center; line-height:24px; cursor:pointer;}
#panel-info:hover{filter:brightness(1.08);}
/* locked panel (§7): visibly dimmed while conversation is in progress. Stays
   clickable (no pointer-events:none) so a tap plays the gentle line, never a
   dead click; the individual controls no-op in JS. */
#panel-frame.locked .panel-cols, #panel-frame.locked .dose{opacity:.32; filter:grayscale(.45);}
#panel-frame.locked .slot, #panel-frame.locked .pbtn, #panel-frame.locked #dose{cursor:default;}
/* settle: opacity-only so no persistent transform lingers to create a stacking
   context that would trap the mixer picker below the Order Strip (§7 layering) */
#panel-frame.settle .panel-cols{animation:panelsettle .5s ease;}
@keyframes panelsettle{0%{opacity:.3;} 100%{opacity:1;}}
.slotcol{display:flex; flex-direction:column; gap:5px; width:150px;}
/* round-10: slots compacted a touch so the bottom MIXER slot lifts clear of the
   "N drop" label above the dose slider — nothing overlaps. */
.slot{background:var(--cream); border-radius:12px; padding:4px 10px; cursor:pointer; border:2.5px solid #d8cbb2; position:relative;}
.slot:hover{border-color:var(--sage);}
.slot small{display:block; font-size:9.5px; letter-spacing:.12em; color:#8a7a5e; font-family:'Baloo 2',cursive; text-transform:uppercase;}
.slot b{font-family:'Baloo 2',cursive; font-size:15px; color:var(--charcoal);}
.slot .dot{position:absolute; right:9px; top:50%; transform:translateY(-50%); width:16px; height:16px; border-radius:50%;}
/* The mixer picker opens UPWARD from the slot's bottom edge (P0-2): the slots
   sit low in the bottom panel, so a downward list ran the last option (Poison)
   off the stage. Bottom-anchored it grows up into the roomy scene above; a
   max-height + scroll is the safety net if the list ever outgrows that space. */
.picker{position:absolute; left:105%; bottom:0; top:auto; background:var(--cream); border-radius:12px; border:2.5px solid #d8cbb2;
  display:none; z-index:9; width:132px; padding:6px; max-height:330px; overflow-y:auto; overscroll-behavior:contain;}
.picker div{padding:5px 8px; border-radius:8px; font-family:'Baloo 2',cursive; font-size:13.5px; color:var(--charcoal); cursor:pointer;}
.picker div:hover{background:var(--sage); color:#fff;}
.meters{flex:1; background:rgba(0,0,0,.25); border-radius:12px; padding:10px 14px;
  display:flex; flex-direction:column; justify-content:space-around;}
.meter{display:flex; align-items:center; gap:8px;}
.meter label{width:58px; font-family:'Baloo 2',cursive; font-size:11.5px; letter-spacing:.08em; color:var(--cream);}
.segs{display:flex; gap:3px; flex:1;}
.seg{height:9px; flex:1; border-radius:3px; background:rgba(255,255,255,.13);}
.seg.on.warmth{background:var(--amber);} .seg.on.kick{background:#d4574e;}
.seg.on.chill{background:#6fa7d8;} .seg.on.bite{background:#8a67a8;}
.dose-spacer{display:none;}   /* the dose has its own row now (Fix 10) — no per-column reserve needed */
.rightcol{width:150px; display:flex; flex-direction:column; gap:8px; align-items:stretch;}
#recipe{background:var(--cream); border-radius:12px; border:2.5px solid #d8cbb2; text-align:center; padding:6px 4px 7px;}
#tankard{height:38px; transform:translateX(-12%);}  /* optical-centre on the mug body, not the handle */
#recipename{font-family:'Baloo 2',cursive; font-size:12.5px; color:var(--charcoal); min-height:16px;}
.pbtn{font-family:'Baloo 2',cursive; font-size:14.5px; border:none; border-radius:12px; padding:6px 0;
  cursor:pointer; letter-spacing:.05em; background:var(--cream); color:var(--charcoal); box-shadow:0 3px 0 #b7a888;}
.pbtn.pour{background:linear-gradient(180deg,var(--glow),var(--elixir)); color:#fff; box-shadow:0 3px 0 #572c73; font-size:17px;}
.pbtn:active{transform:translateY(2px); box-shadow:none;}
.dose{display:flex; align-items:flex-end; gap:10px; flex:0 0 auto; height:30px; position:relative;}
.dose span{font-family:'Baloo 2',cursive; font-size:11.5px; color:var(--cream); letter-spacing:.08em;}
/* the drop slider (Fix 1): notched track with • •• ••• pips under the three
   detents + a live "{n} drop(s)" label that tracks the handle, so the player
   can map "one drop" to the control. --th must equal TH in brew.js. */
.dose-track{flex:1; position:relative; height:30px; display:flex; align-items:center;}
.dose-track input{width:100%; margin:0; accent-color:var(--glow); position:relative; z-index:2;}
/* pips sit at the EXACT thumb-centre for each value (1/2/3) — same th/2 + (100%-th)*frac
   formula the live label uses, so detents, pips and handle all line up (Fix 10). */
.dose-pips{position:absolute; left:0; right:0; bottom:-1px; height:12px; z-index:1;
  --th:14px; pointer-events:none;}
.dose-pips span{position:absolute; transform:translateX(-50%); font-size:9px; line-height:1;
  letter-spacing:1px; color:var(--cream); opacity:.7; font-family:'Nunito',sans-serif;}
.dose-pips span:nth-child(1){left:calc(var(--th)/2);}
.dose-pips span:nth-child(2){left:50%;}
.dose-pips span:nth-child(3){left:calc(100% - var(--th)/2);}
.dose-live{position:absolute; top:-10px; z-index:3; transform:translateX(-50%);   /* round 11: lower, so it can't clip the slot above (the "2 drops" label) */
  font-family:'Baloo 2',cursive; font-size:10.5px; letter-spacing:.04em; white-space:nowrap;
  color:var(--glow); text-shadow:0 1px 3px rgba(0,0,0,.7); pointer-events:none;}
body.portrait .dose-live{font-size:9.5px;}
#panel-frame.brewing{height:11%; bottom:44%; opacity:.96;}
#panel-frame.brewing .panel-cols,#panel-frame.brewing .dose{display:none;}
#brewstrip{position:absolute; right:1.2%; bottom:1.6%; left:40.6%; height:40.5%; z-index:6; display:none; gap:12px;}
#brewstrip.on{display:flex;}
.brewpanel{flex:1; border-radius:14px; border:3px solid #241a12; background:#2e2118; position:relative; overflow:hidden;}
.brewpanel img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;}
.brewpanel .fill{position:absolute; inset:0; background:rgba(20,12,28,.85); animation:brewreveal .6s ease forwards;}
.brewpanel:nth-child(2) .fill{animation-delay:.7s;} .brewpanel:nth-child(3) .fill{animation-delay:1.4s;}
@keyframes brewreveal{from{opacity:1;} to{opacity:0;}}

/* ---------- per-Night light shift (GDD §8) ----------
   A subtle per-Night lighting change done by tinting the scene
   programmatically (never regenerated art): warm gold (Night 1) →
   deepening dusk blue (Night 2) → late-night candlelight + purple
   tap-glow (Night 3). The .scene-tint overlay soft-lights the scene AND
   the character (cohesive lighting) but sits UNDER the UI (panel, bubbles,
   choices) so text stays readable. The mood is set by #stage.night{N},
   driven from Game.state.night. */
.scene-tint{position:absolute; inset:0; pointer-events:none; z-index:4;
  background:var(--night-tint, transparent); mix-blend-mode:soft-light;
  transition:background .7s ease;}
#stage.night1{--night-tint:transparent;}
#stage.night2{--night-tint:rgba(58,104,188,0.62);}   /* dusk — the window cools to blue */
#stage.night3{--night-tint:rgba(52,32,88,0.82);}     /* late night — purple candlelight */
/* Night 3: candlelight — dim the GROUP-screen wall a touch (that screen still
   uses the CSS placeholder). The serve master scene is a baked image now, tinted
   as a whole by .scene-tint; its window/tap accents no longer have separate CSS
   hooks (flagged in PLAN — the whole-scene wash remains the primary mood). */
#stage.night3 #g-img{filter:brightness(0.9) saturate(1.04);}   /* group scene dims a touch on Night 3 (the whole-scene tint carries the rest) */

/* ---------- PORTRAIT REFLOW (GDD §13: stacked serve screen) ---------- */
/* the landscape master scene occupies the TOP band; object-fit:cover crops its
   width and its 71.9% counter top lands at ~0.719×64% ≈ 46% of the portrait
   stage (matching stage.js portrait counterY 0.46). Below the band is the
   letterbox backfill. */
body.portrait .scene-layer{top:0; bottom:auto; height:64%; object-position:center top;}
body.portrait #screen-serve{background:#241a12;}
body.portrait #serve-bubble{left:4%; top:3%; width:70%;}
body.portrait #sage-bubble{left:4%; top:auto; bottom:37.5%; width:78%;}  /* above the stacked panel, matching the choice stack */
body.portrait #panel-frame{left:3%; right:3%; top:64%; bottom:2.5%; height:auto;}
body.portrait .panel-cols{flex-wrap:wrap;}   /* the columns wrap in portrait; the dose keeps its own row below */
body.portrait .slotcol{width:44%;}
body.portrait .rightcol{width:48%;}
body.portrait .meters{flex-basis:100%; order:3;}
body.portrait .picker{left:0; right:0; width:auto; bottom:105%; top:auto;}   /* portrait: open above the slot (panel sits in the bottom third) */
body.portrait #panel-frame.brewing{top:auto; height:9%; bottom:38.5%;}
body.portrait #brewstrip{left:3%; right:3%; top:64%; bottom:2.5%; height:auto;}
body.portrait #title-logo{width:78%; top:13%;}
body.portrait #broadsheet{width:88%; padding:30px 26px;}
body.portrait .panel{width:88%;}
/* portrait: group counter becomes a band mid-frame (the row shrinks to fit
   five across); duo stacks its split top/bottom; epilogue sheet widens */
body.portrait #screen-group{background:#241a12;}
body.portrait #g-img{top:0; bottom:auto; height:64%; object-position:center top;}   /* group scene occupies the top band in portrait (counter ≈46%, matches the cast anchor) */
body.portrait #duo-scene .scene-layer, body.portrait .duo-cut{top:0; bottom:auto; height:64%; object-position:center top;}
body.portrait #duo-box{width:88%; bottom:2.5%;}
body.portrait #epi-sheet{width:88%; padding:30px 26px;}   /* §R15: paper prop clears the masthead in portrait, same as #broadsheet */

/* ---------- PLACEHOLDER PANELS (night-end only now) ---------- */
.placeholder{position:absolute; inset:0; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:14px; text-align:center; padding:0 10%;
  background:radial-gradient(circle at 50% 45%, #33284a, #141019 80%); color:var(--cream);}
.placeholder b{font-family:'Baloo 2',cursive; font-size:30px; letter-spacing:.03em;}
.placeholder span{font-size:15px; opacity:.75; max-width:560px; line-height:1.5;}
/* §11 rest screen (round 10): the Bar Closed art (shuttered bar, CLOSED sign),
   not the flat gradient. A soft centred scrim keeps the "Night N — closed up"
   title + buttons legible over the busy scene. */
#screen-nightend .placeholder{
  background:
    radial-gradient(ellipse 56% 44% at 50% 52%, rgba(14,10,20,.76), rgba(14,10,20,.30) 66%, transparent 82%),
    #1c1b3a url("../assets/scenes/Bar Closed (rest screen).webp?v=31") center/cover no-repeat;}

/* ---------- GROUP — Layout 3 (GDD §11): the whole room, no mixing UI ----------
   Late-night dressing of the same master-scene placeholder: darker wall,
   warmer lamp pools, ONE counter. Cast row + bubble placed programmatically
   (src/screens.js renderGroup / groupLine). */
#g-ms{position:absolute; inset:0; overflow:hidden;}
#g-img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center;}
#group-row .g-char{position:absolute; transform:translateX(-50%); z-index:3;}
#group-row .g-shadow{position:absolute; transform:translateX(-50%); border-radius:50%; z-index:2;
  mix-blend-mode:multiply;   /* §12: darken the wood, don't lay flat black on it (round-7) */
  background:radial-gradient(ellipse, rgba(0,0,0,.92) 0%, rgba(0,0,0,.52) 42%, transparent 70%);}
#group-bubble{display:none;}
#group-bubble.on{display:block;}
#group-bubble:after{left:var(--tailx, 46%);}
#group-bubble.notail:after{display:none;}
#group-bubble .line em{color:var(--ink-soft); font-style:italic; font-weight:600;}

/* ---------- SPLIT DUOLOGUE — Layout 5 (GDD §11) ----------
   Two customers on a diagonal split canvas; the words go in a bottom-centre
   name-box in the speaker's colour; the silent half dims a touch. */
/* Two-shot (round 9): the composed scene fills the screen — backdrop serve scene
   (left character + room + counter) with the right character's cutout shifted onto
   the right of the SAME counter. Identical backgrounds → the wood seam disappears. */
#screen-duo{background:#141019;}
#duo-scene{position:absolute; inset:0; overflow:hidden;}
.duo-cut{position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  object-position:center; opacity:0; transition:opacity .25s ease-in-out;}
/* round 11: both characters are cutouts on the Empty Bar backdrop. Each is masked to
   its OWN character column so the cutout's full-width counter never doubles the
   backdrop's (the backdrop carries the wood; no shifted-grain seam). The left sits
   at its natural ~5–38%; the right is shifted onto the right of the same counter. */
.duo-l{-webkit-mask-image:linear-gradient(90deg, #000 0, #000 42%, transparent 48%);
  mask-image:linear-gradient(90deg, #000 0, #000 42%, transparent 48%);}
.duo-r{transform:translateX(52%);
  -webkit-mask-image:linear-gradient(90deg, transparent 0, #000 4%, #000 44%, transparent 50%);
  mask-image:linear-gradient(90deg, transparent 0, #000 4%, #000 44%, transparent 50%);}
/* the diagonal split as a LIGHTING treatment (not panels): a soft seam always, and
   the silent half dims when the other speaks. */
#duo-split{position:absolute; inset:0; z-index:2; pointer-events:none; transition:background .3s ease;
  background:linear-gradient(100deg, rgba(0,0,0,0) 46%, rgba(6,4,10,.20) 50%, rgba(0,0,0,0) 54%);}
#screen-duo.speak-left #duo-split{
  background:linear-gradient(100deg, rgba(0,0,0,0) 44%, rgba(6,4,10,.20) 50%, rgba(8,6,14,.44) 66%);}
#screen-duo.speak-right #duo-split{
  background:linear-gradient(100deg, rgba(8,6,14,.44) 34%, rgba(6,4,10,.20) 50%, rgba(0,0,0,0) 56%);}
#duo-box{position:absolute; left:50%; transform:translateX(-50%); bottom:4.5%; width:58%; z-index:5;
  background:var(--cream); border-radius:14px; padding:10px 20px 13px; display:none;
  box-shadow:0 10px 26px rgba(0,0,0,.5);}
#duo-box.on{display:block;}
#duo-box .speaker{font-family:'Baloo 2',cursive; font-size:13px; letter-spacing:.06em; text-transform:uppercase;}
#duo-box .line{font-size:15px; line-height:1.4; color:var(--charcoal); font-weight:600; margin-top:3px;}
#duo-box .line em{color:var(--ink-soft); font-style:italic; font-weight:600;}

/* ---------- EPILOGUE — the morning-after Herald (GDD §9) ---------- */
/* R13: the epilogue is a full-screen Herald edition (Ed. L) — it shares the same
   counter-wood backdrop as every other Herald interstitial (was a dark radial). */
#epi-wrap{position:absolute; inset:0; display:flex; flex-direction:column; align-items:center;
  justify-content:center; gap:16px;
  background:#0a0810 url("../assets/scenes/Herald Backdrop (counter top-down).webp?v=31") center/cover no-repeat;}
/* §R15: the epilogue Ed. L prints on the SAME generated 3D paper prop as every other
   full-screen Herald (was a plain CSS card, breaking the 3D look on the wrong-pour path
   Tessa hit) — same framing as #broadsheet: paper fills the sheet, the embossed masthead
   is baked in (text masthead hidden), the article sits in the blank parchment below. The
   epilogue carries more copy than a Night herald, so its type is a touch smaller to fit. */
#epi-sheet{width:54%; max-width:780px; aspect-ratio:2048/1329; overflow:hidden;
  background:url("../assets/ui/Arena Herald Paper (brown).webp?v=31") center/100% 100% no-repeat;
  padding:14.5% 12.4% 2.5% 13.6%; color:#3a2a1c; box-shadow:none; border:none; border-radius:0;
  transform:rotate(-0.8deg);}
#epi-masthead{display:none;}   /* the paper art carries the embossed masthead */
#epi-ed{text-align:center; font-family:'Cormorant Garamond',serif; font-weight:600; font-size:13.5px;
  color:#5a4632; margin-top:0;}
#epi-head{font-family:'Baloo 2',cursive; font-size:17px; margin:5px 0 4px; text-align:center; color:#3a2a1c;}
#epi-story,#epi-conseq,#epi-story2{font-size:12.6px; line-height:1.4; font-weight:600; color:#4a3826; margin-top:6px;}
#epi-conseq{font-style:italic; color:#7a5330;
  border-left:3px solid var(--gold); padding-left:10px;}   /* the ripple — one line, one brew */
#epi-final{font-family:'Cormorant Garamond',serif; font-weight:700; font-size:21px; color:var(--cream);
  letter-spacing:.02em; text-align:center; text-shadow:0 2px 10px rgba(0,0,0,.6);}

/* ---------- TOME OVERLAY (mockup geometry; §11 marginalia rule) ---------- */
#tome{position:absolute; inset:0; z-index:30; display:none; animation:tomein .3s ease;}
#tome.open{display:block;}
@keyframes tomein{from{opacity:0; transform:scale(1.02);} to{opacity:1; transform:none;}}
#tomeimg{position:absolute; inset:0; width:100%; height:100%;}
#tomeclose{position:absolute; top:2.5%; right:1.5%; width:34px; height:34px; border-radius:50%;
  background:rgba(20,14,10,.72); color:#e8d9bd; font-family:'Baloo 2',cursive; font-size:17px;
  text-align:center; line-height:33px; cursor:pointer; z-index:35; border:2px solid #6b5638;}
.tab{position:absolute; left:85.5%; width:9%; z-index:34; cursor:pointer;
  display:flex; align-items:center; justify-content:center;}
.tab svg{width:26px; height:26px; opacity:.85; filter:drop-shadow(0 1px 2px rgba(0,0,0,.5)); transition:all .2s ease;}
.tab:hover svg{transform:translateX(-3px);}
.tab.on svg{opacity:1; filter:drop-shadow(0 0 6px rgba(255,255,255,.9));}
/* onboarding ribbon highlight (§11): the Ledger ribbon pulses gold when the
   Tome is opened from the Knight's-first-page glow, pointing the eye at it */
.tab.ribbon-hi svg{opacity:1; animation:ribbonhi 1s ease-in-out 2;}
@keyframes ribbonhi{0%,100%{transform:translateX(0); filter:drop-shadow(0 1px 2px rgba(0,0,0,.5));}
  50%{transform:translateX(-4px); filter:drop-shadow(0 0 9px rgba(232,183,112,1));}}
#tab-brew{top:19.6%; height:8.4%;} #tab-ledger{top:34.45%; height:8.9%;}
#tab-herald{top:50.3%; height:8.9%;} #tab-song{top:65.15%; height:9%;}
/* NO scrollbars anywhere in the Tome (GDD §11) — the book turns pages */
/* §R15: the Brew Book + Songbook lists live in #pageL and can outgrow the page as
   recipes/tracks unlock — it now scrolls, with a slim AUTO-HIDING scrollbar (no rail,
   light-brown to match the paper) that fades in while scrolling and out when it stops,
   so the page still reads as a book page. The .scrolling class is toggled by JS. */
#pageL{position:absolute; left:18.5%; top:15%; width:24.5%; height:68%; z-index:33; color:var(--ink);
  overflow-y:auto; overflow-x:hidden; padding:0 10px; scrollbar-width:thin; scrollbar-color:transparent transparent;}
#pageR{position:absolute; left:55%; top:15%; width:26%; height:68%; z-index:33; color:var(--ink); overflow:hidden; padding:0 10px;}
#pageL::-webkit-scrollbar{width:7px;}
#pageL::-webkit-scrollbar-track{background:transparent;}
#pageL::-webkit-scrollbar-thumb{background:transparent; border-radius:6px; transition:background .4s ease;}
#pageL.scrolling{scrollbar-color:rgba(150,118,74,.55) transparent;}
#pageL.scrolling::-webkit-scrollbar-thumb{background:rgba(150,118,74,.55);}

/* the character spread (GDD §11): card top-left, nameplate beside it at the
   card's mid-height — no title banner, no divider */
/* the character spread as a real book flow: two CSS columns = the two pages;
   the bio always starts on the LEFT page under the card and the browser
   carries the text onto the right page (GDD §11 reading order and balance) */
/* §R16: widened onto parchment the spread was leaving unused. The painted paper runs
   x 11.1–84.9% with the spine at 49.82%; the old 18.5–81% box wasted ~95px of paper on the
   left and ~50px on the right. Grown by 34px EACH SIDE (symmetric, so the flow's gutter stays
   on the painted spine) — the left limit is the lit-paper onset at ~15.5%, not the paper edge,
   so type never rides up onto the curved page edge. #tomenav deliberately KEEPS the old rect,
   since its nav %s were measured against it. */
#spread{position:absolute; left:15.844%; top:15%; width:67.813%; height:68%; z-index:33; color:var(--ink);}
#spread[hidden]{display:none;}
/* the text viewport RESERVES a bottom band so flowed text and Sage's notes
   can never reach the bottom-corner arrows (the back arrow sits above the
   viewport entirely) — GDD §11 final round: arrows and content never share
   space. Portrait's cream panel reserves a top band too (its page furniture
   sits inside the panel corners). */
#spreadvp{position:absolute; left:0; right:0; top:0; bottom:5%; overflow:hidden;}
body.portrait #spreadvp{top:5%; bottom:5%;}
/* position:relative so #spreadnotes resolves against the FLOW, whose left/right margins
   (set from JS = the note lane width) inset the columns away from the viewport edges */
#spreadflow{height:100%; column-fill:auto; position:relative; transition:transform .3s ease;}
/* §R16: the column is narrower now that the note lanes moved outboard, so the card+nameplate
   row is tightened to keep every name on ONE LINE IN LANDSCAPE ("Hog Rider" is the binding
   case, 105px into the 236px column). The bio text it sits above is what got wider.
   `.bigclip` MUST NOT shrink — it has overflow:hidden and an absolutely-positioned <img>, so its
   flex min-width resolves to 0 and a tight row shears the card art in half rather than wrapping
   the name. PORTRAIT's column is only 161px and no card+name ROW fits there at all, so the name
   drops under the card. (Both caught by the R16 review — portrait was cropping every card;
   P.E.K.K.A lost 58% of hers.) */
#spreadflow .spread-head{padding:30px 0 14px; gap:12px;}
#spreadflow .spread-head .bigclip{flex:0 0 auto;}
#spreadflow .nameplate{font-size:25px;}
body.portrait #spreadflow .spread-head{flex-direction:column; align-items:flex-start; gap:8px;}
/* §R16 supersedes §R14's 74px side padding. The notes no longer live INSIDE the text column —
   they sit in their own lane outboard of it (outer lanes in the flow's margins, inner lanes in
   the gutter), so the printed body text now uses the whole column. See laneMetrics() in
   src/tome.js: lane = NOTEW + STROKE + CLEAR + EDGE, which is what guarantees the ≥12px clearance. */
#spreadflow .ledger-bio{padding:0 2px 12px; font-size:15.5px; line-height:1.5; width:auto; margin:0;}
#spreadflow .hand-note.pinned{margin:6px 2px 16px; break-inside:avoid; -webkit-column-break-inside:avoid;}
#spreadnotes{position:absolute; left:0; top:0; height:100%; pointer-events:none;}
#spreadnotes .margin-note{position:absolute; font-size:14.5px; line-height:1.08; margin:0;}
#spreadnotes .note-arrows{position:absolute; left:0; top:0; overflow:visible;}
body.portrait #spread{left:4%; top:11%; width:92%; height:86%;}

.spread-head{display:flex; align-items:center; gap:18px; padding-top:34px; margin-bottom:14px;
  break-inside:avoid; -webkit-column-break-inside:avoid;}
.nameplate{font-family:'Cormorant Garamond',serif; font-weight:700; font-size:29px; color:var(--ink); line-height:1.05;}

/* All page furniture lives in one overlay spanning the paper box (same rect
   as #spread) — grid AND spread share it, and it's cleared on every render so
   nothing lingers. It's above the pages (z-34) but below the flip (z-36) and
   the close button (z-35). */
#tomenav{position:absolute; left:18.5%; top:15%; width:62.5%; height:68%; z-index:34; pointer-events:none;}
#tomenav > *{pointer-events:auto;}
body.portrait #tomenav{left:4%; top:11%; width:92%; height:86%;}

/* Page-TURN arrows (GDD §11): the ornate fletched icon, RESERVED for turning
   to a prev/next sheet — on the character spread AND the paginated grid. They
   sit ON the flat paper (measured box x 11.1–84.9%, y 7.7–87.6%), clear of the
   page-edge stack, gutter and leather: prev = LEFT page bottom-left, next =
   RIGHT page bottom-right (offsets are % of #tomenav). They sit below the text
   viewport's reserved band, so an arrow can never share space with content. */
.hand-nav{position:absolute; width:66px; height:20px; cursor:pointer; opacity:.9;}
.hand-nav.prev{left:1.4%; bottom:-3.4%;}     /* stage ~19.4%, bottom ~85.3% */
.hand-nav.next{right:1.4%; bottom:-3.4%;}    /* stage ~80.1%, bottom ~85.3% */
body.portrait .hand-nav.prev{left:1.5%; bottom:1%;}
body.portrait .hand-nav.next{right:1.5%; bottom:1%;}
.hand-nav img{height:100%; width:auto; display:block; filter:drop-shadow(0 1px 2px rgba(60,40,20,.35));}
.hand-nav img.flip{transform:scaleX(-1);}
.hand-nav.next img{margin-left:auto;}
.hand-nav:hover{opacity:1;}

/* Back-to-grid: the locked Card Select icon (fanned sage cards, gold emboss),
   directly on the left page's paper top-left with a soft contact shadow and a
   hover/press lift — detailed landscape art, ~68px tall, clear of the stack and
   above the character card. Never a page-turn, never green. */
.grid-btn{position:absolute; left:-4.9%; top:-8.6%; height:68px; cursor:pointer;}
.grid-btn img{height:100%; width:auto; display:block; transition:transform .12s ease, filter .12s ease;
  filter:drop-shadow(0 4px 5px rgba(40,25,12,.42));}
.grid-btn:hover img{transform:translateY(-2px) scale(1.035); filter:drop-shadow(0 7px 8px rgba(40,25,12,.46));}
.grid-btn:active img{transform:translateY(0) scale(.97); filter:drop-shadow(0 2px 4px rgba(40,25,12,.4));}
body.portrait .grid-btn{left:1.5%; top:1%; height:56px;}

/* ---------- the 3D page-turn (GDD §11, round 4) ----------
   ONE leaf turns; the book stays put. The full-bleed BACKDROP is the entire
   static Tome Spread art (it reaches every edge, incl. the top and the
   gutter), so only book ever shows behind the leaf and the destination
   stays occluded until it lands. The leaf covers the WHOLE painted
   page-block edge-to-edge (measured paper: x 11.1→84.9%, y 7.7→87.6% of
   the art, spine at 49.82%), and it is hinged EXACTLY on the spine: the
   leaf and its inner .paper wrapper rotate only about the spine axis (pure
   rotateY, origin on the gutter), so the bound edge cannot drift — no
   rotateX wobble (it tips the bound edge off the spine plane and opens the
   brown gutter-notch gap) and NO filter anywhere in the 3D tree (an
   animated drop-shadow forces the leaf to flatten and smears a translucent
   shadow over the gutter — the "see-through page"). The faces carry an
   opaque paper colour under their art slice, so the leaf can never be
   seen through. Paper softness: the .paper wrapper lags a few degrees
   behind the hinge (the free edge trails, then settles flat), the FREE
   edge's corners round mid-flight, and a sheen band travels the face. */
/* The perspective eye sits on the spine AND level with the LEAF'S TOP EDGE
   (round 5): a point level with the origin never moves vertically under
   projection, and every other leaf point is below it, so NO corner of the
   turning leaf can rise above the page-block top at ANY angle — the
   leather band above the pages stays covered-or-untouched on every frame
   of both turn types (they share this one geometry). The longer lens keeps
   the downward growth of the free edge inside the art's bottom band. */
#pageflip{position:absolute; inset:0; z-index:36; display:none; pointer-events:none;
  perspective:4200px; perspective-origin:49.82% 7.2%;}
body.portrait #pageflip{perspective-origin:50% 4%;}
#pageflip .backdrop{position:absolute; inset:0;
  background-image:url("../assets/ui/Tome Spread.webp"); background-size:100% 100%;}
#pageflip .leaf{position:absolute; top:7.2%; height:81%; transform-style:preserve-3d;}
#pageflip .paper{position:absolute; inset:0; transform-style:preserve-3d;}
#pageflip.fwd .paper{transform-origin:left center;}
#pageflip.rev .paper{transform-origin:right center;}
#pageflip .face{position:absolute; inset:0; overflow:hidden;
  backface-visibility:hidden; -webkit-backface-visibility:hidden;
  background-color:#dfc09e;
  background-image:url("../assets/ui/Tome Spread.webp"); background-repeat:no-repeat;}
/* forward: the RIGHT page lifts off the spine; its back carries the art of
   the region it lands on, so touchdown is seamless */
#pageflip.fwd .leaf{left:49.82%; width:35.48%; transform-origin:left center;}
#pageflip.fwd .face{background-size:281.85% 123.46%;}
#pageflip.fwd .face.front{background-position:77.22% 37.89%;}
#pageflip.fwd .face.back{transform:rotateY(180deg); background-position:22.23% 37.89%;}
/* reverse: the LEFT page lifts back across the spine */
#pageflip.rev .leaf{left:11.12%; width:38.70%; transform-origin:right center;}
#pageflip.rev .face{background-size:258.40% 123.46%;}
#pageflip.rev .face.front{background-position:18.14% 37.89%;}
#pageflip.rev .face.back{transform:rotateY(180deg); background-position:81.27% 37.89%;}
#pageflip.fwd.go .leaf{animation:leafturnF .62s cubic-bezier(.5,.06,.36,1) forwards;}
#pageflip.rev.go .leaf{animation:leafturnR .62s cubic-bezier(.5,.06,.36,1) forwards;}
@keyframes leafturnF{from{transform:rotateY(0);} to{transform:rotateY(-180deg);}}
@keyframes leafturnR{from{transform:rotateY(0);} to{transform:rotateY(180deg);}}
/* the sheet bows: the free edge lags the hinge, then settles flat — the
   lag axis is ALSO the spine, so the bound edge stays pinned throughout */
#pageflip.fwd.go .paper{animation:paperlagF .62s ease-in-out forwards;}
#pageflip.rev.go .paper{animation:paperlagR .62s ease-in-out forwards;}
@keyframes paperlagF{
  0%{transform:rotateY(0);} 12%{transform:rotateY(0);}
  40%{transform:rotateY(9deg);} 65%{transform:rotateY(11deg);}
  88%{transform:rotateY(3deg);} 100%{transform:rotateY(0);}}
@keyframes paperlagR{
  0%{transform:rotateY(0);} 12%{transform:rotateY(0);}
  40%{transform:rotateY(-9deg);} 65%{transform:rotateY(-11deg);}
  88%{transform:rotateY(-3deg);} 100%{transform:rotateY(0);}}
/* the FREE edge's corners soften mid-flight (radius on the faces — the
   leaf/paper must stay unclipped to keep preserve-3d). The free edge is
   the right side of the outgoing right page's front and of the landing
   right page's back; the left side of the other two — never the spine. */
#pageflip.fwd.go .face.front{animation:curlEdgeRt .62s cubic-bezier(.5,.06,.36,1) forwards;}
#pageflip.fwd.go .face.back{animation:curlEdgeLt .62s cubic-bezier(.5,.06,.36,1) forwards;}
#pageflip.rev.go .face.front{animation:curlEdgeLt .62s cubic-bezier(.5,.06,.36,1) forwards;}
#pageflip.rev.go .face.back{animation:curlEdgeRt .62s cubic-bezier(.5,.06,.36,1) forwards;}
@keyframes curlEdgeRt{
  0%{border-radius:0;}
  45%{border-top-right-radius:26px 52px; border-bottom-right-radius:26px 52px;}
  60%{border-top-right-radius:22px 44px; border-bottom-right-radius:22px 44px;}
  100%{border-radius:0;}}
@keyframes curlEdgeLt{
  0%{border-radius:0;}
  45%{border-top-left-radius:26px 52px; border-bottom-left-radius:26px 52px;}
  60%{border-top-left-radius:22px 44px; border-bottom-left-radius:22px 44px;}
  100%{border-radius:0;}}
/* The REV leaf's incoming (back) face is sliced from the right-page region,
   whose far edge runs into the painted ribbon tabs — without a mask the
   turning sheet carries little ribbon chips mid-arc. The mask paints that
   far strip as plain page-edge parchment while the sheet flies, then fades
   just before touchdown so the landed frame is still pixel-identical to
   the art beneath. */
#pageflip .edgemask{position:absolute; top:0; bottom:0; right:0; width:9.4%; display:none;
  background:linear-gradient(90deg, rgba(220,201,164,0) 0%, #dcc9a4 22%, #d3bf99 62%, #bda684 100%);}
#pageflip.rev .face.back .edgemask{display:block;}
#pageflip.rev.go .face.back .edgemask{animation:edgefade .62s linear forwards;}
@keyframes edgefade{0%{opacity:1;} 86%{opacity:1;} 97%{opacity:0;} 100%{opacity:0;}}
/* the moving bend: a sheen + shade band travelling along the visible face */
#pageflip .curlfx{position:absolute; top:-2%; height:104%; width:74%; left:110%; opacity:0;
  background:linear-gradient(100deg, rgba(60,40,20,0) 0%, rgba(60,40,20,.16) 30%,
    rgba(255,248,222,.42) 50%, rgba(60,40,20,.20) 70%, rgba(60,40,20,0) 100%);}
#pageflip.fwd.go .curlfx{animation:curlbandF .62s cubic-bezier(.5,.06,.36,1) forwards;}
#pageflip.rev.go .curlfx{animation:curlbandR .62s cubic-bezier(.5,.06,.36,1) forwards;}
@keyframes curlbandF{0%{left:110%; opacity:0;} 12%{opacity:1;} 88%{opacity:1;} 100%{left:-84%; opacity:0;}}
@keyframes curlbandR{0%{left:-84%; opacity:0;} 12%{opacity:1;} 88%{opacity:1;} 100%{left:110%; opacity:0;}}
/* the curl-shadow sweeping the page the leaf lands on */
#pageflip .sweep{position:absolute; top:7.2%; height:81%; opacity:0; z-index:1;}
#pageflip.fwd .sweep{left:11.12%; width:38.70%;
  background:linear-gradient(90deg, rgba(50,32,16,0) 0%, rgba(50,32,16,.28) 76%, rgba(50,32,16,.4) 100%);}
#pageflip.rev .sweep{left:49.82%; width:35.48%;
  background:linear-gradient(270deg, rgba(50,32,16,0) 0%, rgba(50,32,16,.28) 76%, rgba(50,32,16,.4) 100%);}
#pageflip.go .sweep{animation:sweepin .62s cubic-bezier(.5,.06,.36,1) forwards;}
@keyframes sweepin{0%{opacity:0;} 42%{opacity:0;} 70%{opacity:.9;} 100%{opacity:0;}}
/* portrait: the tome is a cream panel — plain parchment backdrop/leaf */
body.portrait #pageflip .backdrop,body.portrait #pageflip .face{background-image:none; background-color:#efe3c8;}
body.portrait #pageflip .leaf{top:4%; height:92%;}
body.portrait #pageflip.fwd .leaf{left:50%; width:46%;}
body.portrait #pageflip.rev .leaf{left:4%; width:46%;}
.tometitle{font-family:'Cormorant Garamond',serif; font-weight:700; font-size:28px; letter-spacing:.01em;
  border-bottom:2.5px solid rgba(122,103,72,.45); padding-bottom:5px; margin-bottom:10px;}
.tometitle.centre{text-align:center;}
/* printed epigraphs are BODY text (§11) — only Sage's hand is Caveat */
.tome-epigraph{font-family:'Cormorant Garamond',serif; font-style:italic; font-weight:600; font-size:15px;
  color:#6b5a44; margin-bottom:12px; line-height:1.3;}
.hand-note{font-family:'Caveat',cursive; font-size:19px; font-weight:700; color:var(--sage-deep); line-height:1.15; margin:8px 0;}
/* tilts are rotation ONLY — placement belongs to the marginalia engine */
.hand-note.tilt0{transform:rotate(-2.4deg);} .hand-note.tilt1{transform:rotate(1.8deg);}
.hand-note.tilt2{transform:rotate(-1.2deg);}
.hand-note.pinned{margin-top:12px; padding-top:8px; border-top:1.5px dashed rgba(107,127,92,.5);}
.margin-rail{margin:2px 0 6px;}
.entry{padding:7px 9px; border-radius:9px; cursor:pointer; margin-bottom:4px;}
.entry:hover{background:rgba(122,103,72,.12);}
.entry.sel{background:rgba(122,103,72,.18);}
.entry b{font-family:'Cormorant Garamond',serif; font-weight:700; font-size:18.5px; display:block;}
.entry span{font-family:'Cormorant Garamond',serif; font-size:14px; font-weight:600; color:var(--ink-soft);}
.entry.locked{opacity:.5; cursor:default;}
.entry.locked:hover{background:none;}
/* UNIFORM card height (GDD §15): cards clip to their measured content box —
   the selection outline hugs the card itself, never transparent padding */
/* Ledger grid (GDD §11 LOCKED): a FIXED 3-column grid, uniform cells, top-aligned
   rows, consistent gutters — the signed-off single-page layout. The left page
   fills first (header + grid); the right page's grid only appears once the left
   is full, aligned to the left grid's first row via a JS-set padding-top. */
.cardgrid{display:grid; grid-template-columns:repeat(3,1fr); gap:4px 8px; justify-items:center; align-items:start;}
.cardgrid.facing{padding-top:0;}   /* alignment set in JS to match the left grid's header offset */
.cardthumb{cursor:pointer; position:relative; display:flex; flex-direction:column; justify-content:flex-end; align-items:center;}
.cardclip{position:relative; overflow:hidden; border-radius:5px;}
.cardclip img{position:absolute; width:auto; display:block;}
.cardthumb.sel .cardclip{outline:3px solid var(--gold);}
.cardthumb.locked .cardclip img{filter:brightness(0) opacity(.72);}
.cardthumb.locked{cursor:default;}
.cardthumb.locked:after{content:"?"; position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  font-family:'Baloo 2',cursive; font-size:30px; color:rgba(244,236,221,.85);}
.cardthumb small{display:block; text-align:center; font-family:'Baloo 2',cursive; font-size:10.5px; color:var(--ink-soft); margin-top:2px;}
.bigclip{position:relative; overflow:hidden; border-radius:6px; margin:2% auto 10px;
  filter:drop-shadow(0 12px 16px rgba(30,20,10,.20));}
.bigclip img{position:absolute; width:auto; display:block;}
/* the annotated page (GDD §11): printed bio in a narrower centre column,
   Sage's notes placed in the clear margins beside their marks */
.ledger-annot{position:relative; margin-bottom:18px;}
.ledger-annot .ledger-bio{width:60%; margin:0 auto; font-size:16px; line-height:1.52;}
.note-arrows{position:absolute; inset:0; pointer-events:none; overflow:visible;}
.note-arrows path{stroke:var(--sage-deep); stroke-width:1.8; fill:none; stroke-linecap:round; stroke-linejoin:round; opacity:.6;}
.margin-note{position:absolute; margin:0; font-size:14.5px; line-height:1.08;}
.margin-note.foot{font-size:16px;}
.ledger-bio{font-family:'Cormorant Garamond',serif; font-size:15.5px; font-weight:600; line-height:1.42; margin-bottom:4px;}
.marked{border-bottom:2.5px solid rgba(107,127,92,.85); background:rgba(156,175,136,.18); border-radius:2px; padding:0 1px;}
.rp-caption{text-align:center; font-family:'Cormorant Garamond',serif; font-size:15px; font-weight:600;
  color:var(--ink-soft); font-style:italic; line-height:1.35; margin-top:10px;}
.rp-caption.strong{font-style:normal; font-weight:700; margin-top:4px;}
.tome-tankard{display:block; height:96px; margin:14px auto 4px; transform:translateX(-12%);}
.ingdots{display:flex; gap:8px; justify-content:center; margin:10px 0 0;}
.ingdots i{width:20px; height:20px; border-radius:50%; display:block; box-shadow:inset 0 -3px 5px rgba(0,0,0,.25);}
.masthead{font-family:'Pirata One',cursive; font-weight:400; font-size:33px; line-height:1.1; text-align:center;
  letter-spacing:.04em; border-bottom:3px double rgba(122,103,72,.55); padding-bottom:6px;}
.headline{font-family:'Cormorant Garamond',serif; font-weight:700; font-size:20px; margin:10px 0 6px; line-height:1.2;}
.story{font-family:'Cormorant Garamond',serif; font-size:15.5px; font-weight:600; line-height:1.4;}
.ripple{margin-top:12px; font-size:12px; font-weight:700; color:var(--elixir); font-style:italic;}
/* §11 (round 10): the Arena Herald prints on the 3D brown-paper masthead render.
   The paper's embossed masthead + crown flourish own the top ~40%; the article
   drops into the blank parchment below. Text goes warm dark-brown to sit on it. */
/* the wide landscape paper needs a wider page than the standard tall column, so the
   Herald page widens for this tab; the article drops into the blank lower ~55%. */
#pageR.herald-paper{
  left:49%; width:48%; top:13%; height:62%; padding:0;
  background:url("../assets/ui/Arena Herald Paper (brown).webp?v=31") center top / contain no-repeat;}
#pageR.herald-paper .rp-caption{text-align:center; margin-top:42%; color:#5a4632;}
#pageR.herald-paper .headline{color:#3a2a1c; text-align:center; padding:0 14%;}
#pageR.herald-paper .story{color:#4a3826; padding:0 14%; font-size:13.5px;}
#pageR.herald-paper .ripple{color:#7a3fa0; padding:0 14%;}
#phonowrap{position:relative; width:58%; margin:6% auto 0;}
#phonoglow{position:absolute; inset:-14%; border-radius:50%;
  background:radial-gradient(circle, rgba(178,106,224,.4) 0%, rgba(178,106,224,.14) 42%, rgba(178,106,224,0) 72%);
  filter:blur(10px); animation:phonopulse 2.6s ease-in-out infinite;}
@keyframes phonopulse{0%,100%{opacity:.45; transform:scale(.96);} 50%{opacity:1; transform:scale(1.05);}}
#phonowrap img{position:relative; width:100%;}
.plaque{margin-top:8px; text-align:center; font-family:'Cormorant Garamond',serif; font-weight:700; font-size:16.5px; color:var(--ink);}

/* portrait: the 16:9 spread art doesn't fit — fall back to a cream book panel */
body.portrait #tomeimg{display:none;}
body.portrait #tome{background:linear-gradient(175deg,#f7efdc,var(--cream));}
body.portrait .tab{left:auto; width:15%; height:7%; top:1.5% !important; background:rgba(122,103,72,.16); border-radius:10px;}
body.portrait .tab svg{stroke:#4a3a28;}
body.portrait #tab-brew{left:4%;} body.portrait #tab-ledger{left:23%;}
body.portrait #tab-herald{left:42%;} body.portrait #tab-song{left:61%;}
body.portrait #pageL{left:5%; top:11%; width:90%; height:38%;}
body.portrait #pageR{left:5%; top:52%; width:90%; height:45%;}
body.portrait #pageflip{left:0; top:0; width:100%; height:100%;}

/* ---------- SETTINGS & ABOUT OVERLAYS (GDD §11: cream panel, gold trim) ---------- */
.overlay{position:absolute; inset:0; z-index:40; display:none; align-items:center; justify-content:center;
  background:rgba(16,11,22,.72);}
.overlay.open{display:flex;}
.panel{width:52%; max-height:86%; overflow:auto; background:linear-gradient(175deg,#f7efdc,var(--cream));
  border:3px solid var(--gold); border-radius:16px; padding:26px 34px; color:var(--charcoal);
  box-shadow:0 24px 60px rgba(0,0,0,.7);}
.panel h2{font-family:'Baloo 2',cursive; font-size:24px; letter-spacing:.03em;
  border-bottom:2.5px solid rgba(122,103,72,.35); padding-bottom:8px; margin-bottom:16px;}
.setrow{display:flex; align-items:center; justify-content:space-between; gap:18px; margin:14px 0;}
.setrow label{font-family:'Baloo 2',cursive; font-size:16px;}
#lang-select{font-family:'Nunito',sans-serif; font-weight:600; font-size:15px; padding:6px 10px;
  border-radius:10px; border:2.5px solid #d8cbb2; background:#fdf6e8; color:var(--charcoal); cursor:pointer;}
#set-disclaimer{font-size:12.5px; color:var(--ink-soft); margin:-4px 0 10px; line-height:1.45;}
.toggle{min-width:110px;}
/* Round 12 (§11): sound is two live volume sliders + a speaker mute each — dose-slider accent */
.volrow{gap:11px;}
.volrow label{flex:0 0 auto; min-width:104px;}
.vol-track{flex:1; display:flex; align-items:center; height:26px;}
.vol-slider{width:100%; margin:0; accent-color:var(--glow); cursor:pointer;}
.vol-pct{flex:0 0 auto; width:42px; text-align:right; font-family:'Baloo 2',cursive;
  font-size:13px; color:var(--charcoal); opacity:.8; letter-spacing:.02em;}
.vol-mute{flex:0 0 auto; width:30px; height:30px; padding:0; display:flex; align-items:center;
  justify-content:center; background:none; border:none; color:var(--charcoal); cursor:pointer;
  opacity:.85; transition:opacity .15s, transform .15s;}
.vol-mute svg{width:22px; height:22px;}
.vol-mute:hover{opacity:1; transform:scale(1.08);}
.vol-mute .slash{display:none;}
.vol-mute.muted{color:#b46b6b;}
.vol-mute.muted .wave{display:none;}
.vol-mute.muted .slash{display:block;}
#menu-tray .panel{position:relative;}
.tray-mute{position:absolute; top:12px; right:14px; color:var(--cream);}
.tray-mute.muted{color:#d98f8f;}
#about .panel p{font-size:14px; line-height:1.55; font-weight:600; margin:10px 0;}
#btn-set-back,#btn-about-back{margin-top:14px;}

/* ---------- NIGHT CAP overlay (GDD §11): the share-card preview ---------- */
.nc-panel{background:linear-gradient(175deg,#f7efdc,var(--cream)); border:3px solid var(--gold);
  border-radius:16px; padding:14px 18px 16px; box-shadow:0 24px 60px rgba(0,0,0,.7);
  display:flex; flex-direction:column; align-items:center; gap:12px; max-height:96%; position:relative;}
/* §R15: desktop share "copied" toast — two lines (plain + Sage's green hand), floats
   over the panel bottom, fades in/out (JS toggles .on) */
#nc-toast{position:absolute; left:50%; bottom:12px; transform:translate(-50%,8px);
  background:rgba(24,18,28,.94); border-radius:12px; padding:8px 20px 9px; text-align:center;
  opacity:0; pointer-events:none; transition:opacity .25s ease, transform .25s ease; z-index:5;
  box-shadow:0 6px 20px rgba(0,0,0,.55); max-width:90%;}
#nc-toast.on{opacity:1; transform:translate(-50%,0);}
.nc-toast-main{font-family:'Nunito',sans-serif; font-weight:700; font-size:13.5px; color:var(--cream);}
.nc-toast-hand{font-family:'Caveat',cursive; font-weight:700; font-size:16.5px; color:#aac492; margin-top:1px;}
.nc-panel h2{font-family:'Baloo 2',cursive; font-size:19px; letter-spacing:.03em; color:var(--charcoal); margin:0;}
.nc-preview{line-height:0;}
.nc-preview canvas{height:524px; width:auto; border-radius:8px; box-shadow:0 8px 22px rgba(0,0,0,.42);}
.nc-actions{display:flex; gap:10px;}
.epi-actions{display:flex; gap:12px; justify-content:center;}
body.portrait .nc-preview canvas{height:64vh;}

/* ---------- in-game toast (GDD §13: say it in-game, not console-only) ---------- */
#toast{position:absolute; left:50%; top:5%; transform:translateX(-50%) translateY(-14px);
  max-width:74%; background:linear-gradient(180deg,#fdf6e8,var(--cream)); color:var(--charcoal);
  border:2.5px solid var(--gold); border-radius:13px; padding:11px 22px; z-index:60;
  font-family:'Nunito',sans-serif; font-weight:700; font-size:14.5px; line-height:1.35; text-align:center;
  box-shadow:0 10px 28px rgba(0,0,0,.5); opacity:0; pointer-events:none;
  transition:opacity .35s ease, transform .35s ease;}
#toast.on{opacity:1; transform:translateX(-50%) translateY(0);}

/* ---------- in-game HUD + menu (GDD §11, locked 07/07) ---------- */
.hud-btn{position:absolute; z-index:12; display:none; align-items:center; justify-content:center;
  cursor:pointer; padding:0; border:none; background:none; transition:transform .12s ease;}
.hud-btn.on{display:flex;}
#hud-menu{top:2.6%; right:1.6%; width:44px; height:44px; border-radius:50%; color:var(--cream);
  background:linear-gradient(160deg,#4a3527,#2e2118); border:2.5px solid var(--gold);
  box-shadow:0 4px 12px rgba(0,0,0,.5);}
#hud-menu svg{width:22px; height:22px;}
#hud-menu:hover{transform:translateY(-2px);}
#hud-menu:active{transform:translateY(1px);}
/* Tome button — stacked top-right, directly under the menu button, on a small
   cream/gilt plate so the leather journal icon reads against the scene (§11,
   reworked 07/07; brown-on-brown at the old bottom-right corner was invisible
   and it fouled the mixing panel). */
#hud-tome{top:10%; right:1.6%; width:46px; height:46px; border-radius:50%;
  background:radial-gradient(circle at 50% 36%, #fbf3e0, var(--cream));
  border:2.5px solid var(--gold); box-shadow:0 4px 12px rgba(0,0,0,.5);}
#hud-tome img{width:30px; height:30px; object-fit:contain; filter:drop-shadow(0 1px 2px rgba(0,0,0,.45));}
#hud-tome:hover{transform:translateY(-2px);}
#hud-tome:active{transform:translateY(1px);}
/* staged reveal (§11): the icon settles in on first-recipe, then holds a gold
   glow until the player taps it; a once-off pulse fires when later entries land. */
#hud-tome.reveal{animation:tomereveal .55s ease both;}
#hud-tome.glow{box-shadow:0 4px 12px rgba(0,0,0,.5), 0 0 0 3px rgba(232,183,112,.5), 0 0 16px 4px rgba(232,183,112,.7);}
#hud-tome.pulse{animation:tomepulse 1.2s ease;}
@keyframes tomepulse{0%,100%{transform:scale(1); box-shadow:0 4px 12px rgba(0,0,0,.5);}
  30%{transform:scale(1.16); box-shadow:0 4px 12px rgba(0,0,0,.5), 0 0 15px 4px rgba(232,183,112,.9);}
  55%{transform:scale(1);} 75%{transform:scale(1.07);}}
@keyframes tomereveal{0%{transform:scale(.4) translateY(-8px); opacity:0;}
  60%{transform:scale(1.12); opacity:1;} 100%{transform:scale(1);}}

/* menu tray (reuses .overlay/.panel) */
#menu-tray{z-index:45;}
#menu-tray .panel{width:auto; min-width:280px; max-width:340px; text-align:center;}
#menu-tray .panel .btn{display:block; width:100%; margin:6px 0;}
#menu-note{font-size:12px; color:var(--ink-soft); font-style:italic; margin-top:12px;}

/* visible autosave flourish (Night boundary + Save & Quit) */
#autosave-quill{position:absolute; left:2%; bottom:3.5%; z-index:11; display:flex; align-items:center; gap:6px;
  color:var(--cream); font-family:'Baloo 2',cursive; font-size:12.5px; letter-spacing:.04em;
  opacity:0; transform:translateY(6px); pointer-events:none; text-shadow:0 1px 3px rgba(0,0,0,.75);
  transition:opacity .4s ease, transform .4s ease;}
#autosave-quill.on{opacity:.92; transform:translateY(0);}
#autosave-quill svg{width:18px; height:18px; color:var(--gold);}

/* onboarding hint chip — tutorial-through-fiction, dismisses on any input.
   Sits DIRECTLY BESIDE the Tome icon (top-right, just left of it) with its tail
   touching the icon, so "tap here" is next to *here* (§11, round 4). */
#hint-chip{position:absolute; top:10.6%; right:6.4%; left:auto; transform:translateX(6px); z-index:13;
  max-width:230px; background:linear-gradient(180deg,#fdf6e8,var(--cream)); color:var(--charcoal);
  border:2px solid var(--sage); border-radius:11px; padding:7px 13px; font-family:'Nunito',sans-serif;
  font-weight:700; font-size:12px; line-height:1.25; text-align:right; box-shadow:0 6px 18px rgba(0,0,0,.4);
  opacity:0; pointer-events:none; transition:opacity .35s ease, transform .35s ease;}
#hint-chip.on{opacity:1; transform:translateX(0);}
/* tail on the chip's right edge, pointing at the Tome icon */
#hint-chip::after{content:""; position:absolute; top:50%; right:-8px; margin-top:-7px;
  border:7px solid transparent; border-left-color:var(--cream);}
#hint-chip::before{content:""; position:absolute; top:50%; right:-11px; margin-top:-8px;
  border:8px solid transparent; border-left-color:var(--sage);}   /* the sage border of the tail */

/* continue indicator: a soft ▼ in the active bubble's corner while a line waits
   for a click (never during a brew re-request, where clicking doesn't advance) */
.bubble.on::before, #duo-box.on::before{content:"▼"; position:absolute; right:11px; bottom:3px;
  font-size:10px; color:var(--sage-deep); opacity:0; pointer-events:none;
  animation:contbob 1.1s ease-in-out infinite;}
#stage.waiting .bubble.on::before, #stage.waiting #duo-box.on::before{opacity:.6;}
@keyframes contbob{0%,100%{transform:translateY(0);} 50%{transform:translateY(2px);}}

/* ---------- DEV BAR (outside the stage; ?dev=1 or Ctrl+D) ---------- */
#dev{position:fixed; top:10px; right:12px; background:rgba(20,16,25,.92); border:1px solid #3c3350;
  border-radius:10px; padding:8px 10px; z-index:50; color:#cfc6e0; font-size:12px;
  display:flex; gap:6px; align-items:center; flex-wrap:wrap; max-width:380px; font-family:'Nunito',sans-serif;}
#dev[hidden]{display:none;}
#dev button{font-family:inherit; font-size:12px; border-radius:6px; border:1px solid #4a4060;
  background:#241f31; color:#e8e2f4; padding:3px 8px; cursor:pointer;}
