/* ==========================================================================
   THE PORTAL — Round 1 Investor Web Portal
   VISUAL SYSTEM v2  (Visual Experience Rebuild Brief v0.1.4)

   North star: an institutional investor document, an architectural
   publication, and a high-end product story converging into one authored
   web experience.

   TWO MATERIALS, not two themes:
     INK    the field / the space / the product stage      (night, physical)
     PAPER  the document / the argument / the evidence     (archival, printed)
   Materials are assigned per chapter for semantic reasons, the way an
   architectural monograph alternates photographic plates with text pages.
   Every component is written once against material-neutral tokens.

   ? and ! are NOT font glyphs and NOT icons. They are drawn architectural
   marks (assets: <symbol id="mark-q"> / <symbol id="mark-b">) used at
   monumental scale as signage, and at small scale as state nodes.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts
   SIL Open Font License 1.1 — redistribution and subsetting permitted.
   Subset WOFF2 is bundled so investor-side rendering does not depend on
   locally installed faces. Local names are tried first to avoid re-download. */
@font-face {
  font-family: 'Portal Sans';
  src: local('Pretendard'), local('Pretendard Regular'),
       url('../fonts/pretendard-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Portal Sans';
  src: local('Pretendard Medium'),
       url('../fonts/pretendard-500.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Portal Sans';
  src: local('Pretendard SemiBold'),
       url('../fonts/pretendard-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Portal Sans';
  src: local('Pretendard Bold'),
       url('../fonts/pretendard-700.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Portal Serif';
  src: local('Noto Serif KR Light'),
       url('../fonts/notoserifkr-300.woff2') format('woff2');
  font-weight: 300; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Portal Serif';
  src: local('Noto Serif KR Medium'),
       url('../fonts/notoserifkr-500.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Portal Serif';
  src: local('Noto Serif KR SemiBold'),
       url('../fonts/notoserifkr-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}

/* ---------------------------------------------------------------- tokens */
:root {
  --serif: 'Portal Serif', 'Noto Serif KR', 'Nanum Myeongjo', 'Apple SD Gothic Neo', serif;
  --sans:  'Portal Sans', Pretendard, -apple-system, 'Apple SD Gothic Neo',
           'Malgun Gothic', 'Noto Sans KR', sans-serif;

  /* the single inherited accent: the ? mark. never used as ornament. */
  --mark-ink:   #63d6b0;
  --mark-paper: #0b6b53;

  /* architectural rhythm */
  --gutter:  clamp(1.25rem, 4.2vw, 4.5rem);
  --measure: 1440px;
  --col:     minmax(0, 1fr);

  /* chapter pacing — orientation / idea / depth / release */
  --pad-open:    clamp(4.5rem, 9vw, 9rem);
  --pad-close:   clamp(5rem, 11vw, 12rem);
  --gap-idea:    clamp(2.5rem, 5.5vw, 5rem);
  --gap-depth:   clamp(3.5rem, 7vw, 7rem);

  /* S1-S5 semantic spacing roles (repair v0.1.1). S4/S5 are not new values --
     they alias the --gap-idea/--gap-depth this file already used at every
     idea-to-idea and scene-to-scene boundary (22 call sites before this
     repair); S1-S3 are new, filling in the finer rhythm that previously ran
     on ~35 one-off inline rem values (0.5-1.8rem, clustered 1.2-1.6rem for
     what was structurally one relation: block-to-qualifier). */
  --s1: 0.56rem;   /* MICRO   label <-> value */
  --s2: 1rem;      /* COPY    paragraph <-> paragraph */
  --s3: 1.3rem;    /* COMPONENT  heading/body <-> evidence/qualifier */
  --s4: var(--gap-idea);   /* IDEA   one argument <-> next */
  --s5: var(--gap-depth);  /* SCENE  one narrative scene <-> next */

  /* motion — mass and spring, never linear */
  --e-out:   cubic-bezier(0.16, 1, 0.30, 1);      /* settle */
  --e-inout: cubic-bezier(0.77, 0, 0.175, 1);     /* transform */
  --e-soft:  cubic-bezier(0.33, 1, 0.68, 1);
  --t-fast:  0.34s;
  --t-base:  0.62s;
  --t-slow:  0.72s;

  --r-xs: 2px;
  --r-sm: 3px;
  --r-md: 5px;

  /* SCREEN FRAME (repair v0.1.2 - PART A).
     One authored Screen = one complete investor thought = one settle frame.
     --nav-h is written by portal.js from the real measured nav, so the
     settle anchor, scroll-padding and frame height can never disagree. */

  /* ================================================ T0-T6 TYPE ROLES ====
     importance determines hierarchy, not component accident. One family of
     size + line-height per semantic role, shared by every chapter. A
     component may set family / weight / tracking / colour / composition; it
     may not invent a new prose size for a role that already has one. */
  --t0-fs: clamp(2.6rem, 4.6vw, 4.375rem);      --t0-lh: 1.14;  /* monument / hero      42-70px */
  --t1-fs: clamp(2rem, 3.1vw, 2.875rem);        --t1-lh: 1.22;  /* chapter thesis       32-46px */
  --t2-fs: clamp(1.375rem, 1.85vw, 1.875rem);   --t2-lh: 1.38;  /* section thesis       22-30px */
  --t3-fs: clamp(1.125rem, 1.28vw, 1.25rem);    --t3-lh: 1.62;  /* lead / explanatory   18-20px */
  --t4-fs: clamp(0.97rem, 1.05vw, 1.0625rem);   --t4-lh: 1.68;  /* body / evidence    15.5-17px */
  --t5-fs: clamp(0.875rem, 0.95vw, 0.9375rem);  --t5-lh: 1.70;  /* qualifier / boundary 14-15px */
  --t6-fs: clamp(0.75rem, 0.85vw, 0.8125rem);   --t6-lh: 1.50;  /* metadata / instrument 12-13px */
  --t7-fs: 0.68rem;                             --t7-lh: 1.40;  /* decorative only              */

  --nav-h: 63px;
  --screen-pad: clamp(1.5rem, 3vh, 2.75rem);
}

/* -------------------------------------------------------- material: INK */
[data-material='ink'] {
  --bg:      #0a0b0a;
  --bg-2:    #0f1211;
  --bg-3:    #151917;
  --fg:      #f4f2ec;
  --fg-2:    #a6aca5;
  --fg-3:    #71776f;
  --fg-4:    #4e534d;
  --rule:    rgba(244, 242, 236, 0.13);
  --rule-2:  rgba(244, 242, 236, 0.24);
  --rule-3:  rgba(244, 242, 236, 0.42);
  --mark:    var(--mark-ink);
  --mark-wash: rgba(99, 214, 176, 0.10);
  --mark-line: rgba(99, 214, 176, 0.34);
  --solid:   #ffffff;          /* the ! mark */
  --grain:   0.05;
  color-scheme: dark;

  /* semantic contrast roles (repair v0.1.1). --fg-2 already clears 4.5:1
     (8.51:1) so it carries --text-secondary unchanged; --fg-3/--fg-4 stay
     available as --text-decorative for T7-only, non-semantic material,
     because both measure below 4.5:1 (4.29:1 / 2.51:1) and must never
     carry investor-critical prose. --text-tertiary is a new tone, not a
     renamed existing one, calibrated to clear 4.5:1 (5.21:1) so T5/T6
     qualifier and metadata roles stay legible instead of merely present. */
  --text-primary:     var(--fg);
  --text-secondary:   var(--fg-2);
  --text-tertiary:    #7f857d;
  --text-decorative:  var(--fg-3);
}

/* ------------------------------------------------------ material: PAPER */
[data-material='paper'] {
  --bg:      #f1eee7;
  --bg-2:    #e9e5dc;
  --bg-3:    #e1dcd1;
  --fg:      #14171a;
  --fg-2:    #4c5154;
  --fg-3:    #767b7c;
  --fg-4:    #9a9e9c;
  --rule:    rgba(20, 23, 26, 0.15);
  --rule-2:  rgba(20, 23, 26, 0.28);
  --rule-3:  rgba(20, 23, 26, 0.52);
  --mark:    var(--mark-paper);
  --mark-wash: rgba(11, 107, 83, 0.08);
  --mark-line: rgba(11, 107, 83, 0.30);
  --solid:   #14171a;          /* the ! mark */
  --grain:   0.035;
  color-scheme: light;

  /* semantic contrast roles. The paper tertiary was previously calibrated
     against the chapter's base PAPER only (4.82:1 there) -- but the metadata
     that actually uses it sits on --bg-2 cards, where the same colour
     measured 4.44:1. Re-solved against the darker LOCAL background it is
     really rendered on: 4.71:1 on --bg-2, 5.13:1 on base paper. */
  --text-primary:     var(--fg);
  --text-secondary:   var(--fg-2);
  --text-tertiary:    #5f6567;
  --text-decorative:  var(--fg-3);
}

/* ---------------------------------------------------------------- reset */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--nav-h, 63px);
  background: #0a0b0a;
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.75;
  font-weight: 400;
  background: #0a0b0a;
  color: #f4f2ec;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  word-break: keep-all;
  overflow-wrap: break-word;
  font-feature-settings: 'ss01' 1;
}

img, video, svg, iframe { display: block; max-width: 100%; }
h1, h2, h3, h4, h5, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; }
table { border-collapse: collapse; }

::selection { background: var(--mark); color: var(--bg); }

:where(a, button, [tabindex], summary, video, details):focus-visible {
  outline: 2px solid var(--mark);
  outline-offset: 4px;
  border-radius: var(--r-xs);
}

/* film grain — fixed, pointer-events-none, never on a scrolling container */
body::after {
  content: '';
  position: fixed; inset: 0; z-index: 90; pointer-events: none;
  opacity: 0.055;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
}

.u-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.u-skip {
  position: absolute; top: 0; left: 50%; z-index: 200;
  transform: translate(-50%, -150%);
  background: var(--mark-ink); color: #04231a;
  padding: 0.8rem 1.4rem; font-weight: 600; font-size: var(--t5-fs); line-height: var(--t5-lh);
  transition: transform var(--t-fast) var(--e-out);
}
.u-skip:focus { transform: translate(-50%, 0); }

/* ---------------------------------------------------------------- layout */
.chapter {
  background: var(--bg);
  color: var(--fg);
  position: relative;
  padding-block: var(--pad-open) var(--pad-close);
  isolation: isolate;
}
.shell {
  width: 100%; min-width: 0;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.shell :where(li, div, span, p, dl, dt, dd, article, figure, ul, table, iframe) { min-width: 0; }

/* the datum: a chapter's opening rule. architectural, not a card header. */
.datum {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0 clamp(1rem, 2.4vw, 2rem);
  padding-bottom: 1.1rem;
  margin-bottom: var(--gap-idea);
  border-bottom: 1px solid var(--rule-2);
}
.datum__n {
  font-family: var(--sans); font-size: var(--t6-fs); line-height: var(--t6-lh); font-weight: 600;
  letter-spacing: 0.24em; color: var(--mark);
  font-variant-numeric: tabular-nums;
}
.datum__t { font-size: var(--t6-fs); line-height: var(--t6-lh); color: var(--fg-2); letter-spacing: -0.005em; }
.datum__r { font-size: var(--t6-fs); line-height: var(--t6-lh); color: var(--text-tertiary); letter-spacing: 0.14em; }
@media (max-width: 640px) {
  .datum { grid-template-columns: auto minmax(0, 1fr); }
  .datum__r { display: none; }
}

/* ------------------------------------------------------------ typography
   Display uses Noto Serif KR Light at large sizes and Medium at small ones:
   optical weight compensation, not a single blunt bold.                    */
/* .dx is the thesis voice. The accepted thesis sentence is a fixed 44-char
   claim that cannot be shortened, so it is sized to land in three lines and
   leave the monument and the primary actions inside the first screen. */
.dx {
  text-wrap: balance;
  font-family: var(--serif); font-weight: 300;
  font-size: var(--t0-fs);
  line-height: var(--t0-lh); letter-spacing: -0.028em;
}
.d1 {
  text-wrap: balance;
  font-family: var(--serif); font-weight: 300;
  font-size: var(--t1-fs);
  line-height: var(--t1-lh); letter-spacing: -0.026em;
}
.d2 {
  text-wrap: balance;
  font-family: var(--serif); font-weight: 400;
  font-size: var(--t2-fs);
  line-height: var(--t2-lh); letter-spacing: -0.02em;
}
.d3 {
  text-wrap: balance;
  font-family: var(--serif); font-weight: 500;
  font-size: var(--t2-fs);
  line-height: var(--t2-lh); letter-spacing: -0.012em;
}
.d4 {
  font-family: var(--sans); font-weight: 600;
  font-size: var(--t3-fs);
  line-height: var(--t3-lh); letter-spacing: -0.01em;
}

/* Korean measure is set in em (~1 Hangul glyph), never ch */
.m-xs { max-width: 13em; }
.m-sm { max-width: 16em; }
.m-md { max-width: 19em; }
.m-lg { max-width: 23em; }
.m-xl { max-width: 27em; }

.lede {
  text-wrap: pretty;
  font-size: var(--t3-fs);
  line-height: var(--t3-lh); color: var(--fg-2); max-width: 38em;
  font-weight: 400;
}
.prose { text-wrap: pretty; color: var(--fg-2); max-width: 40em;
         font-size: var(--t4-fs); line-height: var(--t4-lh); }
.prose strong { color: var(--fg); font-weight: 600; }
.prose + .prose { margin-top: 1rem; }

.tag {
  font-family: var(--sans); font-size: var(--t6-fs); line-height: var(--t6-lh); font-weight: 600;
  letter-spacing: 0.2em; color: var(--mark);
}

/* running marks: colour only, never a plate, so Korean rhythm is intact */
.q { color: var(--mark); font-weight: 600; }
.b { color: var(--solid); font-weight: 600; }
.nb { white-space: nowrap; }

/* ------------------------------------------------------------- qualifier
   Accepted truth boundaries set as editorial marginalia, not a warning box. */
.qual {
  text-wrap: pretty;
  font-size: var(--t5-fs); line-height: var(--t5-lh); color: var(--text-tertiary);
  max-width: 46em; padding-left: 1.1rem; position: relative;
}
.qual::before {
  content: ''; position: absolute; left: 0; top: 0.62em; bottom: 0.62em;
  width: 1px; background: var(--rule-2);
}
.qual b, .qual strong { color: var(--text-secondary); font-weight: 600; }
.qual--legal::before { background: var(--mark-line); width: 2px; }

/* a hairline note that sits directly under an object */
.note { font-size: var(--t5-fs); line-height: var(--t5-lh); color: var(--text-tertiary); }

/* --------------------------------------------------------- the two marks
   Drawn geometry. .mark sizes the drawn symbol; colour comes from material. */
.mark { display: block; height: 1em; width: auto; overflow: visible; }
.mark--q { color: var(--mark); }
.mark--b { color: var(--solid); }
.mark__stroke { fill: none; stroke: currentColor; stroke-width: 22;
                stroke-linecap: round; stroke-linejoin: round; }
.mark__dot { fill: currentColor; }

/* state node: the mark used as a point on a path */
.node {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.6em; height: 2.6em; flex: none;
  border: 1px solid currentColor; border-radius: 50%;
}
.node .mark { height: 1.15em; }

/* ------------------------------------------------------------- rule work
   Architectural line language: datum lines, dimension ticks, leaders.      */
.rule { height: 1px; background: var(--rule); border: 0; }
.rule--strong { background: var(--rule-2); }

.dim {
  display: flex; align-items: center; gap: 0.7rem;
  font-size: var(--t6-fs); line-height: var(--t6-lh); letter-spacing: 0.16em; color: var(--text-tertiary);
}
.dim::before, .dim::after {
  content: ''; height: 1px; background: var(--rule); flex: 1;
}
.dim--l::before { display: none; }
.dim--r::after { display: none; }

/* ------------------------------------------------------ structural guide
   V2.4R2. A sentence-length line that tells the reader how to read the model
   beneath it is not a caption, and the portal was setting these in `.dim`:
   0.68rem, 0.16em of tracking and --fg-4, which measures 2.3:1 against paper
   and spaces Korean sentence text as if it were a label. The role is now its
   own -- large enough to be noticed while scanning, tracked for a sentence
   rather than for a label, 6.9:1 on paper and 8.6:1 on ink, and still clearly
   secondary to a chapter headline.

   `.dim` is kept for what it is genuinely good at and stays quiet there:
   tertiary locators, measurements, and legal or maturity microcopy.       */
.guide {
  display: flex; align-items: center; gap: 0.6rem;
  font-size: var(--t6-fs); font-weight: 500; letter-spacing: 0.005em;
  line-height: var(--t6-lh); color: var(--fg-2); text-wrap: pretty;
}
.guide::before, .guide::after {
  content: ''; height: 1px; background: var(--rule-2); flex: 1 0 1.2rem;
  margin-inline: 0.3rem;
}
.guide--l::before { display: none; }
.guide--r::after { display: none; }
@media (max-width: 720px) {
  .guide { font-size: var(--t6-fs); line-height: var(--t6-lh); gap: 0.5rem; }
  .guide::before, .guide::after { flex-basis: 0.8rem; }
}

/* ------------------------------------------------------- T0-T7 typography
   Semantic size/role roles (repair v0.1.1). These are additive hooks for
   new or promoted copy, not a rename of the existing display/prose classes
   above -- .dx/.d1-.d4/.lede/.prose/.qual/.note/.tag/.guide/.dim already
   occupy this same ladder and keep their names (see the Result Package's
   T0-T7 mapping table for how each maps). Apply a .tN class alongside a
   structural class (e.g. class="qual t3") when a specific passage needs a
   role its structural class does not already carry at the right weight --
   placed after .qual/.note/.dim/.guide in source order so the override
   actually wins at equal specificity. T3-T6 always resolve to a --text-*
   token that clears 4.5:1; only .t7 is allowed to render at
   --text-decorative, which does not. */
.t0 { font-family: var(--serif); font-weight: 300; font-size: var(--t0-fs); line-height: var(--t0-lh); color: var(--text-primary); }
.t1 { font-family: var(--serif); font-weight: 300; font-size: var(--t1-fs); line-height: var(--t1-lh); color: var(--text-primary); }
.t2 { font-family: var(--serif); font-weight: 400; font-size: var(--t2-fs); line-height: var(--t2-lh); color: var(--text-primary); }
.t3 { font-family: var(--sans); font-weight: 500; font-size: var(--t3-fs); line-height: var(--t3-lh); color: var(--text-secondary); }
.t4 { font-family: var(--sans); font-weight: 400; font-size: var(--t4-fs); line-height: var(--t4-lh); color: var(--text-secondary); }
.t5 { font-family: var(--sans); font-weight: 400; font-size: var(--t5-fs); line-height: var(--t5-lh); color: var(--text-tertiary); }
.t6 { font-family: var(--sans); font-weight: 500; font-size: var(--t6-fs); line-height: var(--t6-lh); color: var(--text-tertiary); }
.t7 { font-family: var(--sans); font-weight: 400; font-size: var(--t7-fs); line-height: 1.4; color: var(--text-decorative); }

/* ---------------------------------------------------------------- action */
.act {
  display: inline-flex; align-items: center; gap: 0.85rem;
  font-family: var(--sans); font-size: var(--t6-fs); line-height: var(--t6-lh); font-weight: 600;
  padding: 0.95rem 1.5rem; border-radius: var(--r-sm);
  cursor: pointer; white-space: nowrap;
  border: 1px solid transparent;
  transition: background var(--t-fast) var(--e-out),
              border-color var(--t-fast) var(--e-out),
              color var(--t-fast) var(--e-out),
              transform 0.16s var(--e-out);
}
.act__go {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.55rem; height: 1.55rem; border-radius: 50%; flex: none;
  background: currentColor; position: relative;
  transition: transform var(--t-fast) var(--e-out);
}
.act__go::before {
  content: '↗'; position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: var(--t6-fs); line-height: 1;
}
.act--primary {
  background: var(--mark); color: var(--bg); border-color: var(--mark);
}
.act--primary .act__go { background: var(--bg); }
.act--primary .act__go::before { color: var(--mark); }
.act--ghost { color: var(--fg); border-color: var(--rule-3); }
.act--ghost .act__go { background: var(--fg); }
.act--ghost .act__go::before { color: var(--bg); }
.act:active { transform: scale(0.97); }
.act--sm { padding: 0.68rem 1.1rem; font-size: var(--t6-fs); line-height: var(--t6-lh); }

.acts-row { display: flex; flex-wrap: wrap; gap: 0.8rem; }

/* underlined text link with a travelling rule */
.tl {
  display: inline-flex; align-items: baseline; gap: 0.4rem;
  font-size: var(--t6-fs); line-height: var(--t6-lh); font-weight: 500; color: var(--text-primary);
  position: relative; padding-bottom: 3px;
}
.tl::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--rule-3);
  transform-origin: left; transition: transform var(--t-base) var(--e-out),
                                      background var(--t-fast) var(--e-out);
}
@media (hover: hover) and (pointer: fine) {
  .act--primary:hover { filter: brightness(1.08); }
  .act--ghost:hover { border-color: var(--fg); background: var(--bg-2); }
  .act:hover .act__go { transform: translate(2px, -2px); }
  .tl:hover::after { background: var(--mark); }
  .tl:hover { color: var(--mark); }
}

/* ---------------------------------------------------------------- chips
   Used for accepted maturity and state labels only. Never as decoration.   */
.chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: var(--t6-fs); font-weight: 500; letter-spacing: 0.01em;
  line-height: var(--t6-lh); color: var(--fg-2);
  padding: 0.3rem 0.62rem; border: 1px solid var(--rule-2);
  border-radius: var(--r-xs); white-space: nowrap;
}
.chip--mark { color: var(--mark); border-color: var(--mark-line); background: var(--mark-wash); }
.chip--plan { color: #c9a24a; border-color: rgba(201, 162, 74, 0.38); }
[data-material='paper'] .chip--plan { color: #8a6410; border-color: rgba(138, 100, 16, 0.34); }
.chip--wrap { white-space: normal; }
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }

/* =========================================================== NAVIGATION
   A thin datum bar, not a floating SaaS pill and not a heavy header.       */
.nav {
  position: fixed; top: 0; left: 0; width: 100%; max-width: 100vw; z-index: 100;
  color: #f4f2ec;
  transition: background var(--t-base) var(--e-out),
              border-color var(--t-base) var(--e-out),
              color var(--t-base) var(--e-out);
  border-bottom: 1px solid transparent;
}
.nav[data-stuck='true'] {
  background: rgba(10, 11, 10, 0.90);
  border-bottom-color: rgba(244, 242, 236, 0.14);
}
/* Material tracking needs script: which chapter is under the bar is a measured
   fact. With no script the bar keeps its own ground so the labels never sit
   white-on-paper. */
html:not(.js) .nav {
  background: rgba(10, 11, 10, 0.92);
  border-bottom-color: rgba(244, 242, 236, 0.14);
}
.nav[data-stuck='true'][data-over='paper'] {
  background: rgba(241, 238, 231, 0.92);
  color: #14171a;
  border-bottom-color: rgba(20, 23, 26, 0.16);
}
@media (min-width: 1024px) {
  .nav[data-stuck='true'] { backdrop-filter: blur(16px) saturate(160%); -webkit-backdrop-filter: blur(16px) saturate(160%); }
}
.nav__in {
  display: flex; align-items: center; gap: 1.4rem; min-width: 0;
  height: 62px; max-width: var(--measure); margin-inline: auto;
  padding-inline: var(--gutter);
}
.nav__brand {
  font-weight: 700; font-size: var(--t6-fs); line-height: var(--t6-lh); letter-spacing: 0.16em; flex: none;
}
.nav__list { display: flex; gap: 0.1rem; margin-left: auto; }
.nav__link {
  display: block; padding: 0.4rem 0.7rem; font-size: var(--t6-fs); line-height: var(--t6-lh); font-weight: 500;
  color: currentColor; opacity: 0.62; white-space: nowrap; position: relative;
  transition: opacity var(--t-fast) var(--e-out);
}
@media (hover: hover) and (pointer: fine) { .nav__link:hover { opacity: 1; } }
.nav__link[aria-current='true'] { opacity: 1; }
.nav__link[aria-current='true']::after {
  content: ''; position: absolute; left: 0.7rem; right: 0.7rem; bottom: 0.05rem;
  height: 1px; background: currentColor;
}
.nav__cta { margin-left: 0.7rem; flex: none; }
.nav__now { display: none; }
.nav__toggle { display: none; }
/* the reading progress is written on every scroll tick, so it is a transform,
   never a width: no layout work belongs on the scroll path */
.nav__prog {
  position: absolute; left: 0; bottom: -1px; height: 1px; width: 100%;
  background: var(--mark-ink);
  transform: scaleX(var(--prog, 0)); transform-origin: left;
}
.nav[data-over='paper'] .nav__prog { background: var(--mark-paper); }

@media (max-width: 1023px) {
  .nav { background: rgba(10, 11, 10, 0.94); }
  .nav[data-over='paper'] { background: rgba(241, 238, 231, 0.95); color: #14171a; }
  .nav__in { height: 56px; gap: 0.7rem; }
  .nav__now {
    display: block; margin-left: auto; font-size: var(--t6-fs); line-height: var(--t6-lh); opacity: 0.6;
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .nav__cta { display: none; }
  .nav__toggle {
    display: grid; place-items: center; width: 40px; height: 34px; flex: none;
    margin-left: 0.4rem; cursor: pointer; position: relative;
  }
  .nav__bar {
    position: absolute; left: 9px; right: 9px; height: 1.5px;
    background: currentColor; transition: transform var(--t-fast) var(--e-inout);
  }
  .nav__bar:nth-child(1) { transform: translateY(-4px); }
  .nav__bar:nth-child(2) { transform: translateY(4px); }
  .nav[data-open='true'] .nav__bar:nth-child(1) { transform: rotate(45deg); }
  .nav[data-open='true'] .nav__bar:nth-child(2) { transform: rotate(-45deg); }

  .nav__list {
    position: fixed; top: 56px; left: 0; width: 100vw; max-width: 100vw;
    flex-direction: column; gap: 0; margin-left: 0;
    background: var(--bg, #0a0b0a); color: inherit;
    border-bottom: 1px solid currentColor;
    padding: 0.4rem var(--gutter) 1.2rem;
    clip-path: inset(0 0 100% 0); visibility: hidden;
    transition: clip-path var(--t-base) var(--e-inout), visibility var(--t-base);
  }
  .nav[data-over='paper'] .nav__list { background: #f1eee7; }
  .nav[data-open='true'] .nav__list { clip-path: inset(0 0 0 0); visibility: visible; }
  .nav__link {
    padding: 0.85rem 0; font-size: var(--t6-fs); line-height: var(--t6-lh); opacity: 1;
    border-bottom: 1px solid rgba(128, 128, 128, 0.22);
    transform: translateY(10px); opacity: 0;
    transition: transform var(--t-base) var(--e-out), opacity var(--t-base) var(--e-out);
  }
  .nav[data-open='true'] .nav__link { transform: none; opacity: 1; }
  .nav[data-open='true'] li:nth-child(1) .nav__link { transition-delay: 60ms; }
  .nav[data-open='true'] li:nth-child(2) .nav__link { transition-delay: 100ms; }
  .nav[data-open='true'] li:nth-child(3) .nav__link { transition-delay: 140ms; }
  .nav[data-open='true'] li:nth-child(4) .nav__link { transition-delay: 180ms; }
  .nav[data-open='true'] li:nth-child(5) .nav__link { transition-delay: 220ms; }
  .nav[data-open='true'] li:nth-child(6) .nav__link { transition-delay: 260ms; }
  .nav[data-open='true'] li:nth-child(7) .nav__link { transition-delay: 300ms; }
  .nav__link[aria-current='true']::after { display: none; }
}

/* ======================================================== SCENE CONTRACT
   Scroll triggers the scene. Scroll does not control the scene.

   portal.js publishes two states on every scene element and CSS does all the
   drawing from them:

     data-scene = idle       composed, readable, nothing has played
                  play       the authored sequence is running on its own clock
                  settled    the readable end state is on screen

     data-vis   = in | out   whether the scene is anywhere near the viewport

   AMBIENT LIFE is CSS animation, and it is allowed to run only inside a scene
   that has settled and is on screen. That is what --amb carries: every ambient
   rule in chapters.css declares `animation-play-state: var(--amb, paused)`, so
   a scene that has not played yet, or has scrolled away, costs nothing.       */
.js [data-scene] { --amb: paused; }
.js [data-scene='settled'][data-vis='in'] { --amb: running; }
@media (prefers-reduced-motion: reduce) { .js [data-scene] { --amb: paused; } }

/* =============================================================== MOTION
   Semantic vocabulary. Each name means something in the investment story.
     ORIENT     a chapter arrives; its datum line draws
     REVEAL     content is uncovered by a mask, not faded in
     TRANSFORM  ? becomes ! ; a state actually changes
     COMPARE    two realities exchange position
     GUIDE      a drawn path completes under an authored clock
     CONFIRM    an evidence state settles into place
   Everything below is gated on .js so no meaning depends on motion.        */

/* ORIENT */
.js [data-m='orient'] { opacity: 0; transform: translateY(10px); }
.js [data-m='orient'].is-in {
  opacity: 1; transform: none;
  transition: opacity var(--t-base) var(--e-out), transform var(--t-base) var(--e-out);
}
.js [data-m='orient'] .datum { --draw: 0; }
.js [data-m='orient'].is-in .datum { --draw: 1; }
.datum { position: relative; }
.datum::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--mark); transform: scaleX(var(--draw, 1)); transform-origin: left;
  transition: transform var(--t-slow) var(--e-inout);
}

/* REVEAL — clip mask, gives the feel of a printed plate being uncovered */
.js [data-m='reveal'] { clip-path: inset(0 0 100% 0); opacity: 0.001; }
.js [data-m='reveal'].is-in {
  clip-path: inset(0 0 0 0); opacity: 1;
  transition: clip-path var(--t-slow) var(--e-inout), opacity 0.4s linear;
  transition-delay: var(--d, 0ms);
}

/* RISE — text blocks arrive with weight */
.js [data-m='rise'] { opacity: 0; transform: translateY(22px); filter: blur(6px); }
.js [data-m='rise'].is-in {
  opacity: 1; transform: none; filter: blur(0);
  transition: opacity var(--t-base) var(--e-out),
              transform var(--t-base) var(--e-out),
              filter var(--t-base) var(--e-out);
  transition-delay: var(--d, 0ms);
}

/* CONFIRM — evidence settles */
.js [data-m='confirm'] { opacity: 0; transform: scale(0.965); }
.js [data-m='confirm'].is-in {
  opacity: 1; transform: none;
  transition: opacity 0.5s var(--e-out), transform 0.72s var(--e-out);
  transition-delay: var(--d, 0ms);
}

/* GUIDE — a drawn path follows scroll progress (set by JS as --p 0..1).
   Gated on .js: with no script the path is simply complete, never blank. */
.js .guide-path {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: calc(var(--len, 1000) * (1 - var(--p, 0)));
}

/* stagger helper */
.js [data-stagger] > * { --d: calc(var(--i, 0) * 70ms); }

/* ---------------------------------------------------------------- SCENE CUT
   A chapter must arrive as a new scene, not as more of the same page. Two
   devices do the work, both keyed off the chapter's own arrival:
     the FOLD    the outgoing material bleeds a short way into the new one,
                 the way a printed signature shows through the next page
     the CUT     the chapter number rises as a ghost numeral behind the datum */
.chapter[data-fold]::before,
.close[data-fold]::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: clamp(90px, 14vh, 190px);
  z-index: 0; pointer-events: none;
}
.chapter[data-fold='ink']::before,
.close[data-fold='ink']::before {
  background: linear-gradient(180deg, rgba(10, 11, 10, 0.55) 0%, rgba(10, 11, 10, 0) 100%);
  box-shadow: inset 0 1px 0 rgba(10, 11, 10, 0.5);
}
.chapter[data-fold='paper']::before,
.close[data-fold='paper']::before {
  background: linear-gradient(180deg, rgba(241, 238, 231, 0.32) 0%, rgba(241, 238, 231, 0) 100%);
  box-shadow: inset 0 1px 0 rgba(241, 238, 231, 0.42);
}
.chapter > .shell, .close > .shell,
.chapter > .screen, .close > .screen { position: relative; z-index: 1; }

.datum[data-cut]::before {
  content: attr(data-cut);
  position: absolute; right: -0.06em; bottom: 0.1em; z-index: -1;
  font-family: var(--serif); font-weight: 300; line-height: 0.78;
  font-size: var(--t7-fs); letter-spacing: -0.05em;
  color: var(--fg); opacity: 0.055; pointer-events: none;
  transform: translateY(calc(18px - var(--cut, 1) * 18px));
  transition: transform 1.1s var(--e-out), opacity 1.1s var(--e-out);
}
.js [data-m='orient'] .datum[data-cut]::before { opacity: 0; }
.js [data-m='orient'].is-in .datum[data-cut]::before { opacity: 0.055; }
@media (max-width: 700px) { .datum[data-cut]::before { display: none; } }

/* --------------------------------------------------------- FIGURE EMPHASIS
   Major numbers are not counted up. The digits are uncovered by a wipe at the
   moment the sentence around them gives them meaning, and the rule under the
   measure draws to the width that number represents. */
.js [data-fig] {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 0.86s var(--e-inout);
  transition-delay: calc(var(--d, 0ms) + 120ms);
}
.js .is-in [data-fig] { clip-path: inset(0 0 0 0); }
.js .figure b[data-fig], .js .terms dd[data-fig] { position: relative; }

/* CH05 measures are uncovered by their own stage of the narrowing sequence,
   not by the block's entry: each number lands as the result of the screen that
   just ran. Higher specificity than the generic .is-in rule above. */
.js .narrow[data-stage] > div::before {
  transform: scaleX(0); transform-origin: left;
  transition: transform 1.1s var(--e-inout);
}
.js .narrow[data-stage] > div [data-fig] { clip-path: inset(0 100% 0 0); }
.js .narrow[data-stage='1'] > div:nth-child(1) [data-fig],
.js .narrow[data-stage='2'] > div:nth-child(1) [data-fig],
.js .narrow[data-stage='2'] > div:nth-child(2) [data-fig],
.js .narrow[data-stage='3'] > div [data-fig] { clip-path: inset(0 0 0 0); }
.js .narrow[data-stage='1'] > div:nth-child(1)::before,
.js .narrow[data-stage='2'] > div:nth-child(1)::before,
.js .narrow[data-stage='2'] > div:nth-child(2)::before,
.js .narrow[data-stage='3'] > div::before { transform: scaleX(1); }
/* the exclusion line lands after its own digits */
.js .narrow[data-stage] .narrow__x { opacity: 0; transition: opacity 0.5s var(--e-out) 0.45s; }
.js .narrow[data-stage='1'] > div:nth-child(1) .narrow__x,
.js .narrow[data-stage='2'] > div:nth-child(1) .narrow__x,
.js .narrow[data-stage='2'] > div:nth-child(2) .narrow__x,
.js .narrow[data-stage='3'] > div .narrow__x { opacity: 1; }

/* ------------------------------------------------------- SCROLL-DRIVEN LINE
   One generic contract for every drawn path in the document: JS writes --p
   (0..1) for the element's own scroll window, CSS does the rest. Nothing
   depends on a path being drawn: the diagrams are legible at --p 0 or 1. */
.wire, .pulse { fill: none; stroke-linecap: round; }
.js .wire {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: calc(var(--len, 1000) * (1 - var(--p, 0)));
}
.pulse { opacity: 0; }
.js .pulse {
  stroke-dasharray: 14 calc(var(--len, 1000) - 14);
  stroke-dashoffset: calc(var(--len, 1000) * (1 - var(--p, 0)) - 14);
  opacity: var(--pulse-o, 0);
}

@media (prefers-reduced-motion: reduce) {
  .js [data-fig] { clip-path: none !important; transition: none !important; }
  .js .narrow > div::before { transform: scaleX(1) !important; transition: none !important; }
  .datum[data-cut]::before { transform: none !important; transition: none !important; }
  .wire { stroke-dashoffset: 0 !important; }
  .pulse { display: none !important; }
}

/* Reduced motion is gentler, not zero. All movement, masking and blur are
   removed; short opacity transitions that make a state change legible stay,
   because losing them would make the stage exchange harder to follow. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js [data-m] {
    opacity: 1 !important; transform: none !important;
    clip-path: none !important; filter: none !important;
    transition: none !important;
  }
  .datum::after { transform: scaleX(1) !important; transition: none !important; }
  .guide-path { stroke-dashoffset: 0 !important; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-property: opacity, color, background-color, border-color !important;
    transition-duration: 140ms !important;
  }
}

/* ======================================================= SCREEN SYSTEM ====
   PART A of the semantic screen repair. Every investor-visible block now
   lives inside exactly one [data-screen]; this file gives that ownership a
   geometry. A normal Screen is one frame: it settles with its authored top
   under the fixed nav and holds one complete investor thought. An
   interactive Screen is one authored visual frame plus an internal semantic
   runway -- the runway belongs to the same Screen and is never allowed to
   reveal the next one before the final HOLD.

   MOBILE keeps native vertical flow (no frame height, no pagination); the
   Screen containers still carry the semantic grouping and the S5 rhythm. */

.screen { position: relative; }

/* desktop paginated mode -- set by portal.js on <html> for fine-pointer,
   wheel-capable viewports large enough for the authored composition */
.paged .chapter,
.paged .close { padding-block: 0; }

.paged .screen {
  min-height: calc(100vh - var(--nav-h));
  padding-block: var(--screen-pad);
  scroll-margin-top: var(--nav-h);
  box-sizing: border-box;
}
/* an interactive Screen owns a runway taller than one frame on purpose */
.paged .screen[data-screen-int] { min-height: calc(100vh - var(--nav-h)); }

/* the chapter opener rule is part of the Screen it opens, never a floating
   band between two of them */
.paged .screen > .datum:first-child { margin-top: 0; }

/* NON-PAGED (mobile / coarse pointer): rhythm only, never a viewport lock */
:where(html:not(.paged)) .screen + .screen { margin-top: var(--s5); }

/* ================================================== S1-S5 SPACING ROLES ===
   Vertical rhythm is a relation, not a number. Every narrative margin in the
   deck now names the relation it encodes; there are no one-off values left
   in the markup. S5 is deliberately absent from inside a Screen -- the
   Screen frame IS that boundary, so the ~19 old --gap-depth call sites that
   used to fake it collapse to S4 (one argument -> the next). */
.s-micro { margin-top: var(--s1); }   .sb-micro { margin-bottom: var(--s1); }
.s-copy  { margin-top: var(--s2); }   .sb-copy  { margin-bottom: var(--s2); }
.s-comp  { margin-top: var(--s3); }   .sb-comp  { margin-bottom: var(--s3); }
.s-idea  { margin-top: var(--s4); }   .sb-idea  { margin-bottom: var(--s4); }
.s-scene { margin-top: var(--s5); }   .sb-scene { margin-bottom: var(--s5); }

/* the frame owns the boundary: a Screen's first block never re-states it,
   and its last block never pushes a gap into the next Screen */
.paged .screen > :first-child { margin-top: 0; }
.paged .screen > :last-child  { margin-bottom: 0; }

/* the hero is one Screen: the thesis takes the free space, the reading key
   and the round framing settle on the frame's baseline */
.paged .chapter.hero { min-height: 0; }
.paged [data-screen="s01"] { display: grid; grid-template-rows: 1fr auto; gap: var(--s3); }

/* PAGED DENSITY. Inside a frame the chapter opener is part of the Screen it
   opens, not a band with an idea-sized gap under it; and the hero thesis is
   given the full measure so the monument resolves in three lines. */
.paged .datum { margin-bottom: var(--s3); }
.paged .hero__in { padding-block: var(--s3); }
.paged .hero__lead { max-width: min(100%, 62rem); }
.paged .floors li { padding-block: clamp(0.55rem, 1.1vw, 0.72rem); }
/* dense instrument rows carry the same roles at frame density */
.paged .floors li { padding-block: clamp(0.45rem, 0.9vw, 0.6rem); }
.paged .elevation { gap: clamp(1.2rem, 2.4vw, 2.2rem); }
.paged .deflist > div { padding-block: 0.5rem; }
.paged .specimen { gap: 0.8rem; }
.paged .close__in { gap: var(--s3); }
/* the structured-output column must resolve inside the frame, never clip */
.paged .build__out li { padding-block: 0.62rem; }
.paged .loadpath { gap: clamp(0.9rem, 1.8vw, 1.5rem); }

/* SHORT-FRAME DENSITY STEP. The role system does not change with viewport
   height -- the same semantic text stays in the same T-band. What tightens
   on a 720px-tall frame is the RHYTHM between blocks and the padding of
   dense instrument rows, which is a spacing decision, not a type decision. */
@media (max-height: 780px) {
  html.paged { --s3: 1.05rem; --s4: clamp(1.6rem, 3.4vw, 2.6rem);
               --screen-pad: clamp(0.85rem, 1.6vh, 2rem); }
  .paged .floors li    { padding-block: 0.3rem; }
  .paged .srow         { padding-block: 0.34rem; }
  .paged .sequence li  { padding-block: 0.62rem; }
  .paged .linelist li  { padding-block: 0.5rem; }
  .paged .deflist > div { padding-block: 0.4rem; }
  .paged .layer         { gap: 0.6rem; }
  .paged .layer__h      { padding-bottom: 0.4rem; }
  .paged .specimen      { gap: 0.7rem; }
  .paged .signpost__b   { padding-block: 0.9rem; }
  .paged .elevation     { gap: clamp(1rem, 2vw, 1.8rem); }
}

/* The hero is the one Screen whose authored top IS the document top: the nav
   floats transparently over it, which is the accepted opening composition.
   Its frame is therefore the whole viewport, not viewport-minus-nav -- without
   this the next Screen shows through under it by exactly the nav's height. */
.paged [data-screen="s01"] { min-height: 100vh; }

/* ============================================ DECK-LEVEL OVERLAY SYSTEM ===
   Overlay disclosure repair, PART A. Disclosure routing is decided by CONTENT
   FIT, not by consistency. A compact detail that resolves inside the slack of
   its own authored Screen stays an inline reveal -- s22 opens 119px into 172 /
   66 / 23px of remaining frame at 1440x900 / 1366x768 / 1280x720 and moves
   neither its settle anchor nor the next Screen's. A detail that cannot -- the
   13-row capital ledger on s24, which grew its Screen from one frame to 2.1
   frames and pushed every settle target below it by 778px -- is not allowed to
   expand the narrative flow at all. It opens here instead.

   WHY A NATIVE <dialog>. The ledger has to draw above chapter scenes that
   carry their own transforms and stacking contexts, take focus away from the
   deck, make the deck inert, and answer ESC. The top layer gives all four as
   engine behaviour; a z-index panel gives none of them reliably on this page.

   The surface is INK because the Screen it belongs to is INK, and it is drawn
   in the deck's own material language -- one hairline rule, sparse green
   signal, editorial whitespace, the same 40x40 control well the trigger uses.
   It is supporting detail in THE PORTAL's voice, not an application modal. */

.odlg {
  margin: auto;                       /* the top layer centres, the document does not */
  inline-size: min(1120px, calc(100vw - 2 * var(--gutter)));
  max-block-size: min(84vh, calc(100vh - 4.5rem));
  padding: 0;
  border: 1px solid var(--rule-2);
  border-radius: var(--r-xs);
  background: var(--bg-2);
  color: var(--text-primary);
  box-shadow: 0 18px 56px rgba(0, 0, 0, 0.46);
  overflow: hidden;                   /* the body owns the scroll, not the surface */
}
.odlg[open] { display: flex; flex-direction: column; }
.odlg::backdrop { background: rgba(6, 8, 7, 0.74); }

/* orientation, not decoration: 200ms of rise so the reader sees WHERE the
   detail came from, and nothing that delays reading it */
.odlg[open] { animation: odlg-in 0.2s var(--e-out) both; }
.odlg[open]::backdrop { animation: odlg-veil 0.2s var(--e-out) both; }
@keyframes odlg-in   { from { opacity: 0; transform: translateY(8px); } }
@keyframes odlg-veil { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .odlg[open], .odlg[open]::backdrop { animation: none; }
}

.odlg__head {
  flex: 0 0 auto;
  display: flex; align-items: flex-start; gap: 1rem;
  padding: clamp(1rem, 2vw, 1.5rem) clamp(1.1rem, 2.2vw, 1.9rem) 0.85rem;
  border-bottom: 1px solid var(--rule-2);
}
.odlg__ttl { min-width: 0; display: grid; gap: 0.28rem; }
.odlg__t {
  text-wrap: balance;
  font-family: var(--serif); font-weight: 400;
  font-size: var(--t2-fs); line-height: var(--t2-lh);
  letter-spacing: -0.02em; color: var(--text-primary);
}
.odlg__tag {
  font-size: var(--t6-fs); line-height: var(--t6-lh);
  font-weight: 400; color: var(--text-tertiary);
}
.odlg__tag:empty { display: none; }

/* the trigger's + turns 45 degrees into x when a disclosure opens. The
   overlay's close control IS that mark, at the same 40x40 well, so the thing
   that closes the detail is visibly the same control that opened it. */
.odlg__x {
  flex: 0 0 auto; margin-left: auto;
  inline-size: 40px; block-size: 40px;
  display: grid; place-items: center;
  cursor: pointer;
  color: var(--mark);
  border: 1px solid var(--rule-2); border-radius: var(--r-xs);
  background: transparent;
  transition: border-color var(--t-fast) var(--e-out),
              background-color var(--t-fast) var(--e-out);
}
.odlg__x::before {
  content: '+';
  font-family: var(--sans); font-size: 1.125rem; font-weight: 500; line-height: 1;
  transform: rotate(45deg);
}
.odlg__x:hover, .odlg__x:focus-visible {
  border-color: var(--mark);
  background: color-mix(in srgb, var(--mark) 10%, transparent);
}

.odlg__body {
  flex: 1 1 auto; min-block-size: 0;
  overflow: auto;
  overscroll-behavior: contain;       /* a boundary never chains into the deck */
  /* NO TOP PADDING, deliberately. A scroll container's padding band is inside
     the scrollable area, so content travels up through it and keeps painting
     above anything stuck at the scrollport's top edge -- which left a ghost
     budget row hanging over the ledger head. The head band starts flush and
     the breathing room is set INSIDE it, where it scrolls correctly. */
  padding: 0 clamp(1.1rem, 2.2vw, 1.9rem) clamp(0.9rem, 2vw, 1.5rem);
}
/* the ledger region is a tab stop only while it actually scrolls, so it
   answers a Tab key with a ring and a programmatic focus with nothing.
   The ring is drawn INSIDE: the surface clips its own overflow. */
.odlg__body:focus:not(:focus-visible) { outline: none; }
.odlg__body:focus-visible { outline-offset: -3px; }
.odlg__body > .more__in { padding-bottom: 0; }

/* WIDE LEDGER VARIANT. The overlay body is the single scrollport for both
   axes, so the authored .scroller stops being a nested one -- which is what
   lets the ledger head stay put while thirteen budget rows travel under it.
   On a narrow sheet the same scrollport carries the table's width. */
.odlg__body .scroller { overflow: visible; }
.odlg__body .ledger thead th {
  position: sticky; top: 0; z-index: 2;
  padding-top: clamp(0.9rem, 2vw, 1.5rem);
  background: var(--bg-2);
}

/* MOBILE: a full-screen sheet, because a phone has no room for a surface that
   is a guest on top of another surface. Dismissal stays explicit. */
@media (max-width: 900px), (max-height: 560px) {
  .odlg {
    inline-size: 100%; max-inline-size: 100%;
    block-size: 100%; max-block-size: 100%;
    margin: 0; border: 0; border-radius: 0; box-shadow: none;
    background: var(--bg);
  }
  .odlg[open] { animation: none; }
  .odlg__head, .odlg__body .ledger thead th { background: var(--bg); }
}

/* PRINT. The detail node lives in the source <details> whenever the overlay is
   shut, so print already reaches it there. This is only the case where the
   reader prints WITH the ledger open: the top layer flattens back into flow
   rather than printing an empty page. */
@media print {
  .odlg[open] {
    position: static !important; display: block !important;
    inline-size: auto !important; max-block-size: none !important;
    overflow: visible !important; border: 0 !important; box-shadow: none !important;
    background: #fff !important; color: #14171a !important;
  }
  .odlg__body { overflow: visible !important; }
  .odlg__x { display: none !important; }
  .odlg::backdrop { display: none !important; }
}
