/* ═══ PHONE, LANDSCAPE: the deck's own composition, at the size the phone really is ═════════════
   Adrian, 2026-08-02: "cutting more stuff isnt an option but sizing it in a way that works or just
   honestly making it a horizontal viewing experience alone is easier at this point and retaining
   information but making it a single slide forced landscape".

   WHY THIS IS NOT THE DESKTOP DECK ZOOMED DOWN, which is the obvious reading of "forced landscape"
   and is the one thing that cannot work. Measured by pipeline/portrait/landscape-probe.mjs, fitting
   the 1600x900 slide inside a landscape phone gives a scale of 0.42 on an iPhone 13 with the
   toolbar collapsed and 0.35 with it out. At 0.42, ALL 13 SLIDES carry runs under the gate's 12px
   hard floor, 11 of 13 fall under the 15px median, and the smallest run on the deck lands at 7.2px.
   To clear a 15px median at that scale the canvas type would have to be 35px; the deck's body copy
   is 26px. The same probe on an iPad mini gives 0.70 and only 3 slides fail, which is the honest
   shape of it: shrink-to-fit is a TABLET answer, not a phone one.

   So the canvas is the phone, natively: 844x390 rather than 1600x900 at 0.42. That is only 23% of
   the desktop's area, but it is 27% MORE than the portrait card gets (390x664), and far more
   importantly it is the RIGHT SHAPE. Landscape phone is 2.16:1 against the desktop's 1.78:1, so
   every horizontal arrangement the deck argues in survives: three reviews as a set, three states of
   one room in a row, two priced columns side by side. Those are the relationships stacking destroys,
   and they are the reason the portrait build kept reading as a document.

   AND NOTHING IS CUT. The portrait card drops one review to fit; here all three fit across, the way
   the control has them. `retaining information` is not a compromise in this orientation, it is what
   the orientation buys.

   THE VERTICAL BUDGET IS THE HARD ONE. 390px of height, against the desktop's 900. Height, not
   width, is what every rule below is spending. */

