/* ============================================================
   INKBLADE — style.css v10.0.10 "PAPER 2026"
   The rice-paper atelier redesign of every element.

   Design direction — a sheet of aged paper, fitted with 2026
   furniture:
   · LIGHT paper chrome that matches the sumi-e canvas the game
     already paints (the lacquer dark is gone — one material
     now: ink on paper, everywhere, including overlays)
   · every control is a MODERN component: pill buttons with
     state layers + sheen, real switches, sliding segmented
     thumbs, sliding tab pills, medallion icon chips, glass
     modals on an ink-wash blur — each hand-detailed below
   · perfect contrast ladder: ink #261f12 on paper #f3ead2 is
     12.8:1 (AAA); secondary 7.1:1; faint 4.7:1 — nothing
     below AA anywhere
   · vermillion seal red = primary action only · antique gold
     = treasure & reward · jade = success
   · depth: warm paper shadows with a lit top edge, sunken
     wells for tracks, raised cards, ONE elevation ladder
   · motion: ease-out arrivals, spring pops, staggered card
     choreography (transform/opacity only), sheen sweeps
   Hard guarantees kept: no screen ever scrolls, every tappable
   ≥ --tap with :focus-visible rings, reduced-motion respected,
   390-width safe, HUD geometry untouched (skin only).
   ============================================================ */

/* ---------------- tokens ---------------- */
:root {
  /* surfaces — warm rice-paper ladder (matches canvas tints) */
  --paper0: #e9dcba;         /* app base — deepest sheet, behind everything */
  --paper1: #f3ead2;         /* screen surface */
  --panel: #f8f1de;          /* card surface L1 — raised sheet */
  --panel2: #fdf8ec;         /* card surface L2 — lit parchment */
  --sunken: #e5d7b4;         /* wells & tracks — pressed paper */
  --sunken2: #dbcca3;        /* deeper well */
  --edge: #d6c69e;           /* hairline — paper fold */
  --edge2: #bfa268;          /* gold-tinged hairline */
  --scrim: rgba(52, 38, 18, .44);   /* ink-wash backdrop */

  /* ink text ladder (contrast on --paper1) */
  --ink: #261f12;            /* 12.8:1 — primary */
  --ink-soft: #584c39;       /*  7.1:1 — secondary */
  --ink-faint: #7d6c4e;      /*  4.7:1 — hints */
  --paper: #261f12;          /* legacy var name: "on-paper text" (dark now) */
  --paper2: #584c39;         /* legacy: secondary */
  --dim: #6d5f43;            /* legacy: muted */
  --faint: #93815f;          /* legacy: faint */

  /* accents */
  --gold: #a3762a;           /* 4.9:1 on paper — usable as TEXT */
  --gold2: #7a571d;
  --gold-hi: #f0d488;
  --gold-bright: #e7bc5e;
  --gold-metal: linear-gradient(135deg, #f3dc9a 0%, #e2b752 38%, #b98a2c 62%, #eed588 100%);
  --seal: #b23c2c;           /* vermillion — primary action */
  --seal2: #8d2a1f;
  --seal-ink: #5c1b13;
  --jade: #47703e;
  --purple: #5f4f95;
  --cold: #3d6478;

  /* rarity (boon frames) */
  --r-common: #6b5c40;
  --r-rare: #3d6478;
  --r-epic: #5f4f95;
  --r-legend: #a3762a;

  /* in-play instruments — the dark ink chip behind #hud-pause / #dash-btn.
     Tome-themed: js/mod/theme/90-palettes.js overwrites both on the root at
     every level start, so the HUD re-tints with the tome around it. The
     values here are the tome-1 (brand sepia) chip — identical to the
     shipped rgba(46,37,20,.88)/rgba(28,22,11,.88) — so a boot without the
     palette module renders exactly as before. */
  --hud-btn-bg: rgba(46, 37, 20, .88);
  --hud-btn-bg2: rgba(28, 22, 11, .88);

  /* type scale */
  --fs-xs: clamp(9px, 2.7vmin, 12px);
  --fs-s: clamp(10.5px, 3.1vmin, 14px);
  --fs-m: clamp(12px, 3.6vmin, 16.5px);
  --fs-l: clamp(15px, 4.6vmin, 21px);
  --fs-xl: clamp(19px, 6.2vmin, 30px);
  --fs-huge: clamp(28px, 9.5vmin, 50px);

  /* shape & rhythm */
  --r-s: 10px;
  --r-m: 14px;
  --r-l: 20px;
  --pad: clamp(8px, 2.2vmin, 16px);
  --gap: clamp(6px, 1.6vmin, 12px);
  --tap: clamp(36px, 9.5vmin, 52px);
  --font: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-display: Georgia, 'Iowan Old Style', 'Palatino Linotype', 'Book Antiqua', 'Noto Serif', serif;

  /* motion */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-spring: cubic-bezier(.3, 1.42, .42, 1);

  /* elevation — warm paper shadows with a lit top edge */
  --shadow-1: 0 1px 2px rgba(66, 48, 22, .10), 0 3px 10px rgba(66, 48, 22, .13), inset 0 1px 0 rgba(255, 253, 244, .75);
  --shadow-2: 0 2px 6px rgba(66, 48, 22, .11), 0 12px 28px rgba(66, 48, 22, .17), inset 0 1px 0 rgba(255, 253, 244, .8);
  --shadow-3: 0 6px 18px rgba(66, 48, 22, .15), 0 26px 58px rgba(66, 48, 22, .22), inset 0 1px 0 rgba(255, 253, 244, .85);
  --shadow-seal: 0 2px 6px rgba(142, 44, 28, .28), 0 10px 24px rgba(142, 44, 28, .34), inset 0 1px 0 rgba(255, 240, 228, .35);

  /* card material (shared paper surface) */
  --card-bg: linear-gradient(180deg, rgba(255, 252, 240, .55), rgba(255, 250, 234, 0) 42%), var(--panel);
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='0.55'/%3E%3C/svg%3E");
}

/* ---------------- reset & layers ---------------- */
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; overflow: hidden; }
body {
  font-family: var(--font);
  background: var(--paper0);
  color: var(--ink);
  font-size: var(--fs-m);
  line-height: 1.4;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  overscroll-behavior: none;
}
button { font-family: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
button:disabled { cursor: default; }
canvas { display: block; }

/* the unified sheet of paper — one grain layer over the whole app
   (game canvas already carries its own baked grain; this whisper
   binds menus and play into the same material). */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  background-image: var(--grain);
  background-size: 160px 160px;
  opacity: .13;
  mix-blend-mode: multiply;
  pointer-events: none;
  z-index: 90;
}

/* press feedback — every tappable gives a physical response */
button:active, .hub-tile:active, .boon-card:active, .page-node:active,
.tier-row:active, .school-chip:active, .contract-card:active, .live-pill:active,
.arch-card:active, .tabbtn:active {
  transform: scale(.965);
}

#app { position: fixed; inset: 0; overflow: hidden; }
#game-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  touch-action: none;
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}

#fit-root {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  pointer-events: none;
}
#fit-root > * { pointer-events: none; }
#ui-root, #fx-root, #toast-root { position: absolute; inset: 0; pointer-events: none; }
#ui-root > *, #fx-root > * { pointer-events: auto; }
#toast-root > * { pointer-events: none; }

#hud-root {
  position: absolute; inset: 0;
  pointer-events: none;
  display: none;
  z-index: 30;
}
#hud-root.play { display: block; }
#hud-root > * { pointer-events: auto; }
#joy-base, #joy-ring, #joy-knob { pointer-events: none !important; }

::-webkit-scrollbar { width: 0; height: 0; }

/* ============================================================
   BOOT SCREEN — the first sheet: ink seal, brush-stroke loader
   ============================================================ */
#boot-screen {
  position: absolute; inset: 0; z-index: 100;
  background:
    radial-gradient(120% 90% at 50% 34%, rgba(255, 251, 238, .8), rgba(255, 251, 238, 0) 55%),
    radial-gradient(140% 120% at 50% 108%, rgba(133, 106, 60, .28), rgba(133, 106, 60, 0) 60%),
    var(--paper1);
  display: flex; align-items: center; justify-content: center;
  transition: opacity .5s var(--ease-out);
}
#boot-screen.hide { opacity: 0; pointer-events: none; }
.boot-inner {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: 34px 44px;
  animation: bootRise .7s var(--ease-out) both;
}
@keyframes bootRise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.boot-seal {
  width: clamp(64px, 16vmin, 96px); height: clamp(64px, 16vmin, 96px);
  display: grid; place-items: center;
  color: var(--seal);
  border: 2px solid rgba(178, 60, 44, .5);
  border-radius: 22%;
  background:
    radial-gradient(80% 80% at 50% 30%, rgba(255, 244, 232, .9), rgba(255, 244, 232, 0)),
    var(--panel);
  box-shadow: var(--shadow-2);
  position: relative;
}
.boot-seal::after {           /* the hanko stamp ring */
  content: '';
  position: absolute; inset: 5px;
  border: 1px dashed rgba(178, 60, 44, .38);
  border-radius: 18%;
}
.boot-seal svg { width: 58%; height: 58%; }
.boot-title {
  font-family: var(--font-display);
  font-size: clamp(30px, 8.5vmin, 54px);
  font-weight: 700;
  letter-spacing: .16em;
  color: var(--ink);
  text-shadow: 0 1px 0 rgba(255, 253, 244, .8);
  position: relative;
  padding-bottom: 8px;
}
.boot-title::after {          /* gold hairline underline */
  content: '';
  position: absolute; left: 12%; right: 12%; bottom: 0;
  height: 2px; border-radius: 2px;
  background: var(--gold-metal);
  opacity: .85;
}
.boot-sub {
  font-family: var(--font-display);
  font-style: italic;
  color: var(--ink-soft);
  font-size: var(--fs-s);
  letter-spacing: .08em;
}
.boot-stroke {
  width: min(240px, 60vw); height: 10px;
  background: var(--sunken);
  border-radius: 6px;
  box-shadow: inset 0 1px 3px rgba(66, 48, 22, .25), inset 0 -1px 0 rgba(255, 253, 244, .5);
  overflow: hidden;
  margin-top: 10px;
}
.boot-stroke-fill {
  height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--seal2), var(--seal) 55%, var(--gold-bright));
  border-radius: 6px;
  box-shadow: 0 0 10px rgba(178, 60, 44, .4);
  transition: width .25s var(--ease-out);
}
.boot-tip {
  color: var(--ink-faint);
  font-size: var(--fs-xs);
  letter-spacing: .12em;
  text-transform: uppercase;
}

/* ============================================================
   LANDSCAPE GATE — v11.0.2 ORIENT-1
   Inkblade is LANDSCAPE ONLY. On Android that is declared natively
   (screenOrientation="sensorLandscape" + appCategory="game", which is
   what keeps the lock enforced on sw>=600dp displays at targetSdk 36+).
   This card is the fallback for every surface the platform will NOT
   lock: a browser on a phone, a tablet/desktop-windowing case where an
   OEM ignores orientation, or any future device that ignores it.

   Rules this block obeys:
   · pure CSS — the ONLY trigger is the orientation media query, so
     there is no JS, no resize listener, no boot-path coupling;
   · `display: none` outside portrait, so it contributes nothing to
     layout and cannot shift the game when a device is landscape;
   · `position: fixed` + `pointer-events: auto` on the shell (v11.0.4
     UX-3): the gate must SWALLOW input while it is visible. It used to
     leave the shell transparent to taps and only the card hit-testable,
     so a tester could tap through the blur, open CHAPTERS and launch a
     page behind it. Nothing behind this layer may receive a tap now —
     `touch-action: none` stops the same tap becoming a scroll/gesture;
   · the id is NEW (#orient-gate) — the old #rotate-overlay element was
     removed in v11.1 and this block replaces its dead rules, so nothing
     in js/ (which still probes old ids defensively) can reach this node.
   ============================================================ */
#orient-gate { display: none; }

@media (orientation: portrait) {
  #orient-gate {
    position: fixed; inset: 0; z-index: 95;
    display: flex; align-items: center; justify-content: center;
    padding: 24px;
    background: var(--scrim);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    pointer-events: auto;            /* v11.0.4 UX-3: capture every tap */
    touch-action: none;
    overscroll-behavior: none;
    -webkit-user-select: none; user-select: none;
  }
  #orient-gate .og-card {
    position: relative;
    display: flex; flex-direction: column; align-items: center;
    gap: clamp(10px, 2.6vmin, 16px);
    width: 100%; max-width: 340px;
    padding: clamp(22px, 6.5vmin, 34px) clamp(18px, 5.5vmin, 30px);
    text-align: center;
    background: var(--card-bg);
    border: 1px solid var(--edge);
    border-radius: var(--r-l);
    box-shadow: var(--shadow-3);
    pointer-events: auto;            /* the card is a target too */
  }
  #orient-gate .og-card::after {     /* the same paper grain every card wears */
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    background-image: var(--grain); opacity: .16; pointer-events: none;
  }
  #orient-gate .og-card > * { pointer-events: none; }
  #orient-gate .og-glyph {
    width: clamp(44px, 15vmin, 62px); height: auto;
    color: var(--ink-soft);
    animation: ogTurn 3.4s var(--ease-out) infinite;
  }
  @keyframes ogTurn {
    0%, 55%, 100% { transform: rotate(0deg); }
    72%, 90% { transform: rotate(-90deg); }
  }
  #orient-gate .og-title {
    font-family: var(--font-display);
    font-size: var(--fs-l);
    line-height: 1.35;
    letter-spacing: .03em;
    color: var(--ink);
  }
  #orient-gate .og-sub {
    font-family: var(--font);
    font-size: var(--fs-xs);
    letter-spacing: .12em;
    text-transform: uppercase;
    line-height: 1.6;
    color: var(--ink-faint);
  }
  #orient-gate .og-seal {
    font-family: var(--font);
    font-size: var(--fs-xs);
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--seal);
    border: 1px solid rgba(178, 60, 44, .42);
    border-radius: 999px;
    padding: 6px 15px;
  }
}
/* ============================================================
   THE BUTTON SYSTEM — every action, one family
   A pill of raised paper with an inked label. Variants:
   primary (vermillion seal) · gold (reward) · ghost (paper) ·
   danger (deep seal) — sizes: small / big / sq (icon square).
   Each carries: state layer, sheen sweep, press physics,
   focus ring, disabled fade. ~200 lines of care.
   ============================================================ */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--tap);
  padding: 0 clamp(16px, 3.4vmin, 26px);
  border-radius: 999px;
  font-weight: 700;
  font-size: var(--fs-s);
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  overflow: hidden;
  transition: transform .18s var(--ease-spring), box-shadow .18s var(--ease-out),
              background-color .18s var(--ease-out), border-color .18s var(--ease-out);
  -webkit-user-select: none; user-select: none;
}
.btn > .ic { width: 1.15em; height: 1.15em; flex: none; }
.btn > .ic svg, .btn > span.ic { display: block; }
.btn span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* hover lift (fine pointers only) */
@media (hover: hover) {
  .btn:not(:disabled):hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-2);
    border-color: var(--edge2);
  }
}
.btn:not(:disabled):active {
  transform: scale(.965);
  box-shadow: 0 1px 3px rgba(66, 48, 22, .14), inset 0 1px 0 rgba(255, 253, 244, .6);
}
.btn:disabled {
  opacity: .45;
  filter: saturate(.6);
  box-shadow: none;
}

