
/* ---------- mobile: rows of cards swipe instead of stacking ---------- */
@media (max-width:640px){
  .swipe{display:flex!important;flex-direction:row!important;flex-wrap:nowrap!important;
    grid-template-columns:none!important;grid-template-rows:none!important;
    overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;gap:14px!important;
    margin-inline:calc(-1 * var(--page-pad));padding-inline:var(--page-pad) var(--page-pad);
    padding-bottom:4px;scrollbar-width:none;-webkit-overflow-scrolling:touch;
    background:transparent!important;border:0!important;overscroll-behavior-x:contain;
    /* a track inside a grid cell must not size the cell to its content: contain the inline size */
    contain:inline-size;min-width:0;width:calc(100% + 2 * var(--page-pad));box-sizing:border-box;
    scroll-padding-inline:var(--page-pad)}
  .swipe::-webkit-scrollbar{display:none}
  /* a card is sized to its track, not to the viewport: a track inside a panel is narrower */
  .swipe > *{flex:0 0 min(82%,360px)!important;scroll-snap-align:start;min-width:0;
    max-width:none!important;width:auto!important;margin:0!important}
  /* hairline grids drew their lines with the grid ground; on a track each card draws its own */
  .swipe.hgrid > *,.hgrid.swipe > *{border:1px solid var(--hairline)}
  .swipe.stats > *{flex-basis:min(60%,240px)!important}
  /* a logo row is a strip of marks, not a row of cards */
  .swipe.clients > *,.swipe.work-names > *{flex-basis:min(46%,190px)!important;border:0}
  .swipe.how-3 > *{flex-basis:min(88%,380px)!important}
  /* inside a padded panel the track keeps to the panel: no bleed, cards sized to the panel */
  .accent-copy .swipe,.st-way .swipe,.cta .swipe{margin-inline:0;width:100%;padding-inline:0}
  .accent-copy .swipe > *{flex-basis:min(88%,320px)!important;white-space:normal;overflow:visible}
  /* a ledger on a coloured panel is drawn in the panel's own ink */
  .accent-copy .swipe-nav,.st-way .swipe-nav{color:rgba(255,255,255,.75)}
  .accent-copy .swipe-nav .dots i,.st-way .swipe-nav .dots i{background:rgba(255,255,255,.35)}
  .accent-copy .swipe-nav .dots i.on,.st-way .swipe-nav .dots i.on{background:#fff}
  .accent-copy .swipe-nav .n,.st-way .swipe-nav .n{color:#fff}
  /* the opening plate: a 118px sliver at 128/43 on a phone says nothing, so it opens up to 16/9 */
  .hero-plate{aspect-ratio:16/9!important;max-height:none}
  /* the ledger under a track: dots that stretch, and a count in the record face */
  .swipe-nav{display:flex;align-items:center;gap:14px;margin-top:14px;
    font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)}
  .swipe-nav .dots{display:flex;gap:6px;align-items:center;flex-wrap:wrap;max-width:70%}
  .swipe-nav .dots i{display:block;width:6px;height:6px;border-radius:3px;background:var(--hairline-strong);
    transition:width var(--dur-reveal) var(--ease-out),background var(--dur-control) ease}
  .swipe-nav .dots i.on{width:20px;background:var(--blue)}
  .swipe-nav .n{margin-left:auto;font-variant-numeric:tabular-nums;color:var(--ink)}
  .swipe-nav .n b{font-weight:400;color:var(--muted)}
  /* the first-reveal nudge: the track slides 22px and back, once, to say it moves */
  .js .swipe.nudge{animation:swipe-nudge 1.1s var(--ease-out) .35s 1}
  @keyframes swipe-nudge{0%{transform:translateX(0)}45%{transform:translateX(-22px)}100%{transform:translateX(0)}}
}
@media (min-width:641px){.swipe-nav{display:none}}
/* the narrowest phones: these rules load last on every page, so they win */
@media (max-width:400px){
  .cta{grid-template-columns:1fr!important}.cta-col,.cta-col > *{min-width:0}
  .stats{grid-template-columns:1fr 1fr!important}.stat{padding:16px 12px 18px!important}.stat-fig{font-size:32px!important}
  .getnow{flex-direction:column!important}.getnow > *{width:auto!important;min-width:0}
  .reg-pub{width:auto!important;max-width:100%;min-width:0}.reg-row{grid-template-columns:1fr!important}
  :root{--page-pad:16px}
  .getnow > *{flex:1 1 auto!important}
}

/* ---------- the motion layer ---------- */
/* a plate settles as it is revealed: the wipe uncovers it while it eases from 1.04 to 1 */
.js .wipe img,.js .hero-plate img{transform:scale(1.04);transform-origin:50% 60%}
.js .wipe.in img,.js .hero-plate.in img{transform:scale(1);
  transition:clip-path var(--dur-reveal) var(--ease-out),transform 1.4s var(--ease-out)}
/* a section head arrives in order: eyebrow, then the H2, then the note */
.js .sec-head > *{opacity:0;transform:translateY(12px)}
.js .sec-head.in > *{opacity:1;transform:none;
  transition:opacity var(--dur-reveal) var(--ease-out),transform var(--dur-reveal) var(--ease-out)}
.js .sec-head.in > *:nth-child(1){transition-delay:0ms}
.js .sec-head.in > *:nth-child(2){transition-delay:90ms}
.js .sec-head.in > *:nth-child(3){transition-delay:180ms}
.js .sec-head.in > *:nth-child(4){transition-delay:260ms}
/* the hero plate drifts against the scroll, a few pixels, so the opening has depth */
.js .hero-plate{will-change:transform}
/* The room the drift moves into, reserved on the plate itself so it lands exactly
   where a plate exists: four heroes carry .hero-main without a plate and would have
   taken the gap for nothing. Reduced motion has no drift, so it reserves nothing. */
.js .hero-plate{margin-bottom:54px}
/* A swipe track hides its scrollbar, so on a phone a row of twenty-five cards looks
   like one card and nothing else. The fade at the trailing edge is the only thing
   telling a reader there is more to the right; it goes away at the end of the scroll. */
@media (max-width:640px){
  .swipe{--fade:24px;
    -webkit-mask-image:linear-gradient(to right,#000 calc(100% - var(--fade)),transparent);
    mask-image:linear-gradient(to right,#000 calc(100% - var(--fade)),transparent)}
  .swipe.at-end{--fade:0px}
}
@media (prefers-reduced-motion:reduce){.js .hero-plate{margin-bottom:0}}
/* a table nobody wrapped: it scrolls inside its own box rather than taking the page */
.tscroll{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
/* a figure that counts is set in the record face so the digits do not jitter */
.count{font-variant-numeric:tabular-nums}
@media (prefers-reduced-motion:reduce){
  .js .wipe img,.js .hero-plate img,.js .wipe.in img,.js .hero-plate.in img{transform:none;transition:none}
  .js .sec-head > *,.js .sec-head.in > *{opacity:1;transform:none;transition:none}
  .js .swipe.nudge{animation:none}
}

/* ---------- every plate drifts inside its frame as the page moves ---------- */
/* The settle above happens once, on reveal. This is the continuous half: the picture
   travels a little slower than the page it sits in, so a plate reads as a window onto
   something behind the paper rather than a rectangle stuck to it.

   It is a scroll-driven CSS animation, not script. The existing hero drift is rAF on the
   main thread and moves exactly one element per page; running that for every plate would
   put the whole page's scroll work in JavaScript. `animation-timeline: view()` is handed
   to the compositor, costs no main-thread frame, and where a browser does not support it
   the @supports block never applies and the plate is simply still. */
@supports (animation-timeline: --plate){
  @media (prefers-reduced-motion:no-preference){
    /* clip, not hidden: `overflow:hidden` makes the frame a scroll container, and
       view() then measures the drift against a box that never scrolls, so every plate
       sat frozen at the start of its range. `overflow:clip` crops without that. */
    /* The frame declares the timeline and the picture rides it. An anonymous `view()`
       on the image resolved late and often stayed inactive until some unrelated restyle
       woke it, which read as a plate that simply never moved. Naming the timeline on the
       frame - the box that actually crosses the viewport - resolves on the first pass. */
    .hero-plate,.prod-plate,.cta-plate,.band-plate,.heritage-plate,
    .accent-fig,.readband,.obj-band,.chain-plate{overflow:clip;
      view-timeline-name:--plate;view-timeline-axis:block}
    .js .hero-plate img,.js .prod-plate img,.js .cta-plate img,.js .band-plate img,
    .js .heritage-plate img,.js .accent-fig img,.js .readband img,.js .obj-band img,
    .js .chain-plate img{
      /* longhands on purpose: the `animation` shorthand writes animation-duration:0s,
         and a scroll-driven animation with a zero duration never advances. Leaving the
         duration at its initial `auto` is what maps it onto the range. */
      animation-name:plate-drift;
      animation-timing-function:linear;
      animation-fill-mode:both;
      animation-timeline:--plate;
      animation-range:cover 0% cover 100%}
    /* the one-shot scale would fight the drift for the same property; the wipe that
       uncovers the plate is untouched and still runs */
    .js .wipe.in img,.js .hero-plate.in img{transition:clip-path var(--dur-reveal) var(--ease-out)}
  }
  /* The travel is what the reader actually sees: the picture is held larger than its
     frame and moves through it, so passing a plate shows more of it than standing
     still does. Scale gives the headroom: at 1.2 there is 10% of the image above and
     below the frame, and the drift stays inside it at 6.5%. */
  @keyframes plate-drift{
    from{transform:scale(1.2) translateY(-6.5%)}
    to{transform:scale(1.2) translateY(6.5%)}
  }
  /* a phone scrolls further per gesture, so the same percentage reads as a lurch */
  @media (max-width:640px){
    @keyframes plate-drift{
      from{transform:scale(1.12) translateY(-3.6%)}
      to{transform:scale(1.12) translateY(3.6%)}
    }
  }
}
.essay h2.r3{margin-top:38px;font-size:21px;letter-spacing:-.01em;line-height:1.3}.essay h3.r4{margin-top:30px;font-size:18px;letter-spacing:0;line-height:1.35}.essay h2.r4{margin-top:30px;font-size:18px;letter-spacing:0;line-height:1.35}.essay h4.r5,.essay h3.r5{margin-top:26px;font-size:17px;line-height:1.4}.ehead h1{font-size:clamp(38px,4.6vw,52px);line-height:1.12;letter-spacing:-.02em;text-wrap:balance}@media (max-width:920px){main .cta-col h2,main .wide-cta h2,main .feed h2{font-size:32px;line-height:1.16}}@media (max-width:560px){main .ehead h1.t-long{font-size:32px;line-height:1.15}main .ehead h1.t-xlong{font-size:30px;line-height:1.16}main .cta-col h2,main .wide-cta h2,main .feed h2{font-size:28px;line-height:1.18}}
@keyframes hero-rise{from{opacity:.01;transform:translateY(14px)}to{opacity:1;transform:none}}
.js .hero-seq > *,.js .hero-seq .hero-body > *{animation:hero-rise .68s var(--ease-out,cubic-bezier(.22,1,.36,1)) both}
.js .hero-seq > *:nth-child(1){animation-delay:.06s}
.js .hero-seq > *:nth-child(2){animation-delay:.14s}
.js .hero-seq > *:nth-child(3){animation-delay:.22s}
.js .hero-seq > *:nth-child(4){animation-delay:.3s}
.js .hero-seq > *:nth-child(5){animation-delay:.38s}
.js .hero-seq .hero-body > *:nth-child(1){animation-delay:.14s}
.js .hero-seq .hero-body > *:nth-child(2){animation-delay:.26s}
@media (prefers-reduced-motion:reduce){.js .hero-seq > *,.js .hero-seq .hero-body > *{animation:none;opacity:1;transform:none}}

/* ---------- subscribe ---------- */
.sub{font-family:var(--font-sans)}
.sub .lab{display:block;font-family:var(--font-mono);font-size:12px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.sub h2,.sub h3{font-family:var(--font-display);font-weight:var(--weight-display);
  font-size:var(--text-h3-sm);line-height:1.25;color:var(--ink);letter-spacing:-.01em;
  text-wrap:balance;margin-top:10px}
.sub .sub-lead{margin-top:8px;font-size:15.5px;line-height:1.55;color:var(--body);max-width:56ch}
.sub form{margin-top:24px;display:grid;gap:14px}
.sub .field{display:grid;gap:7px}
.sub .field > span{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.sub input[type=email]{width:100%;padding:13px 15px;font:inherit;font-size:16px;
  color:var(--ink);background:var(--white);border:1px solid var(--hairline-strong);
  border-radius:var(--radius-control)}
.sub input[type=email]::placeholder{color:var(--muted)}
.sub input[type=email]:focus-visible{outline:2px solid var(--blue);outline-offset:1px;
  border-color:var(--blue)}
.sub .agree{display:flex;gap:11px;align-items:flex-start;font-size:14.5px;
  line-height:1.5;color:var(--body)}
.sub .agree input{margin-top:3px;width:17px;height:17px;flex:none;accent-color:var(--blue)}
.sub .agree a{color:var(--blue);text-decoration:underline;text-underline-offset:3px}
.sub .btn{justify-self:start;border:0;cursor:pointer}
.sub .cf-wrap{min-height:0;margin:0}
.sub .cf-wrap:not(:empty){margin-top:2px}
.sub .fine{font-size:13.5px;color:var(--muted);margin-top:-2px}
.sub .f-msg{font-size:14.5px;color:#9c2f2f;line-height:1.5}
.sub .sub-done{display:grid;gap:8px}
.sub [hidden]{display:none!important}
.sub .sub-done .tick{width:26px;height:26px;border-radius:var(--radius-pill);
  background:var(--blue);color:#fff;display:grid;place-items:center;font-size:14px}
.sub .sub-done h3{margin-top:0}
.sub-plate{margin:0 0 26px;border-radius:10px;overflow:hidden;background:var(--paper);
  line-height:0}
.sub-plate img{display:block;width:100%;height:auto;aspect-ratio:1400/208}

/* article · the workhorse */
.sub-article{max-width:720px;margin:64px auto var(--space-section);
  border-top:1px solid var(--hairline-strong);padding-top:30px}

/* digest · one line above the issue */
.sub-digest{max-width:720px;margin:0 auto 34px;border:1px solid var(--hairline-strong);
  border-radius:10px;background:var(--well);overflow:hidden}
.sub-digest .sub-plate{margin:0;border-radius:0}
.sub-digest .sub-row{padding:16px 18px;display:flex;flex-wrap:wrap;align-items:center;
  gap:12px 18px}
.sub-digest .say{flex:1 1 260px;font-size:15px;color:var(--ink);line-height:1.45}
.sub-digest .say b{font-weight:600}
.sub-digest form{margin-top:0;display:flex;flex-wrap:wrap;gap:8px;flex:1 1 300px}
.sub-digest input[type=email]{flex:1 1 180px;padding:11px 13px}
.sub-digest .fine,.sub-digest .f-msg{flex-basis:100%;margin:0}

/* well · under a guide or a report */
.sub-well{max-width:720px;margin:64px auto var(--space-section);background:var(--well);
  border:1px solid var(--hairline);border-radius:12px;overflow:hidden}
.sub-well .sub-plate{margin:0;border-radius:0}
.sub-well .sub-body{padding:clamp(22px,3vw,32px)}
.sub-well .proof{margin-top:18px;display:grid;gap:9px;padding-top:16px;
  border-top:1px solid var(--hairline-strong)}
.sub-well .proof li{display:flex;gap:10px;font-size:14.5px;color:var(--body);line-height:1.45}
.sub-well .proof .k{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted);flex:none;width:86px;padding-top:2px}

/* dark · token notices. Self-contained: the site has no general dark band, so the
   block brings its own surface and sits in the article column like the others. */
.sub-dark{max-width:720px;margin:64px auto var(--space-section);background:var(--dark);border-radius:14px;
  padding:clamp(24px,4vw,36px);color:var(--on-dark)}
.sub-dark h2,.sub-dark h3{color:var(--on-dark)}
.sub-dark .lab{color:var(--blue-400)}
.sub-dark .sub-lead{color:var(--on-dark-muted)}
.sub-dark .sub-plate{border:1px solid var(--on-dark-hairline);margin-bottom:24px}
.sub-dark .sub-plate img{aspect-ratio:1600/380;object-fit:cover;object-position:50% 46%}
.sub-dark input[type=email]{background:rgba(255,255,255,.06);
  border-color:var(--on-dark-hairline);color:var(--on-dark)}
.sub-dark input[type=email]::placeholder{color:var(--on-dark-muted)}
.sub-dark .agree{color:var(--on-dark-muted)}
.sub-dark .agree a{color:var(--on-dark)}
.sub-dark .fine{color:var(--on-dark-muted)}

/* footer · the quiet line, every route */
.foot-sub{margin-top:40px;padding-top:26px;border-top:1px solid var(--on-dark-hairline);
  display:flex;flex-wrap:wrap;gap:16px 40px;align-items:flex-start;color:var(--on-dark)}
.foot-sub .say{flex:1 1 240px}
.foot-sub .lab{color:var(--on-dark-muted)}
.foot-sub .say p{margin-top:8px;font-size:15px;color:var(--on-dark-muted);max-width:40ch}
.foot-sub form{margin-top:0;flex:1 1 320px;display:flex;flex-wrap:wrap;gap:8px}
.foot-sub input[type=email]{flex:1 1 190px;background:rgba(255,255,255,.06);
  border-color:var(--on-dark-hairline);color:var(--on-dark);padding:11px 13px}
.foot-sub input[type=email]::placeholder{color:var(--on-dark-muted)}
.foot-sub .btn{background:var(--white);color:var(--ink)}
.foot-sub .fine,.foot-sub .f-msg{flex-basis:100%;margin:0;color:var(--on-dark-muted);font-size:13px}
.foot-sub .sub-done{color:var(--on-dark)}
.foot-sub .sub-done h3{color:var(--on-dark);font-size:18px}

/* checkbox · one line inside somebody else's form */
.sub-check{border:1px solid var(--hairline-strong);border-radius:10px;padding:16px 18px;
  background:var(--white)}
.sub-check .agree{font-size:15px;color:var(--ink)}
.sub-check .fine{margin-top:8px;margin-left:28px}

.cf-wrap{margin-top:4px;min-height:34px}
.cf-note{margin-top:10px;font-size:13px;color:var(--muted);line-height:1.5}
.cf-note a{color:var(--blue);text-decoration:none}
.cf-note a:hover{text-decoration:underline;text-underline-offset:3px}
