  /* Display: Sorts Mill Goudy 400, roman AND true italic (one family, one weight).
     Prose: Sentient 400/500/700 (Fontshare). UI: system-ui.
     font-synthesis is off everywhere, so no synthetic oblique or synthetic bold can
     ever be drawn: every italic on this page is the real cut.
     Measured at 100px (canvas, Chrome): Goudy cap 71, x-height 42.5, ascent 96,
     descent 48. All display maths below is computed from those numbers. */
  @font-face{font-family:"Sorts Mill Goudy";font-weight:400;font-style:normal;font-display:swap;src:url("smg-normal.woff2") format("woff2");}
  @font-face{font-family:"Sorts Mill Goudy";font-weight:400;font-style:italic;font-display:swap;src:url("smg-italic.woff2") format("woff2");}
  @font-face{font-family:"Sentient";font-weight:400;font-style:normal;font-display:swap;src:url("sentient400.woff2") format("woff2");}
  @font-face{font-family:"Sentient";font-weight:500;font-style:normal;font-display:swap;src:url("sentient500.woff2") format("woff2");}
  @font-face{font-family:"Sentient";font-weight:700;font-style:normal;font-display:swap;src:url("sentient700.woff2") format("woff2");}

  :root{
    --paper:#f5f3ee; --paper-2:#f0ebe0; --paper-3:#efece4;
    --ink:#1b1a17; --ink-2:#5d594f; --ink-sky:#3c3931; --ink-3:#6f6a5f;
    --line:rgba(27,26,23,.12); --line-2:rgba(27,26,23,.07);
    --navy:#264063; --navy-deep:#1b2e47;
    --gold:#b0842f;
    /* Ghost numerals: paper tones, never gray. */
    --ghost:#f0ece2;
    --display:"Sorts Mill Goudy","Iowan Old Style",Palatino,Georgia,serif;
    --prose:"Sentient","Iowan Old Style",Georgia,serif;
    --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    /* v9: the measure widens and the gutters tighten. The page was running narrow
       with too much dead paper at both rims; the content now takes 1280 with a
       tighter rail, and every photograph is larger inside it. */
    --maxw:1280px; --pad:clamp(18px,3.6vw,56px); --navh:60px;
    /* The book's outer margin: where the numerals and the printed margin notes live. */
    --marg:clamp(0px,12vw,168px); --mgap:clamp(0px,2.8vw,40px);
    /* ~2% film grain, feTurbulence. Always behind content, never over it. */
    --grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
    /* One eased curve for the whole page, so nothing on it moves on a different law. */
    --ease:cubic-bezier(.16,1,.3,1);
    /* The Amazon wordmark's lockup against the button label. Every term measured off
       the rendered button at 36x, none of it assumed; see .act .amz.
         letters' band       0.24 to 107.43 of 182 -> 0.58896 of the svg's height
         so svg height       = cap-height / 0.58896 = 0.7046em / 0.58896 = 1.196em
         letters' centre     = 0.29580 of the svg, 0.24423em above its box centre
         label's cap centre  = 0.0295em BELOW the button's content box centre
         so the push down    = 0.24423 + 0.0295 = 0.274em */
    --amzh:1.196em; --amzy:.274em;
  }
  *{box-sizing:border-box;}
  html{scroll-behavior:smooth;}
  html,body{overflow-x:clip;}
  body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--prose);font-size:1.08rem;line-height:1.62;
       font-synthesis:none;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;}
  /* The one italic voice on the page: Sorts Mill Goudy italic, real cut, weight 400. */
  .ital{font-family:var(--display);font-weight:400;font-style:italic;}
  ::selection{background:rgba(38,64,99,.16);}
  img{max-width:100%;height:auto;display:block;}
  /* NOTE: no image on this site carries loading="lazy". Every photograph is a real
     file now rather than an inlined data URI, but lazy loading is still wrong here:
     it left images blank in a horizontal scroller (whose cards never "approach" the
     viewport vertically), in print, and in any capture of the page. Every img carries
     its intrinsic width and height, so nothing shifts while they arrive, and content
     is visible by default. */
  a{color:inherit;}
  h1,h2,h3,p{margin:0;}
  header,section,footer{position:relative;}
  /* The paper substrate: grain on every paper surface, always behind the content. */
  .chap::before,.foot::before{content:"";position:absolute;inset:0;background-image:var(--grain);
    opacity:.02;pointer-events:none;z-index:0;}
  .shell{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad);}
  .disp{font-family:var(--display);font-weight:400;}
  .grain{position:absolute;inset:0;background-image:var(--grain);opacity:.028;pointer-events:none;z-index:0;}

  /* MOTION: section reveals are position only. Opacity is never below 1, at any time. */
  /* Reveals settle up AND sharpen. The content is on the page at every instant --
     blurred and 16px low is still present, so a missed observer costs crispness for
     a beat, never the words. */
  html.js .reveal{transform:translateY(16px);
    transition:transform .55s var(--ease),filter .6s var(--ease);}
  html.js .reveal.in{transform:none;filter:none;}

  /* ── THE ACTION ───────────────────────────────────────────────────
     One buy button, one navy, at three sizes. It never lifts or scales on hover:
     the fill deepens and the external-link arrow travels out along its own diagonal.
     The shadow is tight, low-offset and tinted to the button's OWN navy rather than
     a black bloom, so the button sits on the paper instead of floating on a cloud. */
  /* line-height is `normal`, not 1.2, and that is load-bearing for the Amazon lockup
     below. At 1.2 the label's line box is fractional (17.088px in the nav, 20.928px in
     the hero) and the flex line rounds it to 17 and 21, which displaces the label's
     line box from the flex line's centre by a DIFFERENT amount at each size: measured
     -0.039px in the nav but -0.961px in the hero. The mark is centred on the flex line,
     so a single em constant cannot then serve both, and the hero's mark sat 1.03px low.
     At `normal` the line box is the font's own ascent+descent, which is exactly the
     relationship --amzy is derived from, and the mark's centre lands 0.274em under the
     label's line box centre at BOTH sizes. Every button here is single-line, so this
     changes nothing else: the buttons resolve 0.06px shorter and 0.08px taller. */
  .act{display:inline-flex;align-items:center;gap:.62em;font-family:var(--sans);font-weight:600;font-size:.95rem;line-height:normal;
       background:var(--navy);color:#fff;border:0;border-radius:3px;padding:13px 24px;text-decoration:none;cursor:pointer;
       box-shadow:0 1px 2px rgba(27,46,71,.28);transition:background .22s var(--ease),box-shadow .22s var(--ease);}
  .act .ar{flex:0 0 auto;transition:transform .28s var(--ease);}
  /* THE AMAZON MARK. The real wordmark, bare: no tile, no chip, no box, every fill
     stripped so it takes the button's own white. It appears only on buttons that
     actually go to Amazon.
     Sizing and centring are measured in the browser, not guessed. In the mark's own
     603x182 box the letterforms run y 0.24 to 107.43 and the smile hangs from 109.76
     to 181.74, so 41% of the glyph box sits BELOW the letters it is named for and a
     mark centred by its box would ride high and read as misaligned.
       letters' height    107.19 of 182   -> svg height = cap-height x 1.698
       letters' centre    53.84 of 182    -> 0.2958 down the svg, not 0.5
     The svg is therefore set to (label cap-height x 1.698) so the LETTERS stand at the
     label's cap height, and then pushed down so the letters' centre lands on the
     label's cap-band centre rather than on its line-box centre. Both terms are
     measured off the rendered button; the resolved values are in --amzh and --amzy and
     the check that they are right is in the QA pass, which reports the residual in px.
     No box, no tile, no chip: the bare mark on the button's own fill. */
  .act .amz{flex:0 0 auto;height:var(--amzh);width:auto;fill:currentColor;
            transform:translateY(var(--amzy));}
  .act:active{box-shadow:0 1px 1px rgba(27,46,71,.34);}
  /* The hero's buy button: the biggest single control on the page, and the only one
     on that screen, so nothing competes with it. */
  .act.big{font-size:1.09rem;padding:19px 34px;border-radius:4px;letter-spacing:.005em;
           box-shadow:0 2px 5px rgba(27,46,71,.3);}
  a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,.act:focus-visible{outline:2px solid var(--navy);outline-offset:3px;}
  @media (hover:hover) and (pointer:fine){
    .act:hover{background:var(--navy-deep);}
    .act:hover .ar{transform:translate(2.5px,-2.5px);}
    .nav-links a:hover{color:var(--navy);}
    .foot a:hover{color:var(--navy);}
    .buylink:hover{border-bottom-color:currentColor;}
  }

  /* ── NAV: floats transparent over the sky, resolves to paper on scroll ── */
  /* v13: THE BAR GETS OUT OF THE WAY.
     Scrolling down past 200px lifts the bar off the top; any upward scroll brings it
     back; at the top of the page it is always down. Transform only, so nothing below
     it moves a pixel either way, and the hero already sits under the bar by its own
     negative margin, so there is nothing to reflow. Reduced motion keeps the behaviour
     and drops the travel: the bar toggles instantly, no transition. */
  .nav{position:sticky;top:0;z-index:60;background:transparent;border-bottom:1px solid transparent;
       transition:border-color .25s var(--ease),background .25s var(--ease),
                  transform .28s cubic-bezier(.16,1,.3,1);}
  .nav.up{transform:translateY(-100%);}
  .nav.solid{background:rgba(245,243,238,.97);backdrop-filter:saturate(1.2);border-bottom-color:var(--line);}
  .nav-in{display:flex;align-items:center;justify-content:space-between;gap:20px;padding-block:13px;min-height:34px;}
  .brand{display:flex;align-items:center;gap:11px;text-decoration:none;}
  .brand .m{width:28px;height:28px;background:url("mark.png") center/contain no-repeat;flex:0 0 auto;}
  .brand .n{font-family:var(--display);font-weight:400;font-size:1.14rem;letter-spacing:.012em;}
  .nav-links{display:flex;gap:24px;}
  .nav-links a{font-family:var(--sans);text-decoration:none;color:var(--ink);font-size:.9rem;transition:color .2s var(--ease);}
  .nav-right{display:flex;align-items:center;gap:12px;}
  .nav .act{padding:10px 17px;font-size:.89rem;}
  .burger{display:none;background:none;border:1px solid var(--line);border-radius:3px;padding:8px 10px;cursor:pointer;color:var(--ink);
          align-items:center;gap:8px;font-family:var(--sans);font-size:.88rem;}
  .burger svg{display:block;}
  .drawer{display:none;position:fixed;left:0;right:0;top:var(--bar,60px);bottom:0;background:var(--paper);z-index:55;
          padding:min(7vh,48px) var(--pad) 40px;overflow-y:auto;}
  .drawer.open{display:block;}
  .drawer a{display:grid;grid-template-columns:3.4rem 1fr;align-items:baseline;font-family:var(--display);font-weight:400;font-size:1.68rem;
            text-decoration:none;color:var(--ink);padding:15px 0;border-bottom:1px solid var(--line-2);}
  .drawer a i{font-style:normal;font-family:var(--display);font-size:.82rem;letter-spacing:.16em;color:var(--navy);}
  .drawer .m-note{font-family:var(--sans);color:var(--ink-3);font-size:.88rem;margin-top:26px;}
  @media (max-width:940px){ .nav-links{display:none;} .burger{display:inline-flex;} }
  /* At 360 the bar holds the brand, the CTA and the drawer trigger without wrapping,
     so the trigger keeps its accessible name but drops the visible word. */
  @media (max-width:520px){
    .nav-in{gap:10px;}
    .brand{gap:8px;} .brand .m{width:24px;height:24px;} .brand .n{font-size:1.03rem;}
    .nav .act{padding:11px 14px;font-size:.82rem;gap:.45em;white-space:nowrap;}
    .burger{padding:8px 9px;min-width:40px;min-height:40px;justify-content:center;}
    .burger .t{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
  }

  /* ── HERO: the sky is the artifact, the title is left, the book is right ──
     Full-bleed dawn sky, one contrail. The image's OWN pixels are masked with a
     long, many-stop bottom feather into --paper, and the section below is the same
     --paper, so there is no seam anywhere. */
  .hero{position:relative;margin-top:calc(-1 * var(--navh));min-height:100svh;overflow:hidden;background:#f2e9d8;}
  /* Legibility and mood: a scrim tuned to the photo's own deep green, heavy at the left
     where the copy sits and across the top behind the bar, clearing to nothing over Dr.
     Bean on the right, and closing to paper at the foot so the section below meets no
     hard seam. Not a flat black wash. */
  .hero::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
    background:
      linear-gradient(to right, #f2e9d8 0%, #f2e9d8 38%, rgba(242,233,216,.92) 48%, rgba(242,233,216,.55) 58%, rgba(242,233,216,.14) 68%, rgba(242,233,216,0) 76%),
      linear-gradient(to bottom, rgba(242,233,216,.62) 0%, rgba(242,233,216,0) 14%),
      linear-gradient(to bottom, rgba(242,233,216,0) 74%, #f2e9d8 97%);}
  @media (max-width:760px){
    /* On a phone the copy runs the full width, so the left-weighted scrim is not
       enough: an even vertical wash keeps every line legible over the greenery. */
    .hero::after{background:
      linear-gradient(to bottom, rgba(242,233,216,.9) 0%, rgba(242,233,216,.58) 40%, rgba(242,233,216,.84) 76%, #f2e9d8 100%);}
    .hero-photo img,.hero-photo video{position:absolute;inset:0;width:100%;height:100%;object-position:50% 26%;}
  }
  .hero-photo{position:absolute;inset:0;z-index:0;}
  /* object-position is 8% across, not centred. The photograph's baked plane speck sits
     at x=105 of 1800, and `cover` crops horizontally on every viewport narrower than
     16:9; centring the crop would throw the speck (and therefore the whole flight's
     anchor) off the left edge on any tall window. Biasing the crop to the left keeps
     it in frame. The sky is near-empty, so the composition pays nothing for it. */
  .hero-photo img,.hero-photo video{position:absolute;right:0;top:0;width:62%;height:100%;
    object-fit:cover;object-position:50% 30%;}
  /* The film dissolves into the paper on its left through the cream scrim; the right
     edge gets the same courtesy from the video's OWN pixels — a long, many-stop
     feather — so the frame floats in the page instead of slamming into the bezel. */
  @media (min-width:761px){
    .hero-photo img,.hero-photo video{
      -webkit-mask-image:linear-gradient(to right,#000 0%,#000 72%,
        rgba(0,0,0,.97) 76%,rgba(0,0,0,.90) 80%,rgba(0,0,0,.78) 84%,rgba(0,0,0,.60) 88%,
        rgba(0,0,0,.40) 92%,rgba(0,0,0,.22) 95%,rgba(0,0,0,.09) 97.5%,transparent 100%);
      mask-image:linear-gradient(to right,#000 0%,#000 72%,
        rgba(0,0,0,.97) 76%,rgba(0,0,0,.90) 80%,rgba(0,0,0,.78) 84%,rgba(0,0,0,.60) 88%,
        rgba(0,0,0,.40) 92%,rgba(0,0,0,.22) 95%,rgba(0,0,0,.09) 97.5%,transparent 100%);}
  }
  /* The phone rule above (line ~193) loses the cascade to the unscoped 62% rule,
     which is written after it. Restated here, AFTER the desktop rule, so on phones
     the film genuinely runs full-bleed under the vertical scrim. */
  @media (max-width:760px){
    .hero-photo img,.hero-photo video{position:absolute;inset:0;right:auto;width:100%;height:100%;object-position:50% 26%;}
  }

  /* THE FLIGHT. v10 takes the aircraft off the scrollbar entirely: it is a load
     moment now, not a scrubbed overlay. Six hundred milliseconds after the page
     settles, one drawn jet leaves the baked speck in the photograph's lower left and
     flies the whole diagonal out through the top-right corner in 2.4 seconds, its
     contrail drawing continuously behind it. It happens once. What is left afterwards
     is the finished trail across the sky.

     The path is a single cubic, built in the photograph's own 1800x1012 pixel space
     and re-solved on every resize from the exact source rectangle the `cover` crop is
     showing, so it is laid on the real photograph and not on the viewport:
       start   (105, 625)      the baked speck, measured off the image
       leaves  at -9deg        the tangent of the baked wisp, so the drawn trail
                               continues the printed one with no kink at the join
       ends    top-right corner of the visible crop, and then runs ON past it along
                               its own end tangent, so the aircraft exits the frame
                               and is clipped by the SVG's own viewport rather than
                               stopping dead in the corner.
     Stroke width and the jet's scale are solved per viewport from the viewBox-to-pixel
     ratio, so the aircraft is ~15 CSS px and the trail ~3.2 CSS px at every size
     instead of growing with the crop.

     Tones are sampled from the photograph: the trail is the warm white of the baked
     wisp at 85%, the jet the warm brown of the baked speck. With no JS, with reduced
     motion, or on a crop that has cut the speck out of frame, the overlay never turns
     on and the page shows the untouched photograph, whose own speck and wisp read
     perfectly well on their own. */
  .ptrail{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none;opacity:0;
    --feather:linear-gradient(to bottom,
      #000 0%, #000 60%,
      rgba(0,0,0,.985) 64%, rgba(0,0,0,.955) 68%, rgba(0,0,0,.905) 72%,
      rgba(0,0,0,.835) 76%, rgba(0,0,0,.735) 80%, rgba(0,0,0,.61) 84%,
      rgba(0,0,0,.465) 87.5%, rgba(0,0,0,.315) 91%, rgba(0,0,0,.18) 94.5%,
      rgba(0,0,0,.075) 97.5%, transparent 100%);
    -webkit-mask-image:var(--feather);mask-image:var(--feather);}
  html.plane-on .ptrail{opacity:1;}
  /* Warm white, 85%, taken off the baked wisp. */
  .ptrail-line{fill:none;stroke:rgba(255,243,222,.85);stroke-linecap:round;stroke-linejoin:round;}
  /* Warm brown, taken off the baked speck. */
  .ptrail-plane{fill:rgba(168,120,58,.95);offset-rotate:auto;offset-distance:0%;
    transform-box:fill-box;transform-origin:50% 50%;}

  .hero-in{position:relative;z-index:2;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad);
           min-height:100svh;display:flex;flex-direction:column;justify-content:center;
           padding-top:calc(var(--navh) + clamp(18px,4vh,44px));padding-bottom:clamp(44px,11vh,120px);}
  .hero-copy{max-width:min(600px,54ch);color:var(--ink);}
  /* The copy enters WITH the flight: half title, title, byline, the three lines, the
     action, 14px up from home on an 80ms stagger. Position only. Opacity is 1 on every
     one of them at every instant, including before the class lands and if it never
     does, so a stalled timer, a backgrounded tab or a capture pass can never leave the
     hero blank: the worst case is copy sitting 14px low. Under html.js only, so with
     no JS it is simply in place. */
  html.js .hero-copy > *{transform:translateY(30px);transition:transform .8s cubic-bezier(.16,1,.3,1);}
  html.js .hero-copy > *:nth-child(1){transition-delay:.05s;}
  html.js .hero-copy > *:nth-child(2){transition-delay:.16s;}
  html.js .hero-copy > *:nth-child(3){transition-delay:.28s;}
  html.js .hero-copy > *:nth-child(4){transition-delay:.4s;}
  html.js .hero-copy > *:nth-child(5){transition-delay:.52s;}
  html.js .hero-copy > :nth-child(2){transition-delay:80ms;}
  html.js .hero-copy > :nth-child(3){transition-delay:160ms;}
  html.js .hero-copy > :nth-child(4){transition-delay:240ms;}
  html.js .hero-copy > :nth-child(5){transition-delay:320ms;}
  html.hero-lit .hero-copy > *{transform:none;}
  .halft{font-family:var(--display);font-weight:400;font-style:italic;font-size:1.35rem;color:var(--navy);letter-spacing:.012em;}
  .hero-t{font-family:var(--display);font-weight:400;font-size:clamp(2.8rem,7.8vw,6.8rem);line-height:.94;letter-spacing:-.012em;color:var(--ink);
          margin-top:.14em;text-wrap:balance;}
  .hero-by{color:var(--ink-2);font-size:1.2rem;margin-top:clamp(14px,2vh,20px);}
  /* Three short lines, no bullet glyphs, no chips, no rules. Spacing ranks them. */
  .hero-pts{list-style:none;margin:clamp(20px,3.2vh,32px) 0 0;padding:0;max-width:46ch;}
  .hero-pts li{color:var(--ink);font-size:1.22rem;line-height:1.5;padding:7px 0;}
  .hero-pts li + li{margin-top:2px;}
  .hero-do{margin-top:clamp(24px,3.8vh,40px);display:flex;flex-direction:column;align-items:flex-start;gap:12px;}
  /* The second action wears the island button's paper, so the pair reads as one
     voice at two volumes rather than the stock filled-plus-outline couplet. */
  .act.sec{background:#f4efe4;color:var(--navy);border:1px solid rgba(39,55,79,.22);}
  .act.sec:hover{background:#fff;}
  /* v13: THE RENDER RIDES UP BESIDE THE TITLE.
     It used to be anchored to the bottom of the hero row and pulled a further
     52-116px down, so it crossed the sky's bottom feather into Chapter I. The client
     read it as sitting too low against the title, and it did: at 1280x900 its cap was
     224px below the title's.
     Raising it to the title's optical band and KEEPING the bleed is not possible on
     this composition. To span from the title cap (y 249 at 1280x900) past the hero's
     floor (904) the render would have to be ~700px tall, which is 670px wide, and the
     right column is only 519-534px wide at 1280-1600: it would run into the copy. So
     the depth bleed is spent and a clean bottom margin is kept in its place. The
     render now ends around y 700 with the CTA's baseline at 748, so the two columns
     resolve together and the feathered sky closes the frame under both.
     The mechanism is the grid, not a magic number: both columns are centred in the
     same row, and with the negative margin gone the art box IS the render, so its cap
     lands at rowCentre - h/2. Measured against the title cap that is +13.5px at 1600,
     +1.5px at 1280x900, -2.5px at 1280x800 and +17.5px at 900: inside the title's own
     cap band at every width, with no term that has to be re-tuned per viewport. */
  @media (max-width:880px){
    .hero-in{grid-template-columns:minmax(0,1fr);row-gap:clamp(22px,4vh,40px);align-items:start;
             padding-top:calc(var(--navh) + clamp(22px,5vh,54px));}
    .hero-pts{max-width:40ch;}
  }

  /* ── THE BOOK GRID ───────────────────────────────────────────────
     Every chapter is a printed page: a generous outer margin holding the
     ghost numeral and the margin notes, and an offset text column.
     Recto puts the margin left; verso puts it right. They alternate. */
  .chap{position:relative;overflow:clip;padding:clamp(54px,8vh,98px) 0;}
  .chap-in{position:relative;z-index:1;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad);
           display:grid;grid-template-columns:var(--marg) minmax(0,1fr);column-gap:var(--mgap);}
  .chap.verso .chap-in{grid-template-columns:minmax(0,1fr) var(--marg);}
  .col{grid-column:2;min-width:0;}
  .chap.verso .col{grid-column:1;}
  .chap.wide{--marg:0px;--mgap:0px;}

  /* Ghost numeral: tonal paper, cropped by the page edge on purpose, always under the
     text. v9 keeps four of them, not seven: at every chapter it had become wallpaper. */
  .ghost{position:absolute;z-index:0;pointer-events:none;user-select:none;
         font-family:var(--display);font-weight:400;font-size:clamp(146px,33vh,364px);line-height:.78;
         letter-spacing:.035em;color:var(--ghost);top:clamp(28px,6vh,86px);will-change:transform;}
  .chap .ghost{left:0;transform:translateX(calc(-1 * var(--gx,16%))) translateY(var(--gy,0px));}
  .chap.verso .ghost{left:auto;right:0;transform:translateX(var(--gx,16%)) translateY(var(--gy,0px));}

  /* Chapter opening */
  .chap-open{margin-bottom:clamp(28px,4.2vh,48px);}
  .folio{font-family:var(--display);font-weight:400;font-size:.82rem;letter-spacing:.22em;text-transform:uppercase;color:var(--navy);}
  .chap-t{font-family:var(--display);font-weight:400;font-size:clamp(2.1rem,5vw,3.7rem);line-height:1.04;letter-spacing:-.008em;
          margin-top:14px;max-width:16ch;text-wrap:balance;}
  /* Epigraph: the memoir's aside voice, so it takes the true italic. Goudy italic
     x-height is 43.8 against Sentient's 50.2 at the same size, so it is set 12% up
     to hold the same optical size it had in the prose face. */
  .epi{font-family:var(--display);font-weight:400;font-style:italic;font-size:clamp(1.28rem,2.4vw,1.7rem);line-height:1.36;
       color:var(--ink-2);margin-top:16px;max-width:40ch;text-wrap:pretty;}

  /* Body copy inside a chapter */
  .prose{color:var(--ink-2);font-size:1.16rem;line-height:1.66;max-width:56ch;}
  .prose p{margin:0 0 18px;}
  .prose p:last-child{margin-bottom:0;}
  .prose b,.prose strong{font-weight:700;color:var(--ink);}
  /* Real drop cap: Sorts Mill Goudy roman, navy, three lines deep. Measured at 100px:
     Goudy cap 71, ascent 96, descent 48; Sentient (the prose face) cap 68.5.
     Prose line-height is 1.66em, so:
       line 1 baseline      = (1.66 - 1.28)/2 + 0.96      = 1.150em
       line 1 cap top       = 1.150 - 0.685               = 0.465em
       line 3 baseline      = 1.150 + 2 x 1.66            = 4.470em
       cap height needed    = 4.470 - 0.465               = 4.005em
       font-size            = 4.005 / 0.71                = 5.64em
     Goudy sits high in its own line box, so the float box has to be short and pushed
     down for the glyph to land right; solving for a bottom of 4.90em (0.08em clear of
     line four) gives line-height .633 and margin-top 1.332 prose-em.
     NOTE the unit trap: em inside ::first-letter resolves against the FIRST LETTER's
     own font-size, so 1.332 prose-em is written as 1.332 / 5.64 = .2361em here. */
  .dc{min-height:5.1em;}
  .dc::first-letter{font-family:var(--display);font-weight:400;font-style:normal;color:var(--navy);
    float:left;font-size:5.64em;line-height:.633;padding-right:.085em;margin-top:.2361em;}
  /* Between 601 and 900 the measure gets wide and these paragraphs run short, so the
     cap drops to two lines: a three-line cap there would hang past its own paragraph.
     Same solve, one line shallower. */
  @media (min-width:601px) and (max-width:900px){
    .dc{min-height:3.4em;}
    .dc::first-letter{font-size:3.30em;line-height:.74;margin-top:.2409em;}
  }

  /* Printed margin note: floats out into the outer margin, becomes an inline aside on phones. */
  .mnote{font-family:var(--display);font-weight:400;font-style:italic;font-size:.97rem;line-height:1.42;color:var(--ink-2);
         border-top:1px solid var(--line);padding-top:9px;margin:6px 0 18px;}
  .m-anchor{position:relative;}
  @media (min-width:901px){
    .mnote{position:absolute;top:var(--mtop,0px);width:var(--marg);margin:0;}
    .chap:not(.verso) .mnote{left:calc(-1 * (var(--marg) + var(--mgap)));}
    .chap.verso .mnote{right:calc(-1 * (var(--marg) + var(--mgap)));}
  }
  @media (max-width:900px){
    .chap-in,.chap.verso .chap-in{grid-template-columns:minmax(0,1fr);}
    .col,.chap.verso .col{grid-column:1;}
    .ghost{font-size:clamp(117px,24vh,198px);}
    .mnote{max-width:34ch;}
  }

  /* ── CHAPTER I: the pitch. ──
     v13: the wide breakpoint used to reserve up to 230px here purely to clear the
     book render, which hung 52-116px below the hero's floor and crossed into this
     chapter. The render now sits up beside the title and clears the hero entirely, so
     that reserve was 90px of dead paper at the top of the first chapter. What is left
     is a chapter opening sized as a chapter opening. */
  #book{padding-top:clamp(80px,12vh,140px);}
  @media (min-width:881px){ #book{padding-top:clamp(96px,13vh,150px);} #book .ghost{top:clamp(64px,9vh,112px);} }
  .pitch{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1.15fr);gap:clamp(24px,3vw,44px);align-items:start;}
  /* (The old .edwrap phone reset lived here; it preceded the .edwrap base rule and
     so never won the cascade. The winning phone override now sits after the deck's
     CSS, below.) */
  /* v20: the editions are the point of this chapter, so they get the full measure
     under the prose instead of a squeezed right-hand column. */
  .pitch.onecol{grid-template-columns:minmax(0,1fr);}
  @media (max-width:820px){ .pitch{grid-template-columns:1fr;} }
  /* Three editions, one shared grid: image row on one baseline, label row on the
     next, so no tile's caption drifts with its image's height. */
  /* Staggered in the right column: the new release leads, the memoir and the bulk
     stack sit beneath it, each on its own baseline so the row is a composition
     rather than three equal tiles. */
  /* ── CHOOSE YOUR EDITION ─────────────────────────────────────────
     A three-card arc: the chosen edition stands centred on its own glass plate,
     the other two fall back and tilt away. With no JS the three simply sit in a
     row, all three links live. */
  .ed-lead{font-family:var(--display);font-size:clamp(1.3rem,2.1vw,1.7rem);color:var(--ink-2);
    margin:0 0 clamp(16px,2.4vh,26px);text-align:center;}
  /* Level with the chapter text, not below it: the block lifts to sit alongside
     the heading and prose. */
  .edwrap{position:relative;align-self:start;margin-top:calc(-1 * clamp(110px,10vw,170px));margin-left:auto;margin-right:calc(-1 * clamp(36px,4.5vw,92px));max-width:clamp(440px,44vw,640px);}
  .edcar{position:relative;outline:none;}
  .edcar:focus-visible{outline:2px solid var(--navy);outline-offset:8px;border-radius:8px;}
  .edstage{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(14px,2vw,28px);align-items:center;}
  html.edcar-on .edstage{display:block;position:relative;height:clamp(330px,34vw,480px);}
  html.edcar-on .ed{position:absolute;left:50%;top:50%;width:clamp(190px,20vw,300px);
    transform:translate(-50%,-50%);
    transition:transform .68s cubic-bezier(.22,1,.36,1),opacity .5s var(--ease);}
  html.edcar-on .ed[data-pos="0"]{transform:translate(-50%,-50%) scale(1);opacity:1;z-index:3;}
  html.edcar-on .ed[data-pos="-1"]{transform:translate(-98%,-47%) scale(.72) rotate(-7deg);opacity:.66;z-index:2;}
  html.edcar-on .ed[data-pos="1"]{transform:translate(-2%,-47%) scale(.72) rotate(7deg);opacity:.66;z-index:2;}
  .ed{position:relative;isolation:isolate;}
  .ed::before{content:"";position:absolute;inset:-5% -7% -8%;border-radius:16px;z-index:-1;
    background:linear-gradient(157deg, rgba(255,253,248,.62) 0%, rgba(255,250,239,.30) 52%, rgba(250,242,228,.16) 100%);
    backdrop-filter:blur(13px) saturate(1.18);-webkit-backdrop-filter:blur(13px) saturate(1.18);
    border:1px solid rgba(255,255,255,.55);
    box-shadow:inset 0 1px 0 rgba(255,255,255,.85), 8px 16px 34px -18px rgba(74,52,26,.32);
    transition:box-shadow .5s var(--ease);}
  html.edcar-on .ed[data-pos="0"]::before{box-shadow:inset 0 1px 0 rgba(255,255,255,.92), 12px 24px 46px -20px rgba(74,52,26,.4);}
  /* The arrows flank the stage, where the cards actually move; only the dots sit
     beneath. Reaching below the deck to turn it was the counterintuitive part. */
  .edctl{display:flex;align-items:center;justify-content:center;gap:18px;margin-top:clamp(14px,2vh,24px);}
  html:not(.edcar-on) .edctl{display:none;}
  /* The arrows flank the fan itself, outside the widest card, vertically centred on
     the deck; the dots stay beneath as the position indicator. */
  html.edcar-on .edctl{position:static;margin-top:clamp(10px,1.6vh,20px);}
  html.edcar-on .edarrow{position:absolute;top:50%;transform:translateY(-50%);z-index:9;}
  /* v40: the deck grew (cards to 300px, stage to 480px) and the fanned side cards
     swallowed the old arrow positions. They flank OUTSIDE the whole fan now, and
     grew with the deck. */
  html.edcar-on .edarrow[data-dir="-1"]{left:calc(-1 * clamp(48px,5vw,86px));}
  html.edcar-on .edarrow[data-dir="1"]{right:calc(-1 * clamp(48px,5vw,86px));}
  html.edcar-on .eddots{position:relative;z-index:9;}
  .edarrow{width:46px;height:46px;border-radius:50%;display:grid;place-items:center;cursor:pointer;
    background:rgba(255,253,248,.5);border:1px solid rgba(120,100,70,.22);color:var(--ink-2);
    backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
    transition:background .25s var(--ease),color .25s var(--ease);}
  .edarrow:hover{background:rgba(255,255,255,.82);color:var(--ink);}
  .edarrow:focus-visible{outline:2px solid var(--navy);outline-offset:3px;}
  .eddots{display:flex;gap:7px;}
  .eddots button{width:7px;height:7px;border-radius:50%;border:0;padding:0;cursor:pointer;
    background:rgba(90,75,50,.26);transition:width .3s var(--ease),background .3s var(--ease);}
  .eddots button[aria-selected="true"]{width:22px;border-radius:4px;background:var(--navy);}
  .eddots button:focus-visible{outline:2px solid var(--navy);outline-offset:3px;}
  @media (prefers-reduced-motion:reduce){ html.edcar-on .ed{transition:none;} }
  /* Name and action ride at the TOP of each card; the cover sits beneath them. */
  .ed{display:flex;flex-direction:column;align-items:center;justify-content:start;text-decoration:none;text-align:center;row-gap:2px;padding:clamp(18px,2vw,28px) clamp(10px,1.2vw,18px) clamp(16px,1.8vw,24px);}
  .ed img{order:3;margin-top:clamp(12px,1.6vw,20px);}
  .ed-k{order:1;}
  .ed-v{order:2;}
  /* v22: the books float on the cream, no card. The cut-out is strict (only the true
     outside ground is removed, never the cover), and the weight comes from a
     drop-shadow that follows each silhouette. */
  .ed img{width:auto;max-width:100%;height:clamp(200px,21vw,330px);object-fit:contain;
          justify-self:center;
          transition:transform .55s var(--ease),filter .4s var(--ease);}
  @media (hover:hover) and (pointer:fine){
    .ed:hover img{transform:translateY(-5px);}
  }
  .ed:focus-visible{outline:2px solid var(--navy);outline-offset:4px;border-radius:3px;}
  /* The blur reveal creates a stacking context that blocks the tiles' multiply
     blend, so this row settles without the blur and the covers always sink into
     the paper. */
  html.js .editions.reveal{filter:none;}
  .ed-k{font-family:var(--prose);font-weight:700;font-size:1.14rem;color:var(--ink);margin-top:0;}
  .ed-v{font-family:var(--prose);font-size:1.04rem;color:var(--navy);text-decoration:underline;text-underline-offset:3px;}
  @media (max-width:820px){ .editions{margin-top:clamp(20px,3vh,32px);} }
  /* Phone: the deck leaves the desktop's pulled-up, bled-right position and stacks
     centred below the prose, whole and on-screen. Written after the .edwrap base so
     it wins the cascade (the earlier reset never did). Arrow overrides only matter
     if the carousel controller ever lands; they keep the controls inside the
     viewport at a full 40px hit size. */
  @media (max-width:820px){
    /* width:100% matters: auto margins on a grid item cancel the default stretch,
       and without it the wrap shrink-wraps to its controls. */
    .edwrap{margin-top:clamp(28px,5vw,40px);margin-left:auto;margin-right:auto;width:100%;max-width:min(100%,480px);}
    /* v47: the active card (heading + link + cover) runs ~400px tall on a phone;
       the desktop stage clamp bottomed out at 330px and the dots sat on the card. */
    html.edcar-on .edstage{height:clamp(430px,118vw,500px);}
    html.edcar-on .edarrow{width:40px;height:40px;}
    html.edcar-on .edarrow[data-dir="-1"]{left:4px;}
    html.edcar-on .edarrow[data-dir="1"]{right:4px;}
    /* The dots keep their 7px look but each rides an invisible 40px pad, clipped
       off the fill, so every stop is a real touch target. */
    .eddots{gap:0;}
    .eddots button{box-sizing:content-box;padding:16.5px;background-clip:content-box;}
    /* The selected pill's background: shorthand resets clip to border-box; pin it. */
    .eddots button[aria-selected="true"]{background-clip:content-box;}
  }
  /* The stacking rule used to target .editions, a class no element carries; the
     grid is .edstage. */
  @media (max-width:680px){ html:not(.edcar-on) .edstage{grid-template-columns:1fr;gap:30px;} .ed img{height:clamp(200px,46vw,260px);} }
  .facts{list-style:none;margin:0;padding:0;font-size:1.08rem;}
  .facts li{display:grid;grid-template-columns:8.4rem 1fr;gap:14px;padding:12px 0;border-top:1px solid var(--line-2);}
  .facts li:last-child{border-bottom:1px solid var(--line-2);}
  .facts .k{font-weight:500;font-size:.95rem;color:var(--ink-3);padding-top:1px;}
  .facts .v{color:var(--ink);}
  .facts .v a{color:var(--navy);font-weight:500;text-decoration:none;border-bottom:1px solid var(--line);}

  /* ── THE SCENE: one pinned, scroll-authored sequence ───────────────
     A 300svh track with a 100svh sticky stage over the sky VIDEO: the same golden
     sky as the hero, with the plane climbing its own contrail. Muted, looped,
     playsinline, never autoplayed by the markup: the controller calls play() only
     after confirming a wide viewport and no reduced-motion request. With no JS, with
     reduced motion, or on a phone, the element simply shows its poster.

     Three phases, all driven by ONE scrubbed value q (the track's scroll progress),
     never a one-way trigger, so scrolling back up restores every earlier state:
       q 0    -> .30   the sentence alone on the sky
       q .30  -> .45   the sentence lifts up and out of frame
       q .45  -> 1     the three timeline beats arrive one after another, each rising
                       from below the frame WITH ITS OWN PHOTOGRAPH, while a navy line
                       draws down their left edge.

     v9: each beat now carries the historical plate that belongs to it, so the
     timeline is pictures and not three paragraphs on an empty sky, and those three
     photographs appear ONCE on the page: here, and nowhere else. The arrival is eased
     with an ease-out cubic per beat rather than the linear ramp v8 used, which is
     what made the sequence read as cheap: linear travel arrives with a pop because
     the eye reads constant velocity as a jump-cut. The plate leads its own text by a
     beat (its travel is scaled 1.14) so the picture lands first and the words settle
     onto it. The beats move on TRANSFORM only and are never faded: at rest they sit a
     full stage-height below their home, outside the stage's clip, so nothing is ever
     hidden behind an opacity of 0. */
  .scene{position:relative;}
  .scenepin{position:relative;}
  .sstage{position:relative;overflow:clip;min-height:88svh;display:grid;align-items:center;}
  .sstage > video,.sstage > #skyStill{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center center;z-index:0;
    background:var(--paper);
    --feather:linear-gradient(to bottom,
      transparent 0%, rgba(0,0,0,.055) 2%, rgba(0,0,0,.145) 5%, rgba(0,0,0,.27) 8.5%,
      rgba(0,0,0,.43) 12%, rgba(0,0,0,.59) 15.5%, rgba(0,0,0,.74) 19%, rgba(0,0,0,.865) 22.5%,
      rgba(0,0,0,.945) 26%, rgba(0,0,0,.99) 29%, #000 32%,
      #000 68%,
      rgba(0,0,0,.99) 71%, rgba(0,0,0,.945) 74%, rgba(0,0,0,.865) 77.5%, rgba(0,0,0,.74) 81%,
      rgba(0,0,0,.59) 84.5%, rgba(0,0,0,.43) 88%, rgba(0,0,0,.27) 91.5%, rgba(0,0,0,.145) 95%,
      rgba(0,0,0,.055) 98%, transparent 100%);
    -webkit-mask-image:var(--feather);mask-image:var(--feather);}
  .sstage-in{position:relative;z-index:1;width:100%;padding-block:clamp(48px,8vh,104px);}
  /* THE FLIGHT, home again. The jet leaves the lower left and climbs out the top
     right of the scene's own sky, drawing its contrail behind it, the first time the
     scene is reached. One pass, position/stroke only, and with no JS or reduced
     motion the sky simply plays without it. */
  .sw{display:inline-block;will-change:transform;}
  .strail{position:absolute;inset:0;width:100%;height:100%;z-index:1;pointer-events:none;opacity:0;}
  html.strail-on .strail{opacity:1;}
  .strail-line{fill:none;stroke:rgba(255,243,222,.8);stroke-width:3.4;stroke-linecap:round;
    stroke-dasharray:var(--sd,2400);stroke-dashoffset:var(--sd,2400);}
  /* v17: the flight is SCRUBBED by the scene pinned scroll (the same q that
     settles the sentence), so the jet climbs exactly as fast as the reader climbs.
     No transitions: the controller writes dashoffset and offset-distance directly. */
  .strail-plane{fill:rgba(168,120,58,.95);offset-path:path("M-40 780 C 420 610, 980 380, 1660 60");
    offset-rotate:auto;offset-distance:0%;}
  .scene-line{font-family:var(--display);font-weight:400;font-size:clamp(1.5rem,5.2vw,4.3rem);line-height:1.12;letter-spacing:-.012em;
             color:var(--ink);}
  .scene-line .sl{display:block;text-wrap:balance;}
  .scene-line em{font-family:var(--display);font-style:italic;font-weight:400;color:var(--navy-deep);}
  .scene-copy{will-change:transform;}
  /* v13: each of the two lines rises 28px into place, on q, with the second trailing
     the first. The offset lives ONLY under scene-on and is written by the controller
     as an inline transform, so off the pin (phone, no JS, reduced motion) the sentence
     is simply set. Opacity is 1 at every instant of this move, and it is a pure
     function of q, so scrolling back up puts the lines back down again. */
  html.scene-on .scene-line .sl{will-change:transform;transform:translate3d(0,28px,0);}

  /* The beats. Off the pin (phone, no JS, reduced motion) they are simply the next
     thing you read, stacked under the sentence, all three visible with their plates. */
  /* v10 takes the whole beat up about a third: the plates are 260px and over at 1280
     rather than 112, the years and headings go with them, and the block takes a wider
     measure and is centred on the stage instead of hanging off its left rim.
     The plate is sized against the viewport's HEIGHT as well as its width, because
     three square plates plus their air have to finish inside one 100svh pin: at 1280
     by 900 that solves to 261px, which is the size the client asked for, and on a
     shorter window it gives ground rather than overflowing the stage's clip. */
  .tl{position:relative;padding-left:clamp(26px,3.6vw,52px);max-width:min(90ch,100%);
      margin-top:clamp(30px,5vh,58px);margin-inline:auto;}
  /* No grey track behind the line: an empty rule sitting on the sky before the beats
     arrive is a decorative hairline, and the navy line IS the element. Its cap is
     clipped by the container's radius so it cannot flip shape as it grows. */
  .tl-rail{position:absolute;left:0;top:6px;bottom:6px;width:3px;border-radius:2px;overflow:hidden;}
  .tl-fill{position:absolute;inset:0;background:var(--navy);transform-origin:top center;transform:scaleY(var(--d,1));}
  .tlb{padding:clamp(8px,1.3vh,15px) 0;display:grid;
       grid-template-columns:clamp(150px,min(21vw,29vh),300px) minmax(0,1fr);
       column-gap:clamp(20px,2.4vw,34px);align-items:start;will-change:transform;}
  .tlb:first-of-type{padding-top:0;} .tlb:last-of-type{padding-bottom:0;}
  /* The plate: square, grayscale, bare on the sky with one tight warm shadow cast
     from a single direction. No border, no frame, no glow. */
  .tlb .plate{margin:0;grid-row:span 3;align-self:start;}
  /* 5:4, not square. At the v10 size three square plates plus their text do not finish
     inside one 100svh pin once the sticky nav's own band is taken off the top, and the
     first year was landing behind the nav. Taking 5% off the top and bottom of each
     negative buys the whole sequence its room while the plate keeps its full WIDTH,
     which is the dimension that carries the size. A shallower crop than that would
     start cutting the subjects. */
  .tlb .plate img{width:100%;aspect-ratio:5/4;object-fit:cover;border-radius:2px;filter:grayscale(1);
                  box-shadow:3px 4px 9px rgba(74,52,26,.26);will-change:transform;}
  .tlb .yr{grid-column:2;font-family:var(--display);font-weight:400;font-size:clamp(1.6rem,2.9vw,2.24rem);line-height:1;
           letter-spacing:.012em;color:var(--navy-deep);}
  .tlb h3{grid-column:2;font-family:var(--display);font-weight:400;font-size:clamp(1.38rem,2.3vw,1.8rem);line-height:1.16;
          letter-spacing:-.004em;margin-top:8px;color:var(--ink);}
  .tlb p{grid-column:2;color:var(--ink);font-size:clamp(1.02rem,1.35vw,1.24rem);line-height:1.48;margin-top:8px;max-width:42ch;}
  /* Phone: the 150px plate floor left the text ~143px wide beside it, wrapping the
     headings three and four deep. One column; the plate sits above its own beat. */
  @media (max-width:560px){
    .tlb{grid-template-columns:minmax(0,1fr);}
    .tlb .plate{grid-row:auto;max-width:240px;}
    .tlb .yr,.tlb h3,.tlb p{grid-column:1;}
  }

  /* v13: the track goes from 250svh to 420svh. The scrubbed span is what actually
     sets the pace (track minus the 100svh pin), so 150svh of scroll becomes 320svh
     and every beat takes 2.1x to 2.7x the wheel it used to. */
  /* v43: Santiago wants the flight savoured -- 420svh read too fast at his scroll
   speed. 560svh stretches the same scrub over a third more wheel, so the plane,
   the statement and the beats all land slower without touching their curves. */
html.scene-on .scene{height:560svh;}
  html.scene-on .scenepin{position:sticky;top:0;height:100svh;}
  html.scene-on .sstage{height:100%;min-height:0;}
  /* Pinned, the sentence and the beats share one cell so they occupy the same air on
     the same sky. Only under scene-on, i.e. only once the controller is running. */
  html.scene-on .sstage-in{display:grid;grid-template-columns:minmax(0,1fr);align-items:center;
    /* Pinned, the stage's air is the pin's to give: the beats are a third larger now
       and the three of them plus the section's own padding have to finish inside one
       screen. Off the pin the generous padding stays.
       The top also has to clear the sticky nav, which is opaque by the time this
       section is on screen and was cutting the first beat's year in half. */
    padding-top:calc(var(--navh) + clamp(12px,2vh,26px));padding-bottom:clamp(12px,2vh,26px);}
  html.scene-on .scene-copy,html.scene-on .tl{grid-area:1/1;align-self:center;}
  html.scene-on .tl{margin-top:0;}
  /* Resting position of the beats before the first frame is written: a full viewport
     below home, outside the stage's clip. Opaque, just not in frame yet. */
  html.scene-on .tlb{transform:translate3d(0,100svh,0);}

  /* v13: THE BEATS ON A PHONE. Below 900 the scene never pins, and the three beats
     were simply sitting there. They now arrive: 18px up, once, on an observer, with
     the year, the heading and the line 60ms apart inside each beat. Position only,
     under html.js only, so with no JS every beat is already home; if the observer
     never fires the worst case is copy sitting 18px low, never missing. The pinned
     controller owns the .tlb element's own transform, this owns the children's, so
     the two can never fight even mid-resize. */
  @media (max-width:899.98px){
    html.js .tlb .yr,html.js .tlb h3,html.js .tlb p,html.js .tlb .plate{
      transform:translateY(18px);transition:transform .5s cubic-bezier(.16,1,.3,1);}
    html.js .tlb h3{transition-delay:60ms;}
    html.js .tlb p{transition-delay:120ms;}
    html.js .tlb.beat-in .yr,html.js .tlb.beat-in h3,html.js .tlb.beat-in p,
    html.js .tlb.beat-in .plate{transform:none;}
  }

  /* ── CHAPTER II: the portrait, and nothing else ────────────────────
     v9 removes the three-plate strip entirely. Those photographs now live once, in
     the pinned scene above. What is left here is the face and eleven words per line
     of the shortest prose on the page, set larger, so the chapter is one clear thing
     on one screen instead of a second telling of the same story. */
  /* v9 pushed the copy hard against the left rim and the portrait hard against the
     right and left 314px of dead paper stranded between them, which is the far-edges
     failure exactly. v10 composes the two as one centred pair: the columns are sized
     to their content rather than to the measure, and the pair is centred on the page,
     so the chapter has a middle again. It also stops the portrait from landing on the
     same right rim the hero's book render just used. */
  /* v13: the text column widens from 38ch to 47ch. The chapter is a list now, and a
     38ch measure broke every one of the three lines across two rows and the longest
     across three, which is the opposite of what a list is for. The pair is still
     centred and the portrait still 340px; the page had a third of its width empty to
     the right of it. At 47ch each of the three lines sets on ONE line at 1280, and so
     does the closing sentence. */
  .open2{display:grid;grid-template-columns:minmax(0,47ch) minmax(210px,340px);
         gap:clamp(30px,4.4vw,66px);align-items:center;justify-content:center;}
  .por{margin:0;}
  .por img{width:100%;border-radius:3px;display:block;}
  .por figcaption{font-family:var(--sans);color:var(--ink-3);font-size:.84rem;margin-top:10px;}
  /* The drop-cap solve above is written against a line-height of 1.66. Setting this
     paragraph to 1.62 pushed the float box 0.02 of a line into line four, so a fourth
     line was indented under a three-line cap. It keeps 1.66. */
  .prose.lead{font-size:clamp(1.14rem,1.75vw,1.34rem);line-height:1.66;max-width:34ch;color:var(--ink-2);}
  /* v13: THE THREE LINES, AND THE ONE THAT LANDS.
     The marker is a 4px navy SQUARE, not a disc, not a dash and not a chip: square
     corners, no curve, one flat ink, which is the same grammar the monogram and the
     calendar frame further down the page are drawn in. So the list reads as belonging
     to this page rather than to a stylesheet's default bullet. Spacing ranks the three
     lines; there are no rules between them.
     The closing line is the payoff, so it takes the full ink while the three above it
     stay in the secondary tone. No drop cap: see the note in the markup. */
  .bl{list-style:none;margin:0;padding:0;}
  .bl li{position:relative;padding-left:1.2em;padding-block:8px;color:var(--ink-2);font-size:1.18rem;
         font-size:clamp(1.14rem,1.75vw,1.34rem);line-height:1.5;}
  /* The square is centred on the first line's X-HEIGHT band, not on its line box, and
     the offset is measured rather than guessed. At the rendered size Sentient's
     baseline sits 23.08px down a 32.16px line box, its x-height band runs 10.76px
     above the baseline, so the band's middle is 17.70px down: 0.8255em, which is a
     font-metric ratio and therefore holds at every size in the clamp. The marker is
     4px, so its top is (padding-block) + 0.8255em - 2px. */
  .bl li::before{content:"";position:absolute;left:.06em;top:calc(5px + .8255em);
                 width:4px;height:4px;background:var(--navy);}
  .prose.bl-close{margin-top:clamp(20px,2.6vh,28px);color:var(--ink);max-width:none;}
  @media (max-width:820px){ .open2{grid-template-columns:minmax(0,1fr);justify-content:stretch;}
                            .por{max-width:340px;} .prose.lead,.bl{max-width:42ch;} }

  /* ── THE FIGURES: why this life is worth the reader's evening ─────
     v9 set these as full-measure rows and the figure and its sentence came apart: a
     numeral at the far left, 230px of dead paper, then a sentence that stopped 430px
     short of the right rim. Two things on a row, not one thing.
     v10 closes it. The band takes its own narrower measure, centred, the figure column
     is only as wide as the widest figure, and the gap between the two is a word-space
     rather than a gulf, so the numeral reads as the first word of its own sentence and
     the sentence finishes the thought. The sentence's first baseline sits on the
     numeral's baseline, which is what actually welds them together.
     the figures count up from zero the first time the band is reached. */
  .figs{position:relative;padding:clamp(52px,8vh,98px) 0;overflow:clip;
        background:linear-gradient(to bottom,
          var(--paper) 0%, var(--paper-2) 13%, var(--paper-2) 87%, var(--paper) 100%);}
  .figs::before{content:"";position:absolute;inset:0;background-image:var(--grain);opacity:.022;pointer-events:none;z-index:0;}
  /* The band's own measure, centred, so the three units sit as one column of type in
     the middle of the paper instead of leaning on the left rim. */
  .figs-in{position:relative;z-index:1;max-width:clamp(520px,68vw,830px);margin-inline:auto;}
  /* A fixed figure column, not a fraction: every sentence on the band starts at the
     same x whatever its figure is, which is the whole point of stacking them. */
  .fig{display:grid;grid-template-columns:clamp(120px,15.5vw,206px) minmax(0,1fr);gap:clamp(14px,1.9vw,30px);
       align-items:baseline;padding:clamp(22px,3.4vh,38px) 0;}
  .fig + .fig{border-top:1px solid var(--line);}
  .fig:first-child{padding-top:clamp(6px,1vh,12px);}
  .fig:last-child{padding-bottom:clamp(6px,1vh,12px);}
  /* Right-aligned in a fixed column. The column has to be fixed or the three sentences
     start at three different x, which is the ragged-parallel-rows failure; but LEFT
     aligned in a fixed column, a two-digit figure sits 148px from its own sentence and
     comes apart from it again. Right-aligned, all three figures end on one axis AND
     every one of them is a single word-space from the words it belongs to. */
  .fig .n{font-family:var(--display);font-weight:400;font-size:clamp(2.7rem,6vw,4.9rem);line-height:.92;
          letter-spacing:.004em;font-variant-numeric:tabular-nums;color:var(--navy);white-space:nowrap;
          text-align:right;}
  .fig .of{display:block;font-family:var(--display);font-size:clamp(1.15rem,2vw,1.62rem);line-height:1.1;
           letter-spacing:.02em;color:var(--navy);opacity:1;margin-top:.22em;white-space:nowrap;}
  /* A worded figure is not a numeral: it cannot be set at numeral size and it must
     be allowed to wrap, or it runs straight under the sentence beside it. */
  .fig .n.word{font-size:clamp(1.6rem,3vw,2.4rem);line-height:1.06;white-space:normal;}
  .fig .l{color:var(--ink);font-size:clamp(1.06rem,1.55vw,1.28rem);line-height:1.5;max-width:34ch;}
  @media (max-width:760px){
    .fig{grid-template-columns:minmax(0,1fr);gap:10px;align-items:start;}
    .fig .n,.fig .of{text-align:left;}
    .fig .n{font-size:clamp(3rem,13vw,4.4rem);}
  }
  /* One action, once, right after the numbers have made the case. Not a banner, not a
     gradient slab: one line of type and one button on the same paper as everything
     else, centred on the measure. */
  .buyband{padding:clamp(42px,6.4vh,76px) 0 clamp(44px,7vh,84px);text-align:center;}
  .buyband p{font-family:var(--display);font-size:clamp(1.44rem,2.9vw,2.14rem);line-height:1.2;color:var(--ink);
             max-width:30ch;margin-inline:auto;text-wrap:balance;}
  .buyband .act{margin-top:clamp(22px,3.4vh,34px);}

  /* ── CHAPTER III: the framework ───────────────────────────────────
     The plate is shown WHOLE. v8 cropped it to fill a tall band and shaved letters at
     some widths; v9 gives the band the plate's own aspect ratio so the picture is
     never cropped at any viewport, and feathers its top, bottom and left edges into
     the page paper (the plate's parchment is rgb(246,230,200) against the page's
     rgb(245,243,238): near enough in value that a long fade dissolves the join, far
     enough in hue that a hard edge would show).

     v11: the plate is the CLIENT'S OWN bright framework, six 3D steps in their brand
     gradient on warm ivory. It is a still image; the navy assembly video is gone from
     this section entirely and the reveal is native (see the wipe, below). */
  .chap.fwsec{padding-top:clamp(60px,9vh,110px);}
  /* THE PLATE IS SHOWN WHOLE AND IS NEVER MASKED, CROPPED OR CLIPPED.
     Three passes got here. v8 cropped it to fill a tall band and shaved letters at
     some widths. The first v9 pass fixed the crop but feathered the PICTURE'S own
     pixels into the page, which washed out the D at the top of the staircase and the
     L at the bottom: a fade has to eat something, and on a band this shallow the only
     thing at the rim is a letter. The second left the picture alone but gave it a
     parchment margin on two sides only, so the navy ran off the left and right edges
     against nothing.
     What ships is a mounted print. The plate sits contained inside a mat of its OWN
     parchment (rgb(246,230,200), sampled from the plate), and it is the MAT that
     dissolves into the page paper, on all four sides. The fade is spent entirely on
     mat, reaching full opacity exactly where the photograph begins, so no pixel of
     the picture is ever faded and no edge of it ever meets the page in a hard line.
     Two nested elements, one mask each: a single element carrying two mask layers
     needs mask-composite, and getting that wrong (compositing the BOTTOM layer with an
     empty backdrop) erases the element outright, which is what it did here. One axis
     per element cannot fail that way. */
  /* v18: the lower half is ONE creamy surface. It fades in after the figures band,
     holds through framework, film, appearances and the invitation, and hands off to
     the navy back matter. No more paper/cream patchwork. */
  /* A warm field, not a flat fill: the brand's cream deepened toward the lower right
     so the glass plates have something real to refract, grained so it never bands. */
  .bookband{position:relative;isolation:isolate;
    background:
      radial-gradient(120% 90% at 18% 12%, #f8f1e4 0%, rgba(248,241,228,0) 60%),
      radial-gradient(110% 80% at 88% 84%, #e7d9bd 0%, rgba(231,217,189,0) 62%),
      linear-gradient(158deg, #f4ecdd 0%, #f0e6d3 46%, #eae0ca 100%);}
  .bookband::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
    background-image:var(--grain);background-size:180px 180px;opacity:.5;}
  .fwsec{background:linear-gradient(to bottom,#f2e9d8 0%,#f2e9d8 30%,#ecdfca 62%,#ead9c5 100%);}
  /* v24: the staircase is a plate INSIDE the page's measure, like every other
     figure, not a full-bleed monument. */
  .fwband{position:relative;margin:clamp(30px,4.6vh,56px) auto 0;max-width:var(--maxw);
    padding-inline:var(--pad);padding-bottom:0;
          --fpad:clamp(24px,4.4vw,80px);
          --fy:linear-gradient(to bottom,
            transparent 0,
            rgba(0,0,0,.06) calc(var(--fpad) * .22), rgba(0,0,0,.19) calc(var(--fpad) * .38),
            rgba(0,0,0,.38) calc(var(--fpad) * .53), rgba(0,0,0,.60) calc(var(--fpad) * .67),
            rgba(0,0,0,.80) calc(var(--fpad) * .79), rgba(0,0,0,.93) calc(var(--fpad) * .89),
            rgba(0,0,0,.99) calc(var(--fpad) * .96), #000 var(--fpad),
            #000 calc(100% - var(--fpad)),
            rgba(0,0,0,.99) calc(100% - var(--fpad) * .96), rgba(0,0,0,.93) calc(100% - var(--fpad) * .89),
            rgba(0,0,0,.80) calc(100% - var(--fpad) * .79), rgba(0,0,0,.60) calc(100% - var(--fpad) * .67),
            rgba(0,0,0,.38) calc(100% - var(--fpad) * .53), rgba(0,0,0,.19) calc(100% - var(--fpad) * .38),
            rgba(0,0,0,.06) calc(100% - var(--fpad) * .22), transparent 100%);
          --fy2:linear-gradient(to bottom, transparent 0, #000 var(--fpad), #000 100%);
          -webkit-mask-image:var(--fy2);mask-image:var(--fy2);}
  /* The mat is the plate's OWN paper, and on the v11 plate that paper is not one
     colour: its ivory is lit from the upper left and falls off to the right, from
     rgb(246,234,217) down the left edge to rgb(227,211,189) at the right rim. v10's
     flat #decbab was matched to the navy plate and would now sit a full 20 levels
     dark against the ivory, printing a border round the picture. The mat therefore
     carries the same left-to-right falloff the plate does, so the join is a
     continuation of the picture's own light rather than a step in tone. This matters
     more in v11 than it did in v10, because the six principles now sit ON the mat as
     well as on the plate, and any seam would run straight through them. */
  /* v19: the render bleeds to its own edges and carries its own floor, so the mat
     inset and the horizontal dissolve that were built for the OLD margin-bearing
     plate are retired — they were slicing 58px off each side of the staircase. */
  .fwmat{background:none;
         padding:0;
         --fpadx:clamp(16px,3vw,64px);
         --fx:linear-gradient(to right,
           transparent 0,
           rgba(0,0,0,.06) calc(var(--fpadx) * .22), rgba(0,0,0,.19) calc(var(--fpadx) * .38),
           rgba(0,0,0,.38) calc(var(--fpadx) * .53), rgba(0,0,0,.60) calc(var(--fpadx) * .67),
           rgba(0,0,0,.80) calc(var(--fpadx) * .79), rgba(0,0,0,.93) calc(var(--fpadx) * .89),
           rgba(0,0,0,.99) calc(var(--fpadx) * .96), #000 var(--fpadx),
           #000 calc(100% - var(--fpadx)),
           rgba(0,0,0,.99) calc(100% - var(--fpadx) * .96), rgba(0,0,0,.93) calc(100% - var(--fpadx) * .89),
           rgba(0,0,0,.80) calc(100% - var(--fpadx) * .79), rgba(0,0,0,.60) calc(100% - var(--fpadx) * .67),
           rgba(0,0,0,.38) calc(100% - var(--fpadx) * .53), rgba(0,0,0,.19) calc(100% - var(--fpadx) * .38),
           rgba(0,0,0,.06) calc(100% - var(--fpadx) * .22), transparent 100%);
         -webkit-mask-image:none;mask-image:none;}
  /* THE FIELD, and why the plate no longer fills it.
     The v10 plate's staircase started 496px in from the left, which left a wide wedge
     of empty parchment for the six principles. The client's plate starts its bottom
     step at x=257 and only 217px of ground survive beside the lowest step: at 1024
     that is 105 CSS px, which will not hold a line of type at any size worth reading.
     Rather than crop the client's picture or shrink the type to nothing, the PLATE is
     set to 88% of the band and pushed to the right, and the 12% it gives up becomes
     solid mat of exactly the plate's own paper. The picture is still shown whole and
     is still never cropped; it simply sits on a wider sheet, and the wedge the lowest
     principle needs is real again.
     v12: the STAGE, not the field, is now the coordinate system for the principles, and
     the stage carries the picture's own aspect ratio (1800/771) with the picture set to
     fill it exactly. Stage space IS image space at every viewport and every aspect: a
     percentage below is a percentage of the photograph, and no fit mode, no decoded-size
     surprise and no future crop can slide the two apart. The mat survives as the stage's
     left margin, because the lowest principle still overhangs the plate's rim onto it
     (11.8% of the image width, against 13.6% of mat: it clears). */
  .fwfield{position:relative;}
  /* v23: no forced ratio — the old 1800/771 plate ratio letterboxed the 16:9 render
     inside a contain box, printing cream bars around the staircase. */
  .fwstage{position:relative;width:100%;margin-left:0;}
  /* The staircase arrives as one object: a longer, deeper settle than the standard
     reveal, no blur (the image is large), scale eases out of 1.5%. */
  html.js .fwstage.reveal,html.js .invite-act.island.reveal{
    transform:translateY(38px) scale(.985);filter:none;
    transition:transform .95s cubic-bezier(.16,1,.3,1);}
  html.js .fwstage.reveal.in,html.js .invite-act.island.reveal.in{transform:none;}
  .fwart{border-radius:8px;}
  .fwart{display:block;width:100%;height:auto;object-fit:cover;}

  /* THE REVEAL: v13 assembles the staircase one step at a time.
     v11/v12 swept ONE diagonal clip across the whole field in 0.9s. It read as a wipe,
     which is a transition, not an assembly. Six steps now arrive in the order they are
     read, L I F T E D, 140ms apart, 380ms each, on the page's own curve. Total 1.08s.

     GEOMETRY. The segment boundary is the plane perpendicular to the staircase's own
     ascent (the letters run from L at 362,675 to D at 1514,214 in the plate's native
     1800x771, a slope of -0.4, so a perpendicular edge leans 17.15% of the STAGE's
     width across its height; the stage carries the picture's aspect exactly, so stage
     space is image space). Write that as one coordinate:
         u(x,y) = 100*x/1800 - 17.15 * y/771
     u is constant along the edge and increases up the stairs. Each step owns the slab
     of u between its own riser and the next one up, and the risers were measured off
     the silhouette in measure_lifted_v12.py:
         riser   x     mid y    u          the step it belongs to
         L       257   655      -0.29      (and everything left of it)
         I       496   537      15.61
         F       728   450      30.43
         T       963   361      45.47
         E      1188   267      60.06
         D      1413   174      74.63      (and everything right of it)
     u runs -17.15 (bottom-left corner) to 100 (top-right corner) over the box.

     MECHANISM, and why it is a mask and not six clipped copies. Six elements would
     each need the plate, and the plate is 190kB of base64: six copies is a 1.1MB
     document. One mask on the one img does the same job. Six mask layers, each a hard
     linear-gradient band, composited with mask-composite:add, so their union is the
     revealed region and each band's own edge is animated by its own registered
     custom property. A gradient stop is a position along the GRADIENT LINE, not along
     x, so u is converted once: for the wipe angle and the plate's 1800/771 box the
     line resolves to  stop% = 0.85368*u + 14.63,  which puts u=-17.15 at 0% and u=100
     at 100% exactly. The six boundaries above become 0, 27.95, 40.61, 53.45, 65.91,
     78.34, 100. Each band is drawn 0.15% (about 2px at 1280) PAST its own end so
     consecutive bands overlap rather than butt, and no hairline of unmasked ground can
     survive between two segments at any viewport.

     THE SIX PRINCIPLES ARE NOT MASKED. They sit in .fwsteps, a sibling of the img, and
     they are right-aligned blocks 21 u wide whose right edges are only ~13 u apart, so
     any cut through the field slices at least one of them. They get their own arrival
     instead: each label rises 14px into place on its own step's 380ms, position only,
     so the name lands with the stair it names and no word is ever cut in half.

     AND IT CANNOT STRAND ANYTHING. The mask exists only under .wiping, which the
     script puts on when the section arrives and takes off 1.25s later come what may.
     There is no hidden resting state in this stylesheet: with no JS, with reduced
     motion, with no observer, or if the animation never runs, the field is the whole
     plate and all six principles. */
  @property --fw1{syntax:'<percentage>';inherits:false;initial-value:0%;}
  @property --fw2{syntax:'<percentage>';inherits:false;initial-value:27.95%;}
  @property --fw3{syntax:'<percentage>';inherits:false;initial-value:40.61%;}
  @property --fw4{syntax:'<percentage>';inherits:false;initial-value:53.45%;}
  @property --fw5{syntax:'<percentage>';inherits:false;initial-value:65.91%;}
  @property --fw6{syntax:'<percentage>';inherits:false;initial-value:78.34%;}
  @keyframes fwA1{from{--fw1:0%}     to{--fw1:28.10%}}
  @keyframes fwA2{from{--fw2:27.95%} to{--fw2:40.76%}}
  @keyframes fwA3{from{--fw3:40.61%} to{--fw3:53.60%}}
  @keyframes fwA4{from{--fw4:53.45%} to{--fw4:66.06%}}
  @keyframes fwA5{from{--fw5:65.91%} to{--fw5:78.49%}}
  @keyframes fwA6{from{--fw6:78.34%} to{--fw6:100%}}
  @keyframes fwLab{from{transform:translateY(-50%) translateY(14px)} to{transform:translateY(-50%)}}
  .fwfield.wiping .fwart{
    --fwm:linear-gradient(68.18deg,transparent 0 0%,     #000 0%,     #000 var(--fw1),transparent var(--fw1)),
          linear-gradient(68.18deg,transparent 0 27.95%, #000 27.95%, #000 var(--fw2),transparent var(--fw2)),
          linear-gradient(68.18deg,transparent 0 40.61%, #000 40.61%, #000 var(--fw3),transparent var(--fw3)),
          linear-gradient(68.18deg,transparent 0 53.45%, #000 53.45%, #000 var(--fw4),transparent var(--fw4)),
          linear-gradient(68.18deg,transparent 0 65.91%, #000 65.91%, #000 var(--fw5),transparent var(--fw5)),
          linear-gradient(68.18deg,transparent 0 78.34%, #000 78.34%, #000 var(--fw6),transparent var(--fw6));
    -webkit-mask-image:var(--fwm);mask-image:var(--fwm);
    -webkit-mask-composite:source-over;mask-composite:add;
    animation:fwA1 380ms cubic-bezier(.16,1,.3,1)   0ms both,
              fwA2 380ms cubic-bezier(.16,1,.3,1) 140ms both,
              fwA3 380ms cubic-bezier(.16,1,.3,1) 280ms both,
              fwA4 380ms cubic-bezier(.16,1,.3,1) 420ms both,
              fwA5 380ms cubic-bezier(.16,1,.3,1) 560ms both,
              fwA6 380ms cubic-bezier(.16,1,.3,1) 700ms both;}
  .fwfield.wiping .fwp{animation:fwLab 380ms cubic-bezier(.16,1,.3,1) both;}
  .fwfield.wiping .fwp:nth-child(1){animation-delay:0ms;}
  .fwfield.wiping .fwp:nth-child(2){animation-delay:140ms;}
  .fwfield.wiping .fwp:nth-child(3){animation-delay:280ms;}
  .fwfield.wiping .fwp:nth-child(4){animation-delay:420ms;}
  .fwfield.wiping .fwp:nth-child(5){animation-delay:560ms;}
  .fwfield.wiping .fwp:nth-child(6){animation-delay:700ms;}
  @media (prefers-reduced-motion:reduce){
    .fwfield.wiping .fwart{-webkit-mask-image:none;mask-image:none;animation:none;}
    .fwfield.wiping .fwp{animation:none;}
  }
  .fwsec{padding-bottom:0 !important;}
  .fw-below{position:relative;padding-top:clamp(40px,6vh,74px);}
  .fw-below > .shell{position:relative;z-index:1;}
  .fw-cap{font-family:var(--sans);color:var(--ink-3);font-size:.84rem;margin-top:clamp(24px,3.6vh,36px);}

  /* ── THE PRINCIPLES, ON THE PLATE ──────────────────────────────────
     LIFTED is spelled exactly ONCE on this page: on the stairs. The six principles
     are no longer a list repeating the plate underneath it, they are set into the
     plate's own parchment, each one in the empty ground immediately to the left of the
     step whose letter it belongs to, so the reading order climbs with the staircase.

     WHAT WENT WRONG IN v11, since the fix only makes sense against it. v11 pinned each
     block's right edge 40 plate-px clear of "the leftmost column of step colour anywhere
     in that step's BAND". On a staircase that climbs to the right, the leftmost colour
     anywhere in a band always belongs to the step BELOW it, so every one of the six was
     pulled a full slot down and left and came to rest in the notch of the WRONG step.
     At 1024 the wedge is small and the error passes for tight spacing; at 2000 the same
     error is 400 CSS px of bare parchment between a name and the step it names, and the
     client read every label as belonging to the step underneath. (The crop was never the
     cause: object-fit:cover is inert on an img that is width:100%/height:auto, and the
     rendered geometry was identical in image space at 1024, 1280, 1600, 1920 and 2560.)

     v12 measures the plate's SILHOUETTE instead (measure_lifted_v12.py, native 1800x771).
     Read down the left edge of the picture and the leftmost colour is a stair: flats
     joined by shallow diagonals. A flat is one block's own vertical front-left edge, its
     RISER. The diagonal below a flat is the next block's TOP FACE running up to meet the
     block above. So each block owns exactly one riser, and that riser is the only piece
     of the picture a label can be pinned to without ambiguity.
        step   letter box (x, y)        its riser: x, rows      label right edge / centre
        D      1477-1551, 174-255       1413, 141-208           1373.4 / 116.1
        E      1262-1321, 274-360       1188, 236-298           1148.4 / 206.8
        T      1038-1097, 366-448        963, 330-392            923.4 / 300.6
        F       811- 863, 461-541        728, 424-477            688.4 / 386.3
        I       584- 613, 554-634        496, 513-561            456.4 / 470.7
        L       337- 387, 635-715        257, 600-711            217.4 / 619.9
     Each block is right-aligned and its right edge is pinned one gap (2.2% of the image
     width, 39.6 px) clear of its OWN riser, so the type runs down beside that riser at a
     fixed distance. Its foot is then set one further gap clear of the point where the
     next step's top face crosses that right edge, and lifted half a gap again, which is
     what keeps the label out of the step below and stops the two reading as one.
     The block sits level with its own step rather than with the middle of its letter,
     because it cannot be both: the letters sit low on faces that fall away to the right,
     and at a letter's own centre line the nearest clear parchment already belongs to the
     step below. That is exactly the trap v11 fell into. What attaches a name to a step
     here is the riser it runs beside, and that is measured: every label sits 39.6-44.6 px
     from its own riser and never nearer than 203 px to any other, a margin of 5.08x at
     its worst, at every width from 1024 to 2560 (qa4/). On the weaker cue, distance from
     the label's right edge to the lettering itself, its own letter is nearest at every
     width too, by 1.07x (I, the tightest) to 3.07x (L). The foot clears the step below by
     58.7 px or more, which is why nothing reads as resting on the next block's top face.
     L has no step beneath it to clear, so it takes the mean foot offset of the other
     five (33.9 px above its own riser's bottom) and keeps the identical relationship.
     All six share ONE width, 23.86% of the image, set by the tightest step (L, walled in
     by the bottom riser at x=257, which it overhangs onto the mat by 11.8%): a common
     width is what puts the six right edges on one clean diagonal instead of a ragged one.
     Type is sized in vw against a field that is ~94vw at every width, so the arrangement
     scales with the picture. Ink is #1b1a17 on the ivory (14.9:1 on the mat, 12.0:1 at
     the darkest ground any of the six sits on) and the second line #3f3b33 (9.5:1, 7.6:1). */
  .fwsteps{position:absolute;inset:0;z-index:1;}
  .fwp{position:absolute;right:var(--r);top:var(--t);width:23.86%;text-align:right;
       transform:translateY(-50%);}
  .fwp b{display:block;font-family:var(--prose);font-weight:700;
         font-size:clamp(13.5px,1.30vw,21px);line-height:1.2;letter-spacing:.002em;color:#1b1a17;}
  .fwp span{display:block;font-family:var(--prose);font-weight:400;
            font-size:clamp(12px,1.06vw,17px);line-height:1.32;color:#3f3b33;margin-top:.26em;}
  .vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}

  /* The compact list is the SUB-1024 form of the same six, and the only one there.
     Above 1024 it is removed outright, so the framework is never spelled twice. */
  .prin{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
        column-gap:clamp(20px,4vw,44px);row-gap:2px;}
  .prin li{display:grid;grid-template-columns:2.4rem minmax(0,1fr);column-gap:12px;align-items:baseline;
           padding:11px 0;border-top:1px solid var(--line-2);}
  .prin .lt{font-family:var(--display);font-weight:400;font-size:2.1rem;line-height:.86;
            color:var(--navy);letter-spacing:.01em;}
  .prin b{display:block;font-weight:700;font-size:1.02rem;line-height:1.28;color:var(--ink);}
  .prin b + span{display:block;color:var(--ink-2);font-size:.95rem;line-height:1.42;margin-top:2px;}
  /* v16: the principles are PRINTED ON the artwork itself (regenerated with the
     text baked into each step face), so the HTML overlay is retired at every width.
     The compact list remains the sub-1024 form, where the baked type gets small. */
  .fwsteps{display:none;}
  .fwstage{width:100%;margin-left:0;}
  @media (min-width:1024px){ .prin{display:none;} }
  @media (max-width:620px){ .prin{grid-template-columns:minmax(0,1fr);} }

  /* ── CHAPTER IV: the record ───────────────────────────────────────
     v8 set this as a small grey list and the client was right that it was boring.
     v9 gives it presence: two blocks, and every entry is a full-measure ROW carrying
     the institution's REAL mark in its REAL colours at one optical size, the
     institution set large in the display face, and the role in navy beneath it. The
     marks are bare on the paper. No tile, no chip, no box, nothing invented: where an
     institution publishes no usable colour artwork its name is set as type in the
     same slot instead.
     The arrival is blur and POSITION only, never opacity, and the pre-blurred state
     exists ONLY under html.js, so with no JS every row is sharp from the first paint.
     A timer backstop lights any row the observer somehow misses. */
  /* v10: an honour roll, not a directory. Every entry is ONE line, mark and
     institution and role on a single baseline in three aligned columns, the rows
     tight. The chapter loses roughly two fifths of its height and reads as a record
     you scan rather than a page you work through. The marks stay real, in colour and
     bare on the paper. */
  .rl-group + .rl-group{margin-top:clamp(26px,3.6vh,42px);}
  .rl-group h3{font-family:var(--display);font-weight:400;font-size:.82rem;letter-spacing:.22em;text-transform:uppercase;
               color:var(--navy);margin:0 0 2px;}
  .rl-list{list-style:none;margin:0;padding:0;}
  .rl{display:grid;grid-template-columns:104px minmax(0,1fr) auto;gap:clamp(14px,2vw,28px);align-items:center;
      padding:9px 0;min-height:50px;border-top:1px solid var(--line-2);}
  .rl-list li:last-child{border-bottom:1px solid var(--line-2);}
  .rl-mark{display:flex;align-items:center;justify-content:flex-start;}
  /* One optical size for six marks of six different proportions: each is bounded in
     the same box and scaled to fit inside it, so nothing is a tile and nothing shouts. */
  .rl-mark img{display:block;max-width:100%;max-height:34px;width:auto;height:auto;}
  /* The one institution with no usable published mark: its name, set as type, in the
     same slot. A logo is never invented to fill the hole. */
  .rl-word{font-family:var(--display);font-weight:400;font-size:.94rem;line-height:1.16;color:var(--ink-2);
           letter-spacing:.014em;}
  .rl-i{font-family:var(--display);font-weight:400;font-size:clamp(1.1rem,1.5vw,1.32rem);line-height:1.2;
        letter-spacing:-.004em;color:var(--ink);display:block;}
  .rl-r{display:block;color:var(--navy);font-size:.9rem;line-height:1.3;text-align:right;}
  html.js .rl{transform:translateY(11px);
              transition:filter .62s var(--ease),transform .62s var(--ease);transition-delay:calc(var(--k,0) * 75ms);}
  html.js .rl.lit{filter:none;transform:none;}
  @media (max-width:760px){
    .rl{grid-template-columns:84px minmax(0,1fr);gap:14px;row-gap:2px;padding:11px 0;}
    .rl-r{grid-column:2;text-align:left;}
  }
  @media (max-width:420px){
    .rl{grid-template-columns:minmax(0,1fr);}
    .rl-r{grid-column:1;}
  }
  .rec{margin-top:clamp(24px,3.6vh,40px);}

  /* v17: the page's second half was a flat cream field after the scene's drama —
     the named "flat fill under everything after the hero" failure. Two toned bands
     carry the atmosphere down: the film screens in a dusk room, the appearances sit
     on the framework's warm mat, and each dissolves back to paper at its edges. */
  .revband{background:#f2e9d8;padding-top:0;}
  /* v47: the rating is a small contained banner in the cards' own surface, not a
     loose display-size sentence floating over the wall. */
  .rev-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap;width:fit-content;
    margin:0 auto clamp(24px,3.4vh,38px);padding:11px 20px;
    background:#faf3e3;border:1px solid rgba(90,74,50,.14);border-radius:10px;
    box-shadow:0 2px 5px rgba(122,94,54,.08);}
  .rev-stars{display:inline-flex;gap:2px;}
  .rev-stars svg{display:block;width:17px;height:16px;}
  .rev-fact{font-family:var(--display);font-size:clamp(1.02rem,1.5vw,1.22rem);color:var(--ink);margin:0;}
  .amz-ink{height:.94em;width:auto;vertical-align:-.17em;color:var(--ink);--amz-smile:#f90;}
  .rev-fact b{font-weight:400;color:var(--navy);}
  /* THE WALL. Full-bleed, two rows sliding opposite ways at reading pace, edges
     dissolved with a long mask so cards are born and die softly instead of popping
     at a hard frame. transform-only, pausable, and inert under reduced motion. */
  .revwall{position:relative;width:100vw;margin-left:calc(50% - 50vw);
    display:grid;gap:clamp(14px,1.8vh,20px);
    -webkit-mask-image:linear-gradient(to right,transparent 0,rgba(0,0,0,.5) 3%,#000 9%,#000 91%,rgba(0,0,0,.5) 97%,transparent 100%);
    mask-image:linear-gradient(to right,transparent 0,rgba(0,0,0,.5) 3%,#000 9%,#000 91%,rgba(0,0,0,.5) 97%,transparent 100%);}
  .revrow{display:flex;width:max-content;}
  html.js .revrow.a{animation:rev-l 80s linear infinite;}
  html.js .revrow.b{animation:rev-r 92s linear infinite;}
  @keyframes rev-l{to{transform:translateX(-50%)}}
  @keyframes rev-r{from{transform:translateX(-50%)}to{transform:none}}
  .revwall:hover .revrow,.revwall:focus-within .revrow{animation-play-state:paused;}
  /* Touch has no hover: a finger held on the wall pauses it, so the moving links
     can be read and tapped deliberately. */
  .revwall:active .revrow{animation-play-state:paused;}
  @media (prefers-reduced-motion:reduce){
    html.js .revrow.a,html.js .revrow.b{animation:none;}
    .revrow [aria-hidden="true"]{display:none;}
    .revrow{flex-wrap:wrap;width:auto;}
    .revwall{width:auto;margin-left:0;-webkit-mask-image:none;mask-image:none;}
  }
  .revc{flex:0 0 auto;width:clamp(228px,17vw,268px);margin-right:clamp(10px,1.2vw,16px);
    display:flex;flex-direction:column;gap:12px;
    background:#faf3e3;border:1px solid rgba(90,74,50,.13);border-radius:10px;
    padding:clamp(12px,1.3vw,16px) clamp(14px,1.5vw,18px);
    box-shadow:0 2px 5px rgba(122,94,54,.10);
    transition:background .25s ease,border-color .25s ease;}
  /* v49: the banner is the section's one link; cards are quotes, not buttons. */
  a.rev-head{text-decoration:none;color:inherit;transition:background .25s ease,border-color .25s ease,box-shadow .25s ease;}
  a.rev-head:hover{background:#fdf8ec;border-color:rgba(90,74,50,.26);box-shadow:0 3px 8px rgba(122,94,54,.12);}
  .revc-top{display:flex;align-items:center;gap:12px;}
  .revc-i{flex:0 0 auto;width:34px;height:34px;border-radius:50%;
    display:flex;align-items:center;justify-content:center;
    font-family:var(--display);font-size:1.14rem;line-height:1;color:#f6efe0;
    padding-bottom:2px;}
  .revc-i.t1{background:#27374f;} .revc-i.t2{background:#5c5a3f;}
  .revc-i.t3{background:#8a4f2c;} .revc-i.t4{background:#3f5348;}
  .revc-i.t5{background:#6e4258;}
  .revc-who{display:flex;flex-direction:column;gap:3px;min-width:0;}
  .revc-n{font-family:var(--prose);font-weight:600;font-size:.94rem;color:var(--ink);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .revc-s{display:flex;gap:2px;line-height:0;}
  .revc-q{font-family:var(--prose);font-style:italic;font-size:.9rem;line-height:1.5;color:var(--ink-2, var(--ink));}
  .revc-f{margin-top:auto;display:flex;align-items:center;gap:.45em;
    font-family:var(--prose);font-size:.8rem;color:var(--ink-3, #7d7361);}
  .revc-amz{height:.95em;width:auto;color:var(--ink);--amz-smile:#f90;transform:translateY(.14em);}
  .revs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(30px,4vh,52px) clamp(22px,3.4vw,54px);align-items:start;}
  .rev{margin:0;}
  .rev p{font-family:var(--display);font-style:italic;font-size:clamp(1.08rem,1.6vw,1.34rem);line-height:1.45;
    color:var(--ink-2);margin:0;}
  .rev footer{font-family:var(--prose);font-size:.92rem;color:var(--ink-3);margin-top:12px;}
  .rev-more{margin:clamp(24px,3.4vh,38px) 0 0;font-size:1rem;}
  .rev-more a{color:var(--navy);}
  @media (max-width:760px){ .revs{grid-template-columns:1fr;gap:26px;} }
  @media (max-width:760px){
    /* The statement was bottoming out at 24px while the wall shouted past it. */
    .scene-line{font-size:clamp(2.05rem,9vw,2.6rem);}
    .revc{width:clamp(208px,66vw,244px);}
    .revc-q{font-size:.9rem;}
    .revc-i{width:33px;height:33px;font-size:1rem;}
  }
  .cta-line{margin:clamp(20px,3vh,32px) auto 0;padding-bottom:clamp(34px,5vh,64px);text-align:center;}
  /* One rung below a section head, never level with it: the figures carry the
     emphasis, the sentence around them stays quiet. */
  .ev-beat{font-family:var(--display);font-weight:400;font-size:clamp(1.15rem,1.9vw,1.55rem);line-height:1.35;
    margin:0 0 clamp(20px,3vh,34px);color:var(--ink-2);}
  .ev-beat .n{color:var(--ink);font-size:1.5em;}
  .chap.eveband{background:#f2e9d8;}
  .chap.warmband{background:#f2e9d8;}
  /* Appearances is the page's widest breath: its head runs larger than the other
     chapters and its epigraph sits on the same line, so the rhythm changes here. */
  .bigappear .epi{color:var(--navy);}
  /* ── CHAPTER V: the video ─────────────────────────────────────────
     The recap keeps streaming from its host with a poster; the mp4 is never inlined. */
  /* v13: the player and its caption are one centred block on the shell's own axis,
     max 900px, rather than a 16:9 box filling an offset text column. */
  .pullq{font-family:var(--display);font-style:italic;font-weight:400;color:var(--ink-2);
    font-size:clamp(1.4rem,2.9vw,2.3rem);line-height:1.3;max-width:34ch;margin:0 0 clamp(22px,3.4vh,38px);}
  .chap.warmband .player{box-shadow:12px 18px 36px -16px rgba(74,52,26,.35);}
  .player{position:relative;border:1px solid var(--line);border-radius:6px;overflow:hidden;aspect-ratio:16/9;background:#12100e;
          margin:clamp(22px,3.4vh,36px) auto 0;max-width:900px;}
  #video .fw-cap{max-width:900px;margin-inline:auto;text-align:center;}
  .player video{display:block;width:100%;height:100%;object-fit:cover;background:#12100e;}
  .player .play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:14px;
                background:rgba(18,16,14,.32);border:0;color:#fff;font-family:var(--sans);cursor:pointer;width:100%;}
  .player .play[hidden]{display:none;}
  .player .disc{width:74px;height:74px;border-radius:50%;border:1.5px solid rgba(255,255,255,.85);display:flex;align-items:center;justify-content:center;background:rgba(27,26,23,.4);transition:background .2s var(--ease);}
  .player .disc svg{margin-left:4px;}
  .player .play span.t{font-size:.9rem;letter-spacing:.02em;}
  @media (hover:hover) and (pointer:fine){ .player:hover .disc{background:rgba(38,64,99,.55);} }
  /* ── CHAPTER VI: appearances ──────────────────────────────────────
     v8 ran an auto-scrolling marquee, v11.1 a one-slide-per-page gallery. v12 runs the
     SPOTLIGHT the client already uses on his own store: the centred photograph holds
     the stage at full weight while its neighbours peek in from both sides, dimmed and
     slightly small, so paging is a hand-off rather than a jump. The frame is also
     smaller than v11.1's, which he read as too big.
     Nothing autoplays. The dimmed state exists ONLY under html.js: with no script
     every slide is full opacity and full size and the row is a plain swipeable strip,
     so nothing on this page can ever rest hidden because a state was not cleared. */
  .rail-head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;flex-wrap:wrap;}
  /* The counter is a reading, not a control: prose face, tabular so the digits never
     shift as the index changes. It stays with the chapter head, above right; the
     arrows have gone down to flank the scroller. */
  .rail-count{font-family:var(--prose);font-size:.95rem;color:var(--ink-3);
              font-variant-numeric:tabular-nums;margin:0;padding-bottom:6px;white-space:nowrap;}
  /* THE STAGE: [prev] [scroller] [next]. --frameh is declared once here and read by
     both the photograph's height and the arrows' offset, so the arrows are centred on
     the FRAME (not on the figure, whose caption would drag them low) by arithmetic
     rather than by eye: 8px of scroller padding, then half a frame, less half a
     button. */
  .rail-stage{--frameh:clamp(340px,44vh,440px);container-type:inline-size;
              position:relative;max-width:var(--maxw);margin-inline:auto;padding-inline:var(--pad);
              margin-top:calc(clamp(24px,3.6vh,40px) - 8px);
              display:flex;align-items:flex-start;gap:clamp(6px,1vw,14px);}
  .rail-arrow{flex:0 0 auto;width:48px;height:48px;margin-top:calc(8px + var(--frameh) / 2 - 24px);
              border-radius:50%;border:1px solid var(--line);background:var(--paper);color:var(--ink);
              display:flex;align-items:center;justify-content:center;cursor:pointer;
              transition:background .2s var(--ease),border-color .2s var(--ease),color .2s var(--ease);}
  .rail-arrow svg{display:block;}
  .rail-arrow:disabled{color:var(--ink-3);opacity:1;border-color:var(--line-2);cursor:default;}
  @media (hover:hover) and (pointer:fine){
    .rail-arrow:not(:disabled):hover{background:var(--navy);border-color:var(--navy);color:#fff;}
  }
  /* THE SCROLLER. The peek is padding, so a slide at flex-basis 100% is exactly the
     scroller's content box and the snap points sit exactly one slide-plus-gap apart:
     centring the last slide and scrolling to the end are then the same number, which
     is why paging never drifts and the arrows disable honestly. The vertical padding
     is the room the ring, the shadow and the 3px hover lift need, since a horizontal
     scroller clips its other axis too; it comes back out of the stage's margin-top. */
  .rail{--peek:11cqi;flex:1 1 auto;min-width:0;display:flex;gap:1.25rem;
        overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;
        scroll-padding-inline:var(--peek);padding-inline:var(--peek);
        padding-top:8px;padding-bottom:16px;scrollbar-width:none;}
  .rail::-webkit-scrollbar{display:none;}
  .rail figure{margin:0;flex:0 0 100%;scroll-snap-align:center;transform-origin:center;}
  /* THE CARD WEIGHT TREATMENT, photo variant. The house recipe (hairline ring, deep
     negative-spread lift, tight contact shadow) on the ACTIVE frame; the neighbours
     keep the ring alone, so weight itself reads as focus. The inks are this book's:
     the shadow is the page's own ink rgb(27,26,23), cast down and tight, never a
     black bloom radiated evenly around the frame. The radius is this page's 6px. */
  .rail figure img{width:100%;height:var(--frameh);object-fit:cover;border-radius:6px;
        box-shadow:0 0 0 1px rgba(27,26,23,.05),
                   0 16px 38px -22px rgba(27,26,23,.22),
                   0 5px 14px -10px rgba(27,26,23,.12);}
  /* THE SPOTLIGHT. Scale is the `scale` property, not a transform, so the hover lift
     below can own `transform` without the two fighting; centre origin also keeps each
     snap area's centre exactly where it was, so the dimming can never move a snap
     point under the scroll. */
  html.js .rail figure{opacity:.55;scale:.9;
        transition:opacity .3s var(--ease),scale .3s var(--ease);}
  html.js .rail figure.is-active{opacity:1;scale:1;}
  html.js .rail figure img{transition:box-shadow .3s var(--ease),transform .9s cubic-bezier(.2,.6,.2,1);}
  html.js .rail figure:not(.is-active) img{box-shadow:0 0 0 1px rgba(27,26,23,.05);}
  /* The one hover on the frame, and it is the client's own: a 3px lift and the ring
     turning navy on the ACTIVE photograph only. No pointer, no press state: the two
     arrows remain the controls. */
  @media (hover:hover) and (pointer:fine){
    html.js .rail figure.is-active:hover img{transform:scale(1.035);
        box-shadow:0 0 0 1px rgba(38,64,99,.30),
                   0 20px 42px -22px rgba(27,26,23,.26),
                   0 6px 16px -10px rgba(27,26,23,.14);}
  }
  .rail figcaption{font-family:var(--sans);font-size:.87rem;color:var(--ink-2);margin-top:11px;}
  /* A peeking photograph is cropped on purpose; a peeking WORD just looks sliced. The
     caption therefore belongs to the lit slide, and fades with it. */
  html.js .rail figcaption{transition:opacity .3s var(--ease);}
  html.js .rail figure:not(.is-active) figcaption{opacity:0;}
  .rail:focus-visible{outline:2px solid var(--navy);outline-offset:4px;}
  /* Below 760 two 48px buttons plus their gaps would eat a third of the row and leave
     the photograph too narrow to read, so the arrows lift out of the flow and float
     ON the lit photograph, inset 10px from its edge: cqi makes that inset exact, since
     the peek and the offset are then both read off the same stage width. Still full
     size, still centred on the frame, still real buttons. */
  @media (max-width:760px){
    .rail-stage{--frameh:300px;}
    .rail-arrow{position:absolute;top:calc(8px + var(--frameh) / 2 - 24px);margin-top:0;z-index:2;
                box-shadow:0 2px 7px -3px rgba(27,26,23,.4);}
    .rail-arrow.p{left:calc(var(--pad) + 11cqi + 10px);}
    .rail-arrow.n{right:calc(var(--pad) + 11cqi + 10px);}
  }
  @media (prefers-reduced-motion:reduce){
    .rail{scroll-behavior:auto;}
    html.js .rail figure{opacity:1;scale:1;transition:none;}
    html.js .rail figure img,html.js .rail figcaption{transition:none;}
    html.js .rail figure:not(.is-active) img{box-shadow:0 0 0 1px rgba(27,26,23,.05),
                   0 16px 38px -22px rgba(27,26,23,.22),
                   0 5px 14px -10px rgba(27,26,23,.12);}
  }
  .upcoming{margin-top:clamp(32px,5vh,54px);display:grid;grid-template-columns:1fr auto;gap:22px;align-items:center;
            border-top:1px solid var(--line);padding-top:28px;}
  .upcoming .disp{font-size:clamp(1.7rem,2.4vw,2.1rem);line-height:1.22;} .upcoming p{color:var(--ink-2);max-width:48ch;}
  @media (max-width:640px){
    .upcoming{grid-template-columns:1fr;}
    /* Grid items stretch by default: without this the navy button and the tlink's
       underline run the full column width. */
    .upcoming .act,.upcoming .tlink{justify-self:start;}
  }
  /* Mobile type caps: the desktop scale reads generous on a laptop and oversized on
     a phone, so the phone steps every running size back down. */
  @media (max-width:640px){
    body{font-size:1.01rem;}
    .prose{font-size:1.06rem;}
    .bl li{font-size:1.06rem;}
    .hero-pts li{font-size:1.06rem;}
    .hero-by{font-size:1.05rem;}
    .halft{font-size:1.14rem;}
    .facts{font-size:1rem;}
    .epi{font-size:1.18rem;}
  }
  /* v13: the empty month, beside the sentence that explains it. Two stroke weights,
     both navy at low opacity: the frame and its header rule read first, the thirty-five
     cells sit behind them, so the drawing has a hierarchy instead of being a wire mesh.
     The stroke is non-scaling, so the hairline is 1.5px whether the mark renders at
     96px or 132px, exactly as the monogram's is. Butt caps, mitre joins, no curve. */
  .cal{margin-top:clamp(24px,3.8vh,38px);display:grid;grid-template-columns:auto minmax(0,1fr);
       gap:clamp(20px,3vw,36px);align-items:center;}
  .cal-m{width:clamp(94px,10.5vw,128px);height:auto;fill:none;stroke-width:1.5;}
  .cal-m path{vector-effect:non-scaling-stroke;stroke-linecap:butt;stroke-linejoin:miter;}
  /* Chromium computes dash patterns in SCREEN units under non-scaling-stroke, which
     breaks the pathLength="1" draw maths and leaves the grid as 1px confetti forever.
     The inner rules give up the non-scaling stroke so their dashes obey pathLength;
     at this render size the width difference is invisible. The frame keeps it. */
  .cal-c path{vector-effect:none;}
  .cal-f path{stroke:rgba(38,64,99,.52);}
  .cal-c path{stroke:rgba(38,64,99,.19);}
  .cal-t .disp{font-size:clamp(1.4rem,2.3vw,1.8rem);line-height:1.24;color:var(--ink);}
  .cal-t :is(h1,p) + p{margin-top:9px;color:var(--ink-2);font-size:1rem;line-height:1.55;max-width:48ch;}
  .cal-t a{color:var(--navy);text-decoration:none;border-bottom:1px solid var(--line);}
  /* Footer social rows: the real marks, bare, in the row's own ink -- no tiles. */
  .soc-a{display:inline-flex;align-items:center;gap:.55em;}
  .soc{width:15px;height:15px;flex:0 0 auto;opacity:.78;}
  @media (max-width:560px){ .cal{grid-template-columns:minmax(0,1fr);row-gap:18px;justify-items:start;} }

  /* ── CHAPTER VII: the invitation ──────────────────────────────────
     The stage photograph now owns the section: it is shown whole, never cropped, at
     roughly 62% of the measure, and the copy beside it is two lines and three
     one-line topics. The form is compact and sits under both. */
  .speak-grid{display:grid;grid-template-columns:minmax(0,1.75fr) minmax(0,1fr);gap:clamp(26px,4vw,54px);
              align-items:center;margin-top:clamp(22px,3.6vh,38px);}
  .speak-img{width:100%;height:auto;border-radius:4px;}
  .speak-lead{color:var(--ink-2);font-size:clamp(1.06rem,1.45vw,1.2rem);line-height:1.58;max-width:36ch;}
  .topics{list-style:none;margin:clamp(18px,2.6vh,26px) 0 0;padding:0;}
  .topics li{padding:13px 0;border-top:1px solid var(--line-2);}
  .topics li:last-child{border-bottom:1px solid var(--line-2);}
  .topics b{font-weight:700;font-size:1.02rem;}
  .topics .g{color:var(--ink-2);font-size:.95rem;display:block;margin-top:2px;}
  /* Centred on the measure: left-aligned under a full-width chapter it put the last
     mass on the page back on the left rim again. v12 retires the request form and
     leaves one line, one button and the number, at the same weight the form held. */
  .invite-act{margin:clamp(28px,4.4vh,44px) auto 0;max-width:760px;text-align:center;}
  .invite-act.island{max-width:none;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;
    gap:clamp(20px,3vw,54px);text-align:left;background:var(--navy);color:#f4efe4;border-radius:6px;
    padding:clamp(32px,4.4vw,60px) clamp(28px,4.2vw,64px);}
  .invite-act.island .invite-line{color:#f7f2e6;}
  .invite-act.island .act{background:#f4efe4;color:var(--navy);}
  .invite-act.island .act:hover{background:#fff;}
  .invite-act.island .invite-line{font-size:clamp(1.35rem,2.3vw,1.9rem);font-family:var(--display);}
  @media (max-width:680px){ .invite-act.island{grid-template-columns:1fr;text-align:center;} }
  .invite-line{font-family:var(--display);font-size:clamp(1.4rem,2.6vw,1.9rem);line-height:1.24;
               color:var(--ink);margin:0 0 clamp(18px,2.6vh,24px);}
  .invite-tel{font-family:var(--sans);font-size:.95rem;color:var(--ink-3);margin:16px 0 0;}

  .invite-tel a{color:var(--ink-2);text-decoration:none;
                border-bottom:1px solid var(--line);transition:border-color .2s var(--ease);}
  .invite-tel a:hover{border-bottom-color:var(--ink-2);}
  .ok{color:var(--navy);font-family:var(--display);font-size:1.15rem;} .hidden{display:none;}
  @media (max-width:860px){ .speak-grid{grid-template-columns:1fr;} }

  /* ── BACK MATTER: the newsletter band ─────────────────────────────── */
  .news{position:relative;background:var(--navy);color:#fff;overflow:hidden;isolation:isolate;padding:clamp(58px,8.6vh,106px) 0;}
  /* The monogram, oversized and ghosted, bleeding off the band's right edge: the
     brand detail that keeps this from being a flat pre-footer slab. */
  .news-mark{position:absolute;right:-6%;top:50%;transform:translateY(-50%);width:min(34vw,420px);aspect-ratio:1;
    background:url("mark.png") center/contain no-repeat;opacity:.1;filter:brightness(4);pointer-events:none;}
  .news .grain{opacity:.05;}
  .news-in{position:relative;z-index:1;text-align:center;max-width:660px;margin-inline:auto;}
  .news .kick{color:#c3cfdd;font-size:1rem;}
  .news h2{font-family:var(--display);font-weight:400;font-size:clamp(2.1rem,5vw,3.4rem);line-height:1.06;letter-spacing:-.006em;margin-top:10px;}
  .news p{color:#d8e1ec;margin:1rem auto 2rem;max-width:44ch;}
  .news form{display:flex;gap:10px;max-width:470px;margin-inline:auto;flex-wrap:wrap;justify-content:center;}
  .news input{flex:1 1 240px;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.58);color:#fff;font-family:var(--sans);padding:12px 16px;border-radius:4px;text-align:center;}
  .news input::placeholder{color:#dfe7f0;} .news input:focus{outline:0;border-color:#fff;}
  .news .act{background:#fff;color:var(--navy);box-shadow:none;}
  .news .act:hover{background:#eef2f7;}
  /* The demo label is on the page, not only in the source: nothing here posts yet and
     the page says so where the promise is made. */
  .note{font-family:var(--sans);color:var(--ink-3);font-size:.82rem;margin-top:14px;}
  .ok{animation:okSettle .45s cubic-bezier(.16,1,.3,1);}
  @keyframes okSettle{from{transform:translateY(10px)}to{transform:none}}
  @media (prefers-reduced-motion:reduce){ .ok{animation:none;} }
  .news .note{color:#a9b7c8;margin-top:12px;}
  .news .ok{color:#fff;}
  /* The buy action is present in this context too, but never as a second button
     beside the first: it is a quiet line under the form, clearly subordinate to it. */
  .buylink{display:inline-block;margin-top:clamp(20px,3vh,28px);font-family:var(--sans);font-size:.92rem;color:#d8e1ec;
           text-decoration:none;border-bottom:1px solid rgba(216,225,236,.4);transition:border-color .2s var(--ease);}
  .news a:focus-visible,.news input:focus-visible,.news .act:focus-visible{outline-color:#fff;}

  /* ── THE SIGNATURE: footer ────────────────────────────────────────── */
  /* v13: the floor is a real floor again. With the wordmark gone the footer needs its
     own bottom padding back (it had none, because LIFTED's baseline WAS the page edge)
     and a little more air at the top, so the mark has room to stand in instead of
     leaving the block looking abandoned. */
  .foot{position:relative;background:var(--paper-3);border-top:1px solid var(--line);
        padding:clamp(52px,7.4vh,84px) 0 clamp(34px,5vh,54px);overflow:clip;}
  .foot > .shell{position:relative;z-index:1;}
  /* The monogram, large and centred, on the layer above the paper grain. It is the
     nav's own mark at the same aspect, so nothing here is redrawn or invented. */
  .foot-mark{width:clamp(88px,11vw,120px);aspect-ratio:1;margin:0 auto clamp(40px,5.6vh,64px);
             background:url("mark.png") center/contain no-repeat;}
  /* Two marks stacked would be a repeat, so the small one in the footer's brand lockup
     stands down and the wordmark beside it carries that row on its own. */
  .foot .brand .m{display:none;}
  .foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:34px;}
  .foot .brand .n{font-size:1.26rem;}
  .foot .blurb{color:var(--ink-3);font-size:.9rem;margin-top:12px;max-width:34ch;}
  .foot h4{font-family:var(--prose);font-size:.95rem;color:var(--ink-3);margin:0 0 12px;font-weight:500;letter-spacing:0;text-transform:none;}
  .foot a{display:block;font-family:var(--sans);color:var(--ink-2);text-decoration:none;font-size:.92rem;margin-bottom:9px;transition:color .2s var(--ease);}
  .foot .cap{font-family:var(--sans);color:var(--ink-3);font-size:.85rem;margin-top:clamp(28px,4.2vh,44px);max-width:60ch;}
  .foot-bot{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-top:20px;padding-top:20px;border-top:1px solid var(--line);
            font-family:var(--sans);color:var(--ink-3);font-size:.82rem;}
  @media (max-width:720px){ .foot-grid{grid-template-columns:1fr 1fr;} .foot .brand-col{grid-column:1/-1;} }
  /* Reduced motion: gentler, not zero. Movement goes, colour and state stay. */
  @media (prefers-reduced-motion:reduce){
    html{scroll-behavior:auto;}
    .nav{transition:border-color .25s var(--ease),background .25s var(--ease);}
    html.js .reveal{transform:none;filter:none;transition:none;}
    html.js .hero-copy > *{transform:none;transition:none;}
    .act .ar{transition:none;}
    .ghost{--gy:0px !important;}
    /* The scene never pins: it collapses to one readable screen with the sentence,
       the poster still, and the three beats stacked under it, line fully drawn. */
    .scene{height:auto !important;}
    .scenepin{position:relative !important;height:auto !important;}
    html.scene-on .tlb{transform:none !important;}
    html.scene-on .scene-line .sl{transform:none !important;}
    html.js .tlb .yr,html.js .tlb h3,html.js .tlb p,html.js .tlb .plate{transform:none;transition:none;}
    html.js .rl{filter:none;transform:none;transition:none;}
    .ptrail{display:none;}
  }

  /* ══════════════════════════════════════════════════════════════════
     THE SITE BECOMES A BOOK IN FOUR SIGNATURES.
     Everything above this line is the one-pager's own stylesheet, unchanged
     except for the font and mark urls, which now point at real files beside
     this one instead of at base64 blobs. Everything below is what the split
     into four pages actually needed, and nothing else: no component here is
     new for its own sake, and every one of them is built out of the grammar
     already established above (the folio, the outer margin, the hairline row,
     the display face for anything that carries weight).
     ══════════════════════════════════════════════════════════════════ */

  /* THE CURRENT PAGE, IN THE BAR. Weight and colour, on the word itself. No
     dot under it, no underline, no pill: the link you are already on simply
     reads heavier and takes the navy. */
  .nav-links a.here{color:var(--navy);font-weight:500;}
  .drawer a.here{color:var(--navy);}
  /* The drawer used to be a chapter index for one long page, so it carried a
     numeral column. It is a list of destinations now, and an empty numeral
     column beside four words is a rule pretending to be structure. */
  .drawer a{grid-template-columns:minmax(0,1fr);}

  /* A page that opens on paper rather than on the sky needs its own top air:
     the hero pulled itself up under the bar, these do not. */
  .chap.first{padding-top:clamp(46px,8vh,104px);}
  /* The last thing on a page is one row, not a chapter, and a full chapter's
     padding above it on top of the previous section's own left 230px of dead
     paper between the two. It takes the row's own air and nothing more. */
  .chap.tail{padding-top:clamp(4px,1vh,14px);}

  /* THE QUIET ACTION. There is one button voice on this site and it is navy
     and it is for buying and booking. Everything else that leads somewhere is
     this: type, the page's own arrow, and a rule under it that darkens. The
     arrow travels on the same diagonal the buy button's does, so the two are
     the same gesture at two weights. */
  .tlink{display:inline-flex;align-items:center;gap:.55em;font-family:var(--sans);font-size:.95rem;
         color:var(--navy);text-decoration:none;white-space:nowrap;position:relative;
         border-bottom:1px solid var(--line);transition:border-color .2s var(--ease);}
  /* An invisible 40px-tall hit area on top of the ~20px line, so the tap target
     meets the minimum without moving the underline or the rhythm around it. */
  .tlink::after{content:"";position:absolute;left:0;right:0;top:50%;height:40px;transform:translateY(-50%);}
  .tlink .ar{flex:0 0 auto;transition:transform .28s var(--ease);}
  @media (hover:hover) and (pointer:fine){
    .tlink:hover{border-bottom-color:currentColor;}
    .tlink:hover .ar{transform:translate(2.5px,-2.5px);}
  }
  /* The one arrow that points back. Same stroke, same cap, same 1.5px. */
  .tlink.back .ar{transition:transform .28s var(--ease);}
  @media (hover:hover) and (pointer:fine){ .tlink.back:hover .ar{transform:translateX(-2.5px);} }

  /* A photograph at the full measure of its column, with the caption in the
     same sans the portrait's carries. Bare: no frame, no ring, no lift. */
  .figw{margin:clamp(26px,4vh,44px) 0 0;}
  .figw img{width:100%;border-radius:3px;}
  .figw figcaption{font-family:var(--sans);color:var(--ink-3);font-size:.84rem;margin-top:11px;}
  /* MOTION 4. The opening photograph on The Author settles from 1.02 to 1 once, on
     entry: the same gesture the book render makes in the hero, on the same curve
     and the same 600ms. It rides the reveal observer that is already on the figure,
     so it needs no script of its own, and the animation runs from 1.02 to 1 with no
     opacity term at all: the photograph is fully drawn from the first paint whether
     or not this ever fires. The 1% overhang is clipped by the figure so the picture
     never grows past the measure of its own column. */
  .figw.settle{overflow:clip;}
  html.js .figw.settle.in img{animation:photoSettle .6s var(--ease) both;}
  @keyframes photoSettle{from{transform:scale(1.02)}to{transform:none}}

  /* THE BREATH ON THE AUTHOR'S PAGE. His own sentence, from the memoir essay on
     this site, at display size in the true italic. No quotation marks, no rule, no
     attribution line, no panel: air above and below is the whole treatment. */
  .pull{font-family:var(--display);font-weight:400;font-style:italic;
        font-size:clamp(1.7rem,3.5vw,2.6rem);line-height:1.22;letter-spacing:-.004em;
        color:var(--ink);max-width:24ch;text-wrap:balance;
        margin:clamp(46px,8vh,96px) 0 clamp(40px,7vh,84px);}

  /* ── THE CHRONOLOGY (The Author) ─────────────────────────────────
     The same three beats the pinned scene tells with photographs, told here
     as print: the year in the display face in a FIXED column so all three
     headings start on one x, the sentence beside it. The plates are not
     repeated here; they appear once on the front page and this is the other
     telling, not the same one twice. */
  .chron{margin-top:clamp(26px,4vh,44px);}
  .chron-b{display:grid;grid-template-columns:8.5rem minmax(0,1fr);gap:clamp(14px,2.4vw,34px);
           padding:clamp(18px,2.8vh,28px) 0;border-top:1px solid var(--line-2);align-items:baseline;}
  .chron-b:last-child{border-bottom:1px solid var(--line-2);}
  .chron .yr{font-family:var(--display);font-weight:400;font-size:clamp(1.5rem,2.5vw,2.05rem);
             line-height:1;letter-spacing:.012em;color:var(--navy-deep);}
  .chron h3{font-family:var(--display);font-weight:400;font-size:clamp(1.32rem,2.1vw,1.72rem);
            line-height:1.14;letter-spacing:-.004em;color:var(--ink);margin:0;}
  .chron p{color:var(--ink-2);font-size:1.04rem;line-height:1.52;margin-top:7px;max-width:48ch;}
  @media (max-width:620px){
    .chron-b{grid-template-columns:minmax(0,1fr);gap:6px;}
  }

  /* ── RECENT SIGNINGS (Events) ────────────────────────────────────
     v14: three lines of type per date and NOTHING drawn around them. No card, no
     border, no rule, no shadow: the stack is the structure, and the space between
     stacks is what separates them. The three lines take three different voices so
     the hierarchy is read off the type rather than off a box.
       the date    Goudy, large. Sorts Mill Goudy ships one weight and
                   font-synthesis is off site-wide, so a display "bold" cannot be
                   drawn here and is not faked: the date carries its weight in
                   SIZE and in full ink, which is the same thing the chapter titles
                   do on every other page.
       the place   Sentient 700, a real bold cut, at reading size.
       the line    Goudy true italic, the aside voice this book already uses.
     THE HANDBILL is marginalia. It is a real artifact of that date, not page
     colour, so it stays, at 110px out in the outer margin the printed margin notes
     already live in, whole and never cropped. Its shadow is the one this site
     allows: tight, low, warm, cast from one direction, so a printed sheet lies on
     the paper instead of floating on a halo. */
  .signs{margin-top:clamp(28px,4.4vh,44px);}
  .sign{position:relative;padding:clamp(26px,4.4vh,44px) 0;}
  .sign .bill{margin:0 0 16px;width:110px;}
  .sign .bill img{width:100%;height:auto;border-radius:2px;
                  box-shadow:3px 4px 9px rgba(74,52,26,.18);}
  .sign .when{font-family:var(--display);font-weight:400;font-size:clamp(1.75rem,3.1vw,2.5rem);
              line-height:1.06;letter-spacing:-.006em;color:var(--ink);}
  .sign .where{font-family:var(--prose);font-weight:700;font-size:clamp(1.02rem,1.35vw,1.16rem);
               line-height:1.3;color:var(--ink);margin-top:10px;}
  .sign .said{font-family:var(--display);font-weight:400;font-style:italic;
              font-size:clamp(1.08rem,1.5vw,1.28rem);line-height:1.38;
              color:var(--ink-2);margin-top:9px;max-width:40ch;}
  /* At 901 and up the sheet leaves the flow and goes out into the margin, level
     with the date it belongs to. The margin is 12vw capped at 168, so the sheet
     takes whichever is smaller and can never be wider than the paper it sits on. */
  @media (min-width:901px){
    .sign .bill{position:absolute;top:clamp(26px,4.4vh,44px);width:min(110px,var(--marg));margin:0;}
    .chap.verso .sign .bill{right:calc(-1 * (var(--marg) + var(--mgap)));}
    .chap:not(.verso) .sign .bill{left:calc(-1 * (var(--marg) + var(--mgap)));}
  }
  /* MOTION 3. The four stacks rise 14px into place in sequence, 80ms apart, once.
     Position only, on the reveal observer that is already on each row, so with no
     JS at all the four stacks are simply printed where they belong. */
  html.js .sign{transform:translateY(14px);transition:transform .5s var(--ease);}
  html.js .sign.in{transform:none;}
  html.js .sign:nth-child(2){transition-delay:80ms;}
  html.js .sign:nth-child(3){transition-delay:160ms;}
  html.js .sign:nth-child(4){transition-delay:240ms;}

  /* ── THE JOURNAL ─────────────────────────────────────────────────
     The date goes out into the book's own outer margin, in the same italic
     the printed margin notes take, so an entry begins with its TITLE rather
     than with a small label stacked over a big one. Below 760 the margin
     folds in and the date becomes the entry's first line, which is what a
     margin note does everywhere else on this site. */
  .entry{display:grid;grid-template-columns:10.5rem minmax(0,1fr);gap:clamp(18px,3.4vw,48px);
         padding:clamp(30px,5vh,54px) 0;border-top:1px solid var(--line);}
  .entry:last-of-type{border-bottom:1px solid var(--line);}
  .entry .when{font-family:var(--display);font-weight:400;font-style:italic;font-size:.97rem;
               line-height:1.42;color:var(--ink-2);}
  .entry h2{font-family:var(--display);font-weight:400;font-size:clamp(1.85rem,3.3vw,2.8rem);
            line-height:1.06;letter-spacing:-.008em;max-width:22ch;text-wrap:balance;}
  /* MOTION 5. A title that is a link takes the navy on hover and on keyboard focus,
     over 150ms, and does not move a pixel: no underline grows, nothing lifts. */
  .entry h2 a{text-decoration:none;color:inherit;transition:color .15s var(--ease);}
  .entry h2 a:hover,.entry h2 a:focus-visible{color:var(--navy);}
  /* One line of the post's own opening, in the prose face. Never two. */
  .entry .lede{font-family:var(--prose);font-weight:400;color:var(--ink-2);
               font-size:1.06rem;line-height:1.6;max-width:56ch;
               margin-top:clamp(12px,1.8vh,18px);}
  .entry .after{margin-top:clamp(16px,2.4vh,22px);}
  /* The one image left in the ledger: the Villa Park handbill, which was the whole
     of the post it belongs to, printed inside that entry at a size you can read the
     sheet at and no larger. Same warm, tight, one-direction shadow as the events
     marginalia, because it is the same kind of object. */
  .inbill{margin:clamp(16px,2.4vh,22px) 0 0;width:min(200px,52%);}
  .inbill img{width:100%;height:auto;border-radius:2px;
              box-shadow:3px 4px 9px rgba(74,52,26,.18);}
  @media (max-width:760px){
    .entry{grid-template-columns:minmax(0,1fr);gap:10px;}
    .inbill{width:min(180px,60%);}
  }

  /* ── ONE POST, ON ITS OWN PAGE ───────────────────────────────────
     Long-form: the prose measure, subheads in the display face, and the six
     principles inside the essay set as run-in headings rather than as a
     second copy of the framework list. */
  /* A post's title is not a chapter title. The chapter measure is 16ch, which
     is right for two or three words and breaks "Lifted to Lead: A Memoir of
     Resilience" into a four line staircase; this one is set smaller and given
     the room to land on two. */
  .chap-t.art{max-width:26ch;font-size:clamp(2rem,4.3vw,3.3rem);}
  .artmeta{font-family:var(--sans);color:var(--ink-3);font-size:.88rem;margin-top:16px;}
  .prose h3{font-family:var(--display);font-weight:400;font-size:clamp(1.44rem,2.4vw,1.92rem);
            line-height:1.16;letter-spacing:-.004em;color:var(--ink);margin:clamp(30px,4.4vh,48px) 0 12px;}
  .prose h4{font-family:var(--prose);font-weight:700;font-size:1.06rem;line-height:1.3;
            color:var(--ink);margin:clamp(22px,3vh,30px) 0 6px;}
  .backrow{margin-bottom:clamp(20px,3vh,30px);}
  .postnav{display:flex;gap:26px;flex-wrap:wrap;align-items:center;
           margin-top:clamp(34px,5vh,56px);padding-top:26px;border-top:1px solid var(--line);}

  /* ── THE FOOTER GAINS A FOURTH COLUMN ────────────────────────────
     The three places he actually posts, as words. A social glyph here would
     be an icon-pack row in a page that has no icon pack, and a box behind one
     would be worse; the links carry themselves. */
  .foot-grid{grid-template-columns:1.35fr 1fr 1fr .95fr;}
  @media (max-width:860px){ .foot-grid{grid-template-columns:1fr 1fr 1fr;} .foot .brand-col{grid-column:1/-1;} }
  @media (max-width:720px){ .foot-grid{grid-template-columns:1fr 1fr;} }
  /* Two colophon strings pushed to opposite rims is a gulf, not a layout.
     They sit together on the grid's own left margin and wrap as one line. */
  .foot-bot{justify-content:flex-start;gap:6px 24px;}

  /* ── MOTION 2: THE EMPTY MONTH DRAWS ITSELF ──────────────────────
     Ten rules, six vertical then four horizontal, 30ms apart, each drawn in 130ms
     on a linear ramp: the last one starts at 270ms and lands at 400ms, which is
     the whole gesture. Linear, and butt caps (already set on .cal-m path), because
     a ruled line is drawn at a constant speed and its ends are square: an eased
     stroke with round caps would read as a swipe rather than as a pen.
     Every path carries pathLength="1", so one dasharray covers a vertical of 80
     user units and a horizontal of 114 alike and nothing has to be measured.
     THE RESTING STATE IS THE COMPLETE GRID. The undrawn state exists only under
     html.js .cal.armed, and only the script adds that class, only when there is an
     IntersectionObserver and motion is not reduced; the script then adds .drawn on
     entry, and a 2s timer adds it regardless. No script, no observer, a stalled
     timer or a backgrounded tab all resolve to the full month. */
  html.js .cal.armed .cal-c path{stroke-dasharray:1;stroke-dashoffset:1;}
  html.js .cal.armed.drawn .cal-c path{animation:calDraw 130ms linear forwards;
                                       animation-delay:calc(var(--i,0) * 30ms);}
  @keyframes calDraw{to{stroke-dashoffset:0;}}

  /* ── MOTION 1: ONE SHEET TURNS INTO THE NEXT ─────────────────────
     Five pages of one book, so a click should read as a page turning rather than
     as a document being replaced. The multi-page View Transitions API does exactly
     that with no script: the browser crossfades the old paper into the new one over
     200ms. It is opacity only, nothing travels, and where the API is missing (or
     the navigation is cross-origin, which every Amazon and social link here is) the
     at-rule is simply unknown and the click behaves as it always did. */
  @view-transition{navigation:auto;}
  ::view-transition-old(root),::view-transition-new(root){
    animation-duration:200ms;animation-timing-function:linear;}

  /* ── REDUCED MOTION, for everything this file added ──────────────
     Gentler, not zero: the crossfade between pages is opacity alone and stays,
     because nothing about it moves. Everything that travels or scales goes. */
  @media (prefers-reduced-motion:reduce){
    html.js .sign{transform:none;transition:none;}
    html.js .figw.settle.in img{animation:none;}
    .tlink .ar,.tlink.back .ar{transition:none;}
  }


/* ── AUDIT FIXES (2026-08) ─────────────────────────────────────────────
   1. In-page anchor targets clear the sticky bar even when the JS handler
      is not what moved us (keyboard focus, a fresh load with a hash). */
#book,#newsletter,#speaking{scroll-margin-top:calc(var(--navh) + 14px);}

/* 2. Small phones: overflow-x:clip on the root SLICES anything wider than the
      viewport instead of scrolling it, so nothing in the hero may exceed the
      column. The three lines wrap, and the one big buy control shrinks so it
      can never run past the right gutter. Fonts are preloaded in <head>, which
      removes the wide-fallback-font flash that was doing most of the clipping. */
@media (max-width:480px){
  .hero-in{padding-inline:20px;}
  .hero-pts{max-width:none;}
  .hero-pts li{overflow-wrap:break-word;}
  .act.big{padding:15px 20px;font-size:1rem;max-width:100%;}
}

/* Editorial image hover: the flyers and the signing photo zoom slowly INSIDE their
   frame; nothing lifts, nothing glows. Pointer-fine only. */
.bill{overflow:hidden;border-radius:6px;}
.figw{overflow:hidden;border-radius:6px;}
@media (hover:hover) and (pointer:fine){
  .bill img,.figw img,.inbill img{transition:transform .9s cubic-bezier(.2,.6,.2,1);}
  .bill:hover img,.figw:hover img,.inbill:hover img{transform:scale(1.04);}
}
@media (prefers-reduced-motion:reduce){
  .bill img,.figw img,.inbill img{transition:none;}
  .bill:hover img,.figw:hover img{transform:none;}
}

/* ── NEWSLETTER PAGE ── */
.nl-pitch{max-width:640px;margin-top:clamp(28px,4.4vh,44px);}
.nl-pitch .prose{margin-top:14px;}
.nl-form{display:flex;gap:12px;margin-top:clamp(22px,3vh,32px);flex-wrap:wrap;}
.nl-form input{flex:1 1 260px;font-family:var(--prose);font-size:1.05rem;padding:13px 16px;
  border:1px solid var(--line);border-radius:3px;background:var(--paper);color:var(--ink);}
.nl-form input:focus-visible{outline:2px solid var(--navy);outline-offset:2px;border-color:var(--navy);}
.nl-done{font-family:var(--display);font-style:italic;font-size:1.2rem;margin-top:18px;}

/* Newsletter image: a composed band, not a slab — capped height, framed crop. */
#join .figw img{max-height:52vh;object-fit:cover;object-position:50% 62%;}
@media (max-width:640px){
  #join .figw img{max-height:38vh;}
  .nl-pitch .chap-t{font-size:clamp(1.9rem,8vw,2.4rem);}
}

/* ── THE CURSOR ── */
html.cursor-on, html.cursor-on * { cursor: none !important; }
.cur-dot,.cur-ring{position:fixed;left:0;top:0;pointer-events:none;z-index:9999;
  will-change:transform;transition:opacity .25s ease;}
.cur-dot{width:6px;height:6px;border-radius:50%;background:var(--ink);
  transition:width .28s cubic-bezier(.22,1,.36,1),height .28s cubic-bezier(.22,1,.36,1),opacity .25s ease;}
.cur-ring{width:26px;height:26px;border-radius:50%;border:1px solid rgba(44,62,93,.35);
  background:rgba(44,62,93,.05);backdrop-filter:blur(1px);-webkit-backdrop-filter:blur(1px);
  transition:width .32s cubic-bezier(.22,1,.36,1),height .32s cubic-bezier(.22,1,.36,1),
             border-color .3s ease,background .3s ease,opacity .25s ease;}
/* v42: the hover state is a liquid-glass lens riding the pointer. Plain-glass
   fallback first (blur+saturate), then the reference filter for engines that
   refract through SVG backdrop filters (Chromium). The lip highlights come from
   inset shadows, the way real glass catches light on its upper edge. */
/* v46: refraction retired -- Chromium garbles SVG displacement under
   backdrop-filter at variable sizes (two maps, two artifacts). The wrap's glass
   now comes entirely from LIGHT: a lit rim, a top-edge gloss, a gentle lift of
   whatever sits beneath. Nothing bends, so nothing can garble, and the label
   under the pane stays perfectly crisp. */
html.cur-hot .cur-ring{
  border:1px solid rgba(255,255,255,.55);
  background:linear-gradient(to bottom, rgba(255,255,255,.14), rgba(255,255,255,.04) 40%, rgba(255,255,255,0) 70%);
  -webkit-backdrop-filter:brightness(1.06) saturate(1.18) contrast(1.02);
  backdrop-filter:brightness(1.06) saturate(1.18) contrast(1.02);
  box-shadow:
    inset 0 1px 1px rgba(255,255,255,.6),
    inset 1.5px 0 1px -1px rgba(255,255,255,.35),
    inset -1.5px 0 1px -1px rgba(255,255,255,.35),
    inset 0 -1px 1.5px -0.5px rgba(255,255,255,.25),
    0 3px 8px rgba(60,45,20,.14);}
html.cur-lock .cur-ring{transition:width .34s cubic-bezier(.22,1,.36,1),height .34s cubic-bezier(.22,1,.36,1),
  border-radius .34s cubic-bezier(.22,1,.36,1),box-shadow .3s ease,border-color .3s ease;}


html.cur-press .cur-ring{width:28px;height:28px;background:rgba(44,62,93,.16);}
html.cur-hot.cur-press .cur-ring{width:66px;height:66px;background:rgba(255,255,255,.1);}
@media (hover:none),(pointer:coarse){ html.cursor-on,html.cursor-on *{cursor:auto !important;} .cur-dot,.cur-ring{display:none;} }
@media (prefers-reduced-motion:reduce){ .cur-dot,.cur-ring{display:none;} html.cursor-on,html.cursor-on *{cursor:auto !important;} }

/* v34: the drawer was the one interactive surface that hard-popped. The panel
   still appears instantly (no opacity gate -- content is present the moment it
   opens); only its links settle 10px into place, auto-firing on the display
   flip. Reduced motion keeps the instant pop. */
html.js .drawer.open a{animation:drawerSettle .38s cubic-bezier(.16,1,.3,1) backwards;}
html.js .drawer.open a:nth-child(2){animation-delay:.03s}
html.js .drawer.open a:nth-child(3){animation-delay:.06s}
html.js .drawer.open a:nth-child(4){animation-delay:.09s}
html.js .drawer.open a:nth-child(5){animation-delay:.12s}
@keyframes drawerSettle{from{transform:translateY(10px)}to{transform:none}}
@media (prefers-reduced-motion:reduce){ html.js .drawer.open a{animation:none;} }

/* THE LETTER'S KNOCK, v54 -- full screen now. A scrim over the whole page and the
   paper card centred in it, in the visitor's face the way Santiago wants it, but
   still the house's paper-and-ink object: one card, a lit rim, a real close. The
   scrim fades, the card settles up; both are optional UI, never content. */
/* The hidden attribute must WIN over display:grid, or the invisible overlay
   keeps covering the page and eats every click -- and while fading, clicks
   already pass through. */
.nlpop[hidden]{display:none;}
.nlpop:not(.up){pointer-events:none;}
.nlpop{position:fixed;inset:0;z-index:90;display:grid;place-items:center;padding:18px;
  background:rgba(27,26,23,.48);
  opacity:0;transition:opacity .35s ease;}
.nlpop.up{opacity:1;}
.nlpop-card{position:relative;width:min(520px,100%);
  background:#f7f0e0;color:var(--ink);border:1px solid rgba(90,74,50,.16);border-radius:12px;
  padding:clamp(26px,4vh,40px) clamp(24px,3.5vw,44px) clamp(20px,3vh,30px);
  box-shadow:0 10px 30px rgba(20,15,8,.3);
  transform:translateY(26px) scale(.98);transition:transform .45s cubic-bezier(.16,1,.3,1);}
.nlpop.up .nlpop-card{transform:none;}
.nlpop-x{position:absolute;top:8px;right:10px;width:44px;height:44px;border:0;background:none;
  font-size:1.6rem;line-height:1;color:var(--ink-3);cursor:pointer;}
.nlpop-x:hover{color:var(--ink);}
.nlpop-t{font-family:var(--display);font-size:clamp(1.6rem,3vw,2rem);margin:0 0 6px;}
.nlpop-s{font-family:var(--prose);font-size:1rem;color:var(--ink-2);margin:0 0 18px;line-height:1.5;}
.nlpop form{display:flex;gap:10px;}
.nlpop input{flex:1 1 auto;min-width:0;border:1px solid rgba(90,74,50,.3);border-radius:6px;
  padding:12px 14px;font:inherit;font-size:1rem;background:#fffdf7;color:var(--ink);}
.nlpop .act{padding:12px 22px;}
.nlpop .nlpop-note{font-family:var(--sans);font-size:.76rem;color:var(--ink-3);margin:12px 0 0;}
.nlpop .ok{margin:12px 0 0;}
@media (prefers-reduced-motion:reduce){
  .nlpop,.nlpop-card{transition:none;}
}
}

/* NEWSLETTER PAGE v52 */
.nl-beats{list-style:none;margin:clamp(30px,4.5vh,52px) 0 0;padding:0;max-width:560px;}
.nl-beats li{padding:clamp(16px,2.4vh,24px) 0;border-bottom:1px solid var(--line-2, rgba(90,74,50,.16));}
.nl-beats li:first-child{border-top:1px solid var(--line-2, rgba(90,74,50,.16));}
.nl-beats b{display:block;font-family:var(--display);font-weight:600;font-size:1.08rem;margin-bottom:4px;}
.nl-beats span{font-family:var(--prose);color:var(--ink-2);font-size:.98rem;line-height:1.5;}
.nl-island{margin-top:clamp(36px,5.5vh,64px);grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);}
.nl-island .nl-sub{font-family:var(--prose);color:#d8d2c2;font-size:.95rem;margin:10px 0 0;}
.nl-island .nl-form{display:flex;gap:10px;align-self:center;}
.nl-island .nl-form input{flex:1 1 auto;min-width:0;border:1px solid rgba(244,239,228,.35);border-radius:6px;
  padding:12px 14px;font:inherit;font-size:.98rem;background:rgba(244,239,228,.1);color:#f4efe4;}
.nl-island .nl-form input::placeholder{color:rgba(244,239,228,.55);}
.nl-island .nl-done{grid-column:1/-1;color:#f4efe4;margin:8px 0 0;}
.nl-island .nl-note{grid-column:1/-1;font-family:var(--sans);font-size:.76rem;color:rgba(244,239,228,.6);margin:10px 0 0;}
.nl-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);
  gap:clamp(24px,3.5vw,56px);align-items:center;margin-top:clamp(26px,4vh,44px);}
.nl-fig{margin:0;}
.nl-fig img{border-radius:8px;}
@media (max-width:760px){ .nl-grid{grid-template-columns:1fr;} }
@media (max-width:680px){ .nl-island{grid-template-columns:1fr;} .nl-island .nl-form{width:100%;} }