/* ---- primary: the vermillion seal ---- */
.btn.primary {
  color: #fff6ec;
  background:
    radial-gradient(120% 150% at 50% -20%, rgba(255, 205, 185, .34), rgba(255, 205, 185, 0) 52%),
    linear-gradient(180deg, #c34a37, var(--seal) 55%, #a23326);
  border-color: rgba(122, 38, 25, .8);
  box-shadow: var(--shadow-seal);
  text-shadow: 0 1px 1px rgba(92, 27, 19, .5);
}
.btn.primary::after {                 /* sheen sweep — light crossing the lacquer */
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255, 246, 232, .34) 48%, rgba(255, 246, 232, .05) 54%, transparent 70%);
  transform: translateX(-130%);
  animation: btnSheen 4.6s var(--ease-out) infinite;
  pointer-events: none;
}
@keyframes btnSheen { 0%, 55% { transform: translateX(-130%); } 85%, 100% { transform: translateX(130%); } }
@media (hover: hover) {
  .btn.primary:not(:disabled):hover { box-shadow: 0 4px 10px rgba(142, 44, 28, .34), 0 16px 34px rgba(142, 44, 28, .4), inset 0 1px 0 rgba(255, 240, 228, .35); }
}
.btn.primary:disabled { background: linear-gradient(180deg, #c98f81, #a97a6d); color: rgba(255, 246, 236, .7); }

/* ---- gold: the reward button ---- */
.btn.gold, .btn.ad-ready {
  color: #33230b;
  background: var(--gold-metal);
  border-color: rgba(122, 87, 29, .75);
  box-shadow: 0 2px 6px rgba(122, 87, 29, .3), 0 10px 24px rgba(163, 118, 42, .38), inset 0 1px 0 rgba(255, 246, 214, .8);
  text-shadow: 0 1px 0 rgba(240, 212, 136, .6);
}
.btn.gold::after, .btn.ad-ready::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 32%, rgba(255, 252, 240, .5) 48%, rgba(255, 252, 240, .08) 55%, transparent 72%);
  transform: translateX(-130%);
  animation: btnSheen 3.4s var(--ease-out) infinite;
  pointer-events: none;
}
.btn.ad-ready { animation: adBreath 2.2s ease-in-out infinite; }
@keyframes adBreath {
  0%, 100% { box-shadow: 0 2px 6px rgba(122, 87, 29, .3), 0 10px 24px rgba(163, 118, 42, .34), inset 0 1px 0 rgba(255, 246, 214, .8); }
  50% { box-shadow: 0 3px 10px rgba(122, 87, 29, .4), 0 16px 36px rgba(163, 118, 42, .5), inset 0 1px 0 rgba(255, 246, 214, .9); }
}

/* ---- ghost: quiet paper ---- */
.btn.ghost {
  background: linear-gradient(180deg, rgba(255, 252, 240, .5), rgba(255, 250, 234, 0) 60%), var(--paper1);
  color: var(--ink-soft);
  border-color: var(--edge);
  box-shadow: 0 1px 2px rgba(66, 48, 22, .08), inset 0 1px 0 rgba(255, 253, 244, .7);
}
@media (hover: hover) {
  .btn.ghost:not(:disabled):hover { color: var(--ink); background: var(--panel); }
}
.btn.ghost:disabled { opacity: .5; }

/* ---- danger ---- */
.btn.danger, .btn.primary.danger {
  color: #fff2ea;
  background:
    radial-gradient(120% 150% at 50% -20%, rgba(255, 190, 172, .26), rgba(255, 190, 172, 0) 52%),
    linear-gradient(180deg, #a23326, var(--seal2) 60%, #74211a);
  border-color: rgba(92, 27, 19, .9);
  box-shadow: 0 2px 6px rgba(92, 27, 19, .3), 0 10px 24px rgba(92, 27, 19, .36), inset 0 1px 0 rgba(255, 226, 214, .28);
  text-shadow: 0 1px 1px rgba(58, 14, 9, .55);
}

/* ---- sizes ---- */
.btn.small {
  min-height: calc(var(--tap) * .66);
  padding: 0 clamp(12px, 2.4vmin, 18px);
  font-size: var(--fs-xs);
  gap: 6px;
  border-radius: 999px;
}
.btn.small > .ic { width: 1em; height: 1em; }
.btn.big {
  min-height: calc(var(--tap) * 1.3);
  font-size: var(--fs-m);
  padding: 0 clamp(22px, 5vmin, 40px);
  box-shadow: var(--shadow-2);
}
.btn.sq {
  width: var(--tap); height: var(--tap);
  padding: 0;
  border-radius: var(--r-m);
}
.btn.sq > .ic { width: 1.5em; height: 1.5em; }

/* icon-only round button (settings gear, back arrows) */
.icon-btn {
  position: relative;
  width: calc(var(--tap) * .92); height: calc(var(--tap) * .92);
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--ink-soft);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  transition: transform .18s var(--ease-spring), box-shadow .18s var(--ease-out), color .18s;
  overflow: hidden;
}
.icon-btn svg { width: 46%; height: 46%; }
@media (hover: hover) {
  .icon-btn:hover { color: var(--ink); transform: translateY(-1px); box-shadow: var(--shadow-2); border-color: var(--edge2); }
}
.icon-btn:active { transform: scale(.92); }
.icon-btn::after {
  content: '';
  position: absolute; inset: 0;
  background: radial-gradient(90% 90% at 50% 20%, rgba(255, 253, 244, .8), rgba(255, 253, 244, 0));
  pointer-events: none;
}

/* ---- price buttons (shop): coin-shaped gold / poor / maxed ---- */
.price-btn {
  position: relative;
  min-height: calc(var(--tap) * .74);
  padding: 0 clamp(14px, 2.6vmin, 22px);
  border-radius: 999px;
  font-weight: 800;
  font-size: var(--fs-s);
  letter-spacing: .06em;
  color: #33230b;
  background: var(--gold-metal);
  border: 1px solid rgba(122, 87, 29, .8);
  box-shadow: 0 1px 0 rgba(255, 246, 214, .9) inset, 0 2px 6px rgba(122, 87, 29, .3), 0 6px 16px rgba(163, 118, 42, .3);
  text-shadow: 0 1px 0 rgba(240, 212, 136, .55);
  transition: transform .16s var(--ease-spring), box-shadow .16s, filter .16s;
  overflow: hidden;
}
@media (hover: hover) {
  .price-btn:not(:disabled):hover { transform: translateY(-1px); box-shadow: 0 1px 0 rgba(255, 246, 214, .9) inset, 0 4px 10px rgba(122, 87, 29, .34), 0 10px 24px rgba(163, 118, 42, .42); }
}
.price-btn:not(:disabled):active { transform: scale(.94); }
.price-btn.poor {               /* not enough currency — honest, quiet */
  background: var(--card-bg);
  color: var(--ink-faint);
  border-color: var(--edge);
  text-shadow: none;
  box-shadow: var(--shadow-1);
}
.price-btn.maxed {
  background: linear-gradient(180deg, #7fa271, var(--jade) 70%, #37582f);
  color: #eef7e6;
  border-color: rgba(45, 68, 38, .9);
  text-shadow: 0 1px 1px rgba(28, 46, 23, .5);
}
.price-btn.danger {
  background: linear-gradient(180deg, #c34a37, var(--seal) 55%, #a23326);
  color: #fff2ea;
  border-color: rgba(122, 38, 25, .8);
}
.price-btn:disabled { opacity: .6; filter: saturate(.7); box-shadow: none; cursor: default; }

/* claim heartbeat — used by road/contract claim CTAs */
.btn.claim-pulse { animation: claimPulse 2.2s ease-in-out infinite, cardIn .28s var(--ease-out) backwards; }
@keyframes claimPulse {
  0%, 100% { transform: scale(1); box-shadow: var(--shadow-seal); }
  50% { transform: scale(1.015); box-shadow: 0 4px 10px rgba(142, 44, 28, .38), 0 14px 32px rgba(142, 44, 28, .48), inset 0 1px 0 rgba(255, 240, 228, .35); }
}

/* spinner (ad preparing) */
.spin { display: inline-grid; place-items: center; animation: spin 1.1s linear infinite; }
.spin svg { width: 1.1em; height: 1.1em; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ============================================================
   THE SWITCH — a modern toggle carved from paper
   Sunken well track, sliding ink thumb, gold ring when on.
   ============================================================ */
.switch {
  position: relative;
  flex: none;
  width: clamp(46px, 11.5vmin, 58px);
  height: clamp(26px, 6.4vmin, 33px);
  border-radius: 999px;
  background: var(--sunken);
  border: 1px solid var(--edge);
  box-shadow: inset 0 2px 5px rgba(66, 48, 22, .22), inset 0 -1px 0 rgba(255, 253, 244, .45);
  transition: background .22s var(--ease-out), border-color .22s;
}
.switch .knob {
  position: absolute;
  top: 50%; left: 3px;
  width: calc(100% - 8px);
  height: calc(100% - 10px);
  max-width: 50%;
  aspect-ratio: 1 / 1;
  height: auto;
  min-width: 20px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fdf8ec, #e8dabb);
  border: 1px solid var(--edge2);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .28), inset 0 1px 0 rgba(255, 253, 244, .9);
  transform: translateY(-50%);
  transition: left .22s var(--ease-spring), background .22s, box-shadow .22s;
}
.switch .knob::after {     /* ink dot in the thumb */
  content: '';
  position: absolute; inset: 34%;
  border-radius: 50%;
  background: var(--ink-faint);
  transition: background .22s, transform .22s;
}
.switch.on {
  background: linear-gradient(180deg, #cf5a41, var(--seal) 60%, #a23326);
  border-color: rgba(122, 38, 25, .85);
  box-shadow: inset 0 2px 5px rgba(92, 27, 19, .4), 0 0 0 3px rgba(178, 60, 44, .14);
}
.switch.on .knob { left: calc(100% - 3px); transform: translate(-100%, -50%); }
.switch.on .knob::after { background: var(--seal2); }
.switch:active .knob { width: calc(100% - 2px); }

/* ============================================================
   THE SEGMENTED CONTROL — two options, one sliding thumb
   ============================================================ */
.segc {
  position: relative;
  display: flex;
  flex: none;
  width: clamp(148px, 34vmin, 210px);
  min-height: calc(var(--tap) * .72);
  border-radius: 999px;
  background: var(--sunken);
  border: 1px solid var(--edge);
  box-shadow: inset 0 2px 5px rgba(66, 48, 22, .2), inset 0 -1px 0 rgba(255, 253, 244, .5);
  padding: 3px;
}
.segc .thumb {
  position: absolute;
  top: 3px; bottom: 3px;
  left: 3px;
  width: calc(50% - 5px);
  border-radius: 999px;
  background: linear-gradient(180deg, #fdf8ec, #efe5c8);
  border: 1px solid var(--edge2);
  box-shadow: 0 2px 6px rgba(66, 48, 22, .22), inset 0 1px 0 rgba(255, 253, 244, .95);
  transition: left .24s var(--ease-spring), transform .24s var(--ease-spring);
  z-index: 0;
}
.segc.right .thumb { left: calc(50% + 2px); }
.segc button {
  position: relative; z-index: 1;
  flex: 1;
  display: grid; place-items: center;
  min-height: calc(var(--tap) * .62);
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ink-faint);
  transition: color .2s;
}
.segc button.on { color: var(--ink); }
.segc button:not(.on):hover { color: var(--ink-soft); }

/* ============================================================
   TABS — sliding pills for hall switches
   ============================================================ */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 5px;
  border-radius: 999px;
  background: var(--sunken);
  border: 1px solid var(--edge);
  box-shadow: inset 0 2px 5px rgba(66, 48, 22, .18);
  width: fit-content;
  max-width: 100%;
}
.tab-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: calc(var(--tap) * .64);
  padding: 0 clamp(12px, 2.4vmin, 20px);
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .09em;
  color: var(--ink-faint);
  transition: color .2s, background .2s, box-shadow .2s, transform .16s var(--ease-spring);
  white-space: nowrap;
}
.tab-pill > .ic { width: 1.1em; height: 1.1em; }
.tab-pill > .ic svg { display: block; }
.tab-pill small { font-size: .82em; opacity: .75; font-weight: 600; }
.tab-pill:not(.on):hover { color: var(--ink-soft); }
.tab-pill.on {
  color: var(--ink);
  background: linear-gradient(180deg, #fdf8ec, #f1e7ca);
  border: 1px solid var(--edge2);
  box-shadow: 0 2px 6px rgba(66, 48, 22, .2), inset 0 1px 0 rgba(255, 253, 244, .95);
  position: relative;
}
.tab-pill.on::after {         /* gold underline tick */
  content: '';
  position: absolute;
  left: 22%; right: 22%; bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: var(--gold-metal);
}
.tab-pill:active { transform: scale(.95); }

/* ============================================================
   WALLET CHIPS — the purse: gem / shard / sigil counters
   ============================================================ */
.sc-wallet { display: flex; gap: 6px; align-items: center; flex: none; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: calc(var(--tap) * .58);
  padding: 0 12px 0 9px;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255, 252, 240, .65), rgba(255, 250, 234, 0) 55%), var(--panel);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  font-weight: 800;
  font-size: var(--fs-s);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.chip .ic { width: 1.05em; height: 1.05em; }
.chip .ic svg { display: block; }
.chip.g  .ic { color: var(--gold); }
.chip.s  .ic { color: var(--cold); }
.chip.si .ic { color: var(--purple); }
.chip .w-val { min-width: 1.2em; text-align: center; }
/* the tick — a currency just gained pops like a coin */
.chip.tick { animation: chipTick .55s var(--ease-spring); }
@keyframes chipTick {
  0% { transform: scale(1); }
  35% { transform: scale(1.14); box-shadow: 0 0 0 4px rgba(163, 118, 42, .22), var(--shadow-1); border-color: var(--gold); }
  100% { transform: scale(1); }
}

/* ============================================================
   TOASTS — ink slips that surface from the margin
   Dark ink chip on paper: the inverse moment, high contrast.
   ============================================================ */
.toast {
  position: relative;
  display: block;
  max-width: min(560px, 86vw);
  margin: 0 auto 8px;
  padding: 10px 18px;
  border-radius: var(--r-m);
  background: linear-gradient(180deg, #3a2f1c, #241c0e);
  color: #f6efdb;
  font-size: var(--fs-s);
  line-height: 1.45;
  box-shadow: 0 10px 26px rgba(40, 28, 12, .5), inset 0 1px 0 rgba(246, 239, 219, .14), inset 0 0 0 1px rgba(246, 239, 219, .07);
  text-align: center;
  animation: toastIn .34s var(--ease-spring) both;
  pointer-events: none;
}
.toast b { color: var(--gold-bright); }
@keyframes toastIn {
  from { opacity: 0; transform: translateY(10px) scale(.96); }
  to { opacity: 1; transform: none; }
}
#toast-root {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-bottom: clamp(10px, 4vmin, 26px);
  z-index: 70;
}

/* ============================================================
   MODALS — a sheet of paper floating on an ink wash
   ============================================================ */
.screen.overlay {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--scrim);
  backdrop-filter: blur(7px) saturate(.9);
  -webkit-backdrop-filter: blur(7px) saturate(.9);
  z-index: 40;
  animation: fadeIn .22s ease-out both;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal {
  position: relative;
  width: min(560px, 92vw);
  max-height: min(86vh, 92svh);
  display: flex; flex-direction: column;
  border-radius: var(--r-l);
  background:
    linear-gradient(180deg, rgba(255, 253, 244, .7), rgba(255, 250, 234, 0) 12%),
    var(--panel);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-3);
  overflow: hidden;
  animation: modalIn .32s var(--ease-spring) both;
}
.modal::before {            /* deckle top edge — hand-cut paper */
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 4px;
  background: linear-gradient(90deg, var(--seal) 0 14%, var(--gold) 14% 18%, transparent 18% 100%);
  opacity: .8;
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(18px) scale(.96); }
  to { opacity: 1; transform: none; }
}
.modal.wide { width: min(760px, 94vw); }
.modal-head { padding: calc(var(--pad) * 1.1) var(--pad) calc(var(--pad) * .6); text-align: center; }
.modal-title {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--ink);
}
.modal-sub {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-s);
  color: var(--ink-soft);
  margin-top: 3px;
  letter-spacing: .03em;
}
.modal-body {
  padding: var(--pad);
  overflow: hidden auto;
  display: flex; flex-direction: column; gap: var(--gap);
  scrollbar-width: none;
}
.modal-foot {
  padding: calc(var(--pad) * .6) var(--pad) calc(var(--pad) * 1.05);
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  border-top: 1px solid var(--edge);
  background: linear-gradient(180deg, rgba(233, 220, 186, .5), rgba(233, 220, 186, 0));
}
.modal-foot.btn-col { flex-direction: column; }
.modal-foot.btn-col > * { width: min(340px, 100%); }