@media (max-width: 820px), (orientation: landscape) and (max-height: 520px) {

  /* ── the canvas ───────────────────────────────────────────────────────────────────────────── */
  /* The deck's fit() zooms .wrap by width. Here the slide IS the viewport and type is set for the
     size it has, so the zoom is switched off in the page's own fit() at the same query. The
     !important is the belt to that braces: fit() writes --fit as an inline custom property, and an
     important declaration in a stylesheet beats a non-important inline one, so a stale JS value
     can never silently re-zoom the deck. */
  /* TYPE SCALES OFF THE SHORTER AXIS, because HEIGHT is what binds here and width is not.
     The first pass sized everything off vw. On an 844x390 screen that reads fine; on the same
     phone with the toolbar out (844x320) the width has not moved at all, so the type does not
     move either, and the copy simply overflows its boxes and is eaten by overflow:hidden. A
     landscape phone varies far more in height than in width, so every size below takes the
     SMALLER of a vw-derived and a vh-derived value, and the deck breathes with the axis that is
     actually running out. */
  /* THE CLAMP MINIMUMS ARE THE FLOORS THEMSELVES, not numbers near them. The first pass set the
     h2 minimum at 21px and let a 7.0vh term carry it, which reads fine on the canvas this file was
     first written against and fails on every real phone: at the iPhone 13's true landscape height
     of 342px, 7vh is 23.94px against a 24px floor, so seven of thirteen slides came back with "no
     24px headline" by six hundredths of a pixel. A floor that is approached from below by a
     viewport-relative term is not a floor. Every minimum below is now the gate's own threshold, so
     the floors hold by construction at any height and the vh term only ever adds.

     AND THE CANVAS THIS WAS FIRST WRITTEN FOR DOES NOT EXIST. 844x390 is the iPhone 13's PORTRAIT
     box with the numbers swapped. The real landscape viewports, from playwright's own device
     descriptors: iPhone 13 750x342, iPhone SE 568x320, Pixel 5 802x293, iPhone 13 Pro Max 832x380.
     The design target is the SHORT end of that, not the swapped-portrait fiction. */
  /* ONE TYPE SCALE CANNOT SERVE THIRTEEN SLIDES, and that was the actual cause of the clipping
     that survived every hand-tuned number. p01 carries 46 characters and p04 carries 1,234, on the
     same canvas. A single global size is therefore always wrong in both directions at once: it is
     too big for p04, which clips, and too small for p01, which sits with half the screen empty.
     Chasing that with per-slide constants only moves the problem to the next viewport, which is
     what happened when the numbers were solved at 844x390 and then met a 293px-tall Pixel 5.

     So every size below is a FORMULA, not a value. `--ls-fit` is a per-slide multiplier that
     phone-autofit.js measures and sets on each .slide: it shrinks that slide, and only that slide,
     until its content fits its frame. Custom properties substitute lazily in the context of the
     element that uses them, so `--ls-fit` set on a .slide reaches every var() inside it while the
     formulas stay declared once here.

     THE HARD FLOOR IS INSIDE THE FORMULA, via max(), so the fitter cannot size its way through it
     no matter how dense a slide is. 12px is the legibility gate's own hard floor and it is the one
     number here that does not scale. If a slide cannot fit at the floor, the fitter says so out
     loud rather than shrinking past it. */
  :root { --fit: 1 !important; }

  /* ═══ THE PHONE'S OWN HARDWARE IS PART OF THE CANVAS, AND NOTHING HERE KNEW IT ════════════════
     Adrian, on a real device, after this sheet had measured 0 clipped across four emulated
     landscape viewports: "how you can not make it some full bleed view in landscape - on my iphone
     the pill is cutting off info".

     WHAT THE PILL IS. Held sideways, the Dynamic Island (a notch on a 13) sits on the phone's SHORT
     edge, which in landscape is the LEFT or RIGHT edge of the slide, and it is opaque hardware
     drawn on top of the page. iOS reports it as env(safe-area-inset-left/right). This sheet's own
     inset is --ls-padx, max(7px, 2.4vw), which is 18px on an iPhone 13 in landscape, so the
     leading edge of every slide was running underneath the island; on #p02 and #p10, which set
     padding:0 so the embroidered border can bleed, all of it was.

     WHY FOUR EMULATED VIEWPORTS SAID ZERO CLIPPED. Emulation renders the WebKit content area and
     none of the device: no island, no home indicator, no browser chrome, and every
     env(safe-area-inset-*) resolves to 0px. That gate was not lying about what it measured, it
     could not see the thing that was wrong. Which is why every reserve below reads from a --sai-*
     custom property rather than from env() directly: seeded from env() on the device, and
     overridable by a headless gate so the SAME layout can be measured under real inset values.
     pipeline/portrait/safe-area-verify.mjs sweeps them 0..59px and reports the largest the deck
     survives, instead of asserting a device profile from a blog post.

     FULL BLEED IS KEPT, which is what he actually asked for. The slide is still 100svw x 100svh and
     every background, border tile and photograph still runs to the physical edge. Only the CONTENT
     box pulls in. A full-bleed picture behind an island is correct; a sentence behind one is not.

     THE BOTTOM IS NOT env()'s TO ANSWER, and that is the trap in the obvious fix. Per Apple's own
     report (developer.apple.com/forums/thread/716552) iOS 15+ returns 0 for
     safe-area-inset-bottom whenever the toolbar is minimised, in BOTH orientations, where iOS 14
     returned 21px in landscape. Minimised is exactly the state in which Safari's compact bar floats
     over the page, so the single value that would fix it is guaranteed to read 0 at the moment it
     is needed.

     AND THE BOTTOM RESERVE IS NOT SHIPPED, because it did not survive its own measurement. The first
     version of this fix floored the bottom at Apple's 21px landscape strip on any coarse pointer.
     safe-area-verify.mjs priced that: six slides stopped fitting on a 293px-tall canvas (p07 +21px,
     p04 +24px, p05, p09, p09b, p10), against a mechanism I cannot confirm from this box — the side
     island is reported geometry, the minimised bottom bar is a hypothesis. Paying 21px of a 293px
     budget to defend a guess, and breaking six slides that currently work to do it, is the wrong
     trade. So --ios-bar stays 0 and only what iOS actually REPORTS is honoured: --sai-b is still
     read, so a device that declares a bottom inset gets it. bc/phone-probe.html prints the real
     numbers off Adrian's own phone; if they say a bottom strip is covering copy, --ios-bar becomes
     that measured value and this comment gets the number instead of the argument. */
  :root {
    --sai-t: env(safe-area-inset-top, 0px);
    --sai-r: env(safe-area-inset-right, 0px);
    --sai-b: env(safe-area-inset-bottom, 0px);
    --sai-l: env(safe-area-inset-left, 0px);
    --ios-bar: 0px;
  }
  /* Left at 0 on purpose, and kept as a named token rather than deleted so that turning it on is a
     one-line change with a measured number behind it, not a rediscovery of the whole problem. */

  .slide {
    --ls-fit: 1;
    --ls-h2:    max(19px,   calc(min(3.3vw,  8.4vh) * var(--ls-fit)));
    --ls-h3:    max(13px,   calc(min(2.2vw,  5.2vh) * var(--ls-fit)));
    --ls-body:  max(12px,   calc(min(1.72vw, 4.2vh) * var(--ls-fit)));
    --ls-small: max(12px,   calc(min(1.62vw, 4.0vh) * var(--ls-fit)));
    --ls-padx:  max(7px,    calc(2.4vw * var(--ls-fit)));
    --ls-pady:  max(4px,    calc(2.2vh * var(--ls-fit)));
    /* The four reserves the whole sheet pads against. Every horizontal inset below is one of these
       rather than --ls-padx, so the island is honoured in ONE place and cannot be forgotten on the
       next slide-specific rule. They do NOT scale with --ls-fit: a hardware inset is a fixed
       physical strip, and shrinking the type does not move the island. */
    --padt: max(var(--ls-pady), var(--safe-t));
    --padr: max(var(--ls-padx), var(--safe-r));
    --padb: max(var(--ls-pady), var(--safe-b));
    --padl: max(var(--ls-padx), var(--safe-l));
    /* THE HARDWARE FLOOR ON ITS OWN, for the slides that deliberately pad TIGHTER than the sheet's
       default and must keep doing so. #p04 carries 1,234 characters and buys width with a 0.72vw
       horizontal inset against the sheet's 2.4vw; #p08 sets its own three-value padding. Folding
       those into --padl/--padr would hand them the generic design padding and undo a measured
       decision. The inset is a FLOOR, not a replacement: max(own, --safe-l) keeps the intent and
       still refuses to put a sentence under the island. */
    --safe-t: var(--sai-t);
    --safe-r: var(--sai-r);
    --safe-b: max(var(--sai-b), var(--ios-bar));
    --safe-l: var(--sai-l);
  }
  html, body { overflow-x: hidden; }
  .wrap { zoom: 1; gap: 0; padding: 0; align-items: stretch; }

  .slide {
    width: 100vw !important; max-width: 100vw;
    height: 100svh !important; min-height: 0;
    padding: var(--padt) var(--padr) var(--padb) var(--padl);
    overflow: hidden; box-shadow: none;
    display: flex !important; flex-direction: column !important;
    gap: max(2px, calc(min(1.4vh, 11px) * var(--ls-fit)));   /* scales with the slide, or gaps eat the budget at small fit */
  }
  /* BLOCKS KEEP THEIR NATURAL HEIGHT AND ARE NOT ALLOWED TO SHRINK. This looks backwards on a
     fixed-height slide and it is the single most important rule here. With the default
     `flex-shrink:1` plus `min-height:0`, a block that does not fit is silently squeezed and its
     own copy spills out under `overflow:hidden` IN THE MIDDLE OF THE SLIDE: p07's three captions
     were printing straight through the foot line, text on text, and every top-level height sum
     still added up perfectly because the boxes had obediently shrunk. Pinned at their natural
     height, the same excess lands at the BOTTOM of the slide where it is obvious, measurable, and
     attributable to one block, which is what landscape-verify.mjs reports. It converts an
     invisible failure into a visible one; it does not create the failure. */
  .slide > * { flex: 0 0 auto; min-width: 0; }

  /* THE GRAIN OVERLAY IS NOT A ROW, AND THE DECK'S OWN ID RULE MAKES IT ONE.
     deck-v10.html:477 sets `#p03>*,#p06>*,#p02>*,#p10>*{position:relative}` at (1,0,0), which beats
     `.grain{position:absolute}` at (0,1,0), so on exactly those four slides the texture overlay
     becomes a zero-height FLEX ITEM that still drags a full row gap behind it. Measured at 802x293:
     p02 overflowed by 63px and p06 by 11px with the grain contributing every pixel of it. This is
     the same defect that put 30px of dead cream under p02's roll-call band in portrait; it was
     fixed there and the landscape sheet inherited it untouched.

     AND THE ID-SCOPED GAPS GO WITH IT. `#p02{gap:30px}` (deck-v10.html:116) is a 1600x900 number at
     ID specificity, so it survived into a 293px frame: four gaps of 30px is 120px, 41% of the whole
     screen, spent on air. Restated here so it scales with the slide like everything else. */
  #p02 .grain, #p03 .grain, #p06 .grain, #p10 .grain { position: absolute !important; }

  /* ═══ 11  THE CLOSING SLIDE ══════════════════════════════════════════════════════════════════
     Left out of the per-slide pass because it measured clean, and it was not: the checks count runs
     that escape the slide box, and p11's failure was that nothing escaped. LOOKING at it is what
     found it. Two ID rules from the desktop survived into the landscape query, as usual:

       `#p11{padding:0}` beat `.slide`'s padding, so the copy sat flush on the top edge of the
       screen and the Kannada's ascenders were shaved by `overflow:hidden`.
       `#p11{display:grid; grid-template-rows:1fr auto}` is the whole composition: .mid takes the
       free row and centres in it, .steps is pinned to the bottom. The landscape sheet forces
       `display:flex` on every slide, which threw that away, so .mid became a 47px block at the top
       and .steps sat right under it with 174px of dead maroon below.

     Restated in the flex the sheet imposes: .mid takes the free space and centres inside it, .steps
     stays last and therefore bottom. Same composition, same two rows, expressed in the layout this
     breakpoint actually uses. */
  /* The bottom stays 0 by design — .steps is pinned to the bottom edge and any bottom padding pushes
     the slide over (measured: +5px on a 293px canvas). Only the hardware FLOOR is added here, which
     is 0 on a phone that reports no bottom inset, so the composition is untouched. */
  #p11 { padding: var(--padt) 0 var(--safe-b); }
  #p11 .mid {
    flex: 1 1 auto; display: grid !important; place-content: center;
    padding: 0 var(--padr) 0 var(--padl); min-height: 0;
  }
  #p11 .kan { font-size: var(--ls-h3) !important; margin: 0 0 max(3px, calc(1.6vh * var(--ls-fit))); }
  #p11 h2 { font-size: max(24px, calc(min(5.6vw, 12vh) * var(--ls-fit))) !important; line-height: 1.04; }
  #p11 .steps {
    padding: max(6px, calc(2.4vh * var(--ls-fit))) var(--padr)
             max(6px, calc(2.4vh * var(--ls-fit))) var(--padl);
    gap: max(8px, calc(2.4vw * var(--ls-fit)));
  }
  /* The step SENTENCE keeps its desktop 28px because `#p11 .steps span` is id-scoped and nothing
     here reached it, while the NUMERAL took --ls-h3 and shrank to about half its partner: on the
     desktop the numeral is 34 against a 28 sentence, larger, and here it had become smaller and
     nearly vanished. Both are set now, in the control's own order. */
  #p11 .steps span { font-size: max(13px, calc(min(2.05vw, 4.6vh) * var(--ls-fit))) !important; }
  #p11 .steps b    { font-size: max(16px, calc(min(2.55vw, 5.7vh) * var(--ls-fit))) !important; }
  #p02, #p03, #p06, #p10 { gap: max(2px, calc(min(1.4vh, 11px) * var(--ls-fit))); }

  /* ── generic floor ────────────────────────────────────────────────────────────────────────── */
  /* Unlike portrait, the desktop's multi-column grids are KEPT: they are the reason to be here.
     Only the type is re-set, and only the fixed pixel columns that assume a 1600px canvas are
     relaxed to fractions. A slide is DONE when it has a block of its own below. */
  .slide h1 { font-size: clamp(30px, 4.4vw, 44px) !important; line-height: 1.02; }
  .slide h2 { font-size: var(--ls-h2) !important; line-height: 1.06; max-width: none; }
  .slide h3 { font-size: var(--ls-h3) !important; line-height: 1.14; }
  .slide p, .slide li, .slide dd, .slide q, .slide figcaption, .slide .sub, .slide .def,
  .slide li span, .slide .amt i, .slide .lede {
    font-size: var(--ls-body) !important; line-height: 1.32;
  }
  .slide dt, .slide .lbl span, .slide label, .slide .places b { font-size: var(--ls-small) !important; }
  .slide img, .slide video { max-width: 100%; }

  /* ═══ 02  THE THREE REVIEWS STAY A SET ═══════════════════════════════════════════════════════
     The desktop argument is three voices read as one body of evidence, side by side. That is what
     the portrait card cannot hold and what this orientation exists to keep. All three, across, with
     the panels the control has, at the size this canvas can carry. Nothing is hidden here: the
     portrait rule that drops review 2 lives in the portrait query and does not reach this one. */
  #p02, #p10 {
    background-image: none !important;
    background-color: #FEF0D6;
    border-style: solid;
    border-width: clamp(13px, 3.4vh, 20px) 0;
    border-image: url(../assets/bg/bg-border-tile.png) 207 150 round;
    padding: 0;
  }
  #p02 > h2, #p02 > .sub { padding: 0 var(--padr) 0 var(--padl); }
  #p02 > h2 { padding-top: clamp(6px, 1.8vh, 12px); }
  #p02 .quotes {
    display: grid !important; grid-template-columns: repeat(3, 1fr) !important;
    gap: clamp(8px, 1.4vw, 14px) !important; padding: 0 var(--padr) 0 var(--padl);
    align-content: start; min-height: 0;
  }
  #p02 .q {
    background: #FFF8EA; border-left: 3px solid var(--brick-dk);
    padding: clamp(6px, 1.8vh, 10px) clamp(8px, 1.1vw, 12px);
    display: block !important; min-height: 0;
  }
  #p02 .q p { color: var(--roast) !important; font-size: var(--ls-body) !important; line-height: 1.3; }
  #p02 .q span {
    color: var(--brick-dk); font-size: var(--ls-small) !important;
    margin-top: clamp(3px, 0.9vh, 6px); display: block;
  }
  #p02 .places {
    display: flex !important; flex-direction: row !important; flex-wrap: wrap;
    padding: clamp(5px, 1.5vh, 9px) var(--padr)
             max(clamp(6px, 1.8vh, 11px), var(--safe-b)) var(--padl);
    gap: clamp(1px, 0.5vh, 4px) clamp(8px, 1.3vw, 16px); align-items: baseline;
  }
  #p02 .places b { width: 100%; }
  #p02 .places i { font-size: var(--ls-h3); }

  /* ═══ 07  THREE STATES OF ONE ROOM, IN A ROW ═════════════════════════════════════════════════
     This is the slide that proves the orientation. Its job is to let you compare three states of
     one wall, and the portrait build stacked them full-bleed at 100vw so you could never see two at
     once: the comparison was physically impossible, which is a fatal finding no amount of type
     tuning reaches. Across, they are a comparison again, on the maroon field the desktop puts them
     on, with their own caption plates. The photographs go back to being objects on a field rather
     than banners. */
  #p07 .steps3 {
    display: grid !important; grid-template-columns: repeat(3, 1fr) !important;
    gap: clamp(8px, 1.3vw, 15px) !important; min-height: 0;
  }
  #p07 .steps3 figure { margin: 0; min-height: 0; display: flex; flex-direction: column; }
  #p07 .steps3 img { width: 100%; aspect-ratio: 4/3; object-fit: cover; }
  #p07 .steps3 figcaption {
    padding: clamp(4px, 1.2vh, 8px) clamp(6px, 0.9vw, 11px);
    font-size: var(--ls-small) !important; line-height: 1.26;
  }
  #p07 .steps3 figcaption b { font-size: var(--ls-h3) !important; display: block; }

  /* ═══ 10  THE LEDGER KEEPS ITS TWO COLUMNS ═══════════════════════════════════════════════════
     LOCKS 7c row 6: a priced column is one group and is never broken up. Side by side, the two
     offers are comparable at a glance, which is what a ledger is for. */
  #p10 .buys {
    display: grid !important; grid-template-columns: 1fr 1fr !important;
    gap: clamp(10px, 1.6vw, 18px) !important; min-height: 0;
  }
  #p10 .buys > section { padding: clamp(8px, 2vh, 14px) clamp(10px, 1.4vw, 16px); min-height: 0; }
  #p10 .amt { font-size: clamp(24px, 3.2vw, 34px) !important; margin-bottom: clamp(4px, 1.2vh, 8px); }
  #p10 li b { font-size: var(--ls-small) !important; display: block; }
  #p10 li { padding-bottom: clamp(3px, 0.9vh, 6px); margin-bottom: clamp(3px, 0.9vh, 6px); }
  #p10 > h2 { padding: 0 var(--padr) 0 var(--padl); }
  #p10 .nolock {
    padding: 0 var(--padr) var(--safe-b) var(--padl); font-size: var(--ls-h3) !important;
  }

  /* ═══ 04  THE DENSEST SLIDE ══════════════════════════════════════════════════════════════════
     1,234 characters, the most on the deck by a margin, and the one that decides whether this
     orientation carries the whole deck or only most of it. Its desktop logic is already horizontal
     (two rails compared side by side), so it is kept and only the type and the fixed pixel widths
     are re-set. Measured, not assumed: see the fits table in bc/LANDSCAPE.html. */
  #p04 .rails { gap: clamp(8px, 1.3vw, 15px) !important; min-height: 0; }
  #p04 .rails img, #p04 .rails video { aspect-ratio: 9/16; }
  #p04 .c .ev img, #p04 .c .ev video { width: clamp(38px, 4.4vw, 62px); }
  #p04 .c { min-height: 0; }
}
