/* fit.css — the CSS half of the fit-to-box pass (B-073).
 *
 * WHY
 * `.slide-content` is `overflow: hidden` (base.css:220), so a slide with one line
 * too many does not scroll and does not warn — it silently loses the bottom of
 * its content, and the deck looks finished. Static character budgets cannot
 * prevent that: the same content occupies different space across 16 presets, two
 * scripts (keep-all makes Korean break at completely different points than
 * English at equal character counts — B-047) and every viewport. So the fit is
 * measured at runtime and applied here.
 *
 * HOW THE SCALE REACHES THE TYPE
 * fit.js sets `--fit-scale` / `--fit-space` INLINE ON EACH `.slide`, and the six
 * type steps plus the slide padding are re-declared below at `.slide` scope so
 * they resolve per slide. Declaring them at `:root` cannot work — a custom
 * property substitutes at its declaration site, so a `:root` step would keep
 * reading the root's scale no matter what a slide sets. Same trap the dark-theme
 * aliases hit (tokens.css:139-145).
 *
 * WHY NOT `transform: scale()`
 * Because it would look right on screen and ship clipped: the PDF path force-
 * resets transforms on slide content (`body.pdf-capture .slide .slide-content > *
 * { transform: none !important }`, base.css:408-412). Scaling the type system
 * survives every export path.
 *
 * THE FLOORS ARE IN THE CSS, NOT IN THE SEARCH
 * Each clamp keeps B-048's decided minimum as its literal MIN term, so no scale
 * fit.js could ever compute will drive text below the projector/A4 legibility
 * floor. The JS only has to notice that it has stopped gaining room.
 *
 * @media screen ONLY
 * print.css pins larger fixed sizes at `:root` for A4 (`--small-size: 0.95rem`,
 * `--body-size: 1.1rem`, print.css:10-12). A `.slide`-scoped declaration
 * out-specifies that, so if this block were not screen-only it would silently
 * delete the print floor.
 *
 * Opt-in: inert until <body> carries `data-fit`.
 */

@media screen {
  body[data-fit] .slide {
    /* B-096: multiply the AUTHORED ladder, never restate it.
       These used to spell out tokens.css's own terms —
       `clamp(2rem, calc(6vw * var(--fit-scale,1)), calc(4.5rem * …))` — which is
       correct arithmetic against the DEFAULT ladder and a silent deletion of any
       other one. A preset's `type.titleSize` lands on `:root` (0,1,0); this rule is
       (0,2,1) and won on every slide, so both art-direction bundles rendered their h2
       at 48px where broadsheet authored 34.4px and signal 57.6px — two decks whose
       whole point is a different type voice, reading identically. `--fit-scale` was
       `unset` throughout, so this was not the pass working: it was the inert layer
       overwriting. The `-base` aliases in tokens.css hold whatever won at `:root`
       (see the note there for why a second name is unavoidable), and both factors
       still default to 1, so a slide fit.js never touched renders as authored.
       The `max(min(...))` wrapper is the B-048 floor, kept. Multiplying the whole
       clamp would scale its floor too, and the gate caught that immediately: small
       resolved to 10.6px at the 0.78 bottom, under the 13.6px projector/A4 floor those
       literals exist to hold. The floor term is `min(base, LITERAL)` rather than the
       literal alone so that scale 1 stays an exact no-op even for a preset that
       authored a step BELOW the shared floor — clamping such a deck up would be this
       same bug wearing the opposite sign. */
    /* B-112: the floors are CANVAS pixels now, like the ladder they guard. They
       used to be rem, which is the window's idea of a pixel — on a scaled canvas
       that is a different unit from the one the type is written in, so a floor of
       `0.95rem` would have bitten at a different point on every screen. Rewritten
       at the canvas equivalent of the same physical minimums: at a 1440 window
       the canvas scale is 0.75, so a 20px canvas floor renders 15px, which is
       B-048's 15.2px projector/A4 floor within a rounding error. */
    --title-size: max(min(var(--title-size-base), 40px), calc(var(--title-size-base) * var(--fit-scale, 1)));
    --h2-size: max(min(var(--h2-size-base), 32px), calc(var(--h2-size-base) * var(--fit-scale, 1)));
    --h3-size: max(min(var(--h3-size-base), 26px), calc(var(--h3-size-base) * var(--fit-scale, 1)));
    --subtitle-size: max(min(var(--subtitle-size-base), 24px), calc(var(--subtitle-size-base) * var(--fit-scale, 1)));
    /* B-048 floors, restated on the canvas: body 20px, small 18px. */
    --body-size: max(min(var(--body-size-base), 20px), calc(var(--body-size-base) * var(--fit-scale, 1)));
    --small-size: max(min(var(--small-size-base), 18px), calc(var(--small-size-base) * var(--fit-scale, 1)));
    /* Rhythm shrinks on its own axis: on component-dense slides the space between
       blocks is where the room actually is, and type scaling alone recovers little
       (measured ~19% of the excess on a 10-row stack).
       This one had the same defect for the same reason — it restated tokens.css:53
       `clamp(2rem, 5vw, 6rem)` and an earlier draft mis-typed the restatement as
       `clamp(1.5rem, 4vw, 6rem)`, changing the padding on every data-fit deck even at
       scale 1 (.slide-content clientHeight 888 -> 926 at 1920x1080). Deriving removes
       the class of bug rather than the instance: there is nothing left to mis-copy. */
    --slide-padding: max(min(var(--slide-padding-base), 2rem), calc(var(--slide-padding-base) * var(--fit-space, 1)));
  }

  /* B-096: the chart box is the one region the type ladder cannot reach —
     `.chart-box` is sized in vh, so scaling a slide moves its heading and its
     rhythm and leaves the plot exactly where it was. That fix is real and it is
     NOT here: it lives in charts.css, inside `.chart-box`'s own rule, where the
     space factor is multiplied into the height directly.
     B-105 moved it. The version that lived here was
     `body[data-fit] .slide .chart-box { height: … }` — (0,3,1) on a FINAL
     property, which silently beat every `.chart-box` rule an author writes in
     deck.css, the file index.html:49 promises is theirs. The rule two blocks up
     states the principle it violated: multiply the authored value, never restate
     it. A custom property can be declared here safely because the author's own
     declaration substitutes at ITS site; a final property cannot.
     Do not re-add a `.chart-box` height rule at this scope. If a future reserved
     region needs the same treatment, give it a `--…-base` custom property and
     multiply it in that component's own stylesheet, the way charts.css does.
     `ci-validate-fit.mjs` pins both halves: that the fit pass still reaches the
     box, and that a deck.css height still wins. */

  /* Deck furniture must not resize slide to slide, or the folio and eyebrow visibly
     breathe as the reader flips. Reset the scale for the motif layer's own
     pseudo-elements — the counter value is scale-immune but the string it renders
     is not. */
  body[data-fit] .slide::after {
    --fit-scale: 1;
    --fit-space: 1;
  }

  /* The honest failure state. When the search bottoms out at the legibility floor
     and the content still does not fit, the slide says so instead of quietly
     cropping: authors get a visible marker, and the CI probe / slide-reviewer can
     name the slide. Kept subtle enough not to wreck a screenshot, and dropped
     entirely from every capture path. */
  body[data-fit] .slide[data-fit-state="overflow"]::before {
    outline: 2px dashed var(--negative);
    outline-offset: -6px;
  }

  body.pdf-capture .slide[data-fit-state="overflow"]::before {
    outline: none;
  }
}