/* ============================================================
   SMALL PARTS — hints, bars, pips, badges, stamps, pager, fx
   ============================================================ */
.hint {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  line-height: 1.55;
  letter-spacing: .02em;
  padding: 4px 2px;
}
.hint b { color: var(--ink-soft); font-weight: 700; }

.bar {
  position: relative;
  height: clamp(10px, 2.4vmin, 14px);
  border-radius: 999px;
  background: var(--sunken);
  box-shadow: inset 0 1px 3px rgba(66, 48, 22, .22), inset 0 -1px 0 rgba(255, 253, 244, .5);
  overflow: hidden;
}
.bar > span:first-child {
  display: block; height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--seal2), var(--seal));
  box-shadow: inset 0 1px 0 rgba(255, 240, 228, .4);
}
.bar.slim { height: 7px; }
.bar.slim > span:first-child { background: linear-gradient(90deg, #8a6a24, var(--gold)); }
.bar-label {
  font-size: var(--fs-xs);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 3px;
}

.pips { display: inline-flex; gap: 3px; font-size: var(--fs-s); color: var(--gold); }
.pip { color: var(--ink-faint); opacity: .55; }
.pip.on { color: var(--seal); opacity: 1; }

.badge {
  position: absolute; top: -6px; right: -4px;
  min-width: 20px; height: 20px;
  padding: 0 6px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: linear-gradient(180deg, #c34a37, var(--seal) 60%, #a23326);
  color: #fff6ec;
  font-size: 11px;
  font-weight: 800;
  border: 1px solid rgba(255, 240, 228, .5);
  box-shadow: 0 3px 8px rgba(142, 44, 28, .45);
  animation: badgePop .3s var(--ease-spring) both;
  z-index: 3;
}
.badge.done {
  background: linear-gradient(180deg, #679256, var(--jade) 70%, #37582f);
  border-color: rgba(238, 247, 230, .6);
  box-shadow: 0 3px 8px rgba(60, 96, 50, .45);
}
@keyframes badgePop { from { transform: scale(0); } to { transform: scale(1); } }

/* the rotated seal stamp — MAXED, claims */
.stamp {
  position: absolute;
  top: 8px; right: 10px;
  padding: 3px 10px;
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: .22em;
  color: var(--seal2);
  border: 2px solid rgba(178, 60, 44, .55);
  border-radius: 4px;
  transform: rotate(6deg);
  background: rgba(253, 248, 236, .72);
  box-shadow: 0 2px 8px rgba(66, 48, 22, .12);
  pointer-events: none;
}

/* pager — page dots for paged grids */
.pager {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 8px;
  flex: none;
}
.pg-btn {
  width: calc(var(--tap) * .6); height: calc(var(--tap) * .6);
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--ink-soft);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  transition: transform .16s var(--ease-spring), opacity .2s;
}
.pg-btn svg { width: 45%; height: 45%; }
.pg-btn:not(:disabled):active { transform: scale(.9); }
.pg-btn:disabled { opacity: .35; }
.pg-dots { display: flex; gap: 6px; }
.pg-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--sunken2);
  border: 1px solid var(--edge);
  transition: transform .2s var(--ease-spring), background .2s, box-shadow .2s;
}
.pg-dot.on {
  background: var(--seal);
  border-color: var(--seal2);
  transform: scale(1.25);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .18);
}
.pg-page {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  min-width: 3.2em;
  text-align: center;
  letter-spacing: .06em;
}

/* floating purchase juice */
.fx-float {
  position: absolute;
  left: 50%; top: 34%;
  transform: translate(-50%, 0);
  font-weight: 800;
  font-size: var(--fs-m);
  color: var(--gold-deep);
  text-shadow: 0 1px 0 rgba(255, 253, 244, .9), 0 2px 8px rgba(163, 118, 42, .4);
  pointer-events: none;
  animation: floatUp .85s var(--ease-out) both;
  white-space: nowrap;
  z-index: 5;
}
@keyframes floatUp {
  0% { opacity: 0; transform: translate(-50%, 6px) scale(.85); }
  18% { opacity: 1; transform: translate(-50%, -4px) scale(1.08); }
  100% { opacity: 0; transform: translate(-50%, -34px) scale(1); }
}

/* progress cards flash on purchase */
.flash { animation: flashGold .5s ease-out; }
@keyframes flashGold {
  0% { box-shadow: 0 0 0 0 rgba(163, 118, 42, 0); }
  30% { box-shadow: 0 0 0 5px rgba(163, 118, 42, .3), var(--shadow-1); border-color: var(--gold); }
  100% { box-shadow: var(--shadow-1); }
}

/* stars (page ratings) */
.stars { color: var(--gold); letter-spacing: .1em; }

/* generic card (help & misc) */
.card {
  background: var(--card-bg);
  border: 1px solid var(--edge);
  border-radius: var(--r-m);
  box-shadow: var(--shadow-1);
  padding: var(--pad);
}

/* layout helpers */
.row { }
.rows { display: flex; flex-direction: column; gap: 6px; }
.row-gap { display: flex; gap: var(--gap); align-items: center; }
.grow { flex: 1 1 auto; min-width: 0; }
.spacer { flex: 1 1 auto; }
.center { text-align: center; }
.mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 14px; }
.btn-col { display: flex; flex-direction: column; gap: 8px; }

/* ============================================================
   ICONOGRAPHY CHIPS — the medallions
   Engraved paper medallions carrying each door's glyph.
   ============================================================ */
.ic { display: inline-flex; align-items: center; justify-content: center; }
.ic svg { width: 100%; height: 100%; }
.ic-chip {
  position: relative;
  display: grid; place-items: center;
  flex: none;
  width: calc(var(--tap) * .94);
  height: calc(var(--tap) * .94);
  border-radius: 32%;
  color: var(--ink-soft);
  background:
    radial-gradient(90% 90% at 50% 24%, rgba(255, 253, 244, .95), rgba(255, 253, 244, 0) 70%),
    linear-gradient(180deg, #f8f1de, #eee2c2);
  border: 1px solid var(--edge2);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .16), inset 0 1px 0 rgba(255, 253, 244, .9), inset 0 -2px 4px rgba(66, 48, 22, .1);
}
.ic-chip::after {         /* engraved inner ring */
  content: '';
  position: absolute; inset: 4px;
  border-radius: 26%;
  border: 1px solid rgba(191, 162, 104, .45);
  pointer-events: none;
}
.ic-chip .ic { width: 52%; height: 52%; }
.ic-chip.sm { width: calc(var(--tap) * .66); height: calc(var(--tap) * .66); border-radius: 30%; }
.ic-chip.sm .ic { width: 50%; height: 50%; }
.ic-chip.md { width: calc(var(--tap) * .8); height: calc(var(--tap) * .8); }
.ic-chip.lg { width: calc(var(--tap) * 1.15); height: calc(var(--tap) * 1.15); }
.ic-chip.lg .ic { width: 54%; height: 54%; }
/* variant tints — one per treasure family */
.ic-chip.gold   { color: var(--gold2); border-color: rgba(163, 118, 42, .55); }
.ic-chip.seal   { color: var(--seal2); border-color: rgba(178, 60, 44, .5); }
.ic-chip.jade   { color: #3a5c33; border-color: rgba(71, 112, 62, .5); }
.ic-chip.cold   { color: #31525f; border-color: rgba(61, 100, 120, .5); }
.ic-chip.purple { color: #4a3d78; border-color: rgba(95, 79, 149, .5); }
/* ============================================================
   SCREEN SCAFFOLD — every non-game screen is a sheet of paper
   laid over the living ink canvas.
   .screen       full-viewport layer
   .screen.dim   frosted-paper scrim over the game world
   .frame        the raised paper sheet carrying the content
   ============================================================ */
.screen {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  z-index: 10;
  background:
    radial-gradient(130% 100% at 50% 0%, rgba(255, 251, 238, .55), rgba(255, 251, 238, 0) 52%),
    radial-gradient(160% 130% at 50% 115%, rgba(133, 106, 60, .3), rgba(133, 106, 60, 0) 62%),
    var(--paper1);
}
.screen.dim {
  background:
    radial-gradient(130% 100% at 50% 0%, rgba(255, 251, 238, .5), rgba(255, 251, 238, 0) 50%),
    rgba(243, 234, 210, .88);
  backdrop-filter: blur(9px) saturate(.92);
  -webkit-backdrop-filter: blur(9px) saturate(.92);
}
.frame {
  flex: 1 1 auto;
  min-height: 0;
  display: flex; flex-direction: column;
  max-width: 1180px;
  width: 100%;
  margin: 0 auto;
  padding: clamp(8px, 2vmin, 18px);
  gap: var(--gap);
}

/* header row: back medallion · title · wallet */
.sc-head {
  flex: none;
  display: flex; align-items: center; gap: 10px;
  padding: calc(var(--pad) * .5) var(--pad);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
}
.sc-back {
  width: calc(var(--tap) * .8); height: calc(var(--tap) * .8);
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--ink-soft);
  background: linear-gradient(180deg, #fdf8ec, #f1e7ca);
  border: 1px solid var(--edge);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .16), inset 0 1px 0 rgba(255, 253, 244, .9);
  transition: transform .16s var(--ease-spring), color .2s;
  flex: none;
}
.sc-back svg { width: 44%; height: 44%; }
.sc-back:hover { color: var(--seal); }
.sc-back:active { transform: scale(.9); }
.sc-title {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--fs-l);
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--ink);
  line-height: 1.15;
  display: flex; flex-direction: column; justify-content: center;
}
.sc-title small {
  font-family: var(--font-display);
  font-style: italic;
  font-size: .58em;
  font-weight: 400;
  letter-spacing: .05em;
  color: var(--ink-faint);
  margin-top: 2px;
  /* v11.0.1 UI-FIX: italic display-font descenders (g, y, p) were
     clipped by the inherited 1.15 line-height on screens like DAILY INK
     ("same ink for everyone" lost its tails). */
  line-height: 1.42;
  padding-bottom: 1px;
}
.sc-desc { font-size: var(--fs-s); color: var(--ink-soft); }
.sc-main { display: flex; flex-direction: column; gap: var(--gap); }

/* v11.1.1 UI-FIX: cards inside a scrollable ledger must keep their
   intrinsic height. A default flex-shrink collapsed .rebirth-panel and
   .season-banner to a few pixels (content clipped) on short screens.
   The body scrolls; it does not squeeze. */
.sc-body > *, .sc-main > * { flex-shrink: 0; }

/* v11.1.1 UI-FIX: a shop tab is a flex column inside the scrollable
   ledger. Its cards must keep their height; before this the sigil
   rebirth panel collapsed from 134px of content to 17px. */
.pane > * { flex-shrink: 0; }

/* body — the scrollable ledger (scroll is hidden, felt not seen) */
.sc-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex; flex-direction: column;
  gap: var(--gap);
  /* v11.0.1 UI-FIX: 1×pad bottom padding let the last row of long ledgers
     (CHRONICLE stat grids) sit hard against the screen edge mid-scroll. */
  padding: calc(var(--pad) * .4) 2px calc(var(--pad) * 1.35) 2px;
  scrollbar-width: none;
}
.sc-foot {
  flex: none;
  display: flex; flex-wrap: wrap; gap: 8px;
  justify-content: center; align-items: center;
  padding: calc(var(--pad) * .5) var(--pad);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
}

/* ============================================================
   HOME — the atelier door, decluttered
   brand strip · hero portrait + goal · ONE mega CTA · a live
   strip of four quiet pills · six doors. ~12 boxes, one tap
   to everything, nothing louder than the CTA.
   ============================================================ */
.screen.home {
  background:
    radial-gradient(130% 120% at 50% -10%, rgba(255, 251, 238, .72), rgba(255, 251, 238, 0) 55%),
    radial-gradient(170% 140% at 50% 118%, rgba(120, 95, 54, .32), rgba(120, 95, 54, 0) 62%),
    rgba(243, 234, 210, .8);
  backdrop-filter: blur(6px) saturate(.95);
  -webkit-backdrop-filter: blur(6px) saturate(.95);
}
.home {
  flex: 1 1 auto;
  min-height: 0;
  display: flex; flex-direction: column;
  max-width: 1180px; width: 100%;
  margin: 0 auto;
  padding: clamp(8px, 2vmin, 18px);
  gap: var(--gap);
}

/* brand strip */
.home-top {
  flex: none;
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
}
.brand { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.brand .t-main {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-display);
  font-size: clamp(18px, 5vmin, 34px);
  font-weight: 700;
  letter-spacing: .18em;
  color: var(--ink);
}
.brand .t-main .ic { width: .8em; height: .8em; color: var(--seal); }
.brand .t-main .ic svg { display: block; }
.brand .t-ver {
  font-size: var(--fs-xs);
  letter-spacing: .3em;
  color: var(--ink-faint);
  text-transform: uppercase;
  padding-left: 2px;
}
.top-actions { display: flex; align-items: center; gap: 8px; flex: none; }

.home-main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex; gap: var(--gap);
  align-items: stretch;
}

/* ---- left rail: the hero portrait + today's goal ---- */
.home-side {
  flex: 0 0 clamp(170px, 24%, 260px);
  display: flex; flex-direction: column; gap: var(--gap);
  min-width: 0;
}
.hero-panel {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  border-radius: var(--r-l);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-2);
  overflow: hidden;
  /* v11.1.1 UI-FIX: flex, not grid. A percentage-max-height grid item
     reported 14px of scrollable overflow at 800×360 (the scan flagged the
     portrait as clipped) even though the portrait was visually inside.
     A flex row anchored to the bottom sizes the same portrait with no
     overflow at every tested height. */
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 34px;              /* name-plate legroom (scrim sits there) */
  cursor: pointer;
  transition: transform .2s var(--ease-spring), box-shadow .2s;
}
.hero-panel::before {           /* gold corner brackets — the frame */
  content: '';
  position: absolute; inset: 7px;
  border-radius: calc(var(--r-l) - 6px);
  border: 1px solid rgba(163, 118, 42, .35);
  border-top-color: rgba(163, 118, 42, .55);
  pointer-events: none;
  z-index: 2;
}
.hero-panel canvas {
  /* fit-in-box: intrinsic size clamped by BOTH maxima — the portrait
     can never spill out of its frame, aspect always preserved */
  max-height: 100%;
  max-width: 92%;
  filter: drop-shadow(0 6px 14px rgba(66, 48, 22, .35));
  transition: transform .3s var(--ease-out);
}
.hero-panel:hover canvas { transform: scale(1.04) translateY(-2px); }
.hero-panel:hover { box-shadow: var(--shadow-3); }
.hero-panel:active { transform: scale(.985); }
.hero-plate {
  position: absolute; z-index: 3;
  left: 0; right: 0; bottom: 0;
  padding: 26px 12px 9px;
  text-align: center;
  background: linear-gradient(180deg, rgba(38, 31, 18, 0), rgba(38, 31, 18, .82) 46%);
  color: #f6efdb;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  pointer-events: none;
}
.hero-plate b {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-l);
  letter-spacing: .08em;
}
.hero-plate .hc-cta {
  display: block;
  font-size: var(--fs-xs);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--gold-bright);
  margin-top: 1px;
}

/* goal card — a page from the daybook, taped to the rail */
.goal-card {
  position: relative;
  flex: none;
  padding: calc(var(--pad) * .9) var(--pad);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.goal-card::before {            /* washi tape */
  content: '';
  position: absolute;
  top: -7px; left: 18px;
  width: 58px; height: 16px;
  background: linear-gradient(180deg, rgba(231, 188, 94, .55), rgba(231, 188, 94, .38));
  border-left: 1px dashed rgba(122, 87, 29, .35);
  border-right: 1px dashed rgba(122, 87, 29, .35);
  transform: rotate(-3deg);
  box-shadow: 0 2px 4px rgba(66, 48, 22, .14);
}
.goal-k {
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: .18em;
  color: var(--seal2);
  text-transform: uppercase;
}
.goal-t {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  color: var(--ink);
  margin: 2px 0 3px;
  line-height: 1.25;
}
.goal-s {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  line-height: 1.5;
}

/* ---- right stack: CTA · live pills · doors ---- */
.home-stack {
  flex: 1 1 auto;
  min-width: 0;
  display: flex; flex-direction: column; gap: var(--gap);
}

/* THE mega CTA — the loudest thing on the page */
.cta-continue {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  min-height: clamp(52px, 12.5vmin, 84px);
  padding: 0 clamp(16px, 3.6vmin, 30px);
  border-radius: var(--r-l);
  color: #fff6ec;
  text-align: left;
  background:
    radial-gradient(120% 160% at 85% -30%, rgba(255, 205, 185, .3), rgba(255, 205, 185, 0) 55%),
    linear-gradient(115deg, #c34a37 0%, var(--seal) 46%, #98301f 100%);
  border: 1px solid rgba(122, 38, 25, .9);
  box-shadow: var(--shadow-seal);
  overflow: hidden;
  transition: transform .2s var(--ease-spring), box-shadow .2s;
}
.cta-continue::after {          /* the sheen */
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 34%, rgba(255, 246, 232, .28) 48%, rgba(255, 246, 232, .05) 56%, transparent 72%);
  transform: translateX(-130%);
  animation: btnSheen 5.2s var(--ease-out) infinite;
  pointer-events: none;
}
.cta-continue > .ic {
  flex: none;
  width: 1.9em; height: 1.9em;
  padding: .3em;
  border-radius: 50%;
  background: rgba(92, 27, 19, .35);
  box-shadow: inset 0 1px 0 rgba(255, 240, 228, .3), inset 0 0 0 1px rgba(255, 240, 228, .22);
}
.cta-continue > .ic svg { display: block; width: 100%; height: 100%; }
.cta-continue .cta-main {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0;
}
.cta-continue .cta-main > span {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: .14em;
  line-height: 1.05;
  text-shadow: 0 2px 2px rgba(92, 27, 19, .5);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cta-continue .cta-main small {
  font-size: var(--fs-xs);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: rgba(255, 232, 216, .85);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (hover: hover) {
  .cta-continue:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(142, 44, 28, .4), 0 18px 44px rgba(142, 44, 28, .46), inset 0 1px 0 rgba(255, 240, 228, .35); }
}
.cta-continue:active { transform: scale(.98); }

/* live strip — today's quiet heartbeat */
.live-strip {
  flex: none;
  display: flex; gap: 6px;
  min-height: 0;
}
.live-pill {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  min-height: calc(var(--tap) * .72);
  padding: 0 10px;
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ink-soft);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  transition: transform .18s var(--ease-spring), box-shadow .18s, color .18s;
  overflow: hidden;
}
.live-pill > .ic { width: 1.05em; height: 1.05em; flex: none; }
.live-pill > .ic svg { display: block; }
.live-pill > span:first-of-type { white-space: nowrap; }
.live-pill .lp-val {
  font-weight: 800;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;
}
.live-pill:hover { color: var(--ink); box-shadow: var(--shadow-2); }
.live-pill:active { transform: scale(.96); }
.live-pill.live > .ic { color: var(--seal); }
.live-pill.live::before {        /* pulsing live dot */
  content: '';
  position: absolute; left: 7px; top: 50%;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--seal);
  transform: translateY(-50%);
  animation: livePulse 1.9s ease-in-out infinite;
}
.live-pill.live { padding-left: 16px; }
@keyframes livePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(178, 60, 44, .5); opacity: .75; }
  50% { box-shadow: 0 0 0 4px rgba(178, 60, 44, 0); opacity: 1; }
}
.live-pill.done { color: var(--jade); }
.live-pill.done > .ic { color: var(--jade); }
.live-pill.done .lp-val { color: #3a5c33; }

/* the six doors */
.tile-grid {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
}
.hub-tile {
  position: relative;
  display: flex; flex-direction: column; justify-content: center;
  gap: 4px;
  min-height: calc(var(--tap) * 1.05);
  padding: calc(var(--pad) * .8);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  overflow: hidden;
  transition: transform .18s var(--ease-spring), box-shadow .18s, border-color .18s;
  min-width: 0;
}
.hub-tile:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-2);
  border-color: var(--edge2);
}
.hub-tile:active { transform: scale(.97); }
.hub-tile .ht-row {
  display: flex; align-items: center; gap: 7px;
  min-width: 0;
}
.hub-tile .ht-name {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  /* v11.0.1 UI-FIX: .1em tracking clipped "INK MARKET" to "INK MAR…"
     inside the 3-column phone grid — .04em keeps every door name whole. */
  letter-spacing: .04em;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hub-tile .ht-sub {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding-left: calc(var(--tap) * .94 + 9px);
}
.hub-tile.hot::after {          /* the burning door — a live event inside */
  content: '';
  position: absolute; top: 0; right: 0;
  width: 46px; height: 46px;
  background: radial-gradient(circle at 100% 0%, rgba(178, 60, 44, .38), rgba(178, 60, 44, 0) 70%);
  pointer-events: none;
  animation: hotGlow 2.4s ease-in-out infinite;
}
@keyframes hotGlow { 0%, 100% { opacity: .6; } 50% { opacity: 1; } }

/* legacy home bits (compat) */
.hub-link {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: var(--tap);
  padding: 0 16px;
  border-radius: 999px;
  font-weight: 700;
  color: var(--ink-soft);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
}
.hub-ver { font-size: var(--fs-xs); color: var(--ink-faint); letter-spacing: .18em; }
.home-foot {
  flex: none;
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  letter-spacing: .1em;
  padding: 0 4px;
}
.lobby-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap);
}
.lobby-stats .stat-cell { text-align: center; min-width: 0; overflow: hidden; }
/* v11.0.1 UI-FIX: long sovereign names / pact values used to force the
   stat grid past its track (the WEEKLY screen shipped 170px off-screen on
   phones — the whole right panel sat outside the viewport). Values now
   wrap inside their cell instead of widening the grid. */
.lobby-stats .stat-cell b { overflow-wrap: anywhere; display: block; }
/* v11.0.1 UI-FIX: daily / endless / weekly lobbies were hard-wired to a
   two-column inline grid (1.15fr 1fr) with no breakpoint — on portrait
   phones the right column overflowed the screen. One shared, responsive
   layout class replaces all three inline styles: side-by-side only when
   there is genuine width (landscape / tablets), stacked on phones. */
.lobby2 {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr;
  align-content: start;
  gap: var(--pad);
}
.lobby2 .lobby-col { display: flex; flex-direction: column; gap: var(--gap); min-height: auto; min-width: 0; }
@media (min-width: 700px) and (min-height: 480px) {
  .lobby2 { grid-template-columns: 1.15fr 1fr; }
}
/* ============================================================
   THE INK MARKET — four halls of trade
   Upgrade cards · hero portraits · relic vault · armory racks.
   One card language everywhere: paper card, medallion, pips,
   coin button. "can" = affordable (gold edge breathes).
   ============================================================ */
.shop-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap);
  align-content: start;
}
.shop-grid.cols2 { grid-template-columns: repeat(2, 1fr); }
.shop-card {
  position: relative;
  display: flex; flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: calc(var(--pad) * .85);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  transition: transform .18s var(--ease-spring), box-shadow .18s, border-color .25s;
  overflow: hidden;
}
.shop-card:hover { box-shadow: var(--shadow-2); }
.shop-card.can { border-color: rgba(163, 118, 42, .6); }
.shop-card.can::before {        /* gold ribbon tick on the affordable */
  content: '';
  position: absolute; top: 0; left: 0;
  width: 34px; height: 3px;
  border-radius: 0 0 4px 0;
  background: var(--gold-metal);
  opacity: .9;
}
.shop-card.hz { flex-direction: column; }
.shop-card .sc-medal {
  width: calc(var(--tap) * .9); height: calc(var(--tap) * .9);
  display: grid; place-items: center;
  border-radius: 30%;
  font-size: calc(var(--fs-l) * 1.15);
  color: var(--gold-deep);
  background:
    radial-gradient(90% 90% at 50% 26%, rgba(255, 253, 244, .95), rgba(255, 253, 244, 0) 70%),
    linear-gradient(180deg, #f6eeda, #eadfc0);
  border: 1px solid rgba(163, 118, 42, .5);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .14), inset 0 1px 0 rgba(255, 253, 244, .9);
  flex: none;
}
.shop-card .sc-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.shop-card .sc-name {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.shop-card .sc-desc {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  line-height: 1.45;
  /* v11.1.1 UI-FIX: the ad card's 3-line copy was cut to 2 lines on a
     3-column phone grid. Cards grow together; the market scrolls. */
  display: block;
  overflow: visible;
}
/* v11.0.1 UI-FIX: cards in one grid row used to end at different heights
   (variable desc lengths, optional mastery rows) so their buy buttons sat
   at different baselines — the market read as a broken masonry. Action
   rows now pin to the card bottom: every button in a row shares a baseline. */
.shop-card .price-btn { margin-top: auto; align-self: stretch; }
.shop-card.ad-card > .btn { margin-top: auto; }

/* the rewarded-ad treasure card — gold leaf in the market */
.shop-card.ad-card {
  border: 1px solid rgba(163, 118, 42, .7);
  background:
    radial-gradient(120% 140% at 50% -20%, rgba(240, 212, 136, .4), rgba(240, 212, 136, 0) 55%),
    var(--card-bg);
}
.shop-card.ad-card .sc-medal {
  color: #7a571d;
  background: radial-gradient(90% 90% at 50% 26%, rgba(255, 252, 240, .95), rgba(255, 252, 240, 0) 70%), var(--gold-metal);
  border-color: rgba(122, 87, 29, .7);
}
.shop-card.ad-card::before { width: 100%; }

/* ---- hall 2: heroes — portrait cards ---- */
.hero-grid4 {
  display: grid;
  /* v11: five heroes — auto-fit keeps the roster graceful at any width */
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--gap);
  align-content: start;
}
.hero-card {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  gap: 3px;
  padding: calc(var(--pad) * .8);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  transition: transform .18s var(--ease-spring), box-shadow .18s, border-color .25s;
  overflow: hidden;
}
.hero-card:hover { box-shadow: var(--shadow-2); }
.hero-card.selected {
  border-color: rgba(178, 60, 44, .65);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .12), var(--shadow-2);
}
.hero-card.selected::after {     /* seal of the wielded */
  content: 'WIELDED';
  position: absolute; top: 6px; right: 6px;
  padding: 2px 7px;
  border-radius: 4px;
  font-size: 8px;
  font-weight: 800;
  letter-spacing: .14em;
  color: #fff2ea;
  background: linear-gradient(180deg, #c34a37, var(--seal2));
  box-shadow: 0 2px 6px rgba(92, 27, 19, .4);
  pointer-events: none;
}
.hero-card.locked { opacity: .92; }
.hero-preview {
  width: clamp(64px, 14vmin, 92px);
  height: auto;
  aspect-ratio: 72 / 78;
  filter: drop-shadow(0 5px 10px rgba(66, 48, 22, .3));
  transition: transform .25s var(--ease-out);
}
.hero-card:hover .hero-preview { transform: translateY(-2px) scale(1.03); }
.hero-card.locked .hero-preview { filter: grayscale(.9) brightness(.92) drop-shadow(0 4px 8px rgba(66, 48, 22, .3)); }
.hero-name {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ink);
}
.hero-title {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  margin-top: -2px;
}
.hero-desc {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  line-height: 1.45;
  text-align: center;
  display: block;
  overflow: visible;
}
.hero-mastery {
  width: 100%;
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
}
.mastery-bar {
  display: block;
  height: 6px;
  margin-top: 3px;
  border-radius: 999px;
  background: var(--sunken);
  box-shadow: inset 0 1px 2px rgba(66, 48, 22, .22);
  overflow: hidden;
}
.mastery-bar > span {
  display: block; height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #8a6a24, var(--gold-bright));
  box-shadow: inset 0 1px 0 rgba(255, 246, 214, .6);
}
.hero-actions {
  display: flex; flex-wrap: wrap; gap: 6px;
  justify-content: center;
  margin-top: auto;
  padding-top: 5px;
  width: 100%;
}

/* ---- hall 3: relic vault — school chips + relic cards ---- */
.school-row {
  display: flex; flex-wrap: wrap; gap: 6px;
  flex: none;
}
.school-chip {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: calc(var(--tap) * .6);
  padding: 0 14px;
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ink-soft);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  transition: transform .16s var(--ease-spring), color .2s, border-color .2s, box-shadow .2s;
}
.school-chip .dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  box-shadow: inset 0 -1px 1px rgba(0, 0, 0, .3), 0 1px 0 rgba(255, 253, 244, .5);
}
.school-chip:hover { color: var(--ink); }
.school-chip.on {
  color: var(--ink);
  border-color: var(--edge2);
  background: linear-gradient(180deg, #fdf8ec, #f1e7ca);
  box-shadow: 0 2px 6px rgba(66, 48, 22, .2), inset 0 1px 0 rgba(255, 253, 244, .95);
}
.school-chip:active { transform: scale(.95); }
.set-line {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  line-height: 1.5;
  padding: 2px 2px;
}
.set-line b { color: var(--ink-soft); }
.relic-grid3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
  align-content: start;
}
.relic-card {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  gap: 3px;
  padding: calc(var(--pad) * .8);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  transition: transform .18s var(--ease-spring), box-shadow .18s, border-color .25s;
}
.relic-card:hover { box-shadow: var(--shadow-2); }
.relic-card.can { border-color: rgba(61, 100, 120, .6); }
.relic-card.locked { opacity: .88; }
.relic-card .rl-ico {
  width: calc(var(--tap) * .9); height: calc(var(--tap) * .9);
  display: grid; place-items: center;
  border-radius: 30%;
  font-size: calc(var(--fs-l) * 1.1);
  color: #31525f;
  background:
    radial-gradient(90% 90% at 50% 26%, rgba(255, 253, 244, .95), rgba(255, 253, 244, 0) 70%),
    linear-gradient(180deg, #f6eeda, #e6dcc0);
  border: 1px solid rgba(61, 100, 120, .45);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .14), inset 0 1px 0 rgba(255, 253, 244, .9);
}
.relic-card.locked .rl-ico {
  color: var(--ink-faint);
  border-color: var(--edge);
  background: var(--sunken);
  box-shadow: inset 0 2px 4px rgba(66, 48, 22, .16);
}
.relic-card .rl-name {
  font-family: var(--font-display);
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--ink);
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}
.relic-card .rl-desc {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  text-align: center;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.relic-card .rl-stage {
  letter-spacing: .14em;
  color: var(--cold);
  font-size: var(--fs-s);
}
.relic-card .off2 { color: var(--sunken2); }
.relic-card .price-btn { margin-top: auto; align-self: stretch; }

/* ============================================================
   THE ARMORY — seven weapon racks
   ============================================================ */
.wpn-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
  align-content: start;
}
.wpn-card {
  position: relative;
  display: flex; flex-direction: column;
  gap: 5px;
  padding: calc(var(--pad) * .8);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  transition: transform .18s var(--ease-spring), box-shadow .18s, border-color .25s;
  overflow: hidden;
}
.wpn-card:hover { box-shadow: var(--shadow-2); }
.wpn-card.equipped {
  border-color: rgba(178, 60, 44, .65);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .1), var(--shadow-2);
}
.wpn-card.locked { opacity: .88; }
.wpn-card.locked .wpn-ico { filter: grayscale(.85); }
.wpn-top {
  display: flex; align-items: center; gap: 9px;
  min-width: 0;
}
.wpn-ico {
  flex: none;
  width: calc(var(--tap) * .86); height: calc(var(--tap) * .86);
  display: grid; place-items: center;
  border-radius: 28%;
  font-size: calc(var(--fs-l) * 1.1);
  color: var(--gold-deep);
  background:
    radial-gradient(90% 90% at 50% 26%, rgba(255, 253, 244, .95), rgba(255, 253, 244, 0) 70%),
    linear-gradient(180deg, #f6eeda, #eadfc0);
  border: 1px solid rgba(163, 118, 42, .5);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .14), inset 0 1px 0 rgba(255, 253, 244, .9);
}
.wpn-name {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--ink);
  line-height: 1.2;
  /* v11.1.1 UI-FIX: "Vermillion Glaive" ellipsized in compact armory. */
  white-space: normal; overflow: visible; text-overflow: clip;
}
.wpn-eq {
  position: absolute; top: 7px; right: 7px;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: .18em;
  color: #fff2ea;
  background: linear-gradient(180deg, #c34a37, var(--seal2));
  border-radius: 4px;
  padding: 2px 7px;
  box-shadow: 0 2px 5px rgba(92, 27, 19, .35);
}
.wpn-stats {
  display: flex; flex-wrap: wrap; gap: 4px 12px;
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
.wpn-stat b { color: var(--ink); font-weight: 800; }
.wpn-bar {
  height: 6px;
  border-radius: 999px;
  background: var(--sunken);
  box-shadow: inset 0 1px 2px rgba(66, 48, 22, .2);
  overflow: hidden;
}
.wpn-bar > span {
  display: block; height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #8a6a24, var(--gold-bright));
}
.wpn-perk {
  font-size: var(--fs-xs);
  color: var(--jade);
  line-height: 1.4;
}
.wpn-desc {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.wpn-cost { font-size: var(--fs-xs); color: var(--ink-faint); letter-spacing: .06em; }
.wpn-actions {
  display: flex; flex-direction: column; gap: 6px;
  margin-top: auto;
  padding-top: 5px;
}
.wpn-actions > * { width: 100%; }

/* rebirth panel — the sigil hall's red corner */
.rebirth-panel {
  position: relative;
  display: flex; flex-direction: column; gap: 7px;
  padding: calc(var(--pad) * .95);
  border-radius: var(--r-m);
  background:
    radial-gradient(120% 150% at 50% -30%, rgba(178, 60, 44, .12), rgba(178, 60, 44, 0) 55%),
    var(--card-bg);
  border: 1px dashed rgba(178, 60, 44, .5);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.rebirth-title {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: .16em;
  color: var(--seal2);
}
.seal-text { color: var(--seal2); font-weight: 700; }
/* ============================================================
   THE SAGA — tomes, pages, tiers, pacts
   A bookshelf of fourteen tomes; each page a hand-inked node on
   the chapter ribbon. Tiers climb a ladder; pacts are oaths.
   ============================================================ */
.tome-grid {
  display: grid;
  /* 14 tomes — 7 columns lands them in exactly two rows on wide screens
     (the wide/short breakpoints below keep that 2-row fit). */
  grid-template-columns: repeat(7, 1fr);
  gap: var(--gap);
}
.tome-card {
  position: relative;
  display: flex; flex-direction: column;
  gap: 3px;
  padding: calc(var(--pad) * .75);
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  min-width: 0;
  overflow: hidden;
  transition: transform .18s var(--ease-spring), box-shadow .18s, border-color .2s;
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 30ms);
}
.tome-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); border-color: var(--edge2); }
.tome-card:active { transform: scale(.97); }
.tome-card.current {
  border-color: rgba(178, 60, 44, .7);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .12), var(--shadow-2);
}
.tome-card.locked { opacity: .55; filter: saturate(.5); }
.tome-card .tm-num {
  font-family: var(--font-display);
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: .18em;
  color: var(--gold-deep);
}
.tome-card .tm-name {
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--ink);
  line-height: 1.3;
  /* v11.1.1 UI-FIX: long tome names (The Verdant Scroll Garden,
     The Vermillion Deep) need a third line on a 7-column card. */
  display: block;
  overflow: visible;
}
.tome-card .tm-stars {
  font-size: var(--fs-xs);
  color: var(--gold);
  letter-spacing: .12em;
}
.tome-card .tm-seal {            /* the wax seal on the current tome */
  position: absolute; top: 6px; right: 6px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #d0604a, var(--seal2) 70%);
  box-shadow: 0 2px 4px rgba(92, 27, 19, .4), inset 0 1px 1px rgba(255, 226, 214, .5);
  animation: sealBreath 2.4s ease-in-out infinite;
}
@keyframes sealBreath {
  0%, 100% { box-shadow: 0 2px 4px rgba(92, 27, 19, .4), inset 0 1px 1px rgba(255, 226, 214, .5), 0 0 0 0 rgba(178, 60, 44, .35); }
  50% { box-shadow: 0 2px 4px rgba(92, 27, 19, .4), inset 0 1px 1px rgba(255, 226, 214, .5), 0 0 0 6px rgba(178, 60, 44, 0); }
}
.tome-card .tm-swash {
  position: absolute; bottom: -2px; left: 8%;
  width: 84%; height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, transparent, var(--edge2), transparent);
  opacity: .7;
}

/* pages — the ribbon of nodes */
.page-wrap {
  display: flex; align-items: stretch; gap: 6px;
  min-height: 0;
}
.page-node {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  min-height: clamp(44px, 10vmin, 64px);
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  transition: transform .16s var(--ease-spring), box-shadow .18s, border-color .2s;
  animation: cardIn .34s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 26ms);
  padding: 4px 6px;
}
.page-node:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.page-node:active { transform: scale(.96); }
/* v11.0.1 UI-FIX: the page title is rendered inside .page-detail (ui.js
   levels()), never inside .page-node — the old .page-node .page-name scope was
   dead. Plain class so the star-gated "SEALED" heading (also .page-name) is
   covered by the same rule. */
.page-name {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--ink);
  text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}
.page-node.done { border-color: rgba(71, 112, 62, .5); }
/* the node's own number carries the state for every browser — this is the
   static fallback, so the ribbon reads correctly without :has(). */
.page-node.done .n { color: #3a5c33; }
/* the title in .page-detail describes the SELECTED node, so its colour has to
   be driven by that node's classes. :has() = Chrome/WebView 105+; older
   WebViews drop this declaration only and keep the readable --ink base. */
.page-wrap:has(.page-node.done.sel) .page-detail .page-name { color: #3a5c33; }
.page-node.sel {
  border-color: rgba(178, 60, 44, .75);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .14), var(--shadow-2);
}
.page-node.locked { opacity: .5; filter: grayscale(.4); }
.page-node.boss {
  background:
    radial-gradient(120% 130% at 50% -20%, rgba(178, 60, 44, .14), rgba(178, 60, 44, 0) 55%),
    var(--card-bg);
}
.page-node.boss .n { color: var(--seal2); }
.page-wrap:has(.page-node.boss.sel) .page-detail .page-name { color: var(--seal2); }
.page-ribbon {
  flex: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  padding: 6px 10px;
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: clamp(72px, 16vw, 130px);
}
.page-detail, .page-info {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  text-align: center;
  line-height: 1.4;
}
.page-detail b, .page-info b { color: var(--ink-soft); }
.page-quote {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-s);
  color: var(--ink-soft);
  line-height: 1.5;
  padding: 4px 2px;
}
.page-launch { display: flex; flex: 1 1 auto; align-items: center; gap: 8px; margin-top: 0; min-width: 0; }

/* tier ladder — the difficulty climb */
.tier-ladder {
  display: flex; flex-direction: column; gap: 4px;
}
.tier-row {
  display: flex; align-items: center; gap: 10px;
  padding: 5px 12px;
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  transition: transform .16s var(--ease-spring), box-shadow .18s, border-color .2s;
  min-width: 0;
}
.tier-row:active { transform: scale(.98); }
.tier-row .tr-name {
  font-family: var(--font-display);
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--ink);
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-transform: uppercase;
}
.tier-row .tr-stars { color: var(--gold); letter-spacing: .12em; font-size: var(--fs-xs); }
.tier-row .tr-note { font-size: var(--fs-xs); color: var(--ink-faint); flex: none; }
.tier-row.sel {
  border-color: rgba(178, 60, 44, .7);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .12), var(--shadow-1);
}
.tier-row.locked { opacity: .5; }

/* omen row — the pact selection */
.omen-row {
  display: flex; flex-wrap: wrap; gap: 6px;
  align-items: center;
  padding: 7px 10px;
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
}
.omen-label {
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: .16em;
  color: var(--seal2);
  text-transform: uppercase;
  margin-right: 2px;
}
.omen-chip {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: calc(var(--tap) * .56);
  padding: 0 12px;
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  color: var(--ink-soft);
  background: linear-gradient(180deg, #fdf8ec, #f1e7ca);
  border: 1px solid var(--edge);
  box-shadow: 0 1px 3px rgba(66, 48, 22, .14), inset 0 1px 0 rgba(255, 253, 244, .9);
  transition: transform .16s var(--ease-spring), border-color .2s, color .2s, box-shadow .2s;
}
.omen-chip:hover { color: var(--ink); border-color: var(--edge2); }
.omen-chip:active { transform: scale(.94); }
.omen-chip small { font-weight: 800; color: var(--gold-deep); }
.omen-chip.none { opacity: .55; }

/* ============================================================
   DAILY / WEEKLY — the calendar leaves
   ============================================================ */
.daily-row, .done-row {
  display: flex; align-items: center; gap: 10px;
  padding: calc(var(--pad) * .7) var(--pad);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
}
.done-row { border-color: rgba(71, 112, 62, .45); }
.daily-empty {
  padding: calc(var(--pad) * .9);
  border-radius: var(--r-m);
  background: var(--sunken);
  box-shadow: inset 0 2px 5px rgba(66, 48, 22, .14);
  color: var(--ink-faint);
  font-size: var(--fs-s);
  text-align: center;
  font-family: var(--font-display);
  font-style: italic;
}

/* ============================================================
   CONTRACTS — the board of commissions
   ============================================================ */
.contract-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap);
  align-content: start;
}
.contract-card {
  position: relative;
  display: flex; flex-direction: column;
  gap: 4px;
  padding: calc(var(--pad) * .8);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  transition: box-shadow .18s, border-color .25s, opacity .25s;
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 30ms);
}
.contract-card:hover { box-shadow: var(--shadow-2); }
.contract-card.done { border-color: rgba(71, 112, 62, .55); }
.contract-card.claimed { opacity: .62; }
.contract-card.claimed::after {
  content: 'CLAIMED';
  position: absolute; top: 40%; left: 50%;
  transform: translate(-50%, -50%) rotate(-8deg);
  font-size: var(--fs-l);
  font-weight: 800;
  letter-spacing: .3em;
  color: rgba(71, 112, 62, .5);
  border: 2px solid rgba(71, 112, 62, .4);
  border-radius: 6px;
  padding: 2px 14px;
  background: rgba(253, 248, 236, .6);
  pointer-events: none;
}
.contract-top {
  display: flex; align-items: center; gap: 8px;
  min-width: 0;
}
.contract-ico {
  flex: none;
  width: calc(var(--tap) * .72); height: calc(var(--tap) * .72);
  display: grid; place-items: center;
  border-radius: 28%;
  font-size: var(--fs-l);
  color: var(--gold-deep);
  background: linear-gradient(180deg, #f6eeda, #eadfc0);
  border: 1px solid rgba(163, 118, 42, .5);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .14), inset 0 1px 0 rgba(255, 253, 244, .9);
}
.contract-text {
  font-size: var(--fs-s);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.35;
  min-width: 0;
}
.contract-scope {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  letter-spacing: .16em;
  text-transform: uppercase;
  margin-top: -1px;
}
.contract-reward {
  font-size: var(--fs-xs);
  color: var(--gold-deep);
  font-weight: 700;
  letter-spacing: .04em;
}
.contract-foot { margin-top: 2px; }

/* ============================================================
   THE INK ROAD — a folded map of tiers
   ============================================================ */
.road-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 6px;
}
.road-cell {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  min-height: clamp(40px, 9vmin, 58px);
  padding: 5px 4px;
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  transition: transform .16s var(--ease-spring), box-shadow .18s, border-color .2s;
  animation: cardIn .34s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 26ms);
}
.road-cell:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); }
.road-cell.next {
  border-color: rgba(178, 60, 44, .75);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .14), var(--shadow-2);
}
.road-cell.mile {
  background:
    radial-gradient(120% 130% at 50% -20%, rgba(163, 118, 42, .18), rgba(163, 118, 42, 0) 55%),
    var(--card-bg);
  border-color: rgba(163, 118, 42, .6);
}
.road-cell.done { border-color: rgba(71, 112, 62, .5); opacity: .9; }

/* ============================================================
   BOSS RUSH — the gauntlet ladder
   ============================================================ */
.rush-row {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 6px;
}
.rush-cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  min-height: clamp(38px, 8.5vmin, 54px);
  padding: 4px;
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--ink);
  text-align: center;
  min-width: 0;
  animation: cardIn .34s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 26ms);
}
.rush-cell.unknown { opacity: .45; filter: grayscale(.5); }
.rush-cell.next {
  border-color: rgba(178, 60, 44, .75);
  box-shadow: 0 0 0 3px rgba(178, 60, 44, .14), var(--shadow-1);
}
/* v11.0.1 UI-FIX: sovereign names inside the rush grid wrapped to
   3 ragged lines on narrow phones ("The Calligrapher" was the worst). Two
   clean lines max, ellipsis after. */
.rush-cell small {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
  line-height: 1.25;
  max-width: 100%;
}

/* season banner — the live event placard */
.season-banner {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  padding: calc(var(--pad) * .8) var(--pad);
  border-radius: var(--r-m);
  background:
    radial-gradient(120% 160% at 0% -30%, rgba(231, 188, 94, .3), rgba(231, 188, 94, 0) 55%),
    var(--card-bg);
  border: 1px solid rgba(163, 118, 42, .55);
  box-shadow: var(--shadow-2);
  overflow: hidden;
  min-width: 0;
}
.season-ico {
  flex: none;
  width: calc(var(--tap) * .9); height: calc(var(--tap) * .9);
  display: grid; place-items: center;
  border-radius: 30%;
  color: #7a571d;
  background: radial-gradient(90% 90% at 50% 26%, rgba(255, 252, 240, .95), rgba(255, 252, 240, 0) 70%), var(--gold-metal);
  border: 1px solid rgba(122, 87, 29, .7);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .16), inset 0 1px 0 rgba(255, 246, 214, .8);
}
.season-ico .ic, .season-ico svg { width: 52%; height: 52%; }
.season-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.season-name {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--ink);
}
.season-perk { font-size: var(--fs-xs); color: var(--ink-soft); line-height: 1.4; }
.season-days {
  margin-left: auto;
  flex: none;
  display: flex; flex-direction: column; align-items: center;
  padding: 4px 12px;
  border-radius: var(--r-s);
  background: var(--sunken);
  box-shadow: inset 0 2px 4px rgba(66, 48, 22, .14);
}
.season-days b { font-size: var(--fs-l); color: var(--seal2); font-variant-numeric: tabular-nums; }
.season-days span { font-size: 9px; letter-spacing: .16em; color: var(--ink-faint); text-transform: uppercase; }

/* ============================================================
   SETTINGS — the seven ledgers
   ============================================================ */
.set-groups {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap);
  align-content: start;
}
.set-group {
  position: relative;
  display: flex; flex-direction: column;
  gap: 6px;
  padding: calc(var(--pad) * .8) var(--pad);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 30ms);
}
.set-group-head {
  display: flex; align-items: center; gap: 9px;
  font-family: var(--font-display);
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: .2em;
  color: var(--ink);
  padding-bottom: 3px;
  border-bottom: 1px solid var(--edge);
}
.set-group.mini { grid-column: span 1; }
.set-group.danger {
  border-color: rgba(178, 60, 44, .5);
  background:
    radial-gradient(120% 150% at 100% -30%, rgba(178, 60, 44, .1), rgba(178, 60, 44, 0) 55%),
    var(--card-bg);
}
.row {
  display: flex; align-items: center; gap: 12px;
  padding: 5px 2px;
  border-radius: var(--r-s);
  min-width: 0;
}
.row + .row { border-top: 1px dashed rgba(214, 198, 158, .6); }
.row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.row-name {
  font-size: var(--fs-s);
  font-weight: 700;
  color: var(--ink);
  letter-spacing: .03em;
}
.row-desc {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  line-height: 1.45;
}
.row-ico {
  flex: none;
  width: calc(var(--tap) * .74); height: calc(var(--tap) * .74);
  display: grid; place-items: center;
  border-radius: 28%;
  font-size: var(--fs-m);
  color: var(--ink-soft);
  background: linear-gradient(180deg, #fdf8ec, #f1e7ca);
  border: 1px solid var(--edge);
}
.row-act { flex: none; }
.set-val {
  font-size: var(--fs-s);
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--sunken);
  box-shadow: inset 0 1px 3px rgba(66, 48, 22, .14);
  white-space: nowrap;
}
/* v11.0.1 UI-FIX: the recovered-save row stacks two small actions where other
   settings rows hold a single control — without this it fell back to inline
   boxes touching each other. Matches the .row-act / .row-gap control cluster. */
.rec-btns {
  flex: none;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: 6px;
  max-width: 52%;
}
/* the range slider — a brush track (volume) */
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: clamp(120px, 26vmin, 190px);
  height: 26px;
  background: transparent;
  cursor: pointer;
  flex: none;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 10px;
  border-radius: 999px;
  background: var(--sunken);
  box-shadow: inset 0 1px 3px rgba(66, 48, 22, .22), inset 0 -1px 0 rgba(255, 253, 244, .5);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 22px; height: 22px;
  margin-top: -6px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fdf8ec, #e8dabb);
  border: 1px solid var(--edge2);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .28), inset 0 1px 0 rgba(255, 253, 244, .9);
  transition: transform .15s var(--ease-spring);
}
input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.15); }
input[type="range"]::-moz-range-track {
  height: 10px; border-radius: 999px;
  background: var(--sunken);
  box-shadow: inset 0 1px 3px rgba(66, 48, 22, .22);
}
input[type="range"]::-moz-range-thumb {
  width: 20px; height: 20px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fdf8ec, #e8dabb);
  border: 1px solid var(--edge2);
  box-shadow: 0 2px 5px rgba(66, 48, 22, .28);
}

/* settings overlay (over pause) */
.settings-ov { z-index: 50; }

/* ============================================================
   ARCHIVES — medals, codex, chronicle, help
   ============================================================ */
.arch-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gap);
}
.arch-card {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px;
  min-height: calc(var(--tap) * 1.5);
  padding: calc(var(--pad) * .8);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  min-width: 0;
  transition: transform .18s var(--ease-spring), box-shadow .18s, border-color .2s;
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 30ms);
}
.arch-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-2); border-color: var(--edge2); }
.arch-card:active { transform: scale(.97); }
.arch-card .arch-name {
  font-family: var(--font-display);
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: .14em;
  color: var(--ink);
}
.arch-card .arch-sub { font-size: var(--fs-xs); color: var(--ink-faint); }

/* achievements — the medal wall */
.ach-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
  align-content: start;
}
.ach-cell {
  display: flex; align-items: center; gap: 9px;
  padding: calc(var(--pad) * .6) calc(var(--pad) * .8);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  transition: box-shadow .18s, border-color .25s;
  animation: cardIn .34s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 26ms);
}
.ach-cell:hover { box-shadow: var(--shadow-2); }
.ach-cell.unlocked {
  border-color: rgba(163, 118, 42, .6);
  background:
    radial-gradient(110% 140% at 0% -20%, rgba(231, 188, 94, .22), rgba(231, 188, 94, 0) 55%),
    var(--card-bg);
}
.ach-medal {
  flex: none;
  width: calc(var(--tap) * .76); height: calc(var(--tap) * .76);
  display: grid; place-items: center;
  border-radius: 50%;
  font-size: var(--fs-l);
  color: var(--ink-faint);
  background: var(--sunken);
  box-shadow: inset 0 2px 4px rgba(66, 48, 22, .16), inset 0 -1px 0 rgba(255, 253, 244, .5);
}
.ach-cell.unlocked .ach-medal {
  color: var(--gold-deep);
  background: radial-gradient(80% 80% at 50% 28%, rgba(255, 252, 240, .95), rgba(255, 252, 240, 0) 70%), var(--gold-metal);
  border: 1px solid rgba(122, 87, 29, .7);
  box-shadow: 0 2px 6px rgba(122, 87, 29, .3), inset 0 1px 0 rgba(255, 246, 214, .8);
}
.ach-main { flex: 1 1 auto; min-width: 0; }
.ach-name {
  font-size: var(--fs-s);
  font-weight: 700;
  color: var(--ink);
  line-height: 1.25;
  white-space: normal; overflow: visible; text-overflow: clip;
}
.ach-desc {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  line-height: 1.4;
  display: block;
  overflow: visible;
}
.ach-state {
  flex: none;
  font-size: var(--fs-xs);
  font-weight: 800;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.ach-cell.unlocked .ach-state { color: var(--jade); }

/* codex — the bestiary grid */
.codex-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--gap);
  align-content: start;
}
.codex-cell {
  display: flex; flex-direction: column; align-items: center;
  gap: 3px;
  padding: calc(var(--pad) * .6) 4px;
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  animation: cardIn .34s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 26ms);
}
.codex-cell.unknown { opacity: .6; }
.codex-ico {
  font-size: var(--fs-l);
  color: var(--ink-soft);
  line-height: 1.2;
}
.codex-cell.unknown .codex-ico { color: var(--ink-faint); }
.codex-name {
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink);
  text-align: center;
  line-height: 1.2;
  /* v11.1.1 UI-FIX: 8-column compact codex cut "Hatch Knight". Let the
     name take a second line; the grid row grows and the ledger scrolls. */
  white-space: normal; overflow: visible; text-overflow: clip;
  max-width: 100%;
}
.codex-sub {
  font-size: 9px;
  color: var(--ink-faint);
  letter-spacing: .04em;
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}

/* chronicle — stat ledgers */
.stat-cols {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
  align-content: start;
}
.stat-col {
  display: flex; flex-direction: column;
  gap: 6px;
  min-width: 0;
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 30ms);
}
.sec-label {
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .24em;
  color: var(--seal2);
  text-transform: uppercase;
  text-align: center;
  padding: 2px 0;
}
.stat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 5px;
}
.stat-cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  min-height: clamp(40px, 8.5vmin, 54px);
  padding: 5px 3px;
  border-radius: var(--r-s);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  text-align: center;
}
.stat-cell span {
  font-size: 9px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1.2;
  white-space: normal; overflow: visible; text-overflow: clip;
  max-width: 100%;
}
.stat-cell b {
  font-size: var(--fs-m);
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* help — the six strokes */
.help-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--gap);
  align-content: start;
}
.help-card {
  padding: calc(var(--pad) * .85);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow-1);
  min-width: 0;
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 30ms);
}
.help-card h3 {
  font-family: var(--font-display);
  font-size: var(--fs-s);
  font-weight: 700;
  letter-spacing: .18em;
  color: var(--seal2);
  margin-bottom: 5px;
}
.help-card p {
  font-size: var(--fs-s);
  color: var(--ink-soft);
  line-height: 1.6;
}
.help-card p b { color: var(--ink); }
.kbd-hint {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  padding: 1px 7px;
  border: 1px solid var(--edge);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--panel2);
  font-family: ui-monospace, Menlo, Consolas, monospace;
}

/* result ledger (pause / death) */
.result-stats {
  display: flex; flex-direction: column; gap: 3px;
  padding: calc(var(--pad) * .7) var(--pad);
  border-radius: var(--r-m);
  background: var(--sunken);
  box-shadow: inset 0 2px 5px rgba(66, 48, 22, .14);
}
.result-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px;
  font-size: var(--fs-s);
  color: var(--ink-soft);
}
.result-row b {
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.event-good { color: var(--jade); font-weight: 700; }
.event-bad { color: var(--seal2); font-weight: 700; }
/* ============================================================
   IN-RUN OVERLAYS — boons, events, pause, death
   Same paper language as the menus: the run never changes
   material when a sheet slides in. Logic untouched; skin new.
   ============================================================ */
.boon-row {
  display: flex; gap: var(--gap);
  justify-content: center;
  flex-wrap: wrap;
}
.boon-card {
  position: relative;
  flex: 1 1 0;
  min-width: clamp(120px, 24vmin, 180px);
  max-width: 220px;
  display: flex; flex-direction: column;
  gap: 4px;
  padding: calc(var(--pad) * .85);
  border-radius: var(--r-m);
  background: var(--card-bg);
  border: 1px solid var(--edge);
  border-top: 3px solid var(--r-common);
  box-shadow: var(--shadow-2);
  cursor: pointer;
  min-height: calc(var(--tap) * 1.7);
  overflow: hidden;
  transition: transform .18s var(--ease-spring), box-shadow .18s;
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 40ms);
}
.boon-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-3);
}
.boon-card:active { transform: scale(.96); }
.boon-card.r-common { border-top-color: var(--r-common); }
.boon-card.r-rare   { border-top-color: var(--r-rare); }
.boon-card.r-epic   { border-top-color: var(--r-epic); }
.boon-card.r-legend {
  border-top-color: var(--r-legend);
  background:
    radial-gradient(120% 150% at 50% -30%, rgba(231, 188, 94, .25), rgba(231, 188, 94, 0) 55%),
    var(--card-bg);
}
.boon-card.fusion {
  border: 1px solid rgba(95, 79, 149, .55);
  border-top: 3px solid var(--purple);
  background:
    radial-gradient(120% 150% at 50% -30%, rgba(95, 79, 149, .14), rgba(95, 79, 149, 0) 55%),
    var(--card-bg);
}
.boon-icon {
  font-size: calc(var(--fs-xl) * 1.2);
  line-height: 1.1;
  color: var(--ink);
}
.boon-name {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--ink);
}
.boon-rarity {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: .22em;
  text-transform: uppercase;
}
.boon-card.r-common .boon-rarity { color: var(--r-common); }
.boon-card.r-rare   .boon-rarity { color: var(--r-rare); }
.boon-card.r-epic   .boon-rarity { color: var(--r-epic); }
.boon-card.r-legend .boon-rarity { color: var(--r-legend); }
.boon-card.fusion   .boon-rarity { color: var(--purple); }
.boon-desc {
  font-size: var(--fs-xs);
  color: var(--ink-soft);
  line-height: 1.5;
}
.boon-lv {
  font-size: var(--fs-xs);
  font-weight: 800;
  color: var(--gold-deep);
  letter-spacing: .08em;
}
.fusion-cost {
  font-size: var(--fs-xs);
  color: var(--purple);
  font-weight: 700;
}

/* tutorial bubble — the margin note */
.tut-bubble {
  position: absolute;
  max-width: min(320px, calc(100vw - 52px));
  padding: 10px 16px;
  border-radius: var(--r-m);
  background: linear-gradient(180deg, #fdf8ec, #f6eeda);
  border: 1px solid var(--edge2);
  box-shadow: var(--shadow-2);
  font-size: var(--fs-s);
  color: var(--ink);
  line-height: 1.5;
  z-index: 42;
  pointer-events: none;
  animation: tutIn .4s var(--ease-spring) both;
}
.tut-bubble::before {         /* the margin tail */
  content: '';
  position: absolute;
  left: -8px; bottom: 14px;
  width: 14px; height: 14px;
  background: linear-gradient(135deg, #f6eeda 50%, transparent 50%);
  border-left: 1px solid var(--edge2);
  border-bottom: 1px solid var(--edge2);
  transform: rotate(45deg);
}
@keyframes tutIn {
  from { opacity: 0; transform: translate(10px, 8px); }
  to { opacity: 1; transform: none; }
}
.tut-step {
  display: inline-block;
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: .16em;
  color: var(--seal2);
  text-transform: uppercase;
  margin-bottom: 3px;
}

/* ============================================================
   HUD — ink instruments floating over the paper canvas
   Geometry & behavior identical to the shipped game; only the
   skin is refined (dark ink chips — the same instrument color
   the canvas itself uses for player/enemy ink).
   ============================================================ */
.hud-btn {
  position: relative;
  width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  border-radius: 50%;
  color: #f3ead2;
  /* tome-themed ink chip: the palette writes --hud-btn-bg/-bg2 per tome;
     the literal fallback is the exact pre-palette colour, so an absent
     token can never change the HUD. */
  background: linear-gradient(180deg, var(--hud-btn-bg, rgba(46, 37, 20, .88)), var(--hud-btn-bg2, rgba(28, 22, 11, .88)));
  border: 1px solid rgba(243, 234, 210, .22);
  box-shadow: 0 6px 16px rgba(20, 14, 6, .45), inset 0 1px 0 rgba(243, 234, 210, .18);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  transition: transform .14s var(--ease-spring);
}
.hud-btn:active { transform: scale(.93); }
#hud-pause {
  position: absolute;
  top: max(10px, env(safe-area-inset-top));
  left: max(12px, env(safe-area-inset-left));
  z-index: 31;
}
/* v11.0.4 UX-3 (defect 3) — the run's build, on screen for good.
   A pick used to be confirmed by a 3.45 s toast, and the room tip that lands
   in the same second replaced it, so "Harvest Hand" was never read. This is
   a read-out rebuilt from the player's own boons: nothing can overwrite it,
   and it is never a control (pointer-events: none). */
#hud-boons {
  position: absolute;
  top: calc(max(10px, env(safe-area-inset-top)) + var(--tap) + 8px);
  left: max(12px, env(safe-area-inset-left));
  z-index: 31;
  display: flex; flex-wrap: wrap; gap: 6px;
  max-width: min(56vw, 420px);
  pointer-events: none;
}
.boon-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(28, 22, 11, .84);
  border: 1px solid rgba(243, 234, 210, .24);
  color: #f3ead2;
  font-family: var(--font);
  font-size: var(--fs-xs);
  letter-spacing: .04em;
  line-height: 1.35;
  white-space: nowrap;
  box-shadow: 0 4px 12px rgba(20, 14, 6, .38);
  animation: boonChipIn .28s var(--ease-out) both;
}
.boon-chip > i { font-style: normal; color: var(--gold); font-size: 1.05em; }
.boon-chip > b { color: var(--gold); font-weight: 700; margin-left: 1px; }
.boon-chip.fusion { border-color: rgba(217, 169, 63, .62); color: #ffe9b8; }
.boon-chip.more { opacity: .72; }
@keyframes boonChipIn {
  from { opacity: 0; transform: translateY(-3px); }
  to { opacity: 1; transform: none; }
}
/* the same chips, listed in full, in the pause panel */
.pause-ov .modal { max-height: min(92vh, 96svh); }
.pause-ov .modal-head { padding: calc(var(--pad) * .85) var(--pad) calc(var(--pad) * .4); }
.pause-ov .modal-body { gap: 8px; }
.pause-boons {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
}
.pause-boons .pb-k {
  flex: 0 0 auto;
  font-family: var(--font);
  font-size: var(--fs-xs);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.pause-boons .pb-row { display: flex; flex-wrap: wrap; gap: 5px; }
.pause-boons .boon-chip { padding: 2px 8px; font-size: 10px; }
/* v11.0.4 UX-3: on a short landscape phone the pause card already had to
   scroll before the stats ended — the run's build must not be the thing that
   falls off it. Density wins over flavour here. */
@media (max-height: 480px) {
  .pause-ov .modal-head { padding: 10px var(--pad) 4px; }
  .pause-ov .modal-title { font-size: var(--fs-l); }
  .pause-ov .modal-sub { display: none; }
  .pause-ov .modal-body { padding: 8px var(--pad); gap: 6px; }
  .pause-ov .result-stats { padding: 6px 10px; gap: 1px; }
  .pause-ov .result-row { font-size: var(--fs-xs); }
  .pause-ov .modal-foot { gap: 6px; padding: 5px var(--pad) 7px; }
  .pause-ov .hint { display: none; }
  .pause-boons .boon-chip { padding: 2px 7px; font-size: 10px; }
}
#dash-btn, .hud-btn.dash {
  position: absolute;
  right: max(16px, env(safe-area-inset-right));
  bottom: max(18px, env(safe-area-inset-bottom));
  z-index: 31;
  flex-direction: column;
  gap: 0;
  width: calc(var(--tap) * 1.12);
  height: calc(var(--tap) * 1.12);
  color: #fff2ea;
  background:
    radial-gradient(110% 130% at 50% -20%, rgba(255, 205, 185, .3), rgba(255, 205, 185, 0) 55%),
    linear-gradient(180deg, #c34a37, var(--seal) 55%, #a23326);
  border: 1px solid rgba(255, 240, 228, .3);
  box-shadow: 0 8px 20px rgba(92, 27, 19, .5), inset 0 1px 0 rgba(255, 240, 228, .35);
}
.hud-btn.dash > span {
  font-size: calc(var(--fs-l) * 1.25);
  font-weight: 800;
  line-height: 1;
  text-shadow: 0 2px 2px rgba(92, 27, 19, .5);
}
.hud-btn.dash > small {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: .2em;
  text-transform: uppercase;
  opacity: .85;
}
.hud-btn.dash.pressed {
  transform: scale(.88);
  box-shadow: 0 4px 10px rgba(92, 27, 19, .5), inset 0 2px 6px rgba(92, 27, 19, .45);
}

/* joystick — the living brush handle */
#joy-base {
  position: absolute;
  left: max(18px, env(safe-area-inset-left));
  bottom: max(16px, env(safe-area-inset-bottom));
  width: calc(var(--tap) * 2.1);
  height: calc(var(--tap) * 2.1);
  opacity: .5;
  transition: opacity .25s ease-out;
  z-index: 31;
}
#joy-base.live { opacity: 1; }
#joy-ring {
  position: absolute; inset: 0;
  border-radius: 50%;
  border: 2px solid rgba(38, 31, 18, .5);
  background: radial-gradient(circle at 50% 42%, rgba(253, 248, 236, .16), rgba(253, 248, 236, .04) 62%, rgba(38, 31, 18, .1));
  box-shadow: 0 6px 18px rgba(20, 14, 6, .28), inset 0 2px 6px rgba(20, 14, 6, .14);
}
#joy-ring::before, #joy-ring::after {   /* compass ticks */
  content: '';
  position: absolute;
  background: rgba(38, 31, 18, .4);
  border-radius: 2px;
}
#joy-ring::before { left: 50%; top: 7px; bottom: 7px; width: 1.5px; transform: translateX(-50%); }
#joy-ring::after  { top: 50%; left: 7px; right: 7px; height: 1.5px; transform: translateY(-50%); }
#joy-base.live #joy-ring {
  border-color: rgba(178, 60, 44, .8);
  box-shadow: 0 0 0 3px rgba(253, 248, 236, .55), 0 0 0 6px rgba(178, 60, 44, .18), 0 10px 24px rgba(92, 27, 19, .3), inset 0 0 20px rgba(178, 60, 44, .12);
}
#joy-base.live #joy-ring::before { background: rgba(178, 60, 44, .4); }
#joy-knob {
  position: absolute;
  left: 50%; top: 50%;
  width: 38%; height: 38%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: linear-gradient(180deg, #fdf8ec, #ddd0aa);
  border: 1px solid rgba(122, 87, 29, .55);
  box-shadow: 0 5px 12px rgba(20, 14, 6, .4), inset 0 2px 0 rgba(255, 253, 244, .95), inset 0 -3px 6px rgba(122, 87, 29, .25);
}
#joy-base.live #joy-knob {
  background: linear-gradient(180deg, #f6e7c8, #e3c184);
  border-color: rgba(122, 87, 29, .85);
  box-shadow: 0 6px 16px rgba(92, 27, 19, .45), inset 0 2px 0 rgba(255, 250, 230, .95), inset 0 -3px 8px rgba(122, 87, 29, .4);
}
#joy-hint {
  position: absolute;
  left: max(18px, env(safe-area-inset-left));
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + var(--tap) * 2.1 + 10px);
  max-width: min(56vw, 230px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-xs);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(38, 31, 18, .75);
  background: rgba(253, 248, 236, .6);
  padding: 3px 12px;
  border-radius: 999px;
  border: 1px solid rgba(38, 31, 18, .25);
  pointer-events: none;
  animation: joyHintFloat 2.2s ease-in-out infinite;
  z-index: 31;
}
@keyframes joyHintFloat {
  0%, 100% { transform: translateY(0); opacity: .8; }
  50% { transform: translateY(-4px); opacity: 1; }
}

/* ============================================================
   RESPONSIVE — every screen still fits with ZERO scroll
   ============================================================ */
@media (max-height: 420px) {
  .home-side { flex: 0 0 clamp(132px, 22%, 200px); }
  .hero-panel canvas { max-height: 76%; max-width: 90%; }
  .goal-card { padding: 6px 10px; }
  .goal-s { display: none; }
  .goal-t { font-size: var(--fs-s); }
  .cta-continue { min-height: clamp(46px, 11vmin, 64px); }
  .tome-grid { grid-template-columns: repeat(7, 1fr); }
  /* 14 tomes ÷ 7 → two even rows (7 + 7) on short landscape too */
  .tome-card { padding: calc(var(--pad) * .5); gap: 2px; }
  .ach-grid { grid-template-columns: repeat(4, 1fr); }
  .shop-grid, .relic-grid3, .wpn-grid { grid-template-columns: repeat(3, 1fr); }
  .hero-grid4 { grid-template-columns: repeat(5, 1fr); }
  .stat-cols { grid-template-columns: repeat(4, 1fr); }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .contract-grid { grid-template-columns: repeat(4, 1fr); }
  .codex-grid { grid-template-columns: repeat(8, 1fr); }
  .help-list { grid-template-columns: repeat(3, 1fr); }
  .sc-title small { display: none; }
}
@media (max-width: 760px) {
  .home-side { flex-basis: 30%; }
  .tile-grid { grid-template-columns: repeat(3, 1fr); }
  .tome-grid { grid-template-columns: repeat(4, 1fr); }
  .codex-grid { grid-template-columns: repeat(4, 1fr); }
  .ach-grid { grid-template-columns: repeat(2, 1fr); }
  .shop-grid, .relic-grid3, .wpn-grid { grid-template-columns: repeat(2, 1fr); }
  .hero-grid4 { grid-template-columns: repeat(2, 1fr); }
  .stat-cols { grid-template-columns: 1fr; }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .contract-grid { grid-template-columns: 1fr; }
  .help-list { grid-template-columns: 1fr 1fr; }
  .lobby-stats { grid-template-columns: repeat(2, 1fr); }
  .boon-row { flex-wrap: wrap; }
  .boon-card { max-width: 46%; }
  .live-strip { flex-wrap: wrap; }
  .live-pill { flex: 1 1 44%; }
  .set-groups { grid-template-columns: 1fr; }
  .arch-grid { grid-template-columns: repeat(2, 1fr); }
}
/* very narrow phones — strip pills stack 2×2, home tiles 2 columns */
@media (max-width: 560px) {
  .tile-grid { grid-template-columns: repeat(2, 1fr); }
  .home-side { display: none; }   /* the portrait lives on in the HEROES hall */
  .live-pill { flex: 1 1 40%; }
  .tome-card { padding: calc(var(--pad) * .6); gap: 2px; }
}

/* 14 tomes on short-but-not-tiny windows (a resized desktop window, a split
   screen): at ≤760px wide the 4-column rule above would need four card rows
   and push the last cards under the fold. Five columns land 5 + 5 + 4 in three
   rows, so the ledger stays scroll-free from 480 up to 660 tall — below 480
   the compact block's seven columns take over. */
@media (max-width: 760px) and (max-height: 660px) {
  .tome-grid { grid-template-columns: repeat(5, 1fr); }
  .tome-card { padding: calc(var(--pad) * .45); gap: 1px; }
  /* the cards are ~75px wide here: keep the label and the stars on one line so
     a wrapped line can't inflate every row of the 3-row layout */
  .tome-card .tm-num { font-size: var(--fs-xs); letter-spacing: .06em; }
  .tome-card .tm-stars { white-space: nowrap; overflow: hidden; }
}

/* compact mode — short landscape phones (≤820×480) */
@media (max-width: 820px) and (max-height: 480px) {
  .sc-body { padding: calc(var(--pad) * .6); gap: calc(var(--gap) * .7); }
  .sc-head { padding: calc(var(--pad) * .5) var(--pad); }
  .sc-foot { padding: calc(var(--pad) * .5) var(--pad); }
  .hint { font-size: var(--fs-xs); }

  .tome-grid { grid-template-columns: repeat(7, 1fr) !important; }
  .tome-card { padding: calc(var(--pad) * .5); gap: 2px; }

  .wpn-card { padding: calc(var(--pad) * .6); gap: 3px; }
  .wpn-desc, .wpn-perk { display: none; }
  .wpn-stats { gap: 2px; margin: 1px 0; }
  .wpn-grid { grid-template-columns: repeat(3, 1fr) !important; grid-auto-rows: min-content; }
  .hero-preview { width: 44px; }
  .hero-card { padding: calc(var(--pad) * .5); gap: 2px; }
  .hero-desc { display: none; }
  .hero-title { font-size: var(--fs-xs); }
  .hero-grid4 { grid-template-columns: repeat(5, 1fr) !important; }

  .page-quote { display: none; }
  .tier-row { padding: 4px 10px; }
  .tier-ladder { gap: 3px; }
  .omen-chip small { display: inline; }
  .omen-row { padding: 5px 10px; }
  .page-launch { margin-top: 2px; }
  .page-ribbon { flex-basis: clamp(52px, 10vw, 84px); }
  .page-node { min-height: clamp(38px, 8vmin, 54px); }

  .shop-grid { grid-template-columns: repeat(3, 1fr) !important; }
  .shop-card { padding: calc(var(--pad) * .6); gap: 3px; }
  .relic-grid3 { grid-template-columns: repeat(3, 1fr) !important; }
  .relic-card { padding: calc(var(--pad) * .55); gap: 2px; }
  .school-row { gap: 4px; }
  .school-chip { padding: 4px 9px; }
  .set-line { display: none; }
  .shop-grid .hint, .pane .hint { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

  .codex-grid { grid-template-columns: repeat(8, 1fr) !important; }
  .ach-grid { grid-template-columns: repeat(4, 1fr) !important; }
  .ach-cell { padding: calc(var(--pad) * .45) calc(var(--pad) * .6); }
  .stat-cols { grid-template-columns: repeat(4, 1fr) !important; }
  .stat-grid { grid-template-columns: repeat(2, 1fr) !important; }
  .stat-cell { padding: calc(var(--pad) * .5) 4px; min-height: 40px; }
  .contract-grid { grid-template-columns: repeat(4, 1fr) !important; }
  .contract-card { min-height: 74px; padding: calc(var(--pad) * .6); }

  .boon-card { padding: calc(var(--pad) * .7); gap: 3px; }
  .modal-body { padding: calc(var(--pad) * .7); }

  .hub-tile { padding: calc(var(--pad) * .5); gap: 2px; }
  .hub-tile .ic-chip { width: calc(var(--tap) * .8); height: calc(var(--tap) * .8); }
  .goal-card { padding: calc(var(--pad) * .5) var(--pad); }
  /* v11.1.1 UI-FIX: a 2-line clamp still cut the tutorial sentence at
     800×360 / 780×360 / 640×360. In compact landscape the card shows the
     kicker and title only; the full sentence lives in the tutorial. */
  .goal-s { display: none; }
  .live-pill { padding: 5px 9px; }

  .road-cell { min-height: 34px; }
  .stat-cell { min-height: 40px; }
  .lobby-stats { grid-template-columns: repeat(4, 1fr) !important; }
  .rush-cell { padding: 3px 1px; }

  .row { padding: calc(var(--pad) * .55) var(--pad); gap: 8px; }
  .row-ico { width: calc(var(--tap) * .72); height: calc(var(--tap) * .72); font-size: var(--fs-m); }
  .help-list { grid-template-columns: repeat(3, 1fr) !important; }
  .help-card { padding: calc(var(--pad) * .6); }
  .arch-grid { grid-template-columns: repeat(2, 1fr) !important; }
  .arch-card { min-height: 64px; padding: calc(var(--pad) * .7); }
}

.set-group .row .btn.small { flex-shrink: 0; }

/* settings/data rows: the pill is the last item in a flex row; on
   853×480 its two longest labels (AD CHOICES, READ POLICY) were 9px
   short. Tighter padding + tracking buys the space without shrinking
   the font or wrapping the label. */
@media (max-width: 900px) {
  .set-group .row .btn.small { padding: 0 8px; gap: 4px; letter-spacing: .04em; font-size: 11px; }
}

/* narrow-but-tall landscape (foldable inner, a 700–800px split
   window): the hero rail eats the width the six doors need. Hide it;
   the portrait and goal remain one tap away in HEROES / the tome map.
   Kept above the short-landscape rules so phone landscape (<=500 tall)
   keeps the rail. */
@media (max-width: 800px) and (min-height: 500px) {
  .home-side { display: none; }
}

/* sub-500px-wide windows (rare 480×320-class panels): the tome
   progress line and shop action pills need a little more room. */
@media (max-width: 500px) {
  .tm-prog { font-size: 8px; letter-spacing: 0; }
  .shop-card .btn { font-size: 10px; padding: 0 8px; }
}

/* short landscape: four pills in one row still need to be readable
   without truncating the value (853×480 and 915×412 are the tight cases) */
@media (max-height: 520px) {
  .live-pill { font-size: 10px; padding: 0 8px; gap: 5px; }
  .live-pill.live { padding-left: 15px; }
}

/* narrow phone: recover the 1–2px that ellipsized INK MARKET */
@media (max-width: 700px) {
  .hub-tile .ht-row { gap: 5px; }
  .hub-tile .ht-name { letter-spacing: 0; }
}

/* ultra-compact (≤700×420) */
@media (max-width: 700px) and (max-height: 420px) {
  /* v11.1.1 UI-FIX: keep the label. At ≤700px the strip is already 2×2
     (flex: 1 1 44% from the 760px rule), so DAILY / CONTRACTS / ROAD /
     WEEKLY fit beside their value. Hiding the label left only "open",
     "0/3", "tier 0/100", "week 44" — readable to the code, not the player. */
  .live-pill { padding: 5px 8px; }
  .wpn-actions { flex-direction: row !important; flex-wrap: nowrap; gap: 4px; }
  .wpn-actions > * { flex: 1 1 0; min-height: 28px; padding: 0 4px; font-size: var(--fs-xs); }
  .wpn-card { gap: 2px; }
  .ach-cell { padding: 4px 8px; gap: 4px; }
  .ach-medal { font-size: var(--fs-l); }
  .pager { margin-top: 2px; }
  .wpn-stats { display: none; }
  .wpn-top { gap: 4px; }
  .wpn-name { font-size: var(--fs-s); }
  .ach-desc { display: block; overflow: visible; }
  .ach-state { font-size: var(--fs-xs); }
  .tab-pill { padding: 4px 9px; font-size: var(--fs-xs); }
  .set-groups { grid-template-columns: 1fr; }
  .set-group { padding: calc(var(--pad) * .5) calc(var(--pad) * .8); gap: 4px; }
}

/* ============================================================
   MOTION — the choreography layer
   Staggered card entrances (transform/opacity only — GPU cheap);
   progress bars fill from the left; the CTA breathes.
   ============================================================ */
.hub-tile, .shop-card, .relic-card, .hero-card, .wpn-card, .tome-card,
.contract-card, .ach-cell, .codex-cell, .road-cell, .help-card, .stat-col,
.goal-card, .rebirth-panel, .season-banner, .set-group, .arch-card,
.live-pill, .cta-continue {
  animation: cardIn .38s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 30ms);
}
@keyframes cardIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.stat-cell, .rush-cell {
  animation: cardIn .34s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 26ms);
}
.btn, .price-btn, .hub-link, .sc-back, .pg-btn {
  animation: cardIn .28s var(--ease-out) backwards;
  animation-delay: calc(min(var(--i, 0), 12) * 20ms);
}
@keyframes popin { from { opacity: 0; transform: scale(.95); } to { opacity: 1; transform: none; } }

/* progress bars fill from the left */
.bar > span:first-child, .bar.slim > span:first-child, .mastery-bar span, .wpn-bar span {
  transform-origin: left center;
  animation: barIn .6s var(--ease-out) .1s backwards;
}
@keyframes barIn { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ultra-compact screens: motion budget trimmed, no layout change */
@media (max-width: 700px) and (max-height: 420px) {
  .hub-tile, .shop-card, .relic-card, .hero-card, .wpn-card, .tome-card,
  .contract-card, .ach-cell, .codex-cell, .road-cell, .help-card, .stat-col,
  .goal-card, .rebirth-panel, .season-banner, .set-group, .arch-card,
  .stat-cell, .rush-cell, .btn, .price-btn, .hub-link, .sc-back, .pg-btn,
  .live-pill, .cta-continue {
    animation-duration: .24s;
    animation-delay: calc(min(var(--i, 0), 8) * 16ms);
  }
}

/* ============================================================
   A11Y & FINISH
   ============================================================ */
/* visible focus for keyboard & D-pad users — seal ring on paper */
button:focus-visible, .hub-tile:focus-visible, .page-node:focus-visible,
.tier-row:focus-visible, .school-chip:focus-visible, .boon-card:focus-visible,
.row:focus-visible, .codex-cell:focus-visible, .hero-panel:focus-visible,
.live-pill:focus-visible, .arch-card:focus-visible, .tome-card:focus-visible,
.cta-continue:focus-visible, .switch:focus-visible, .segc button:focus-visible {
  outline: 2px solid var(--seal);
  outline-offset: 2px;
}

/* reduced motion kills the choreography — accessibility first */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  .screen, .modal, .toast,
  .hub-tile, .shop-card, .relic-card, .hero-card, .wpn-card, .tome-card,
  .contract-card, .ach-cell, .codex-cell, .road-cell, .help-card, .stat-col,
  .goal-card, .rebirth-panel, .season-banner, .set-group, .arch-card,
  .stat-cell, .rush-cell, .btn, .price-btn, .hub-link, .sc-back, .pg-btn,
  .bar > span:first-child, .bar.slim > span:first-child, .mastery-bar span,
  .wpn-bar span, .live-pill, .cta-continue, .hub-tile.hot::after,
  .tome-card.current .tm-seal, .btn.claim-pulse, .btn.primary::after,
  .cta-continue::after, .hero-panel::before, .btn.gold::after,
  .badge, .chip.tick, #joy-hint {
    animation: none !important;
    animation-delay: 0s !important;
  }
}
/* ============================================================
   SUPPLEMENT — content-detail classes carried by template
   strings (tome progress, page tags, daily boards, road tiers,
   victory ledgers, archives labels).
   ============================================================ */
.arch-name {
  font-family: var(--font-display);
  font-size: var(--fs-m);
  font-weight: 700;
  letter-spacing: .14em;
  color: var(--ink);
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}
.arch-sub {
  font-size: var(--fs-xs);
  color: var(--ink-faint);
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}

/* tome card progress line */
.tm-prog {
  font-size: 9px;
  letter-spacing: .06em;
  color: var(--ink-faint);
  line-height: 1.4;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tome-card.locked .tm-prog { color: var(--seal2); }

/* page node internals */
.page-node .n, .page-node .tag {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: .14em;
  color: var(--ink-faint);
  text-transform: uppercase;
}
.page-node .n { color: var(--ink-soft); font-size: var(--fs-s); letter-spacing: .06em; }
.page-node.boss .tag { color: var(--seal2); }

/* daily leaderboard row internals */
.rank {
  flex: none;
  font-size: var(--fs-s);
  font-weight: 800;
  color: var(--gold-deep);
  font-variant-numeric: tabular-nums;
  min-width: 2.4em;
}
.score {
  margin-left: auto;
  font-size: var(--fs-s);
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.daily-row { font-size: var(--fs-s); color: var(--ink-soft); }
.daily-row > span:nth-child(2) { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* road cell internals */
.road-t {
  font-size: var(--fs-xs);
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.road-cell.next .road-t { color: var(--seal2); }
.road-g {
  font-size: calc(var(--fs-m) * 1.15);
  line-height: 1.1;
  color: var(--gold-deep);
}
.road-cell.done .road-g { opacity: .75; }

/* muted inline (star remainders, notes) */
.dim { color: var(--ink-faint); opacity: .85; }
.off { color: var(--ink-faint); opacity: .5; letter-spacing: .1em; }
.lv { color: var(--gold-deep); font-weight: 800; }

/* pause overlay guard (behavioral class — kept for parity) */
.pause-ov { z-index: 45; }

/* generic utility classes still referenced by older routes */
.tag { letter-spacing: .12em; text-transform: uppercase; }

/* ============================================================================
   v11.1 NATIVE PASS — appended layer
   Everything below is additive. No rule above was deleted, so any rollback is
   a matter of removing this block.
   ============================================================================ */

/* --- B2 · Sepia Catacombs / page ribbon -----------------------------------
   The 8 .page-node children are authored for a ROW (flex: 1 1 0 = equal
   widths) but .page-ribbon was flex-direction: column, so 8 × 44 px stacked
   into ~385 px inside a 200–280 px body and pushed the CTA off screen.
   Ribbon becomes a full-width row, detail stacks underneath.               */
.page-wrap { flex-direction: column; gap: 8px; }
.page-ribbon {
  flex-direction: row;
  align-items: stretch;
  justify-content: center;
  width: 100%;
  min-width: 0;
  gap: 4px;
  padding: 5px;
  overflow: hidden;
}
.page-ribbon .page-node { min-height: clamp(38px, 8.5vmin, 56px); }
.page-ribbon .page-node .tag { font-size: 8.5px; letter-spacing: .04em; }
@media (max-width: 720px) {
  .page-ribbon .page-node .tag { display: none; }
  .page-ribbon .page-node .n { font-size: 15px; }
}

/* --- B3 · Ink Road pager --------------------------------------------------- */
.road-pager {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; width: 100%; margin: 2px 0 6px;
}
.road-pager-label {
  flex: 1 1 auto; text-align: center;
  font-size: var(--fs-xs); letter-spacing: .12em;
  color: var(--ink-faint); font-weight: 700;
}
@media (min-width: 760px) {
  .road-grid { grid-template-columns: repeat(8, 1fr); }
}

/* --- B7 · Inferno affix banner --------------------------------------------
   The weekly affix used to render at --fs-xs in --ink-faint (~10 px on a
   near-paper background). It is now a real banner.                          */
.affix-banner {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px; padding: 6px 10px;
  border-radius: var(--r-s);
  border: 1px solid rgba(178, 60, 44, .45);
  border-left: 3px solid var(--seal2);
  background: linear-gradient(90deg, rgba(178, 60, 44, .10), rgba(178, 60, 44, .02));
  text-align: left;
}
.affix-ico { font-size: 17px; line-height: 1; color: var(--seal2); }
.affix-main { display: flex; flex-direction: column; min-width: 0; }
.affix-main b {
  font-size: var(--fs-s); letter-spacing: .10em; color: var(--ink);
  text-transform: uppercase;
}
.affix-main small { font-size: var(--fs-xs); color: var(--ink-soft); }

/* B7 · the subtitle was hidden on short viewports by @media 3082.
   v11.1 B1: the override must match the SAME condition class as the hide
   rule, or it never applies. A landscape phone is ~800-950 wide and
   340-450 tall: (max-width:760px) never matched it while
   (max-height:420px) did. Keyed on height instead, and widened to 480px so
   it covers 420px-and-under phones plus taller short landscapes. */
@media (max-height: 480px) {
  .sc-title small {
    display: block;
    font-size: var(--fs-xs);
    line-height: 1.2;
    max-height: 2.4em;
    overflow: hidden;
  }
}

/* --- B5 · touch devices must never inherit desktop hover states ----------- */
@media (hover: none) {
  .page-node:hover, .road-cell:hover, .boon-card:hover, .tome-card:hover,
  .hub-tile:hover, .wpn-card:hover, .hero-card:hover, .relic-card:hover,
  .btn:hover, .tile:hover, .shop-item:hover, .ach-card:hover {
    transform: none !important;
  }
}
@media (hover: hover) {
  .page-node:hover { transform: translateY(-2px); }
}

/* --- B5 · notch / gesture bar safe area ----------------------------------- */
#fit-root {
  padding: env(safe-area-inset-top, 0px)
           calc(env(safe-area-inset-right, 0px) + 2px)
           env(safe-area-inset-bottom, 0px)
           calc(env(safe-area-inset-left, 0px) + 2px);
}
#hud-root {
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* v11.1.1 UI-FIX — last-word override for sub-500px panels.
   .tm-prog is declared after the responsive block, so this must come last. */
@media (max-width: 500px) {
  .tm-prog { font-size: 8px; letter-spacing: 0; }
}

/* v11.1.1 UI-FIX — hero roster on a 640px-wide phone: five columns
   crushed every title/description. Three columns keep the text whole;
   the roster scrolls. */
@media (max-width: 740px) {
  .hero-grid4 { grid-template-columns: repeat(3, 1fr) !important; }
}

/* v11.1.1 UI-FIX — compact modal pass.
   640×360: the boon card row was shrinking inside the modal body (card
   descriptions cut mid-sentence) and the death modal's stats overflowed
   behind its footer. Give the modal the available height, stop the boon
   row from shrinking, and keep the death hints out of the compact card
   (the salvage line is a stats row now). */
@media (max-height: 420px) {
  .modal { max-height: min(94vh, 96svh); }
  .boon-row { flex-shrink: 0; }
  .death-hint { display: none; }
}

/* v11.1.1 UI-FIX — boon cards are a column flex; their text children
   were shrinking to the card's stretched row height, cutting descriptions
   mid-sentence. Keep every child at its intrinsic height; the modal body
   scrolls if the row is taller than the sheet. */
.boon-card > * { flex-shrink: 0; }
