/* ==========================================================================
   CORSAIR — token layer + component set
   Palette taken from the Pantones specified in the launch decal artwork.
   Warm light, cool shadow: the ramp pivots at Slate (4285 C).
   ========================================================================== */

:root{
  /* 2026-10-16, the user: the perforations "too big and dense ... in line with
     the DNA section ones, keep them consistent across the website". One
     perforation everywhere: the scan's wide-spaced holes, 2.2px on 36px. */
  --perf:radial-gradient(circle, transparent 0 2.2px, #000 2.7px) 18px 18px / 36px 36px;
  --perf-lip:radial-gradient(circle, transparent 0 2.4px, rgba(255,255,255,.6) 2.6px 3px, transparent 3.3px) 18px 18px / 36px 36px;
  /* --- warm end ------------------------------------------------------- */
  --paper:      #F8F5F0;   /* oklch(97.5% .006 77)  page ground, never cream */
  --cream:      #EBE2D5;   /* Eggnog 12-0601 TCX    figure grounds          */
  --cream-deep: #E8DDC9;   /* 9182 C                DROP's own colour       */
  --sand:       #DED3C5;   /* 9186 C                dividers on cream       */
  --shell:      #EFE4CE;   /* 9184 C                warmest light step      */
  --almond:     #D2C1AD;   /* 13-1012 TCX           Frosted Almond, deepest
                              warm step and the only real chroma we own     */
  --taupe:      #B4ADA6;   /* Warm Gray 4 C         hairline / fill only    */
  /* --- cool end ------------------------------------------------------- */
  --slate:      #676169;   /* 4285 C                secondary text, pivot   */
  --ink:        #12171D;   /* Black 6 C             body text, dark ground  */

  /* semantic assignment, light theme */
  /* 2026-09-16, by the user: the page ground is white. Paper, cream, sand and
     shell are accents now — the tonal section bands, figure grounds under the
     product, the scale track, the plan cards — never the backdrop. Everything
     that reads --bg is a surface meant to match the page, and follows. */
  /* 2026-10-25, the user: "add the subtle offwhite beige bg colour to the
     whole website". Paper is the page ground again (it was, until
     2026-09-16); Cream stays a material surface, never the backdrop. */
  --bg:        var(--paper);
  --surface:   var(--cream);
  --surface-2: var(--sand);
  --fg:        var(--ink);
  --fg-2:      var(--slate);
  --rule:      var(--taupe);
  --rule-soft: color-mix(in oklab, var(--taupe) 45%, transparent);
  --edge:      var(--almond);   /* the pinstripe, taken off the APEX edge */
  /* the one hue the interface carries, and only in the two charts on the
     racquet pages (the user, 2026-10-08). Champagne for the racquet being
     read, Taupe for the one it is compared with, coral for the reading it
     is most decided on. See DESIGN.md. */
  --glow:      oklch(86% 0.07 84);
  --glow-them: var(--taupe);
  --glow-deep: oklch(70% 0.095 78);  /* the same champagne, deep enough to read on white */
  --glow-hot:  oklch(78% 0.095 38);

  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --mono: "Martian Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* spacing: varied on purpose, not one repeated value */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 72px; --s-9: 112px; --s-10: 168px;
  /* 2026-10-27, the user: "too much whitespace in between imagineered in
     london and dna. Look for these spacing issues and follow the industry
     format for sections and spacing". One rhythm between sections: --sec
     from the last content of one to the first of the next (120 on a wide
     screen, 72 on a phone), each side giving half (--sec-h). Measured gaps
     ran from 102 to 403 before. A page's first section keeps its larger
     opening under the masthead; a heading keeps its own, closer step. */
  --sec: clamp(72px, 8.4vw, 120px); --sec-h: calc(var(--sec) / 2);

  --gutter: clamp(20px, 4vw, 56px);
  /* the page measure. The masthead, every .wrap and every pinned block share
     it, so content lines up on any viewport instead of some blocks hugging
     the window edge while others centre inside 1560. */
  --page: 1560px;
  --measure: 68ch;

  /* ---- the type scale, 2026-10-07 ---------------------------------------
     Matched to Satisfy at the user's direction ("some are too small"): their
     floor is 14px, mono for the small voice and the grotesque for anything
     that names or commands. Nothing set in HTML text runs under 14px now;
     the 10px labels and 12px notes that carried most of the site are gone.
     Only the SVG badge lockups keep their own sizes, drawn in the badge's
     coordinate space. Every header and title is Archivo at 700. */
  /* 2026-10-16, the user: the nine-step scale from the type review. Titles
     and leads had grown seventeen sizes page by page; every text style now
     takes one of these. Desktop / phone values in the comments. */
  --fs-display:   clamp(2.3rem,5.4vw,4.8rem);     /* 76.8 / 36.8 hero, endline, launch */
  --fs-title:     clamp(1.875rem,3.4vw,3rem);     /* 48 / 30 page titles               */
  --fs-statement: clamp(1.9rem,3.6vw,3.25rem);    /* 51.8 / 30.4 statements            */
  --fs-heading:   clamp(1.25rem,1.9vw,1.75rem);   /* 27.4 / 20 section headings        */
  --fs-name:      clamp(1.125rem,1.4vw,1.25rem);  /* 20 / 18 names, prices, verdicts   */
  --fs-lead:      clamp(1.0625rem,1.32vw,1.1875rem); /* 19 / 17 standfirsts and leads  */
  --fs-label: 14px;   /* Archivo notes and buttons                         */
  --fs-mono:  10px;   /* the typewriter accent, always capitals, at .08em. 14px on
                         2026-10-07, 12px on 10-08 ("too big on mobile"),
                         10px the same day ("slightly too big, reduce it by
                         2pt"); capitals read larger than lower case */
  --fs-body:  16px;   /* running copy in Archivo                            */
  --header-h: 68px;

  /* ---- ledger #09: four breakpoints, and only four ------------------------
     The stylesheet carried sixteen distinct widths — 420, 520, 620, 640, 700,
     719, 720, 760, 767, 860, 880, 900, 1000, 1200, plus 1000 declared inline
     on both racquet pages. 719, 720 and 767 were three separate systems
     disagreeing about where a phone ends, which is why the desktop, tablet
     and phone layouts had drifted apart from each other rather than from any
     one decision. Settled with the user 2026-09-09:

         phone    < 640      tablet  640–899      desktop  900–1199
         wide     >= 1200    page cap 1560 (--page, above)

     Each old break was mapped by what it was trying to say rather than by
     arithmetic nearest: everything that meant "phone" went to 640, everything
     that meant "tablet or narrower" went to 900. Max-width queries are written
     at .98 below the bound so that a viewport of exactly 900px matches the
     min-width side only — at 900 the old max-width:900/min-width:900 pair would
     both have applied and the later rule would silently have won.

     Verified after the change: 13 routes at 375, 641, 700, 768, 899, 900, 1000
     and 1200 — 104 measurements, no horizontal overflow at any of them. */

  /* Surfaces move on quint / expo: a long tail, content settling into place.
     Controls move on their own curve. See DESIGN.md section 5. */
  --ease: cubic-bezier(.22,1,.36,1);        /* out-quint · surfaces        */
  --ease-expo: cubic-bezier(.16,1,.3,1);    /* out-expo  · large travel    */
  --ease-detent: cubic-bezier(0,.55,.45,1); /* out-circ  · controls        */
  --ease-seat: cubic-bezier(.32,0,.28,1);   /* symmetric · press down      */
  --t-fast: .18s; --t: .32s; --t-slow: .6s;
  --ease-sweep: cubic-bezier(.4,0,.2,1);    /* symmetric · light crossing  */
  --t-press: .1s;                            /* finger down, no tail       */
  --t-detent: .42s;                          /* release, arrives flat      */
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; overflow-x:clip}
body{
  margin:0; background:var(--bg); color:var(--fg);
  font-family:var(--mono); font-size:var(--fs-mono); line-height:1.8; letter-spacing:.04em;  /* inherited as 0.4px by Archivo text; mono blocks set .08em themselves */
  /* 2026-10-08, by the user: the typewriter face is always set in capitals.
     The body default is the mono, so the body carries them, and every rule
     that sets Archivo opts back out with text-transform:none. */
  text-transform:uppercase;
  font-weight:400; -webkit-font-smoothing:antialiased;
  overflow-x:clip;
}

/* ---- running copy takes the body face -----------------------------------
   body{} above sets mono as the document default, which meant the site's
   running copy was set in Martian Mono throughout — measured at 82% of body
   characters on APEX and 95% on Terms, including a 277-character product
   description. DESIGN.md's Mono Earns It Rule says mono never sets body copy
   and never appears as a texture to signal that the brand is technical.

   Raised with the user 2026-09-09 with three options: leave it and amend the
   rule, move the long-form only, or flip body{} outright. They chose the
   middle one, so this list is deliberately a list. Sentences take Archivo;
   the instrument-label texture that gives the site its character — eyebrows,
   part codes, counters, .playline's dot-separated strip, the price and every
   tabular numeral — stays mono, and is what the rule always meant by earning
   it. .cond is scoped to the drawer because the same class sets a sentence
   there and a label ("Set01, Love All") on the product pages. */
.doc p, .doc li, .doc .lead,
.talk-body p,
.pdp-desc,
.use .u-v,
.bulk-p,
.cookie-t,
.rev-note,
.grade-note,
.enlist .roster,
.web-say,
.svc .v,
.bag-drawer .cond{font-family:var(--display); text-transform:none; font-size:var(--fs-label)}
img,svg,canvas,video{display:block; max-width:100%}
a{color:inherit; text-decoration:none}
button{font:inherit; color:inherit; background:none; border:0; cursor:pointer}
/* typed values keep the case they were typed in; placeholders follow the face */
/* fields people type into stay at 14px: the 10px mono is a label size */
input,select,textarea{text-transform:none}
input::placeholder{text-transform:uppercase}
:focus-visible{outline:2px solid var(--fg); outline-offset:3px}
::selection{background:var(--ink); color:var(--cream)}

/* --- type ---------------------------------------------------------------
   Archivo carries everything editorial. Martian Mono is held back for
   content that is genuinely measured. */
.d1,.d2,.d3,.h,.eyebrow{font-family:var(--display); text-transform:uppercase}
.page-title{
  font-family:var(--display); font-weight:800; text-transform:uppercase;
  /* capped at the endline's 4.8rem: no header on the site outruns Greatness Awaits */
  font-size:var(--fs-display); line-height:.88; letter-spacing:-.04em; margin:0;
}
/* 2026-09-16: where the title sits over a hero scene it opens up. The user
   tried a round of alternative faces and chose to keep Archivo and add air
   instead — tight tracking reads as a headline, open tracking as a mark. The
   same value goes on the type that is overlaid on a hero: the three break
   titles, the shop hero's tag and the stage callouts on the racquet pages.
   The hero's closing line, Greatness Awaits, was opened and then reverted by
   the user the same day; it keeps its own tight setting. Titles that sit on
   the page rather than over a scene keep the tight setting too. */
/* the hero title itself goes further: not bold, and wider still. Regular
   weight with wide tracking in capitals is the luxury register; hierarchy
   against the standfirst beneath it comes from size, 76px over 18px, since
   both now sit at 400. */
.break-head .page-title{letter-spacing:.12em; font-weight:800}
/* the two sentence-case titles over the later scenes take a lighter opening —
   mixed case gaps long before uppercase does */
.break-head .d3{letter-spacing:.02em}
.d1{font-size:var(--fs-display); line-height:.95; font-weight:800; text-transform:uppercase; letter-spacing:-.03em; margin:0}
.d2{font-size:var(--fs-statement); line-height:1.04; font-weight:800; text-transform:uppercase; letter-spacing:-.025em; margin:0}
.d3{font-size:var(--fs-heading); line-height:1.18; font-weight:800; text-transform:uppercase; letter-spacing:-.015em; margin:0}
.h{font-size:var(--fs-name); line-height:1.3; font-weight:800; text-transform:uppercase; letter-spacing:-.005em; margin:0}
.lede{font-family:var(--display); font-weight:400; font-size:var(--fs-lead); line-height:1.55; letter-spacing:-.01em; max-width:var(--measure); text-transform:none}
.eyebrow{
  font-family:var(--mono); font-size:var(--fs-mono); font-weight:500;
  letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2);
}
.num{font-family:var(--mono); font-variant-numeric:tabular-nums; font-weight:500; text-transform:uppercase}
/* the condition is never dropped, only quieted */
.cond{color:var(--fg-2); font-size:var(--fs-mono); letter-spacing:.08em}
.muted{color:var(--fg-2)}

/* 2026-10-08, by the user: titles are Archivo ExtraBold (800) and always in
   capitals, as "Greatness Awaits" was first set. The rules above that style
   a heading carry 800 and uppercase themselves; this is the floor for any
   heading that has no rule of its own. */
:is(h1,h2,h3,h4,h5,h6){font-family:var(--display); font-weight:800; text-transform:uppercase}

/* --- layout -------------------------------------------------------------- */
.wrap{max-width:var(--page); margin-inline:auto; padding-inline:var(--gutter)}
.stack>*+*{margin-top:var(--s-5)}
.rule{border:0; border-top:1px solid var(--rule-soft); margin:0}

/* --- header -------------------------------------------------------------- */
.skip{
  position:fixed; top:8px; left:8px; z-index:200; transform:translateY(-160%);
  background:var(--fg); color:var(--bg); padding:12px 18px; border-radius:999px;
  font-family:var(--display); font-weight:700; font-size:var(--fs-label);
  opacity:0; pointer-events:none; transition:opacity .2s var(--ease), transform .2s var(--ease); text-transform:none}
.skip:focus-visible{transform:none; opacity:1; pointer-events:auto}
/* every scroll-driven section can be stepped over by keyboard */
.skip-past{
  position:absolute; left:var(--gutter); top:calc(var(--header-h) + 8px); z-index:80;
  transform:translateY(-8px); opacity:0; pointer-events:none;
  transition:opacity .2s var(--ease), transform .2s var(--ease);
  background:var(--fg); color:var(--bg); padding:10px 16px; border-radius:999px;
  font-family:var(--display); font-weight:700; font-size:var(--fs-label); text-transform:none}
.skip-past:focus-visible{transform:none; opacity:1; pointer-events:auto}
.masthead{
  position:sticky; top:0; z-index:60; height:var(--header-h);
  display:flex; align-items:center; gap:var(--s-6);
  background:transparent;
}
/* 2026-10-25, the user: "make the nav bar frosted glass with a subtle
   refractive edge at the bottom. No perforations", then "increase nav
   transparency similar to dna section and the edge glint must refract with
   a glass like effect seamlessly". The glass is a layer behind the
   masthead's contents, run edge to edge past the .wrap measure. Since the
   same evening (the user: "make the whole nav bar liquid glass but add more
   opaqueness. So solid no edge"): one sheet for the full height, Paper at 80%
   over a deep frost, with no band, line or shade at its foot. In Chromium
   the whole sheet is liquid glass: an SVG displacement (#navref, drawn by
   corsair.js at the bar's size) curves the page into its top and bottom
   like the rounded faces of thick glass, neutral through the middle.
   Elsewhere it is the frost alone. No holes. */
.masthead::before,.masthead::after{
  content:""; position:absolute; left:min(0px, calc(50% - 50vw)); right:min(0px, calc(50% - 50vw));
  pointer-events:none; transition:opacity .35s var(--ease-detent)}
.masthead::before{
  top:0; bottom:0; z-index:-1;
  background:color-mix(in oklab, var(--bg) 80%, transparent);
  -webkit-backdrop-filter:blur(22px) saturate(1.5) brightness(1.04); backdrop-filter:blur(22px) saturate(1.5) brightness(1.04)}
.navref .masthead::before{backdrop-filter:url(#navref) blur(22px) saturate(1.5) brightness(1.04)}
.masthead::after{
  display:none}
@media (prefers-reduced-transparency:reduce){
  .masthead::before,.navref .masthead::before{background:var(--bg); -webkit-backdrop-filter:none; backdrop-filter:none}
}
/* 2026-10-25, the user: "add the sand to the edge of all frosted glass cards
   across the website. Very thin". A 0.75px line of Sand drawn just inside
   the edge of every frosted card (the brand and Synthesis plates, the film's
   cards, the plan cards, the bag's set cards), following its corners. The
   small glass controls (counters, chips, switches) are not cards and keep
   none. A sanctioned exception to the no-outline rule, for these cards. */
.man-plate,.tf-card,.plan-opt,.bag-pc{outline:.75px solid var(--sand); outline-offset:-.75px}

/* wordmark via mask so it inherits currentColor and flips with the theme */
.mark{
  display:block; width:132px; aspect-ratio:667/131; background:currentColor;
  -webkit-mask:url(../brand/corsair-wordmark.svg) center/contain no-repeat;
          mask:url(../brand/corsair-wordmark.svg) center/contain no-repeat;
}
.masthead nav{display:flex; gap:var(--s-6); margin-left:auto; font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase}
/* the current page and the pointer are shown by contrast, Slate to Ink */
.masthead nav a{padding-block:6px; color:var(--fg-2); transition:color var(--t-detent) var(--ease-detent)}
.masthead nav a:hover,.masthead nav a[aria-current="page"]{color:var(--fg)}
/* The cart is an icon, not a word: the header carries one wordmark and no
   other lettering competing with it. The count appears only when it exists. */
.cart,.acct,.srch{
  display:inline-flex; align-items:center; justify-content:center; gap:9px;
  min-height:44px; min-width:44px; padding-inline:6px; color:var(--fg); cursor:pointer;
  transition:color var(--t-detent) var(--ease-detent), opacity var(--t-detent) var(--ease-detent);
}
.cart svg,.acct svg,.srch svg{
  width:20px; height:20px; display:block; flex:none;
  fill:none; stroke:currentColor; stroke-width:1.4;
  stroke-linecap:round; stroke-linejoin:round;
  transition:transform var(--t-detent) var(--ease-detent);
}
.cart:hover svg,.acct:hover svg,.srch:hover svg{transform:translateY(-1px)}
.cart:active svg,.acct:active svg,.srch:active svg{transform:scale(.9);
  transition-duration:var(--t-press); transition-timing-function:var(--ease-seat)}
/* the account link goes live with the shop domain; until then it is inert
   rather than pointing nowhere */
.acct[aria-disabled="true"]{opacity:.34; pointer-events:none}

/* ---- global search ------------------------------------------------------ */
.find-scrim{position:fixed; inset:0; z-index:170; background:color-mix(in oklab,var(--ink) 38%,transparent);
  opacity:0; transition:opacity var(--t) var(--ease-detent)}
.find-scrim[data-open]{opacity:1}
.find{
  position:fixed; inset:0 0 auto 0; z-index:180; background:var(--bg);
  border-bottom:1px solid var(--rule); padding:var(--s-6) 0 var(--s-5);
  transform:translateY(-101%); transition:transform var(--t-slow) var(--ease-expo);
}
.find[data-open]{transform:none}
.find-in{max-width:1180px; margin-inline:auto; padding-inline:var(--gutter)}
.find-bar{display:flex; align-items:center; gap:var(--s-4); border-bottom:1px solid var(--rule)}
.find-bar svg{width:19px; height:19px; flex:none; fill:none; stroke:var(--fg-2); stroke-width:1.4;
  stroke-linecap:round; stroke-linejoin:round}
.find input{
  flex:1; min-height:52px; border:0; background:none; color:var(--fg);
  font-family:var(--display); font-weight:700; font-size:var(--fs-heading);
  letter-spacing:-.02em; text-transform:none}
.find input::placeholder{color:var(--rule); font-weight:400}
.find input:focus{outline:none}
.find-x{min-height:44px; padding-inline:6px; font-family:var(--mono); font-size:var(--fs-mono);
  letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2); cursor:pointer;
  transition:color var(--t-detent) var(--ease-detent)}
.find-x:hover{color:var(--fg)}
.find-out{margin-top:var(--s-4); max-height:52svh; overflow-y:auto}
.find-hit{display:grid; grid-template-columns:minmax(0,1fr) auto; gap:var(--s-4);
  align-items:baseline; padding:14px 0; border-bottom:1px solid var(--rule-soft); color:inherit}
.find-hit b{font-family:var(--display); font-weight:700; font-size:var(--fs-body); letter-spacing:.09em;
  text-transform:uppercase}
.find-hit span{display:block; font-size:var(--fs-mono); color:var(--fg-2); margin-top:3px; letter-spacing:.08em}
.find-hit em{font-style:normal; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em;
  text-transform:uppercase; color:var(--fg-2); white-space:nowrap}
.find-hit b{transition:color var(--t-detent) var(--ease-detent)}
.find-hit:hover b,.find-hit:focus-visible b{color:var(--fg-2)}
.find-none{margin:var(--s-5) 0 var(--s-4); color:var(--fg-2); font-size:var(--fs-mono); letter-spacing:.08em}
@media (prefers-reduced-motion:reduce){ .find,.find-scrim{transition:none} }
.cart-n{
  font-family:var(--mono); font-size:var(--fs-mono); font-weight:500; letter-spacing:.08em;
  font-variant-numeric:tabular-nums; color:var(--fg); min-width:1ch; text-transform:uppercase}
.cart[data-n="0"] .cart-n{display:none}
/* The nav was hidden below 860px when it carried six items. It carries two,
   so it fits: hiding it left phones with no route to the shop. */
@media (max-width:899.98px){
  .masthead{gap:var(--s-4)}
  .masthead nav{gap:var(--s-5)}
}
@media (max-width:639.98px){
  .masthead{gap:var(--s-3)}
  .masthead nav{gap:var(--s-4); font-size:var(--fs-mono); letter-spacing:.08em}
  .bag{font-size:var(--fs-mono); letter-spacing:.08em}
}

/* ---- the nav sheet ------------------------------------------------------
   Below 720px the masthead kept a wordmark, two words and three icons in
   335px of usable width. It now keeps only what a shopper actually reaches
   for — the wordmark, the route to the frames, and the bag — and everything
   else moves behind one control. Scoped to html:not(.no-js) so a page
   without script keeps the full nav rather than losing it behind a button
   that cannot open. */
.burger{display:none}
.burger-b{display:block; width:19px; height:13px; position:relative}
.burger-b i{
  position:absolute; left:0; right:0; height:1.4px; background:currentColor;
  transition:transform var(--t-detent) var(--ease-detent);
}
.burger-b i:first-child{top:0}
.burger-b i:last-child{bottom:0}
.burger:active .burger-b i:first-child{transform:translateY(1.5px)}
.burger:active .burger-b i:last-child{transform:translateY(-1.5px)}

.nav-scrim{
  position:fixed; inset:0; z-index:130;
  background:color-mix(in oklab,var(--ink) 42%,transparent);
  opacity:0; transition:opacity var(--t) var(--ease-detent);
}
.nav-scrim[data-open]{opacity:1}
.navsheet{
  position:fixed; inset:0 0 0 auto; z-index:140; width:min(86vw,360px);
  background:var(--bg); border-left:1px solid var(--rule);
  display:flex; flex-direction:column; overflow-y:auto;
  transform:translateX(100%); transition:transform var(--t-slow) var(--ease-expo);
}
.navsheet[data-open]{transform:none}
.navsheet[hidden],.nav-scrim[hidden],.bag-drawer[hidden],.bag-scrim[hidden],.find[hidden],.find-scrim[hidden]{display:none}
.navsheet-head{
  display:flex; align-items:center; justify-content:space-between;
  padding:var(--s-6) var(--s-6) var(--s-5); border-bottom:1px solid var(--rule-soft);
}
.navsheet-main{display:flex; flex-direction:column; padding:var(--s-4) var(--s-6) var(--s-3)}
.navsheet-main a{
  display:flex; align-items:center; min-height:56px;
  font-family:var(--display); font-weight:700; text-transform:uppercase;
  font-size:var(--fs-heading); letter-spacing:-.022em; line-height:1.1;
  border-bottom:1px solid var(--rule-soft);
}
.navsheet-main a:last-child{border-bottom:0}
.navsheet-main a[aria-current="page"]{color:var(--fg-2)}
.navsheet-acts{display:flex; flex-direction:column; padding:0 var(--s-6) var(--s-4)}
.navsheet-act{
  display:flex; align-items:center; gap:12px; width:100%; min-height:48px;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg); text-align:left; cursor:pointer;
  border-top:1px solid var(--rule-soft);
  transition:color var(--t-detent) var(--ease-detent);
}
.navsheet-act svg{
  width:17px; height:17px; flex:none; fill:none; stroke:currentColor;
  stroke-width:1.4; stroke-linecap:round; stroke-linejoin:round;
}
.navsheet-act[aria-disabled="true"]{color:var(--rule); pointer-events:none}
/* 2026-10-17, the user: the support list "more condensed, too much spacing".
   It follows the actions directly instead of being pushed to the foot, and
   the rows close up to 32px (36px under a finger) */
.navsheet-sup{
  display:flex; flex-direction:column;
  padding:var(--s-4) var(--s-6) var(--s-5); border-top:1px solid var(--rule-soft);
}
.navsheet-sup .eyebrow{margin:0 0 var(--s-1); color:var(--fg)}
.navsheet-sup a{
  display:flex; align-items:center; min-height:32px; font-size:var(--fs-mono); color:var(--fg-2);
  transition:color var(--t-detent) var(--ease-detent); letter-spacing:.08em}
.navsheet-sup a:hover{color:var(--fg)}

@media (max-width:639.98px){
  html:not(.no-js) .masthead nav,
  html:not(.no-js) .masthead .srch,
  html:not(.no-js) .masthead .acct{display:none}
  html:not(.no-js) .burger{
    display:inline-flex; align-items:center; justify-content:center;
    min-width:44px; min-height:44px; color:var(--fg); cursor:pointer;
  }
  /* nav carried margin-left:auto; with it hidden the bag inherits the job */
  html:not(.no-js) .cart{margin-left:auto}
  .mark{width:106px}
}
@media (prefers-reduced-motion:reduce){ .navsheet,.nav-scrim{transition:none}
  .btn{transition:none} .btn::before{display:none} }

/* --- buttons ------------------------------------------------------------- */
/* The button does not move. Not on hover, not on press — a scale was tried and
   read as the control drifting under the cursor. The affordance is a glint
   that runs once round the edge, on an eased curve, and is gone (2026-10-25,
   the user: "replace the animation with only edge glint animations. No light
   effects across the button"; it replaced a light band swept across the
   face). It is an animation rather than a hover state so it always travels
   the same way. It is drawn on a hairline ring at the edge, so nothing ever
   crosses the label. */
.btn{
  position:relative; overflow:hidden; isolation:isolate;
  display:inline-flex; align-items:center; justify-content:center; gap:12px;
  min-height:46px; padding:12px 24px; line-height:1.2;   /* 52/15·30 until 2026-10-27: "slightly smaller"; the body's 1.8 line-height had set it at 51 */
  /* Not a pill and not a box: two opposite corners are cut, at the same rake
     as the flats on the comparison star. The
     clip takes the outline with it, so focus is drawn as a ring inside. */
  --chamfer:13px;
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  background:var(--fg); color:var(--bg); border:1px solid var(--fg);
  font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.01em;
  cursor:pointer; text-align:center;
  transition:background-color var(--t-detent) var(--ease-detent),
             color var(--t-detent) var(--ease-detent),
             border-color var(--t-detent) var(--ease-detent); text-transform:none}
.btn .arw{display:inline-block}
.btn:focus-visible{
  outline:none;
  box-shadow:inset 0 0 0 2px var(--bg), inset 0 0 0 4px var(--fg);
}
.btn--ghost:focus-visible{box-shadow:inset 0 0 0 2px var(--fg)}
/* The glint: a bright arc on a conic sweep, masked to a 2px ring at the
   edge (2026-10-25, the user: "the edge glint must refract with a glass like
   effect seamlessly"; it was a single white arc on a 1.5px ring). --glint turns it once round the button; the cut corners clip it as
   they clip the button, so it slips off one flat and back onto the next. */
@property --glint{syntax:"<angle>"; inherits:false; initial-value:0deg}
.btn::before{
  content:""; position:absolute; z-index:-1; inset:0; padding:2px; pointer-events:none;
  /* a specular arc split like light along a glass edge: a warm fringe leads,
     white at the crest, a cool fringe trails; a fainter counter-glint runs
     opposite, where the light leaves the glass. Feathered, so it has no ends */
  background:
    conic-gradient(from var(--glint), transparent 0deg 284deg,
      rgba(255,206,160,.28) 312deg, rgba(255,255,255,.95) 330deg,
      rgba(168,212,255,.32) 346deg, transparent 360deg),
    conic-gradient(from calc(var(--glint) + 180deg), transparent 0deg 300deg,
      rgba(255,255,255,.34) 332deg, transparent 360deg);
  filter:blur(.35px);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite:xor;
          mask:linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  opacity:0;
}
/* slow and eased at both ends: it fades in, carries round and fades out */
.btn:hover::before,.btn:focus-visible::before{animation:btn-glint 1.4s var(--ease-sweep)}
@keyframes btn-glint{
  0%{--glint:0deg; opacity:0} 14%{opacity:1} 86%{opacity:1} 100%{--glint:360deg; opacity:0}
}
/* 2026-10-09, "no basic outline boxes anywhere": the secondary button is a
   tonal fill, Cream on the page, never a hairline drawn round nothing.
   2026-10-27, the user: "make the buttons solid. No perforation. Check for
   all buttons across the website": the secondary is solid Cream (it was
   glass with holes), and turns Ink slowly on hover; the waiting button is a
   solid muted fill. */
.btn--ghost{background:color-mix(in oklab, var(--cream) 72%, var(--bg)); border-color:transparent; color:var(--fg);
  transition:background-color .55s var(--ease-detent), color .55s var(--ease-detent)}
.btn--ghost:hover{background:var(--fg); border-color:var(--fg); color:var(--bg)}
.btn[disabled]{background:color-mix(in oklab, var(--taupe) 26%, var(--bg)); border-color:transparent; color:var(--slate); cursor:not-allowed}
.btn[disabled]::before{animation:none; opacity:0}
.btn--full{width:100%}

/* ---- the primary, solid, 2026-10-27 --------------------------------------
   The user: "make the buttons darker, slightly smaller ... make the buttons
   solid. No perforation". It was smoked glass with the site's perforation at
   its far end (2026-10-17). Now a solid face of Ink taken a fifth of the way
   to black, a faint light across its top and a darker lip at its foot; hover
   deepens it slowly towards black. No frost, no holes. --dg is registered
   so the deepening eases rather than snaps. */
@property --dg{syntax:"<percentage>"; inherits:true; initial-value:22%}
.btn:not(.btn--ghost):not([disabled]){background:transparent; border-color:transparent; --dg:22%;
  transition:--dg .7s var(--ease-detent), color var(--t-detent) var(--ease-detent)}
.btn:not(.btn--ghost):not([disabled]):hover{--dg:44%}
.btn:not(.btn--ghost):not([disabled])::after{
  content:""; position:absolute; inset:0; z-index:-2; pointer-events:none;
  background:
    linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,0) 55%),
    color-mix(in oklab, var(--ink), #000 var(--dg));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14), inset 0 -1px 0 rgba(0,0,0,.5)}

/* ---- the glass, 2026-10-16 ------------------------------------------------
   The user: "the new frosted glass with transparent perforations should be
   subtle but used in different places". The tech film's cards, quietened:
   champagne over Taupe at a low alpha (--glass-a), a backdrop frost, and the
   racquet's own drilled holes punched clean through on one side, so the page
   shows in each one. Solid where the words are, perforated where they are not
   (--glass-from sets the side). No edge, no shadow. It is a layer under the
   element, never a mask on it, so a label is never cut. Used on the plan and
   grade choices, the quantity counter, the secondary and waiting buttons and
   the bag's next steps. The film's cards keep their own, stronger mix. */
.plan-opt,.pay-marks li,.bag-pc{position:relative; isolation:isolate}
.plan-opt::after,.pay-marks li::after,.bag-pc::after{
  content:""; position:absolute; inset:0; z-index:-2; pointer-events:none; border-radius:inherit;
  /* 2026-10-16, the user: "add some frosted glass feel ... to add some depth".
     A pane, not a swatch: a light falling across the face from the top left
     over the tint, a bright lip on the top edge and a faint one all round,
     and a breath of shadow at the foot. */
  background:
    linear-gradient(150deg, rgba(255,255,255,.62), rgba(255,255,255,.14) 44%, rgba(255,255,255,0) 72%),
    color-mix(in oklab, color-mix(in oklab, var(--glow) 40%, var(--taupe)) var(--glass-a,26%), transparent);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px rgba(255,255,255,.38),
             inset 0 -1px 0 color-mix(in oklab, var(--ink) 7%, transparent);
  -webkit-backdrop-filter:blur(16px) saturate(1.3); backdrop-filter:blur(16px) saturate(1.3);
  --holes:var(--perf);
  -webkit-mask:linear-gradient(var(--glass-from,90deg), #000 52%, transparent 94%), var(--holes);
  -webkit-mask-composite:source-over;
  mask:linear-gradient(var(--glass-from,90deg), #000 52%, transparent 94%), var(--holes);
  mask-composite:add;
  transition:background-color var(--t-detent) var(--ease-detent), opacity var(--t-detent) var(--ease-detent)}
/* the panes (not the buttons, which keep their cut corners): softened
   corners, a lifted shadow, and each drilled hole given a bright lip on the
   side the light comes from, so the perforations read as cut through glass */
.plan-opt,.pay-marks li,.bag-pc{border-radius:var(--glass-r,10px)}
.plan-opt,.bag-pc{box-shadow:0 1px 1px color-mix(in oklab, var(--ink) 4%, transparent),
  0 16px 32px -22px color-mix(in oklab, var(--ink) 34%, transparent)}
.pay-marks li{--glass-r:7px; box-shadow:0 8px 18px -14px color-mix(in oklab, var(--ink) 30%, transparent)}
.qty,.bag-qty,.freq-step,.freq-seg,.grade-seg{
  /* the buttons' own edge: two opposite corners cut, so the dark fill a
     control takes on hover follows the same line as Add to Bag beside it */
  --chamfer:11px; border-radius:0;
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer))}
.qty button,.bag-qty button,.freq-u,.grade-opt{border-radius:0}
.pay-marks li::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none; border-radius:inherit;
  background:var(--perf-lip);
  -webkit-mask:linear-gradient(var(--glass-from,90deg), transparent 50%, #000 92%);
  mask:linear-gradient(var(--glass-from,90deg), transparent 50%, #000 92%);
  transition:opacity var(--t-detent) var(--ease-detent)}
/* ---- the counters and toggles, solid, 2026-10-27 ---------------------------
   The user: "make the counters have the same animation and no glitches. Slow
   and premium feel but nothing too gaudy". The quantity counters, the
   delivery stepper and the two switches are solid Cream (they were glass with
   holes). A press no longer shrinks a key and a hover no longer flashes it
   Ink: a key takes a slow, quiet tone. The whole counter takes the buttons'
   edge glint, once round on hover, in a soft Ink so it shows on Cream. */
.qty,.bag-qty,.freq-step,.freq-u,.grade-opt{position:relative; isolation:isolate;
  background:color-mix(in oklab, var(--cream) 62%, var(--bg))}
.qty::before,.bag-qty::before,.freq-step::before{
  content:""; position:absolute; z-index:2; inset:0; padding:1.5px; pointer-events:none;
  background:
    conic-gradient(from var(--glint), transparent 0deg 284deg,
      color-mix(in oklab, var(--glow-deep) 40%, transparent) 312deg, color-mix(in oklab, var(--ink) 55%, transparent) 330deg,
      color-mix(in oklab, var(--taupe) 50%, transparent) 346deg, transparent 360deg);
  filter:blur(.35px);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite:xor;
          mask:linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  opacity:0}
.qty:hover::before,.bag-qty:hover::before,.freq-step:hover::before,
.qty:focus-within::before,.bag-qty:focus-within::before,.freq-step:focus-within::before{animation:btn-glint 1.6s var(--ease-sweep)}

/* --- spec chips ---------------------------------------------------------- */
/* documented rule is a 44px minimum target; honour it on touch */
/* ---- ledger #10: the Forty-Four Rule, applied past the chips -------------
   DESIGN.md asks for 44x44 on every control and the stylesheet honoured it on
   .chip alone, which left 22 targets under the minimum on one product page at
   375: ten footer product links at 335x22, five nav-sheet links at 258x40,
   the masthead wordmark at 106x21, two sequence-skip links two pixels short
   and a ghost button two pixels narrow.

   The rule says the visual treatment may be smaller than the target but the
   target may not be smaller than 44, so these grow their box rather than
   their type. Growing a hit area with an overlay instead would have made
   adjacent footer links overlap — they sit on a 28px pitch — and an
   overlapping target mis-taps, which is worse than a small one. The footer's
   6px row gap goes to zero at the same time so the 44px boxes tile exactly:
   no overlap, no dead strip between them.

   One target is deliberately left alone. "Speak to a specialist" runs inline
   inside a sentence, and WCAG 2.5.8 exempts a target inline in a line of
   text for the good reason that growing it would break the paragraph. It is
   a link in prose, not a control. */
@media (hover:none),(pointer:coarse){
  .chip{min-height:44px; padding-inline:18px}
  .masthead > a,
  .crumb a,
  .decide-foot > a,
  .skip-past{min-height:44px; display:inline-flex; align-items:center}
  .navsheet-sup > a{min-height:36px}
  /* inline-flex drops the space before an arrow; the gap puts it back */
  .decide-foot > a{gap:8px}
  /* these reached 44 tall and stayed narrow: short labels in a flex row */
  .crumb a,
  .shop-jump > a{min-width:44px}
  /* the accessory cross-links sit at 17px tall in their own block */
  .acc-more > a{min-height:44px; display:inline-flex; align-items:center; gap:8px; min-width:44px}
  footer .cols nav{row-gap:0}
  .btn{min-width:44px}
}

/* --- spec table ---------------------------------------------------------- */

/* --- accordions ---------------------------------------------------------- */

/* --- figures ------------------------------------------------------------- */
.fig{position:relative; background:var(--surface); overflow:hidden}
.fig--carbon{background:var(--ink)}

/* --- reveal -------------------------------------------------------------- */
.rv{transition:opacity var(--t-slow) var(--ease-expo), transform var(--t-slow) var(--ease-expo)}
/* Content is visible by default. It is only hidden once the reveal observer
   has actually been created, so a JS error or a failed load can never leave
   the page blank. */
.rv-on .rv{opacity:0; transform:translateY(14px)}
.rv-on .rv[data-in]{opacity:1; transform:none}

/* --- pinned scroll sequences -------------------------------------------- */
.pin{position:relative}
.pin .sticky{position:sticky; top:0; height:100svh; display:grid; place-items:center; overflow:hidden;
  max-width:var(--page); margin-inline:auto; padding-inline:var(--gutter)}
.pin .stage{position:relative; width:100%; height:100%; display:grid; place-items:center}
.pin canvas{width:min(88vw,860px); height:auto}
.callout{
  position:absolute; max-width:min(290px,42vw); opacity:0; transform:translateY(8px);
  transition:opacity var(--t) var(--ease), transform var(--t) var(--ease);
}
.callout[data-on]{opacity:1; transform:none}
.callout .k{font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.08em; text-transform:uppercase}
.callout .v{font-size:var(--fs-mono); line-height:1.7; color:var(--fg-2); margin-top:5px; letter-spacing:.08em}
.callout .lead{width:52px; height:1px; background:var(--fg); margin-bottom:9px}

/* --- sticky buy bar ------------------------------------------------------ */
.buybar{
  position:fixed; inset:auto 0 0 0; z-index:70;
  background:color-mix(in oklab, var(--bg) 94%, transparent);
  border-top:1px solid var(--rule);
  padding:var(--s-3) max(var(--gutter), calc(50vw - var(--page)/2 + var(--gutter)));
  display:flex; align-items:center; gap:var(--s-5);
  transform:translateY(102%); visibility:hidden;
  transition:transform var(--t-slow) var(--ease-expo), visibility 0s linear var(--t-slow);
}
/* visibility, not just transform: a bar parked off-screen with only a
   transform is still in the tab order, so a keyboard user lands on a button
   they cannot see */
.buybar[data-show]{transform:none; visibility:visible; transition-delay:0s}
.buybar .btn{margin-left:auto; min-height:46px; padding:12px 26px}
@media (max-width:639.98px){ .buybar .meta{display:none} .buybar .btn{margin-left:0; flex:1} }

/* --- reduced motion: equivalent, not degraded --------------------------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important}
  .rv{opacity:1; transform:none}
  .pin{height:auto!important}
  .pin .sticky{position:static; height:auto; padding-block:var(--s-8)}
  .pin .stage{display:block}
  .callout{position:static; opacity:1; transform:none; max-width:none; margin-top:var(--s-5)}
  .buybar{transform:none}
}

/* ==========================================================================
   PLAY-STYLE LAYER — price is subtext; what the racquet does is the headline
   ========================================================================== */
.price-quiet{font-family:var(--mono); font-size:var(--fs-mono); font-weight:500; letter-spacing:.08em; color:var(--fg-2); font-variant-numeric:tabular-nums; text-transform:uppercase}
.price-quiet b{font-weight:500; color:var(--fg)}

.verdict{font-family:var(--display); font-weight:700; font-size:var(--fs-name); line-height:1.35; letter-spacing:-.015em; margin:var(--s-4) 0 var(--s-6); max-width:24ch; text-transform:none}
.outcomes{list-style:none; margin:var(--s-5) 0 0; padding:0}
.outcomes li .o-k{font-family:var(--display); font-weight:700; text-transform:none; font-size:var(--fs-label)}

/* ---- the row chart ----------------------------------------------------------
   2026-10-08, matched to the user's reference rather than translated: pairs
   of opposites in centred label columns either side of a hairline, the
   racquet as a large ink dot, a wide soft sky glow joining the dots down the
   rows, the most decided reading as a coral point, and that row's labels in
   pills. One grid for the whole chart (rows are display:contents) so every
   column lines up. The other racquet appears only when the toggle under the
   chart asks for it. */
.dc{position:relative; isolation:isolate; display:grid; grid-template-columns:minmax(0,9.5rem) minmax(0,1fr) minmax(0,9.5rem);
  column-gap:var(--s-4); row-gap:var(--s-2); align-items:center}
.dc-row{display:contents}
.dc-pole{justify-self:center; text-align:center; padding:9px 14px; border-radius:999px;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; line-height:1.35; color:var(--fg)}
.dc-track{position:relative; height:76px}
.dc-track::before{content:""; position:absolute; left:0; right:0; top:50%; height:1px; background:var(--rule)}
.dc-m{position:absolute; inset:0; pointer-events:none; transform:translateX(50%);
  transition:transform 1.1s var(--ease-expo) calc(var(--i,0) * 90ms), opacity var(--t-detent) var(--ease-detent)}
.dc.on .dc-m{transform:translateX(calc(var(--x) * 1%))}
.dc-m i{position:absolute; left:0; top:50%; border-radius:50%; transform:translate(-50%,-50%)}
.dc-m--me i{width:16px; height:16px; background:var(--fg)}
.dc-m--them i{width:8px; height:8px; background:color-mix(in oklab,var(--taupe) 55%,var(--slate))}
/* the coral point: a coral disc round a small ink centre, with a soft coral
   bloom under it. The colours sit on pseudo-elements so the bloom paints
   beneath the disc. */
.dc-row--peak .dc-m--me i{width:24px; height:24px; background:none}
.dc-row--peak .dc-m--me i::before,.dc-row--peak .dc-m--me i::after{content:""; position:absolute; border-radius:50%}
.dc-row--peak .dc-m--me i::after{inset:-8px; background:var(--glow-hot); filter:blur(7px); opacity:.85}
.dc-row--peak .dc-m--me i::before{inset:0; z-index:1;
  background:radial-gradient(circle, var(--fg) 0 4px, var(--glow-hot) 4.5px)}
/* comparison off by default */
.dc:not(.cmp) .dc-m--them{opacity:0}
@media (max-width:639.98px){
  [data-web]:has(.blob){padding:var(--s-6) var(--s-3)}
  [data-web]:has(.blob) .web-head{padding-inline:var(--s-3)}
  .dc{grid-template-columns:minmax(0,5.6rem) minmax(0,1fr) minmax(0,5.6rem); column-gap:var(--s-2)}
  .dc-pole{padding:7px 10px}
  .dc-track{height:68px}
}

/* ---- the blob -----------------------------------------------------------------
   The five measures as a web, soft rather than ruled: the racquet's shape
   is a closed curve filled with the sky glow and blurred into a blob, a
   crisp edge over it, the coral point where it is most decided. The other
   racquet is a fine dashed curve, shown only on request. It grows out of the
   centre the first time it is on screen. */
.blob{position:relative}
.blob-fig{position:relative; max-width:560px; margin-inline:auto}
.blob-fig svg{display:block; width:100%; height:auto; overflow:visible; color:currentColor}
.blob-grid line{stroke:currentColor; stroke-width:1; opacity:.16}
.blob-ring{fill:none; stroke:currentColor; stroke-width:1; stroke-dasharray:1 6; stroke-linecap:round; opacity:.32}
.blob-me{transform-box:view-box; transform-origin:260px 232px; transform:scale(.15); opacity:0;
  transition:transform 1.4s var(--ease-expo), opacity .8s var(--ease-expo)}
.blob.on .blob-me{transform:none; opacity:1}
.blob-pt-me{fill:currentColor}
.blob-hot{fill:var(--glow-hot)}
.blob-hot-halo{fill:var(--glow-hot); opacity:.8}
.blob-pts{transition:opacity .6s var(--ease-expo) .5s; opacity:0}
.blob.on .blob-pts{opacity:1}
.blob-pt-them{fill:color-mix(in oklab,var(--taupe) 70%,currentColor); transition:opacity var(--t-detent) var(--ease-detent)}
.blob:not(.cmp) .blob-pt-them{opacity:0}
.blob-lab{position:absolute; transform:translate(-50%,-50%); white-space:nowrap;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:inherit}
.web-fig:has(.blob){grid-column:1 / -1; align-self:stretch}
.web:has(.blob) .web-table{grid-column:1 / -1}
[data-web][data-model="apex"] .blob-hot-halo{opacity:.65}

.dc-trail{position:absolute; inset:0; width:100%; height:100%; z-index:-1; overflow:visible; pointer-events:none}
/* the rows' wave fades up once the dots have settled */
.dc .wave-me{opacity:0; transition:opacity 1.1s var(--ease-expo) .45s}
.dc.on .wave-me{opacity:1}
.en-them{transition:opacity var(--t-detent) var(--ease-detent)}
:is(.dc,.blob):not(.cmp) .en-them{opacity:0}

/* the frequency waves (rows and web): each short arc carries its own weight and
   opacity from the script, so only the stroke colour is set here */
.wave path{fill:none; stroke-linecap:round; stroke-linejoin:round}
.wave-me path{stroke:var(--glow)}
/* the rows sit on white, so their wave takes the deeper champagne */
.dc .wave-me path{stroke:var(--glow-deep)}
/* DROP's web sits on its cream, where the light champagne disappears */
[data-web][data-model="drop"] .wave-me path{stroke:var(--glow-deep)}
.wave-them path{stroke:var(--glow-them)}

/* ---- the compare toggle ---------------------------------------------------- */
.cmp-foot{grid-column:1 / -1; display:flex; justify-content:center; margin-top:var(--s-4)}
.cmp-toggle{display:inline-flex; align-items:center; gap:10px; min-height:44px; padding:0 4px;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2);
  transition:color var(--t-detent) var(--ease-detent)}
.cmp-toggle:hover,.cmp-toggle[aria-pressed="true"]{color:var(--fg)}
.cmp-toggle i{display:block; width:10px; height:10px; border-radius:50%; border:1px solid currentColor;
  transition:background var(--t-detent) var(--ease-detent), transform var(--t-detent) var(--ease-detent)}
.cmp-toggle[aria-pressed="true"] i{background:currentColor; transform:scale(.8)}
[data-web][data-model="apex"] .cmp-toggle{color:var(--taupe)}
[data-web][data-model="apex"] .cmp-toggle:hover,[data-web][data-model="apex"] .cmp-toggle[aria-pressed="true"]{color:var(--cream)}
/* 2026-10-27, the user: "make the Compare with DROP button CTA obvious so
   that it's clickable". A solid key, square, in the page's tone: Ink on a
   light section, Cream on APEX's Ink; deepening on hover, the dot filling
   when the other racquet is laid over. */
.cmp-toggle{min-height:46px; padding:0 22px; color:var(--bg); background:var(--fg); cursor:pointer;
  transition:background var(--t-detent) var(--ease-detent), color var(--t-detent) var(--ease-detent)}
.cmp-toggle:hover,.cmp-toggle[aria-pressed="true"]{color:var(--bg); background:color-mix(in oklab, var(--fg), #000 35%)}
[data-web][data-model="apex"] .cmp-toggle{color:var(--ink); background:var(--cream)}
[data-web][data-model="apex"] .cmp-toggle:hover,[data-web][data-model="apex"] .cmp-toggle[aria-pressed="true"]{color:var(--ink); background:var(--paper)}
@media (prefers-reduced-motion:reduce){ .dc-m,.dc .wave-me,.blob-me,.blob-pts,.cmp-toggle i{transition:none} }

/* ---- talk to a specialist -------------------------------------------------
   2026-09-27: its own row under the fold list, not a fold of it.
   2026-10-09: an Ink tile with the button's chamfer.
   2026-10-13, the user: "simplify this, make it talk to a specialist, no
   unnecessary text, and as a rule no text hierarchy in CTAs." One line, one
   weight: the signal, the words, the arrow. The transmission (a point with
   two rings leaving it) still runs quietly and claims nobody is online; on
   hover a band of light crosses once and the arrow seats. */
.talk-cta{--chamfer:12px; position:relative; display:flex; align-items:center; gap:var(--s-4);
  margin-top:var(--s-5); min-height:50px; padding:13px 20px;   /* 56/16·22 until 2026-10-27 */
  clip-path:polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)),
                    calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  color:var(--paper); background:color-mix(in oklab, var(--ink), #000 22%); overflow:hidden; isolation:isolate;
  transition:background-color .7s var(--ease-detent)}
.talk-cta:hover{background:color-mix(in oklab, var(--ink), #000 44%)}
/* 2026-10-27: the band of light across the face went for the buttons' edge
   glint ("only edge glint animations. No light effects across the button") */
.talk-cta::after{content:""; position:absolute; z-index:-1; inset:0; padding:2px; pointer-events:none;
  background:
    conic-gradient(from var(--glint), transparent 0deg 284deg,
      rgba(255,206,160,.28) 312deg, rgba(255,255,255,.95) 330deg,
      rgba(168,212,255,.32) 346deg, transparent 360deg),
    conic-gradient(from calc(var(--glint) + 180deg), transparent 0deg 300deg,
      rgba(255,255,255,.34) 332deg, transparent 360deg);
  filter:blur(.35px);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite:xor;
          mask:linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  opacity:0}
.talk-cta:hover::after,.talk-cta:focus-visible::after{animation:btn-glint 1.4s var(--ease-sweep)}
.talk-cta:focus-visible{outline:none; box-shadow:inset 0 0 0 2px var(--ink), inset 0 0 0 4px var(--paper)}
.talk-sig{position:relative; flex:none; width:18px; height:18px; color:color-mix(in oklab, var(--paper) 82%, transparent)}
.talk-sig i{position:absolute; left:50%; top:50%; border-radius:50%; transform:translate(-50%,-50%)}
.talk-sig i:first-child{width:6px; height:6px; background:currentColor}
.talk-sig i:not(:first-child){width:18px; height:18px; border:1px solid currentColor; opacity:0;
  animation:talk-ring 2.6s var(--ease-expo) infinite}
.talk-sig i:nth-child(3){animation-delay:1.3s}
@keyframes talk-ring{0%{transform:translate(-50%,-50%) scale(.3); opacity:.75} 100%{transform:translate(-50%,-50%) scale(1); opacity:0}}
.talk-k{flex:1; font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.01em; text-transform:none}
.talk-go{flex:none; display:grid; place-items:center}
.talk-go svg{width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round;
  transition:transform var(--t-detent) var(--ease-detent)}
.talk-cta:hover .talk-go svg,.talk-cta:focus-visible .talk-go svg{transform:translateX(4px)}
@media (prefers-reduced-motion:reduce){ .talk-sig i:not(:first-child){animation:none; opacity:.35} .talk-sig i:nth-child(3){opacity:0}
  .talk-cta::after{display:none} }

/* ==========================================================================
   IMAGE SLOTS — built for the courtside, macro and slow-motion material.
   Swap the reserved state for a real <img>/<video>; the frame is unchanged.
   ========================================================================== */
.shot{position:relative; overflow:hidden; background:var(--ink); isolation:isolate}
.shot>img,.shot>video{width:100%; height:100%; object-fit:cover; display:block}
.shot--wide{aspect-ratio:21/9}
.shot--court{aspect-ratio:16/9}
.shot--portrait{aspect-ratio:4/5}
.shot--square{aspect-ratio:1}
.shot[data-reserved]{
  display:grid; align-content:end; gap:7px; padding:var(--s-6);
  background:linear-gradient(color-mix(in oklab,var(--ink) 74%,transparent), color-mix(in oklab,var(--ink) 95%,transparent)), var(--shot-plate,none) center/cover no-repeat, var(--ink);
}
.shot[data-reserved] .slate{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--taupe)}
.shot[data-reserved] .slate-h{font-family:var(--display); font-weight:700; font-size:var(--fs-name); letter-spacing:-.01em; color:var(--cream); max-width:26ch; text-transform:none}
.shot .caption{position:absolute; left:var(--s-6); top:var(--s-6); z-index:2; font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--taupe)}

/* ==========================================================================
   THE TRIBE — ominous, restrained, just within reach. Never a newsletter box.
   ========================================================================== */

.enlist{border-top:1px solid var(--rule); border-bottom:1px solid var(--rule); padding-block:var(--s-8)}
.enlist form{display:block; margin-top:var(--s-5)}
.signup-grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,320px),1fr));
  gap:var(--s-7) var(--s-8); align-items:start}
.signup-row{display:flex; gap:var(--s-3); margin-top:var(--s-4); flex-wrap:wrap}
.signup-row .btn{flex:none}
@media (max-width:639.98px){ .signup-row .btn{width:100%; justify-content:center} }
.enlist input{flex:1; min-height:52px; padding:14px 16px; background:color-mix(in oklab, var(--cream) 55%, var(--bg)); border:0; border-radius:0;
  transition:background-color var(--t-detent) var(--ease-detent); color:var(--fg); font-family:var(--mono); font-size:var(--fs-label); letter-spacing:.02em; text-transform:uppercase}
.enlist input::placeholder{color:var(--fg-2)}
.enlist input:hover{background:var(--cream)}
.enlist input:focus-visible{background:var(--cream); outline:2px solid var(--fg); outline-offset:2px}
.enlist .roster{font-size:var(--fs-label); color:var(--fg-2); margin-top:var(--s-4)}
.enlist [data-msg]{font-size:var(--fs-mono); margin-top:var(--s-3); min-height:1.4em; letter-spacing:.08em}
@media (max-width:899.98px){ .enlist-grid{grid-template-columns:1fr; gap:var(--s-6)} }

.series{display:inline-flex; align-items:center; gap:10px; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.series::before{content:""; width:26px; height:1px; background:var(--rule)}

/* ==========================================================================
   RHYTHM
   Three levels, and the scroll should be able to feel the difference between
   them: a chapter opening takes the most air, a pinned subsection takes less
   because the viewport already frames it, and an image takes a beat of its
   own so it never reads as decoration attached to the paragraph above.
   Photography lands in .spread and .plate; both are spaced as figures, not
   as blocks of text.
   ========================================================================== */
.opening{padding-block:var(--s-10) var(--sec-h)}
/* after another section, an opening takes the rhythm, not a page's first opening */
main > * + .opening{padding-top:var(--sec-h)}
/* stacked chapters would otherwise double their air at the seam */
.opening + .opening{padding-top:var(--s-7)}
.opening .folio + *{margin-top:var(--s-7)}
/* The vertical rhythm of a section head, held in one place rather than in an
   inline style on every element: rule, then display type, then the reading
   text, then whatever it leads to. Three steps, each smaller than the last,
   which is the hierarchy. */
.opening .standfirst + .standfirst{margin-top:var(--s-5)}
.opening :is(.standfirst,.d2,.d3,.pull) + .btn{margin-top:var(--s-6)}
.opening--split .standfirst:first-of-type{margin-top:var(--s-6)}

/* A section head is a heading and the text that supports it, not a heading
   with a column of nothing beside it. The page runs to 1560px and a 46ch
   paragraph left roughly two thirds of that empty. From 1200px the head
   splits in two even columns: display type on the left at the scale it
   deserves, the reading text on the right, both hanging from the same folio
   rule and starting on the same line. 1200 rather than 1024 because below it
   the right column falls under about 45 characters, which is too tight a
   measure to read; there it stacks, unchanged. */
@media (min-width:1200px){
  .opening--split{
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    column-gap:var(--s-8); row-gap:0; align-items:start;
  }
  /* the column is the measure now, so the element cap can open up a little */
  .opening--split>.standfirst{max-width:60ch}
  .opening--split>*{grid-column:2}
  .opening--split>.folio{grid-column:1/-1}
  .opening--split>:is(.pull,.d2,.d3){grid-column:1; grid-row:2}
  /* the supporting text starts level with the heading, not underneath it */
  .opening--split>.standfirst:first-of-type{grid-row:2; margin-top:var(--s-7)}
}
.endline{padding-block:var(--sec-h); border-top:1px solid var(--rule-soft)}
.endline .d2{max-width:none}
.spread,.plate{margin-block:var(--s-9)}
.spread + .opening,.plate + .opening,.plate-row + .opening{padding-top:var(--s-7)}
footer{border-top:1px solid var(--rule); padding-block:var(--s-7) var(--s-8); font-size:var(--fs-mono); color:var(--fg-2); letter-spacing:.08em}
@media (max-width:639.98px){
  .opening{padding-block:var(--s-8) var(--s-7)}
  .endline{padding-block:var(--s-8) var(--s-7)}
  .spread,.plate{margin-block:var(--s-7)}
}

/* ==========================================================================
   THE SPREAD
   A magazine sets an image and gives it room; advertising sets type on top
   of it. This is the former. Light ground, asymmetric grid, plate bleeding
   off one edge, the line reading beneath it.
   ========================================================================== */
/* the plate bleeds off the right edge of the page, not the whole viewport */
.plate-wrap{position:relative; margin-right:calc(var(--gutter) * -1)}
.plate{
  position:relative; aspect-ratio:4/5; overflow:hidden; background:var(--surface);
  /* the whole cover has to sit in one screen; crop rather than push the line down */
  max-height:calc(100svh - 216px);
}
.plate .fr{position:absolute; inset:0; clip-path:inset(0 0 0 100%); transition:clip-path 1.05s var(--ease-expo)}
.plate .fr[data-on]{clip-path:inset(0 0 0 0)}
.plate .fr img{width:100%; height:100%; object-fit:cover; transform:scale(1.02)}
/* the plate leans a little toward the pointer. Restrained, not a toy. */
.plate{transform:perspective(1400px) rotateX(var(--px,0deg)) rotateY(var(--py,0deg)); transition:transform .7s var(--ease)}

/* the page title treatment. Uppercase, 800, tight. Every page uses it. */

/* caption + index sit under the plate, the way a spread is credited */
.plate-credit{display:flex; align-items:baseline; justify-content:space-between; gap:var(--s-5); margin-top:var(--s-3); padding-right:var(--gutter)}
.plate-cap{font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.plate-idx{display:flex; gap:var(--s-3)}
.plate-idx button{
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg-2);
  padding:4px 2px;
  transition:color var(--t-detent) var(--ease-detent), border-color var(--t-detent) var(--ease-detent); text-transform:uppercase}
.plate-idx button[data-on]{color:var(--fg)}

@media (max-width:899.98px){
  .cover-grid{grid-template-columns:1fr; gap:var(--s-6)}
  .plate-wrap{margin-right:calc(var(--gutter) * -1); margin-left:calc(var(--gutter) * -1); order:-1}
  .plate{aspect-ratio:3/4; max-height:56svh}
  .plate-credit{padding-inline:var(--gutter)}
  .plate{transform:none}
}

/* full-bleed plates elsewhere on the page */
/* A spread is content, not a section ground, so it is held to the page
   measure like every other block rather than run to the window edge. */
.spread{
  width:calc(100% - var(--gutter)*2);
  max-width:calc(var(--page) - var(--gutter)*2);
  margin-inline:auto; overflow:hidden; background:var(--surface);
}
.spread img{width:100%; height:100%; object-fit:cover; display:block}
.spread--wide{aspect-ratio:16/7}
.spread--tall{aspect-ratio:3/4}
.zoom{overflow:hidden}
.zoom img{transition:transform 1.3s var(--ease-expo)}
.zoom:hover img{transform:scale(1.035)}

/* magazine furniture */
.folio{display:flex; align-items:baseline; gap:var(--s-5); border-bottom:1px solid var(--rule-soft); padding-bottom:var(--s-4)}
.folio .n{font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.08em; text-transform:none}
.folio .t{font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.pull{font-family:var(--display); font-weight:800; letter-spacing:-.035em; line-height:1.0;
  font-size:var(--fs-statement); max-width:16ch; margin:0; text-transform:uppercase}
.standfirst{font-family:var(--display); font-weight:400; font-size:var(--fs-lead); line-height:1.5; color:var(--fg-2); max-width:46ch; text-transform:none}

/* word rise */
.rise{display:inline-block; overflow:hidden; vertical-align:bottom}
.rise>span{display:inline-block; transform:translateY(108%); transition:transform .95s var(--ease-expo)}
.rise[data-in]>span,[data-in] .rise>span{transform:none}

/* cursor: a caption that follows you, not a decorative ring */
.cursorlab{
  position:fixed; top:0; left:0; z-index:9999; pointer-events:none;
  padding:6px 12px; border-radius:999px; background:var(--fg); color:var(--bg);
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  opacity:0; transform:translate(-50%,-160%) scale(.9);
  transition:opacity .3s var(--ease), transform .4s var(--ease-expo);
  white-space:nowrap;
}
.cursorlab[data-on]{opacity:1; transform:translate(-50%,-160%) scale(1)}
@media (hover:none),(pointer:coarse){ .cursorlab{display:none} }

/* 2026-10-07, by the user: no underline as a highlight, anywhere on the site.
   A link answers the pointer with contrast instead: it steps from Ink to
   Slate on the control curve (or Slate to Ink where it rests in Slate, as the
   nav and the crumbs do). Arrows that travel with a link still seat 4px right. */
.ul{transition:color var(--t-detent) var(--ease-detent)}
.ul:hover{color:var(--fg-2)}
.ul .arw{display:inline-block; transition:transform var(--t-detent) var(--ease-detent)}
.ul:hover .arw{transform:translateX(4px)}

@media (prefers-reduced-motion:reduce){
  .plate .fr{transition:none}
  .plate{transform:none!important}
  .rise>span{transform:none}
  .cursorlab{display:none}
  .zoom img{transition:none}
  .btn .arw{transition:none}
}

/* ==========================================================================
   THE BREAK
   A pinned rail of swing stages. Vertical scroll drives horizontal travel;
   each stage swells through a focus band and falls back as it leaves, so
   slivers of what is coming and what has gone are always in view.
   Everything moves on transform. No layout property is animated.
   ========================================================================== */
.break{position:relative}
.break .sticky{
  position:sticky; top:0; height:100svh; overflow:hidden;
  display:grid; grid-template-rows:auto 1fr auto; gap:var(--s-5);
  padding-block:calc(var(--header-h) + var(--s-5)) var(--s-7);
  /* the measure sits here rather than on the head and foot, so the scene
     between them is clipped to the same content box the type is set on */
  max-width:var(--page); margin-inline:auto; padding-inline:var(--gutter);
}
/* Head, scene and foot all sit inside the sticky's measure, so the headline,
   the drawing and the button below it start and end on the same two lines. */
.break-head,.break-foot{padding-inline:0}
/* above the plate and the end card: the title clears before the photograph
   arrives, and the foot stays reachable over it */
[data-break] .break-head,[data-break] .break-foot{position:relative; z-index:4}
/* The standfirst reads as the second line of the headline, so it sits under
   it. Pushed out to the opposite margin it read as a separate column and,
   at wide viewports, as unrelated to the words it belongs to. */
.break-head{display:flex; flex-direction:column; align-items:flex-start; gap:var(--s-4)}
.break-head .standfirst{max-width:44ch; margin:0}

.scene{position:relative; height:100%; min-height:0; overflow:hidden}
/* the section scene does not travel, so it centres instead of panning */
[data-section] .scene,[data-manifest] .scene,[data-assembly] .scene{display:grid; place-items:center}
[data-section] .scene svg,[data-manifest] .scene svg,[data-assembly] .scene svg{transform:none!important}
[data-section] .cut,[data-section] .internals{color:var(--fg)}
.scene svg{
  display:block; will-change:transform; color:var(--fg); max-width:none;
  /* width and height are set explicitly in JS from the row's real height;
     aspect-ratio plus width:auto resolves circularly here and under-fills */
}
/* The manifest stacks a drawn field under the plates. The canvas takes both
   dimensions from the stack, never from its own box: a canvas with an auto
   dimension writes its measured size back into its attributes and grows. */
.scene-stack{position:relative; display:block; max-width:none}
.scene-stack canvas{position:absolute; inset:0; width:100%; height:100%; display:block}
.scene-stack svg{position:relative; display:block}

.scene .ghost{transition:none}

/* The release plates, 2026-10-13: HTML panes of frosted glass over the field
   of four hundred, with a compact height set by their content (no edge:
   the steel ring went 2026-10-13). --frost (0 to 1) is written by the scene as a
   pane blooms, so the frost condenses with the pane and clears as it goes. */
.man-plates{position:absolute; inset:0; z-index:2; pointer-events:none}
/* a section's own plates, as a list (home's Synthesis, 2026-10-25): read by
   the scene and by a screen reader, shown only where the scene cannot run */
.man-key{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; margin:0; padding:0; list-style:none}
.no-js .man-key,.man-key--open{position:static; width:auto; height:auto; overflow:visible; clip:auto; white-space:normal;
  display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr)); gap:var(--s-4);
  max-width:var(--page); margin:var(--s-6) auto var(--s-8); padding-inline:var(--gutter)}
.no-js .man-key li,.man-key--open li{display:grid; gap:6px; padding:16px 20px 18px; border-radius:10px;
  background:color-mix(in oklab, color-mix(in oklab, var(--glow) 40%, var(--taupe)) 22%, var(--bg))}
.man-key-k{margin:0; font-family:var(--display); font-weight:800; text-transform:uppercase; font-size:var(--fs-label); letter-spacing:.02em}
.man-key-v{margin:0; font-family:var(--display); font-size:var(--fs-label); line-height:1.5; text-transform:none; color:var(--fg-2)}
.no-js .synth-field .scene{display:none}
.synth-field .break-foot .synth-sign{margin:0; flex:none}
/* 2026-10-27 ("too much whitespace in between imagineered in london and
   dna"): the stage gave the field a whole-height middle row, so the sign sat
   300px below a shorter field. The field's row is sized to the field (by the
   screen's height, or its width on a phone, at the field's own 1.38:1), and
   head, field and sign sit together, centred, a section's steps apart. */
.synth-field .sticky{grid-template-rows:auto auto auto; align-content:center; gap:var(--s-7)}
/* 2026-10-27 ("make the bg grid larger, too much space around the section"): from 56svh / 540 */
.synth-field .scene{height:min(70svh, 680px, calc((100vw - 2 * var(--gutter)) / 1.38))}
@media (max-width:719.98px){
  /* on a phone the field is narrow and short, so centring the group left the
     spare height under the sign, as a gap before DNA: the field's row takes
     the spare height instead (the cards pop up over it), the sign at the foot */
  .synth-field .sticky{grid-template-rows:auto 1fr auto; align-content:stretch}
  .synth-field .scene{height:auto}
}
/* the Synthesis cards. 2026-10-25: frosted glass. 2026-10-27, in turn: led by
   pictures, spread and popping in ("add images for each of the popups. Less
   text more images"); the words on frosted glass over the picture ("make
   these images with text on a frosted glass card overlaid", a sanctioned
   exception to the Clean Plate Rule, for these cards only); then (the user:
   "make them staggered so they pop up at different areas of the same screen,
   especially on mobile. Also, we don't have rounded edges, so make the edges
   sharp for the card itself and make the frosted glass a smaller part of the
   overall image card. Also make it more genie effect so appear and disappears
   from a distance"):
   - square corners, card and pane; the picture portrait (4:5), the pane a
     small, tight strip at its foot;
   - each card at its own place, on a phone too (zig-zag: top left, bottom
     right, top right, bottom left), so the next appears somewhere new while
     the last recedes;
   - the genie: a card comes forward out of the depth of the field, squeezed,
     tilted back and soft, unfolding from the corner nearest the field's
     middle, and goes back the same way, quicker. */
.synth-field .man-plate{
  background:var(--surface); padding:0; gap:0; display:block; overflow:hidden; border-radius:0;
  -webkit-backdrop-filter:none; backdrop-filter:none;
  box-shadow:0 34px 60px -30px color-mix(in oklab, var(--ink) 50%, transparent);
  width:clamp(320px, 30%, 380px)!important;
  opacity:0; filter:blur(10px);
  transform:translate(-50%,-50%) perspective(900px) translateZ(-640px) rotateX(16deg) scale(.6,.22);
  transition:opacity .38s ease-in, transform .5s cubic-bezier(.55,0,.9,.45), filter .42s ease-in}
.synth-field .man-plate[data-on]{opacity:1; filter:blur(0);
  transform:translate(-50%,-50%) perspective(900px) translateZ(0) rotateX(0) scale(1,1);
  transition:opacity .4s ease-out, transform .9s cubic-bezier(.16,1,.3,1), filter .6s ease-out}
/* unfolding from the corner nearest the field's middle (positions in index.html) */
.synth-field .man-plate:nth-child(1){transform-origin:100% 100%}
.synth-field .man-plate:nth-child(2){transform-origin:0 100%}
.synth-field .man-plate:nth-child(3){transform-origin:100% 0}
.synth-field .man-plate:nth-child(4){transform-origin:0 0}
.synth-field .man-plate--img::after{display:none}
.man-plate-img{display:block; width:100%; height:auto; aspect-ratio:4/5; object-fit:cover; background:var(--surface)}
.synth-field .man-plate .man-plate-glass{position:absolute; left:8px; right:8px; bottom:8px; padding:9px 11px 10px; border-radius:0;
  background:linear-gradient(150deg, rgba(255,255,255,.5), rgba(255,255,255,.14) 60%), color-mix(in oklab, var(--bg) 55%, transparent);
  -webkit-backdrop-filter:blur(18px) saturate(1.5); backdrop-filter:blur(18px) saturate(1.5);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85)}
.synth-field .man-plate--img .man-plate-k{padding:0; font-size:var(--fs-label)}
.synth-field .man-plate--img .man-plate-v{padding:2px 0 0; font-size:var(--fs-label); line-height:1.32; color:color-mix(in oklab, var(--ink) 80%, transparent)}
@media (max-width:719.98px){
  /* a phone: the upright field, the cards zig-zagging over it */
  .synth-field .scene{overflow:visible}
  /* the card as wide as a phone allows while still stepping side to side, so
     the locked line takes the fewest rows and the pane stays a strip; a
     taller picture (3:4) for the same reason */
  .synth-field .man-plate{width:min(88%, 270px)!important}
  .synth-field .man-plate-img{aspect-ratio:3/4}
  .synth-field .man-plate:nth-child(1){left:42%!important; top:31%!important; transform-origin:100% 100%}
  .synth-field .man-plate:nth-child(2){left:58%!important; top:69%!important; transform-origin:0 0}
  .synth-field .man-plate:nth-child(3){left:58%!important; top:31%!important; transform-origin:0 100%}
  .synth-field .man-plate:nth-child(4){left:42%!important; top:69%!important; transform-origin:100% 0}
}
@media (prefers-reduced-motion:reduce){
  .synth-field .man-plate,.synth-field .man-plate[data-on]{transition:none; filter:none;
    transform:translate(-50%,-50%)}
}
.man-plate{
  position:absolute; overflow:hidden; border-radius:10px;
  display:grid; gap:4px; padding:16px 22px 18px; text-align:left;
  background:
    linear-gradient(150deg, rgba(255,255,255,.55), rgba(255,255,255,.1) 44%, rgba(255,255,255,0) 72%),
    color-mix(in oklab, var(--bg) 82%, transparent);
  -webkit-backdrop-filter:blur(18px) saturate(1.3); backdrop-filter:blur(18px) saturate(1.3);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.85), 0 1px 1px color-mix(in oklab, var(--ink) 5%, transparent),
    0 28px 56px -30px color-mix(in oklab, var(--ink) 45%, transparent);
  /* 2026-10-25: popups. Off, a plate waits small and transparent; on, it
     springs to size (a slight overshoot) as it appears, and goes on a
     short fade without the spring */
  opacity:0; transform:translate(-50%,-50%) translateY(10px) scale(.86);
  transition:opacity .22s var(--ease-detent), transform .26s var(--ease-detent);
  will-change:transform, opacity}
.man-plate[data-on]{
  opacity:1; transform:translate(-50%,-50%) scale(1);
  transition:opacity .3s var(--ease-detent), transform .6s cubic-bezier(.34,1.56,.64,1)}
@media (max-width:719.98px){
  /* on a phone the popup takes most of the field's width */
  .man-plate{width:min(88%, 340px)!important; padding:14px 18px 16px}
}
@media (prefers-reduced-motion:reduce){ .man-plate,.man-plate[data-on]{transition:none} }
/* Every frosted pane, 2026-10-13, the user: "add perforations to any
   frosted element", then "perforations to the cards above as well, and
   remove the outline" (the steel ring went from every pane the same day).
   The perforations are the racquet's own: a field of drilled holes, a dark
   bore with a light lip on a 12px pitch, solid in one corner of the pane and
   dissolving before the words. No edge of any kind: the frost is the pane. */
.man-plate::after{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background-image:radial-gradient(circle, var(--bg) 0 2.2px, transparent 2.7px);
  background-size:36px 36px; background-position:18px 18px;
  -webkit-mask-image:radial-gradient(130% 120% at 100% 0%, #000 0%, #000 22%, rgba(0,0,0,.5) 44%, transparent 70%);
  mask-image:radial-gradient(130% 120% at 100% 0%, #000 0%, #000 22%, rgba(0,0,0,.5) 44%, transparent 70%)}
.man-plate::after{
  -webkit-mask-image:linear-gradient(270deg, #000 0%, rgba(0,0,0,.5) 14%, transparent 30%);
  mask-image:linear-gradient(270deg, #000 0%, rgba(0,0,0,.5) 14%, transparent 30%)}
.man-plate>*{position:relative; z-index:1}
.man-plate-k{margin:0; font-family:var(--display); font-weight:800; text-transform:uppercase;
  font-size:var(--fs-label); letter-spacing:.02em; line-height:1.25; color:var(--fg)}
.man-plate-v{margin:0; font-family:var(--display); font-size:var(--fs-label); line-height:1.45;
  text-transform:none; color:var(--fg-2)}

/* ---- the trailer plate ---------------------------------------------------
   The trailer resolves onto the object. The plate is the photograph, held
   inside the sticky's clip, with an Ink veil raised under the endline only —
   the same move .shot makes, at half its density, because this frame is the
   hero and the veil is here to carry type rather than to set a mood, and it
   only comes up once the frame has been held clean. Type is Paper, the warm
   near-white. Measured on the rendered composite, worst single pixel under
   the endline / wordmark: 4.9 / 4.8:1 at 1024, 6.1 / 7.5:1 at 375 with the
   phone crop. AA body rather than merely AA large; the mean is above 10:1. */
.break-plate{
  position:absolute; inset:0; z-index:2; margin:0; overflow:hidden;
  opacity:0; pointer-events:none; isolation:isolate;
}
.break-plate img{width:100%; height:100%; object-fit:cover; display:block}
/* a 16:9 frame in a portrait sticky crops hard, and centred it loses the
   face to the right edge; on phones the crop is biased so both racquets
   stay in frame */
@media (max-width:767.98px){ .break-plate img{object-position:66% 50%} }
.break-plate::after{
  content:""; position:absolute; inset:0;
  background:var(--ink); opacity:var(--veil,0);
}

/* ---- the trailer end card ------------------------------------------------
   The hero plays as a short trailer: the title card opens it, the swing runs,
   and it closes on the endline over the wordmark. Absolutely positioned so it
   sits outside the sticky's three-row grid, and driven from renderBreak on
   opacity and transform only. */
.break-end{
  position:absolute; inset:0; z-index:3;
  display:grid; place-content:center; justify-items:center;
  gap:clamp(12px,1.8vw,var(--s-5));
  text-align:center; pointer-events:none; opacity:0;
  padding-inline:var(--gutter);
  /* it lands on the veiled plate, never on the page ground */
  color:var(--paper);
}
.break-end__line{
  font-family:var(--display); font-weight:800; text-transform:uppercase;
  font-size:var(--fs-display); line-height:.9; letter-spacing:-.038em;  /* stays tight: the user reverted this one on 2026-09-16 */
  margin:0; color:inherit;
}
/* the wordmark reuses .mark, so it stays the supplied artwork on a mask and
   never a system font or a re-tracked copy */
.break-end__mark{width:clamp(158px,23vw,300px); color:inherit}
@media (prefers-reduced-motion:reduce){
  /* the sticky unpins, so the end card simply follows the scene as content */
  .break-end{position:static; opacity:1; transform:none; pointer-events:auto;
    padding-block:var(--s-8) 0; place-content:start center; color:var(--fg)}
  /* and the plate stops being a beat: it is the hero's photograph, in flow,
     unveiled, with the endline back on paper under it */
  .break-plate{position:static; opacity:1; aspect-ratio:16/9; margin-top:var(--s-6)}
  .break-plate::after{content:none}
}

/* ---- the hero film, 2026-10-16 -----------------------------------------
   One rally cut from two generated takes, scrubbed by scroll. It starts fully
   immersive, at the user's direction: the eye bleeds to every edge, behind
   the masthead, and the masthead, the title and the button sit on it in Paper
   and scroll away from it as the film begins (the one place type sits on the
   picture, with the endline). The section is pulled up under the masthead
   and the sticky loses its measure, so the film reaches the viewport edges at
   any width; the head and foot keep the measure themselves. It closes on the
   horizon: the wordmark lands, then the endline under it, on an Ink veil. */
.hero-film.hf-on{margin-top:calc(-1 * var(--header-h))}
.hero-film.hf-on .sticky{max-width:none; padding-inline:0}
.hero-film.hf-on .break-head,.hero-film.hf-on .break-foot{
  position:relative; z-index:4; width:100%; max-width:var(--page);
  margin-inline:auto; padding-inline:var(--gutter); box-sizing:border-box}
.hero-film.hf-on .break-head{color:var(--paper); will-change:opacity, transform}
.hero-film.hf-on .break-head .series,.hero-film.hf-on .break-head .standfirst{color:color-mix(in oklab,var(--paper) 86%,transparent)}
.hero-film.hf-on .break-head .series::before{background:color-mix(in oklab,var(--paper) 60%,transparent)}
.hero-film.hf-on .break-head .page-title{color:var(--paper)}
.hero-film .hf-box{min-height:0}
.hf{position:absolute; inset:0; z-index:2; overflow:hidden; background:var(--ink); pointer-events:none}
.hf canvas{position:absolute; inset:0; width:100%; height:100%; display:block}
.hf-veil{position:absolute; inset:0; background:var(--ink); opacity:0}
/* a faint scrim under the masthead for the whole film, so its Paper marks
   hold over the bright skies */
.hf::after{content:""; position:absolute; inset:0 0 auto 0; height:calc(var(--header-h) * 2.2);
  background:linear-gradient(180deg, color-mix(in oklab,var(--ink) 38%,transparent), transparent)}
/* the shade under the opening type: Ink from the top and the foot, gone
   once the type has scrolled off */
.hf-shade{position:absolute; inset:0;
  background:linear-gradient(180deg, color-mix(in oklab,var(--ink) 62%,transparent) 0%, color-mix(in oklab,var(--ink) 18%,transparent) 42%, transparent 62%, color-mix(in oklab,var(--ink) 34%,transparent) 100%)}
.hero-film:not(.hf-on) .hf{display:none}
.hero-film .break-plate{display:none}
.hero-film:not(.hf-on) .break-plate{display:block}
.hero-film.hf-on .break-end{opacity:1}
.hero-film.hf-on .break-end__mark{order:-1}
.hero-film.hf-on .break-end__line,.hero-film.hf-on .break-end__mark{opacity:0; will-change:opacity, transform}
.hero-film .break-foot{transition:opacity .3s var(--ease-detent)}
.hero-film .break-foot[data-off]{opacity:0; pointer-events:none}
/* the masthead over the film: no ground, no rule, Paper marks */
.masthead{transition:background-color .35s var(--ease-detent), border-color .35s var(--ease-detent), color .35s var(--ease-detent),
  opacity .7s var(--ease-detent), transform .7s var(--ease-detent)}
/* 2026-10-27, the user: "make the video a full takeover of the home section,
   including the navbar. So it only comes back once the video has ended".
   While the hero film plays (html.hv-hold, set in the page's head and taken
   off when the film ends, cannot play, or is scrolled past; the head's own
   script takes it off after fourteen seconds if anything stalls).
   Then (the user): "it should only be taken over for the frosted glass,
   meaning the logo and buttons should still be seen". So the glass alone
   lifts away and the marks turn Paper over the film, as over the old one. */
.hv-hold .masthead::before,.hv-hold .masthead::after{opacity:0}
.hv-hold .masthead{background:transparent; border-bottom-color:transparent; color:var(--paper);
  --fg:var(--paper); --fg-2:color-mix(in oklab,var(--paper) 82%,transparent)}
.masthead::before,.masthead::after{transition:opacity .7s var(--ease-detent)}
.hero-over .masthead::before,.hero-over .masthead::after{opacity:0}
.hero-over .masthead{background:transparent; border-bottom-color:transparent; color:var(--paper);
  --fg:var(--paper); --fg-2:color-mix(in oklab,var(--paper) 82%,transparent)}
.no-js .hero-film .break-plate{position:static; opacity:1; aspect-ratio:16/9; margin-top:var(--s-6)}
.no-js .hero-film .break-plate::after{content:none}
.no-js .hero-film .break-end{position:static; opacity:1; color:var(--fg); padding-block:var(--s-8) 0}

.break-note{font-size:var(--fs-mono); line-height:1.65; color:var(--fg-2); margin:0; letter-spacing:.08em}
@media (max-width:639.98px){
  .stage .note{display:none}
  .stage .meta{display:none}   /* the foot already names the focused stage */
  .break-note{margin-top:var(--s-3)}
  .break-foot{flex-wrap:wrap; row-gap:var(--s-3)}
}
.break-foot{display:flex; align-items:baseline; gap:var(--s-5); flex-wrap:wrap}
.break-read{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2); font-variant-numeric:tabular-nums; margin:0; flex:none}
/* the caption is the scene's commentary, so it reads at every size rather
   than only on the layouts that have no callouts of their own */
.break-note{display:block; flex:1 1 34ch; max-width:62ch; margin:0}

/* reduced motion and no-JS: the finished chronophotograph, panned by hand */
@media (prefers-reduced-motion:reduce){
  .break{height:auto!important}
  .break .sticky{position:static; height:auto; padding-block:var(--s-8)}
  .scene{overflow-x:auto; height:auto}
  .scene svg,.scene .scene-stack{height:min(60svh,520px); transform:none!important}
  .break-progress{display:none}
}
.no-js .break .sticky{position:static; height:auto}
.no-js .scene{overflow-x:auto; height:auto}
.no-js .scene svg,.no-js .scene .scene-stack{height:min(60svh,520px)}

/* ==========================================================================
   THE STORYBOARD
   Design meets technicality, in sequence. Deliberately static: this page
   already carries a scroll motion below it and does not need a second.
   ========================================================================== */
@media (max-width:899.98px){ .board{grid-template-columns:repeat(2,1fr)} .board .f-3,.board .f-6,.board .f-2{grid-column:span 2} }
@media (max-width:639.98px){ .board{grid-template-columns:1fr} .board .f-3,.board .f-6,.board .f-2{grid-column:span 1} }

/* ==========================================================================
   TONE + EDGE
   Nuance without a new hue: sections separate by tonal step within the warm
   ramp, and an edge pinstripe lifted off the APEX frame catches odd corners
   as they enter. Everything here stays inside the specified Pantones.
   ========================================================================== */
.tone{position:relative}
.tone--shell{background:var(--shell)}
.tone--cream{background:var(--surface)}
.tone--sand{background:var(--surface-2)}
.tone--ink{background:var(--ink); color:var(--cream)}
.tone--ink .folio .t,.tone--ink .standfirst,.tone--ink .muted{color:var(--taupe)}
.tone--ink .folio{border-bottom-color:color-mix(in oklab,var(--taupe) 32%,transparent)}

/* a section break that is a joint, not a gap */
.joint{display:flex; align-items:center; gap:var(--s-5); padding-block:var(--s-6)}
.joint::before{content:""; height:1px; flex:1; background:var(--rule-soft)}
.joint .cr{width:26px; flex:none; color:var(--edge); opacity:.9}
.joint .lbl{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.joint::after{content:""; height:1px; width:56px; flex:none; background:var(--edge)}

/* the pinstripe. Two hairlines meeting at one corner, drawn as the section
   arrives, with a highlight travelling the length the way light runs along
   an edge guard. Odd corners only: never all four. */
.ps{position:absolute; width:clamp(64px,7vw,116px); aspect-ratio:1; pointer-events:none; z-index:2}
.ps svg{width:100%; height:100%; overflow:visible}
.ps .base{stroke:var(--edge); stroke-width:1.25; fill:none;
  stroke-dasharray:var(--len,200); stroke-dashoffset:calc(var(--len,200) * (1 - var(--draw,0)))}
.ps .glint{stroke:var(--fg); stroke-width:1.25; fill:none;
  stroke-dasharray:0 var(--len,200);
  stroke-dashoffset:calc(var(--len,200) * (1 - var(--glint,0)) * -1);
  opacity:calc(var(--draw,0) * .85)}
.ps--tl{top:var(--s-5); left:var(--s-5)}
.ps--tr{top:var(--s-5); right:var(--s-5); transform:scaleX(-1)}
.ps--br{bottom:var(--s-5); right:var(--s-5); transform:scale(-1)}
.ps--bl{bottom:var(--s-5); left:var(--s-5); transform:scaleY(-1)}
@media (max-width:639.98px){ .ps{display:none} }
@media (prefers-reduced-motion:reduce){
  .ps .base{stroke-dashoffset:0}
  .ps .glint{display:none}
}

/* ---------------------------------------------------------------- the web --
   Identity by line style and texture, never by hue: the palette has no
   chroma to spare. The table is not a fallback, it is half the answer. */
.web{display:grid; grid-template-columns:minmax(0,340px) minmax(0,1fr);
  gap:var(--s-7) var(--s-8); align-items:start}
.web-head{grid-column:1 / -1}
.web-fig{grid-column:1; align-self:center}
.web-table{grid-column:2; align-self:center}
.web-table{width:100%; border-collapse:collapse; font-size:var(--fs-mono); letter-spacing:.08em}
.web-table caption{text-align:left; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em;
  text-transform:uppercase; color:var(--fg-2); padding-bottom:var(--s-4)}
.web-table th,.web-table td{text-align:left; padding:11px 0; border-top:1px solid var(--rule-soft); font-weight:400}
.web-table thead th{font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.08em; text-transform:uppercase}
.web-table tbody th{color:var(--fg-2); font-size:var(--fs-mono); width:46%; letter-spacing:.08em}
.web-table td{font-family:var(--mono); font-size:var(--fs-mono); text-transform:uppercase; letter-spacing:.08em}
.web-table td:first-of-type{font-weight:500}
@media (max-width:899.98px){
  .web{grid-template-columns:1fr; gap:var(--s-6)}
  .web-head,.web-fig,.web-table{grid-column:1}
}

/* ------------------------------------------------- the star map, chosen --
   Selecting a model changes the section's character, not just a highlight:
   APEX takes the frame dark, DROP takes it to its own cream. */
.web-head{margin-bottom:var(--s-6)}
.web-pick{display:inline-flex; gap:1px; background:var(--rule); padding:1px; border-radius:999px}
.web-pick button{
  min-height:44px; padding:9px 20px; border-radius:999px; background:var(--bg); color:var(--fg-2);
  font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.08em;
  text-transform:uppercase;
  transition:background var(--t-detent) var(--ease-detent), color var(--t-detent) var(--ease-detent);
}
.web-pick button[aria-pressed="true"]{background:var(--fg); color:var(--bg)}
.web-lead{font-family:var(--display); font-weight:800; font-size:var(--fs-name);
  line-height:1.25; letter-spacing:-.015em; margin:var(--s-5) 0 0; max-width:26ch; text-transform:uppercase}
.web-say{margin:var(--s-3) 0 0; font-size:var(--fs-label); line-height:1.65; color:var(--fg-2); max-width:44ch}

[data-web]{position:relative; padding:var(--s-7); transition:background .55s var(--ease), color .55s var(--ease)}
[data-web][data-model="apex"]{background:var(--ink); color:var(--cream)}
[data-web][data-model="apex"] .web-pick{background:color-mix(in oklab,var(--taupe) 40%,var(--ink))}
[data-web][data-model="apex"] .web-pick button{background:var(--ink); color:var(--taupe)}
[data-web][data-model="apex"] .web-pick button[aria-pressed="true"]{background:var(--cream); color:var(--ink)}
[data-web][data-model="apex"] .web-say,[data-web][data-model="apex"] .web-table tbody th{color:var(--taupe)}

[data-web][data-model="apex"] .web-table caption{color:var(--taupe)}
/* 2026-10-08, the user: the contrast on Cream Deep was poor; a subtle beige.
   Paper, the palest warm step, keeps DROP's warmth and lets the wave read. */
[data-web][data-model="drop"]{background:var(--paper)}
/* the unselected column recedes rather than disappearing */
.web-table td[data-dim="true"]{opacity:.38; transition:opacity .4s var(--ease)}
.web-table td{transition:opacity .4s var(--ease)}
@media (prefers-reduced-motion:reduce){ [data-web],.web-table td,.web-pick button{transition:none} }

/* Synthesis (home). Until 2026-10-25 the court photograph set off the grid
   behind a stepped story; since then the section is the brand page's field
   of four hundred with the four beats as popups (see the manifest scene and
   .man-key). Only the sign under the field keeps a rule of its own. */
.synth-sign{margin:var(--s-8) 0 0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}

/* ---- the story (brand) ------------------------------------------------------
   2026-10-08. One wide image that drifts with the scroll, then a single
   reading column under it, offset to the right of the page's centre so the
   section reads as an essay rather than another spread. */
.story{padding-block:var(--sec-h)}
.story-plate{margin:var(--s-6) 0 0; will-change:transform}
.story-plate .ed-img{aspect-ratio:16/7}
.story-text{max-width:660px; margin:var(--s-8) 0 0 auto; padding-right:clamp(0px,6vw,96px)}
.story-text .d2{margin:0; max-width:16ch}
.story-lead{margin:var(--s-6) 0 0; font-family:var(--display); font-weight:400; text-transform:none;
  font-size:var(--fs-lead); line-height:1.5; letter-spacing:-.01em; color:var(--fg)}
.story-text .standfirst{margin:var(--s-5) 0 0; max-width:none}
/* the manifesto's three tenets: a mono key over each, a hairline between */
.story-tenets{list-style:none; margin:var(--s-7) 0 0; padding:0}
.story-tenets li{padding-block:var(--s-5); border-top:1px solid var(--rule-soft)}
.story-tenets li .standfirst{margin-top:var(--s-3)}
.tenet-k{display:block; font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.story-close{margin:var(--s-6) 0 0; font-family:var(--display); font-weight:500; text-transform:none;
  font-size:var(--fs-lead); line-height:1.5; letter-spacing:-.005em; color:var(--fg)}
@media (max-width:899.98px){ .story-plate .ed-img{aspect-ratio:4/3} .story-text{margin-top:var(--s-7); padding-right:0} }

/* ---- the editorial spreads (brand: the lab, design) -------------------------
   2026-10-08. Text in one column, the images in the other, set off the grid
   and overlapping like a magazine spread. Since 2026-10-27 (the user: "remove
   the names under the images on the brand page and keep just 2 images per
   section"): two images, a large plate high on the outer side and a smaller
   one lower on the inner side, over its corner; no captions. Each image
   drifts at its own rate as the page scrolls (data-float, transform only),
   so the pair floats; under reduced motion they hold still. The design
   spread mirrors the lab's. On a phone the pair keeps its overlap. */
.ed{display:grid; grid-template-columns:minmax(0,5fr) minmax(0,7fr); gap:var(--s-8); align-items:center; padding-block:var(--sec-h)}
.ed--flip{grid-template-columns:minmax(0,7fr) minmax(0,5fr)}
.ed--flip .ed-text{order:2}
.ed-text .d2{margin:var(--s-6) 0 0; max-width:14ch}
.ed-text .standfirst{margin:var(--s-5) 0 0; max-width:44ch}
.ed-text .standfirst + .standfirst{margin-top:var(--s-4)}
.ed-figs{position:relative; min-height:clamp(460px,48vw,760px)}
.ed-fig{position:absolute; margin:0; will-change:transform}
.ed-img{display:block; overflow:hidden; background:var(--surface)}
.ed-img img{display:block; width:100%; height:100%; object-fit:cover}
.ed-fig .ed-img{aspect-ratio:4/5}
.ed-fig--a{left:0; top:0; width:60%}
.ed-fig--c{right:0; bottom:0; width:44%; z-index:1}
.ed--flip .ed-fig--a{left:auto; right:0}
.ed--flip .ed-fig--c{right:auto; left:0}
@media (max-width:899.98px){
  .ed,.ed--flip{grid-template-columns:1fr; gap:var(--s-6); padding-block:var(--s-8)}
  .ed--flip .ed-text{order:0}
  .ed-figs{min-height:0; display:grid; grid-template-columns:repeat(12,minmax(0,1fr))}
  .ed-fig{position:relative; width:auto; inset:auto}
  .ed-fig--a{grid-column:1 / 10; grid-row:1}
  .ed-fig--c{grid-column:6 / 13; grid-row:2; margin-top:calc(-1 * var(--s-9))}
  .ed--flip .ed-fig--a{grid-column:4 / 13}
  .ed--flip .ed-fig--c{grid-column:1 / 8}
}

/* ---- Home turf (brand, VI), 2026-10-27: the line and the button beside the
   photograph of APEX and DROP on a London court; stacked on a phone */
.turf{display:grid; grid-template-columns:minmax(0,5fr) minmax(0,6fr); gap:var(--s-8); align-items:end}
.turf-fig{margin:0; will-change:transform}
.turf-fig .ed-img{aspect-ratio:4/5}
@media (max-width:899.98px){ .turf{grid-template-columns:1fr; gap:var(--s-6)} }

/* ==================================================================== CART ==
   Desktop: a side panel, because the cart is a place beside the page.
   Mobile: a bottom sheet, because the thumb is at the bottom of the phone
   and a full-height side panel on a 375px screen is just a broken page.

   Hierarchy, loudest to quietest:
     1 subtotal      display 800, the largest thing in the sheet
     2 checkout      full-width, the only filled control
     3 product name  display 800 caps
     4 line price    mono, tabular
     5 sub-line      slate, small
     6 remove        quietest, still a 44px target
   ========================================================================== */
.bag-scrim{position:fixed; inset:0; z-index:150; background:color-mix(in oklab,var(--ink) 42%,transparent);
  opacity:0; transition:opacity var(--t) var(--ease-detent)}
.bag-scrim[data-open]{opacity:1}
.bag-drawer{
  position:fixed; inset:0 0 0 auto; z-index:160; width:min(94vw,540px);
  background:var(--bg); border-left:0; box-shadow:-30px 0 80px -40px color-mix(in oklab,var(--ink) 45%,transparent);
  display:flex; flex-direction:column;
  transform:translateX(100%); transition:transform var(--t-slow) var(--ease-expo);
}
.bag-drawer[data-open]{transform:none}

.bag-grip{display:none}
.bag-head{display:flex; align-items:center; gap:var(--s-3);
  padding:var(--s-7) var(--s-7) var(--s-5)}
.bag-title{margin:0; font-family:var(--display); font-weight:800; font-size:var(--fs-heading); line-height:1; letter-spacing:-.015em; text-transform:uppercase}
.bag-count{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.bag-x{
  margin-left:auto; align-self:center; display:grid; place-items:center; width:44px; height:44px; margin-right:-10px;
  color:var(--fg-2); transition:color var(--t-detent) var(--ease-detent);
}
.bag-x svg{width:18px; height:18px; fill:none; stroke:currentColor; stroke-width:1.4; stroke-linecap:round}
.bag-x:hover{color:var(--fg)}
/* the bag scrolls as one: line items and the bulk notice together, so the
   notice can never take space the checkout button needs */
.bag-body{flex:1; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch}
.bag-lines{padding:var(--s-2) var(--s-7) var(--s-6)}
.bag-empty{margin:0 0 var(--s-5); color:var(--fg-2)}

/* Line-item mark. No product photography exists, so a line carries the same
   drawn silhouette the product pages do, at hairline weight. */
.line-item{display:flex; align-items:flex-start; gap:var(--s-4); min-width:0}
.line-thumb{flex:0 0 auto; width:42px; height:auto; overflow:visible}

.bag-line{
  display:grid; grid-template-columns:clamp(96px,26%,128px) minmax(0,1fr);
  gap:var(--s-5); padding:var(--s-5) 0; align-items:start;
}
.bag-line + .bag-line{border-top:1px solid var(--rule-soft)}
.bag-line .line-photo{width:100%; border-radius:6px}
.bag-info{display:grid; gap:4px; min-width:0}
.bag-top{display:flex; justify-content:space-between; align-items:baseline; gap:var(--s-4)}
.bag-name{font-family:var(--display); font-weight:800; font-size:var(--fs-name); line-height:1.1; letter-spacing:-.01em; text-transform:uppercase; margin:0}
.bag-sub{margin:0; font-family:var(--display); font-size:var(--fs-label); line-height:1.5; color:var(--fg-2); text-transform:none}
.bag-meta{margin:2px 0 0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.bag-price{margin:0; font-family:var(--display); font-weight:700; font-size:var(--fs-body); font-variant-numeric:tabular-nums; white-space:nowrap; text-transform:none; text-align:right}

/* the quantity and remove sit under the details, on the photograph's foot */
.bag-ctl{display:flex; align-items:center; justify-content:space-between; gap:var(--s-4); margin-top:var(--s-3)}
.bag-qty{display:inline-flex; align-items:center; --glass-a:34%; --glass-from:0deg}
.bag-qty button{
  width:44px; height:44px; border-radius:0; font-size:16px; line-height:1; color:var(--fg-2);
  transition:background var(--t-detent) var(--ease-detent), color var(--t-detent) var(--ease-detent),
             transform var(--t-detent) var(--ease-detent);
}
.bag-qty button:hover{background:color-mix(in oklab, var(--ink) 7%, transparent); color:var(--fg)}
.bag-qty button:active{background:color-mix(in oklab, var(--ink) 12%, transparent); transition-duration:.18s}
.bag-qty span{min-width:30px; text-align:center; font-family:var(--mono); font-size:var(--fs-mono); font-variant-numeric:tabular-nums; text-transform:uppercase; letter-spacing:.08em}
.bag-rm{
  display:inline-flex; align-items:center; min-height:44px;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg-2);
  transition:color var(--t-detent) var(--ease-detent);
}
.bag-rm:hover{color:var(--fg)}

.bag-foot{padding:var(--s-5) var(--s-7) var(--s-6); background:var(--bg);
  box-shadow:0 -24px 40px -32px color-mix(in oklab,var(--ink) 30%,transparent)}
.bag-sum{margin:0; display:grid; gap:var(--s-2)}
.bag-row{display:flex; justify-content:space-between; align-items:baseline; gap:var(--s-4); margin:0}
.bag-row dt{font-family:var(--display); font-size:var(--fs-label); color:var(--fg-2); text-transform:none}
.bag-row dd{margin:0; font-family:var(--display); font-size:var(--fs-label); color:var(--fg); text-transform:none}
.bag-row--total{padding-top:var(--s-3); margin-top:var(--s-2); border-top:1px solid var(--rule-soft)}
.bag-row--total dt{font-weight:700; color:var(--fg)}
.bag-row--total dd{font-weight:800; font-size:var(--fs-heading); letter-spacing:-.02em; font-variant-numeric:tabular-nums}
.bag-fine{margin:var(--s-2) 0 0; font-family:var(--display); font-size:var(--fs-label); line-height:1.55; color:var(--fg-2); text-transform:none}
/* complete your set: two glass cards, each going to its page */
.bag-pair{padding:0 var(--s-7) var(--s-6)}
.bag-pair[hidden]{display:none}
.bag-pair-t{margin:0 0 var(--s-3); font-family:var(--mono); font-weight:400; font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.bag-pair-row{display:grid; grid-template-columns:1fr 1fr; gap:var(--s-3)}
.bag-pc{display:grid; grid-template-columns:56px minmax(0,1fr); gap:var(--s-3); align-items:center; padding:10px; color:inherit; --glass-a:20%}
.bag-pc-fig{display:block; aspect-ratio:4/5; overflow:hidden; border-radius:5px; background:var(--surface)}
.bag-pc-fig img{width:100%; height:100%; object-fit:cover; display:block}
.bag-pc-t{display:grid; gap:2px; min-width:0}
.bag-pc-n{font-family:var(--display); font-weight:800; font-size:var(--fs-label); text-transform:uppercase; letter-spacing:.01em}
.bag-pc-p{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.bag-pc-go{font-family:var(--display); font-weight:700; font-size:var(--fs-label); text-transform:none; margin-top:2px}
.bag-pc:hover{--glass-a:34%}
/* 2026-10-15, the user: the product, not a placeholder; no figure said twice;
   the ways to pay; and what happens after Checkout */
.line-photo{flex:0 0 auto; width:56px; aspect-ratio:4/5; overflow:hidden; background:var(--surface)}
.line-photo img{width:100%; height:100%; object-fit:cover; display:block}
.bag-each{display:block; margin-top:4px; font-size:var(--fs-mono); color:var(--fg-2); letter-spacing:.08em; text-align:right}
.bag-assure{margin:var(--s-4) 0 0; font-family:var(--display); font-size:var(--fs-label); line-height:1.5; color:var(--fg-2); text-align:center; text-transform:none}
/* the ways to pay: quiet tonal chips in the mono, never logos, never outlined */
.pay-marks{list-style:none; margin:var(--s-4) 0 0; padding:0; display:flex; flex-wrap:wrap; gap:6px}
.pay-marks li{display:flex; align-items:center; justify-content:center; height:30px; padding:0 10px; --glass-a:22%}
/* the marks: one tone, through a mask, each at its own proportion */
.pay-ico{display:block; height:14px; background:currentColor; color:var(--fg);
  -webkit-mask:var(--m) center/contain no-repeat; mask:var(--m) center/contain no-repeat}
.pay-ico--visa{--m:url(../img/pay/visa.svg); aspect-ratio:28.4/9.2; height:11px}
.pay-ico--mastercard{--m:url(../img/pay/mastercard.svg); aspect-ratio:24.8/16.1; height:16px}
.pay-ico--amex{--m:url(../img/pay/amex.svg); aspect-ratio:21.8/19; height:17px}
.pay-ico--apple-pay{--m:url(../img/pay/apple-pay.svg); aspect-ratio:119/51.3; height:16px}
.pay-ico--google-pay{--m:url(../img/pay/google-pay.svg); aspect-ratio:28/11.3; height:15px}
.pay-ico--shop-pay{--m:url(../img/pay/shop-pay.svg); aspect-ratio:26.4/10.9; height:14px}
.shop-pay{display:flex; align-items:center; flex-wrap:wrap; gap:var(--s-3); margin-top:var(--s-3)}
.shop-pay .pay-marks{margin:0}
.shop-pay-k{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.bag-total{display:flex; justify-content:space-between; align-items:baseline; gap:var(--s-4)}
.bag-total span{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.bag-total b{font-family:var(--display); font-weight:700; font-size:var(--fs-heading); letter-spacing:-.03em; font-variant-numeric:tabular-nums; text-transform:none}
.bag-foot .btn{margin-top:var(--s-5); min-height:52px}   /* 56 until 2026-10-27 */
.bag-foot .pay-marks{justify-content:center; margin-top:var(--s-4)}
.bag-foot .cond{margin-top:var(--s-3)}
/* phone-only pieces of the bag (2026-10-27): hidden on a wider sheet */
.bag-trust,.bag-mini{display:none}
.bag-mini{margin:2px 0 0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}

/* ---- the bulk notice ----------------------------------------------------
   Nine frames is past a personal order, so the bag says so. It opens on
   grid-template-rows, which with clip-path on the button fill is one of the
   only two layout properties this system animates, and it arrives on expo
   like every other surface. Registration marks frame it, because the system
   uses them for anything the brand is asserting is a measured object, and a
   club allocation is one. No colour, no fill, no badge: a hairline draws
   across and the text settles under it. */
.bulk{
  display:grid; grid-template-rows:0fr; opacity:0;
  transition:grid-template-rows var(--t-slow) var(--ease-expo),
             opacity var(--t-slow) var(--ease-expo);
}
.bulk[data-on]{grid-template-rows:1fr; opacity:1}
.bulk-in{overflow:hidden; min-height:0}
.bulk-box{position:relative; margin:0 var(--s-6) var(--s-6); padding:var(--s-5) 0 var(--s-5)}
.bulk-rule{
  display:block; height:1px; background:var(--fg); margin-bottom:var(--s-4);
  transform:scaleX(0); transform-origin:left;
  transition:transform .92s var(--ease-expo) .1s;
}
.bulk[data-on] .bulk-rule{transform:scaleX(1)}
.bulk-h{
  font-family:var(--display); font-weight:800; font-size:var(--fs-name); line-height:1.2;
  letter-spacing:-.015em; margin:var(--s-3) 0 var(--s-2); text-transform:uppercase}
.bulk-p{font-size:var(--fs-label); line-height:1.65; color:var(--fg-2); margin:0; max-width:38ch}
.bulk-p .tbc{font-size:var(--fs-mono); letter-spacing:.08em}
.bulk-p + .bulk-p{margin-top:var(--s-3)}
.bulk-cta{
  display:inline-flex; align-items:center; gap:10px; min-height:44px;
  margin-top:var(--s-2); font-family:var(--display); font-weight:700; font-size:var(--fs-label);
  letter-spacing:.01em; color:var(--fg); text-transform:none}
.bulk-cta .arw{display:inline-block; transition:transform var(--t-detent) var(--ease-detent)}
.bulk-cta:hover .arw{transform:translateX(5px)}
@media (prefers-reduced-motion:reduce){
  .bulk,.bulk-rule,.bulk-cta .arw{transition:none}
  .bulk[data-on] .bulk-rule{transform:none}
}

/* ---- the sheet ---------------------------------------------------------- */
@media (max-width:639.98px){
  .bag-drawer{
    inset:auto 0 0 0; width:auto; max-height:86svh;
    border-left:0; border-top:1px solid var(--rule);
    border-radius:18px 18px 0 0;
    transform:translateY(100%);
  }
  .bag-grip{
    display:block; width:38px; height:3px; border-radius:999px;
    background:var(--rule); margin:10px auto 0; flex:none;
  }
  .bag-head{padding:var(--s-4) var(--s-5) var(--s-3)}
  .bag-lines{padding:var(--s-1) var(--s-5) var(--s-5)}
  .bag-pair{padding:0 var(--s-5) var(--s-5)}
  .bag-foot{padding:var(--s-4) var(--s-5) calc(var(--s-4) + env(safe-area-inset-bottom))}
  .bag-assure{font-size:var(--fs-label)}
  /* 2026-10-27, the user, on a phone: "this section ... is too big. It
     doesn't clearly show the add ons or look premium. Ensure the buttons and
     details are minimised in this view or remove clutter". The sheet's foot
     keeps the total, one line of terms and the button; the delivery row, the
     fine print, the reassurance and the pay marks leave it, the last two for
     a quiet block in the scroll after Complete your set. */
  .bag-foot .bag-row:not(.bag-row--total),.bag-foot .bag-fine,.bag-foot .bag-assure,.bag-foot .pay-marks{display:none}
  .bag-foot .bag-row--total{padding-top:0; margin-top:0; border-top:0}
  .bag-foot .bag-row--total dd{font-size:var(--fs-name)}
  .bag-mini{display:block}
  .bag-foot .btn{margin-top:var(--s-3); min-height:48px}
  .bag-trust{display:block; padding:var(--s-2) var(--s-5) var(--s-5); text-align:center}
  .bag-trust .bag-assure{margin:0; font-size:var(--fs-label)}
  .bag-trust .pay-marks{justify-content:center; margin-top:var(--s-3)}
  .bag-trust .pay-marks li{height:26px; padding:0 8px}
}

/* ================================================================== SHOP ==
   2026-10-07, in Satisfy's collection format at the user's direction. One
   even grid of everything sold: the photograph alone, nothing laid over it,
   and under it the name in Archivo Bold capitals and the price in the mono,
   centred. No buttons on the cards; the product page sells. A filter bar on a
   hairline replaces the section jump. Three across from 900, two below it,
   as Satisfy runs two on a phone. */
.shop-head{display:flex; align-items:flex-end; justify-content:space-between; gap:var(--s-4) var(--s-6);
  flex-wrap:wrap; padding-block:var(--s-7) var(--s-5)}
.shop-title{font-family:var(--display); font-weight:800; text-transform:uppercase;
  font-size:var(--fs-title); line-height:.95; letter-spacing:-.03em; margin:0}
.shop-svc{margin:0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg-2); text-transform:uppercase}
/* under the grid: delivery and returns on the left, the ways to pay opposite */
.pgrid:has(+ .shop-foot){margin-bottom:var(--s-7)}
.shop-foot{display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:var(--s-4) var(--s-6);
  padding-block:var(--s-5); margin-bottom:0; border-top:1px solid var(--rule-soft)}   /* the margin went 2026-10-27: the next section brings its own half of the rhythm */
.shop-foot .shop-pay{margin-top:0}
.shop-bar{display:flex; align-items:center; justify-content:space-between; gap:var(--s-4);
  border-top:1px solid var(--rule); border-bottom:1px solid var(--rule-soft); min-height:56px}
.shop-filter{display:flex; flex-wrap:wrap; gap:0 var(--s-5)}
.shop-filter button{display:inline-flex; align-items:center; min-height:48px; padding:0;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg-2);
  transition:color var(--t-detent) var(--ease-detent); text-transform:uppercase}
.shop-filter button:hover{color:var(--fg)}
.shop-filter button[aria-pressed="true"]{color:var(--fg)}
.shop-count{margin:0 0 0 auto; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg-2); text-transform:uppercase}
.pgrid{list-style:none; margin-block:var(--s-6) var(--s-9); display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--s-8) var(--s-6)}
.pcard[hidden]{display:none}
.pcard-link{display:flex; flex-direction:column; align-items:center; text-align:center; color:inherit}
.pcard-fig{position:relative; display:block; width:100%; aspect-ratio:4/5; overflow:hidden; background:var(--surface)}
.pcard-fig img{width:100%; height:100%; object-fit:cover; display:block; transition:transform var(--t-slow) var(--ease-expo)}
a.pcard-link:hover .pcard-fig img{transform:scale(1.03)}
.pcard-fig--mark{display:grid; place-items:center}
.pcard-fig--mark svg{width:clamp(64px,8vw,104px); height:auto; color:var(--fg-2)}
.pcard-name{font-family:var(--display); font-weight:800; text-transform:uppercase;
  font-size:var(--fs-name); line-height:1.3; letter-spacing:-.01em; margin:var(--s-4) 0 0}
.pcard-price{margin:6px 0 0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg); text-transform:uppercase}
.pcard-link--soon .pcard-price{color:var(--fg-2)}
a.pcard-link:focus-visible{outline:2px solid var(--fg); outline-offset:4px}
@media (max-width:899.98px){ .pgrid{grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s-7) var(--s-4)} }
/* complete your order: the shop's cards, four across, two on a phone */
.complete{padding-block:var(--sec-h) 0; border-top:1px solid var(--rule-soft); margin-top:var(--sec-h)}
.complete .pgrid{margin-block:var(--s-6) var(--s-8); grid-template-columns:repeat(4,minmax(0,1fr)); gap:var(--s-7) var(--s-5)}
@media (max-width:899.98px){ .complete .pgrid{grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s-7) var(--s-4)} }
@media (max-width:639.98px){ .shop-head{padding-block:var(--s-6) var(--s-4)} .shop-svc{width:100%}
  .shop-filter{gap:0 var(--s-4)} .shop-count{display:none} }
.shop-count{white-space:nowrap}
@media (prefers-reduced-motion:reduce){ .pcard-fig img{transition:none} }

/* ---- carousels ------------------------------------------------------------
   2026-10-07, after Satisfy's galleries, at the user's direction. A native
   scroll-snap track: a finger, a trackpad and the keyboard move it with no
   script, and the script only adds the arrows, the count and the marks. The
   marks are squares, and the one in view turns to a diamond: no underline,
   no pill. Cut-out stills sit on the cream figure ground; the photograph
   fills its frame. Nothing is written on any slide. */
.car{position:relative}
.car-track{display:flex; overflow-x:auto; scroll-snap-type:x mandatory; scrollbar-width:none;
  overscroll-behavior-x:contain; -webkit-overflow-scrolling:touch}
.car-track::-webkit-scrollbar{display:none}
.car-track:focus-visible{outline:2px solid var(--fg); outline-offset:3px}
.car-slide{position:relative; flex:0 0 100%; aspect-ratio:4/5; overflow:hidden; scroll-snap-align:start;
  background:var(--surface); display:block}
.car-slide img{width:100%; height:100%; object-fit:cover; display:block}
.car-slide--cut img{object-fit:contain; padding:7% 12%}
.car-ctl{display:flex; align-items:center; gap:var(--s-4); margin-top:var(--s-3)}
.car-count{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg-2);
  font-variant-numeric:tabular-nums; min-width:5ch; text-transform:uppercase}
.car-dots{display:flex; align-items:center; gap:8px}
.car-dots i{display:block; width:6px; height:6px; background:var(--rule);
  transition:background var(--t-detent) var(--ease-detent), transform var(--t-detent) var(--ease-detent)}
.car-dots i[data-on]{background:var(--fg); transform:rotate(45deg)}
.car-btn{display:inline-grid; place-items:center; width:44px; height:44px; color:var(--fg-2);
  transition:color var(--t-detent) var(--ease-detent), opacity var(--t-detent) var(--ease-detent)}
.car-btn:hover{color:var(--fg)}
.car-btn svg{width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:1.4; stroke-linecap:round; stroke-linejoin:round}
.car-ctl .car-btn:first-of-type{margin-left:auto}
/* without script there is nothing for the controls to drive */
.car:not([data-car-on]) :is(.car-ctl,.car-btn,.car-dots){display:none}
/* on a shop card the marks sit centred under the photograph, and the arrows
   come up on the photograph's edges only for a pointer that can hover */
.pcard-car .car-dots{justify-content:center; margin-top:var(--s-3); min-height:6px}
/* 2026-10-27, the user: "keep just the arrows, no boxes": a bare chevron in
   Paper, a soft shade under it so it reads on a light photograph or a dark */
.pcard-car .car-btn{position:absolute; top:calc(50% - 22px - 9px); z-index:2; opacity:0;
  background:none; color:var(--bg); filter:drop-shadow(0 1px 3px rgba(18,23,29,.5))}
.pcard-car .car-btn svg{width:22px; height:22px; stroke-width:1.6}
.pcard-car .car-btn:hover{color:#fff}
.pcard-car .car-btn--prev{left:var(--s-3)}
.pcard-car .car-btn--next{right:var(--s-3)}
.pcard-car:hover .car-btn,.pcard-car .car-btn:focus-visible{opacity:1}
@media (hover:none){ .pcard-car .car-btn{display:none} }
.pcard-car + .pcard-link .pcard-name{margin-top:var(--s-3)}
/* a single-photograph card has no marks; its name drops by the same height
   so every name and price in a row sits on one line */
.pcard-link > .pcard-fig + .pcard-name{margin-top:calc(var(--s-3) * 2 + 6px)}
@media (prefers-reduced-motion:reduce){ .car-dots i,.car-btn{transition:none} }

/* ---- the shop rail (home) -------------------------------------------------
   The shop's cards on one row that scrolls sideways and snaps, with the way
   to the full shop and two arrows for a pointer. Cards are a quarter of the
   page on a wide screen, two-thirds on a phone so the next one shows. */
/* 2026-10-09, the user: the phone showed all five squeezed into one screen
   (minmax(0,68%) let every column shrink to fit) and asked for "a full rail
   so users can interact. Editorial style." Columns now have a fixed share of
   the track, so the row always overflows; the track bleeds to the right edge
   of the screen and starts on the page's left margin; captions are set like
   an index. (The dropped every-second card went 2026-10-13: "align the
   collection cards, no up & down, all inline.") A hairline meter and a count say where the row is. A mouse
   can drag the row; touch and the keyboard scroll natively. */
.rail{--rail-in:max(var(--gutter), calc((100vw - var(--page)) / 2 + var(--gutter)));
  padding-block:var(--sec-h)}
.rail-head{display:flex; align-items:flex-end; justify-content:space-between; gap:var(--s-4); flex-wrap:wrap}
.rail-ctl{display:flex; align-items:center; gap:var(--s-2)}
.rail-all{display:inline-flex; gap:8px; margin-right:var(--s-4); font-family:var(--mono); font-size:var(--fs-mono);
  letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2); transition:color var(--t-detent) var(--ease-detent)}
.rail-all:hover{color:var(--fg)}
.rail-all .arw{transition:transform var(--t-detent) var(--ease-detent)}
.rail-all:hover .arw{transform:translateX(3px)}
.rail-track{list-style:none; margin:var(--s-7) 0 0; padding:0 var(--rail-in) var(--s-2);
  display:grid; grid-auto-flow:column; grid-auto-columns:clamp(260px, 27vw, 420px);
  gap:clamp(20px, 3vw, 48px); align-items:start;
  overflow-x:auto; scroll-snap-type:x mandatory; scroll-padding-inline:var(--rail-in);
  scrollbar-width:none; overscroll-behavior-x:contain; cursor:grab}
.rail-track::-webkit-scrollbar{display:none}
.rail-track:focus-visible{outline:2px solid var(--fg); outline-offset:-2px}
.rail-track[data-drag]{cursor:grabbing; scroll-snap-type:none; user-select:none}
.rail-item{scroll-snap-align:start}
.rail-link{display:block; color:inherit}
.rail-fig{position:relative; display:block; aspect-ratio:4/5; overflow:hidden; background:var(--surface)}
.rail-fig img{width:100%; height:100%; object-fit:cover; display:block; -webkit-user-drag:none;
  transition:transform 1.2s var(--ease-expo), filter var(--t-slow) var(--ease-expo)}
.rail-link:hover .rail-fig img{transform:scale(1.045)}
.rail-fig--mark{display:grid; place-items:center; color:var(--fg-2);
  background:radial-gradient(120% 90% at 30% 20%, var(--shell) 0%, var(--cream) 55%, var(--almond) 140%)}
.rail-fig--mark svg{width:clamp(56px,6vw,88px); height:auto; transition:transform 1.2s var(--ease-expo)}
.rail-link:hover .rail-fig--mark svg{transform:translateY(-6px)}
.rail-cap{display:grid; grid-template-columns:auto 1fr auto; column-gap:var(--s-4); row-gap:6px;
  align-items:baseline; margin-top:var(--s-4)}
.rail-n{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg-2); font-variant-numeric:tabular-nums}
.rail-name{margin:0; font-family:var(--display); font-weight:800; text-transform:uppercase;
  font-size:var(--fs-name); line-height:1.1; letter-spacing:-.01em}
.rail-price{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; white-space:nowrap}
.rail-sub{grid-column:2 / -1; font-family:var(--display); font-size:var(--fs-label); line-height:1.5;
  color:var(--fg-2); text-transform:none; transition:color var(--t-detent) var(--ease-detent)}
.rail-link:hover .rail-sub{color:var(--fg)}
.rail-link:focus-visible{outline:2px solid var(--fg); outline-offset:4px}
.rail-foot{display:flex; align-items:center; gap:var(--s-5); margin-top:var(--s-6)}
.rail-meter{position:relative; flex:1; height:1px; background:var(--rule-soft); overflow:hidden}
.rail-meter i{position:absolute; left:0; top:0; bottom:0; width:20%; background:var(--fg); transform-origin:left center}
.rail-count{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--fg-2); font-variant-numeric:tabular-nums}
@media (max-width:899.98px){ .rail-track{grid-auto-columns:46vw} }
@media (max-width:639.98px){
  .rail-track{grid-auto-columns:76vw; gap:var(--s-4)}
  .rail-ctl .car-btn{display:none} .rail-all{margin-right:0}
}
@media (hover:none){ .rail-track{cursor:auto} }
@media (prefers-reduced-motion:reduce){ .rail-fig img,.rail-fig--mark svg{transition:none} }

/* The supplied marks. Both ship with pure-black fills and #D9D9D9 corners;
   they are recoloured to currentColor and a hairline token on the way in,
   so nothing on the page carries a colour from outside the palette. */
.krs-badge{display:block; margin:0; width:auto; height:56px; color:var(--fg)}
/* What the frame carries, as marks rather than a list. Hairlines only, and
   each one draws the thing it names rather than an icon standing in for it. */
.tech-row{list-style:none; display:flex; flex-wrap:wrap; align-items:flex-start;
  gap:var(--s-5) var(--s-6); margin:var(--s-5) 0 0; padding:0}
.tech-row li{flex:0 0 auto}
/* every badge is one lockup on the KRS format: the mark in the upper 22 units
   of a 28-unit box, the wordmark on the baseline beneath it, all rendered at
   the same height so the four read as one set */
.tech-row .krs-badge{height:56px; width:auto; max-width:none}
/* A badge carrying a second line runs six units deeper and is drawn six units
   taller, so every badge in the row is rendered at the same two pixels per
   unit. Same mark size, same type size, and one acronym baseline across the
   set — only `head` hangs below it. */
.tech-row .krs-badge--tall{height:68px}
/* The three later badges set their wordmark as real text rather than outlined
   glyphs: it reaches screen readers and search, it is what DESIGN.md asks for,
   and it is the only way ACE could be typeset or `head` given its own weight.
   KRS keeps the supplied artwork. Sized about a tenth below the outlines it
   replaces. */
.badge-wm{
  /* 600 and wide, not 800: KRS is the supplied monoline face, and a heavy
     grotesque beside it reads as a different set. Cap height lands ~4.5
     against its 5.0, which is the tenth off that was asked for. */
  font-family:var(--display); font-weight:600; font-size:6.2px;
  letter-spacing:.62px; fill:currentColor; text-anchor:middle; text-transform:none}
.badge-sub{
  font-family:var(--display); font-weight:400; font-size:4.9px;
  letter-spacing:.5px; fill:currentColor; text-anchor:middle; text-transform:none}
/* ---- shop sections ------------------------------------------------------
   Frames are the range and accessories sit under them. A second tile grid
   would read as equal weight, so the accessories are a ruled list: same
   system, quieter voice. */
.shop-jump{
  display:flex; gap:var(--s-6); align-items:center;
  border-top:1px solid var(--rule-soft);
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
}
.shop-jump a{
  display:inline-flex; align-items:center; min-height:44px; color:var(--fg-2);
  transition:color var(--t-detent) var(--ease-detent);
}
.shop-jump a:hover{color:var(--fg)}
/* ---- the cookie notice --------------------------------------------------
   A bar on a hairline, not a modal and not a scrim: it never blocks the page,
   and both choices are one tap with nothing behind a settings sheet. Sits
   above the buybar and below the cart, so an open drawer covers it. */
.cookie{
  position:fixed; inset:auto 0 0 0; z-index:120;
  background:var(--bg); border-top:1px solid var(--rule);
  transform:translateY(101%); transition:transform var(--t-slow) var(--ease-expo);
}
.cookie[data-open]{transform:none}
.cookie-in{
  max-width:var(--page); margin-inline:auto;
  padding:var(--s-5) var(--gutter) calc(var(--s-5) + env(safe-area-inset-bottom));
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:var(--s-4) var(--s-6);
}
.cookie-t{margin:0; font-size:var(--fs-label); line-height:1.65; color:var(--fg-2); max-width:56ch}
.cookie-acts{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s-3) var(--s-4)}
.cookie-btn{min-height:44px; padding:11px 22px; font-size:var(--fs-label)}
.cookie-link{
  display:inline-flex; align-items:center; min-height:44px;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg-2); transition:color var(--t-detent) var(--ease-detent);
}
.cookie-link:hover{color:var(--fg)}
@media (max-width:639.98px){
  .cookie-in{flex-direction:column; align-items:stretch; gap:var(--s-4)}
  .cookie-acts{justify-content:flex-start}
  .cookie-btn{flex:1 1 auto; justify-content:center}
  .cookie-link{width:100%}
}
@media (prefers-reduced-motion:reduce){ .cookie{transition:none} }

/* ---- the factors: the scan ---------------------------------------------------
   2026-10-15, the user, after a reference of a shoe under refractive frosted
   glass: "Make the whole ... section frosted glass ... with a sort of
   refractive blur effect. Only when the mouse [passes] over the particular
   areas ... should it uncover and highlight that area with a minimal zoom."
   Then: tappable on a phone; the ground in the site's own colours rather than
   dark; the hotspots in the site's theme; the note over the image as an
   exception to The Clean Plate Rule (this section only; the mono readouts
   went the same day); wide-spaced perforations in place of the reference's
   grid. DROP is cut from the studio shot (specimen.webp) and drawn to a
   canvas twice: once deeply frosted (light dispersed at three radii) and once
   sharp, shown only through a stepped window at the point being scanned. */
.scan{--scan-ground:color-mix(in oklab, var(--taupe) 52%, var(--paper));
  position:relative; isolation:isolate; padding-block:var(--s-7) var(--sec-h)}
/* the perforations are holes, not marks (the user, 2026-10-15: "the
   perforations should show the BG not be filled"): the ground is a sheet
   with wide-spaced holes punched through it, so the page shows through */
.scan::before{content:""; position:absolute; inset:0; z-index:-1; background:var(--scan-ground);
  -webkit-mask:var(--perf); mask:var(--perf)}
.scan-field{position:relative; max-width:var(--page); margin-inline:auto;
  height:clamp(560px, 88svh, 920px); touch-action:pan-y}
.scan-field canvas{position:absolute; inset:0; width:100%; height:100%; display:block}
/* the hotspots: an Ink disc with a Paper crosshair, a slow ring leaving it;
   chosen, the disc turns Paper and the cross Ink */
.scan-pts{position:absolute; inset:0; z-index:3; pointer-events:none}
.scan-pt{position:absolute; width:44px; height:44px; margin:-22px 0 0 -22px; pointer-events:auto;
  display:grid; place-items:center; border-radius:50%; cursor:crosshair}
.scan-pt::before{content:""; position:absolute; width:22px; height:22px; border-radius:50%;
  background:var(--ink); transition:background-color var(--t-detent) var(--ease-detent), transform var(--t-detent) var(--ease-detent)}
.scan-pt::after{content:""; position:absolute; width:44px; height:44px; border-radius:50%;
  border:1px solid color-mix(in oklab, var(--ink) 55%, transparent); opacity:0;
  animation:scan-ring 3.2s var(--ease-expo) infinite}
.scan-pt i{position:relative; width:12px; height:12px; pointer-events:none}
.scan-pt i::before,.scan-pt i::after{content:""; position:absolute; background:var(--paper);
  transition:background-color var(--t-detent) var(--ease-detent)}
.scan-pt i::before{left:50%; top:0; bottom:0; width:1px; transform:translateX(-50%)}
.scan-pt i::after{top:50%; left:0; right:0; height:1px; transform:translateY(-50%)}
.scan-pt[aria-expanded="true"]::before{background:var(--paper); transform:scale(1.18)}
.scan-pt[aria-expanded="true"] i::before,.scan-pt[aria-expanded="true"] i::after{background:var(--ink)}
.scan-pt[aria-expanded="true"]::after{animation:none; opacity:0}
.scan-pt:focus-visible{outline:2px solid var(--ink); outline-offset:2px}
@keyframes scan-ring{0%{transform:scale(.5); opacity:.7} 100%{transform:scale(1.15); opacity:0}}
/* the note beside the open window */
.scan-note{position:absolute; z-index:2; width:min(300px, 34vw); pointer-events:none;
  opacity:0; transform:translateY(6px); transition:opacity .32s var(--ease-detent), transform .32s var(--ease-detent)}
.scan-note[data-on]{opacity:1; transform:none}
.scan-note-k{margin:0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em;
  text-transform:uppercase; color:var(--fg); font-variant-numeric:tabular-nums}
.scan-note-v{margin:8px 0 0; font-family:var(--display); font-size:var(--fs-label); line-height:1.6;
  color:var(--fg); text-transform:none}
/* the note 10% larger on desktop (2026-10-29, the user: "increase the font
   size on desktop by 10%"), its measure widened to match */
@media (min-width:900px){
  .scan-note{width:min(330px, 34vw)}
  .scan-note-k{font-size:calc(var(--fs-mono) * 1.1)}
  .scan-note-v{font-size:calc(var(--fs-label) * 1.1)}
}
@media (max-width:899.98px){
  .scan{padding-block:var(--s-6) var(--s-7)}
  .scan-field{height:auto; aspect-ratio:2/3; max-height:none}
  .scan-note{left:var(--gutter)!important; right:var(--gutter); top:auto!important; bottom:var(--s-5); width:auto}
}
@media (prefers-reduced-motion:reduce){
  .scan-pt::after{animation:none}
  .scan-note{transition:none}
}

/* ---- the technology film (shop) ---------------------------------------------
   2026-10-15, the user, over several rounds: the APEX film scrubbed by the
   scroll, cut out so the racquet stands on the page itself, almost
   page-bleed; glass cards overlaid in the shot as it plays, "slightly more
   rectangular", the site's frosted glass with a light champagne-grey veil, no
   shadow and no edge, perforations punched clean through so the shot shows in
   each hole; a hairline leader from each card to its part, the card set a
   little away from the part. The cards travel with the film through their
   beat; on a phone they sit in the clear space above the film. The two whip
   moves in the source cut behind a frost-blur pulse rather than dissolve. */
.tf-head{padding-block:var(--sec-h) var(--s-5)}
.tf{position:relative}
.tf-sticky{position:sticky; top:0; height:100svh; overflow:hidden; isolation:isolate}
/* 2026-10-25, the user: "too much white space in this section. Add an epic
   but subtle bg glow which doesn't interfere with the racquet". A halo of
   the champagne glow behind the film, a breath of the warm coral low in it,
   on its own layer under the canvas, so it lights the stage round the
   racquet and never sits on it, a card or a leader. It breathes slowly;
   reduced motion holds it still. */
.tf-sticky::before{
  content:""; position:absolute; inset:-8%; z-index:0; pointer-events:none;
  background:
    radial-gradient(ellipse min(44vw,600px) min(40svh,440px) at 50% 52%,
      color-mix(in oklab, var(--glow) 58%, transparent) 0%,
      color-mix(in oklab, var(--glow) 26%, transparent) 42%,
      transparent 100%),
    radial-gradient(ellipse min(30vw,420px) min(22svh,260px) at 54% 64%,
      color-mix(in oklab, var(--glow-hot) 20%, transparent) 0%,
      transparent 100%),
    radial-gradient(ellipse min(70vw,980px) min(60svh,640px) at 50% 50%,
      color-mix(in oklab, var(--glow) 14%, transparent) 0%,
      transparent 100%);
  animation:tf-breathe 9s ease-in-out infinite alternate;
}
@keyframes tf-breathe{from{opacity:.72; transform:scale(.97)} to{opacity:1; transform:scale(1.03)}}
@media (max-width:719.98px){
  /* on a phone the halo is taller than wide, to fill the stage round the frame */
  .tf-sticky::before{
    background:
      radial-gradient(ellipse 72vw 34svh at 50% 52%,
        color-mix(in oklab, var(--glow) 58%, transparent) 0%,
        color-mix(in oklab, var(--glow) 26%, transparent) 45%,
        transparent 100%),
      radial-gradient(ellipse 48vw 18svh at 56% 62%,
        color-mix(in oklab, var(--glow-hot) 20%, transparent) 0%,
        transparent 100%),
      radial-gradient(ellipse 110vw 52svh at 50% 50%,
        color-mix(in oklab, var(--glow) 16%, transparent) 0%,
        transparent 100%)}
}
@media (prefers-reduced-motion:reduce){ .tf-sticky::before{animation:none} }
.tf canvas{position:absolute; inset:0; z-index:1; width:100%; height:100%; display:block}
.tf-still{display:none}
.tf-card{position:absolute; z-index:3; pointer-events:none; isolation:isolate;
  width:min(400px, calc(100vw - 32px)); padding:16px 22px 18px; display:grid; gap:6px;
  background:color-mix(in oklab, color-mix(in oklab, var(--glow) 45%, var(--taupe)) 42%, transparent);
  -webkit-backdrop-filter:blur(18px) saturate(1.3); backdrop-filter:blur(18px) saturate(1.3);
  --holes:var(--perf);
  -webkit-mask:linear-gradient(90deg,#000 62%,transparent 86%), var(--holes);
  -webkit-mask-composite:source-over;
  mask:linear-gradient(90deg,#000 62%,transparent 86%), var(--holes);
  mask-composite:add;
  opacity:0; filter:blur(8px); transform:translate3d(0,12px,0);
  transition:opacity .55s var(--ease-expo), filter .55s var(--ease-expo), transform .7s var(--ease-expo)}
.tf-card[data-on]{opacity:1; filter:none; transform:none}
.tf-kick{margin:0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.tf-headline{display:flex; align-items:baseline; gap:12px; flex-wrap:wrap}
.tf-t{margin:0; font-family:var(--display); font-weight:800; text-transform:none; font-size:var(--fs-name); letter-spacing:.01em; line-height:1; color:var(--fg)}
.tf-n{margin:0; font-family:var(--display); font-size:var(--fs-label); color:var(--fg-2); text-transform:none}
.tf-v{margin:2px 0 0; font-family:var(--display); font-size:var(--fs-label); line-height:1.55; max-width:30ch; color:var(--fg); text-transform:none}
.tf-lead{position:absolute; inset:0; width:100%; height:100%; z-index:2; pointer-events:none; overflow:visible}
.tf-lead path{fill:none; stroke:var(--fg); stroke-width:1.6; opacity:.7}   /* 2026-10-15: "slightly thicker" */
.tf-lead .ring{fill:color-mix(in oklab, var(--bg) 70%, transparent); stroke:var(--fg); stroke-width:1.6; opacity:0}
.tf-lead .dot{fill:var(--fg); opacity:0}
.tf-bar{position:absolute; left:8px; right:8px; bottom:0; height:1px; background:var(--rule-soft); overflow:hidden; z-index:2}
.tf-bar i{position:absolute; inset:0; background:var(--fg); transform-origin:left; transform:scaleX(0)}
/* the key: what a screen reader reads; shown as the cards themselves only
   where the film does not run */
.tf-key{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; margin:0; padding:0; list-style:none}
.tf-foot{padding-block:var(--s-6) var(--sec-h)}
@media (max-width:640px){
  .tf-card{width:min(300px, calc(100vw - 32px)); padding:12px 16px 14px}
}
/* no script, or reduced motion: a still of the whole racquet and the three
   technologies under it, in the same glass */
.no-js .tf-sticky,.tf[data-static] .tf-sticky{position:relative; height:auto}
.no-js .tf canvas,.tf[data-static] canvas,.no-js .tf-card,.tf[data-static] .tf-card,
.no-js .tf-lead,.tf[data-static] .tf-lead,.no-js .tf-bar,.tf[data-static] .tf-bar{display:none}
.no-js .tf-still,.tf[data-static] .tf-still{display:block; width:100%; max-width:1100px; height:auto; margin:0 auto}
/* DROP's turn is a tall frame: its still sits at a racquet's size */
.tf--turn .tf-still{max-width:min(420px,80%)}
.no-js .tf-key,.tf[data-static] .tf-key{position:static; width:auto; height:auto; overflow:visible; clip:auto; white-space:normal;
  display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr)); gap:var(--s-4);
  max-width:var(--page); margin:var(--s-6) auto 0; padding-inline:var(--gutter)}
.no-js .tf-key li,.tf[data-static] .tf-key li{padding:16px 20px 18px; display:grid; gap:6px;
  background:color-mix(in oklab, color-mix(in oklab, var(--glow) 45%, var(--taupe)) 30%, var(--bg))}
.tf-key-k{margin:0; font-family:var(--display); font-weight:800; text-transform:none; font-size:var(--fs-name); line-height:1}
.tf-key-n{margin:0; font-size:var(--fs-label); color:var(--fg-2)}
.tf-key-v{margin:0; font-size:var(--fs-label); line-height:1.55}

/* ---- the trade enquiry --------------------------------------------------
   One control, and the address beside it. No form, so no field styling and
   nothing on the page that needs a backend to be honest. */
.trade-open{display:flex; flex-wrap:wrap; align-items:center; gap:var(--s-4) var(--s-5); margin:var(--s-7) 0 var(--s-4)}
.trade-open .cond{margin:0; max-width:44ch}
.doc--form .roster{max-width:60ch}
.doc [data-trademail][aria-disabled="true"]{pointer-events:none}

/* ---- the revolving frame -------------------------------------------------
   Replaces the three static gallery stills on the product pages. SVG rather
   than canvas, so it stays sharp at any size and its paths scale with the box;
   the loop only touches frames that are on screen. */
.spinwrap{position:relative; aspect-ratio:4/5; display:grid; place-items:center; overflow:hidden}
.spinwrap .fr-svg{width:auto; height:88%; max-width:100%; display:block}
/* the photographed turntable fills the figure; the frames carry their own margin */
.spinwrap .fr-seq{position:absolute; inset:0; width:100%; height:100%; display:block}
/* 2026-09-27: the product pages open on a photograph rather than the spin.
   It fills the figure edge to edge (both are shot at its 4:5), and the
   label and registration corners go to Paper over the dark lower corner —
   measured 6.9:1 (APEX) and 8.8:1 (DROP) where the label sits. */
.fig--photo img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block}

.acc-buy{display:flex; flex-direction:column; align-items:flex-end; gap:var(--s-3)}
.acc-price{font-family:var(--display); font-weight:700; font-size:var(--fs-name); letter-spacing:-.02em; margin:0; text-transform:none}
.acc-buy .btn{min-height:44px; padding:11px 22px; font-size:var(--fs-label)}
.acc-buy .acc-note{white-space:nowrap}

/* ---- the product page ------------------------------------------------------
   2026-09-28: one layout for every product page. It lived inline on APEX and
   DROP, and the accessory pages had their own: a 320px image column pinned to
   the left with the buy column running away from it, which read as a smaller
   and different kind of page. Now the grips, bags and ORB pages use the
   racquet grid exactly, so the image is the same size and sticks the same way.
   The gallery sticks and the detail scrolls past it: the buy column carries
   description, attributes, service and the specialist, so a static image left
   a half-page void beside it. */
.pdp{display:grid; grid-template-columns:minmax(0,1.12fr) minmax(400px,.88fr); gap:var(--s-8); padding-top:var(--s-5)}
/* The sticky lives on an inner wrapper: a grid item set to align-self:start
   shrinks to its content, which leaves sticky no room to travel. */
.pdp-media__in{position:sticky; top:calc(var(--header-h) + var(--s-5))}
.pdp .buy{align-self:start}
/* the product name on a product page: Archivo Bold, 28 to 36px as Satisfy
   sets its product title, not the 77px editorial title */
.pdp .buy .page-title{font-size:var(--fs-title); line-height:1.0; letter-spacing:-.025em}
/* 2026-10-08, by the user: the racquet product pages carry the supplied name
   lockups again rather than set type, as supplied, corner brackets included
   ("it's missing the corner bits"). The one place The Clean Plate Rule gives
   way: the brackets are part of the lockup artwork, not a frame laid over a
   photograph. Sized as they were before 2026-10-07. */
.page-title.model{line-height:0}
.model-mark{display:block; width:100%; height:auto; color:var(--fg)}
.page-title.model .model-mark{max-width:min(100%,286px)}
/* tablet: the image at roughly 45%, the buy column at 55% (ledger #02) */
@media (max-width:1199.98px){ .pdp{grid-template-columns:minmax(0,.82fr) minmax(0,1fr); gap:var(--s-6)} }
/* below 640 there is no room for two columns of anything */
@media (max-width:639.98px){ .pdp{grid-template-columns:1fr} .pdp-media__in{position:static} }
/* the end of an accessory page is the way to the other two */
.pdp + .shop-jump{margin-top:var(--s-9)}
/* bags are not photographed yet: the drawn mark stands in, on the figure ground */
.acc-mark.acc-mark--lg svg{width:clamp(96px,12vw,150px); height:auto; color:var(--fg-2)}
.acc-more{margin-top:var(--s-2)}

/* ---- the plan chooser ----------------------------------------------------
   Two options shown together rather than a switch, because the point is the
   comparison: what each one costs and what it commits to. The selected one
   takes an ink rule; nothing else changes, so the difference a reader is
   weighing is the price, not the styling. */
/* 190px, not 150: at 150 two cards still fit a 335px phone column, which left
   the frequency select 106px wide when "Every three months" needs 147px to
   render — two of its four options were clipped on every phone. At 190 the
   pair stacks below about 400px and the select gets the full column. */
.plan{border:0; margin:var(--s-5) 0 0; padding:0; display:grid; gap:var(--s-3);
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
/* 2026-10-27, the user: "change the subscription cards with sand colour
   perforations. Make them more frosted glass like and less squarish. It
   should feel premium so add the controls separately below". Rounder (16px),
   more air, a deeper frost at a lighter tint; the perforations are Sand dots
   rather than holes; the chosen card's edge deepens from Sand, slowly. The
   delivery controls left the card for a row of their own beneath both. */
.plan-opt{
  position:relative; display:grid; gap:4px; cursor:pointer;
  padding:var(--s-5) var(--s-5) var(--s-5) 46px; --glass-r:16px;
  --glass-a:14%;
  transition:outline-color .6s var(--ease-detent);
}
.plan-opt:hover{--glass-a:22%}
.plan-opt:has(input:checked){--glass-a:30%; outline-color:color-mix(in oklab, var(--ink) 45%, var(--sand))}
.plan-opt input{position:absolute; left:18px; top:calc(var(--s-5) + 2px); margin:0;
  width:16px; height:16px; accent-color:var(--fg)}
.plan-opt:has(input:focus-visible){outline:2px solid var(--fg); outline-offset:2px}
/* the card's own glass: deeper frost, no holes cut; the perforation is drawn
   in Sand on the far side instead */
.plan-opt::after{
  -webkit-backdrop-filter:blur(22px) saturate(1.4); backdrop-filter:blur(22px) saturate(1.4);
  -webkit-mask:none; mask:none}
.plan-opt::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none; border-radius:inherit;
  background:radial-gradient(circle, var(--sand) 0 2.2px, transparent 2.7px) 18px 18px / 36px 36px;
  -webkit-mask:linear-gradient(90deg, transparent 55%, #000 92%); mask:linear-gradient(90deg, transparent 55%, #000 92%)}
.plan-k{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.plan-v{font-family:var(--display); font-weight:700; font-size:var(--fs-name); letter-spacing:-.02em; text-transform:none; margin-top:2px}
.plan-n{font-size:var(--fs-label); line-height:1.5; color:var(--fg-2); font-family:var(--display); text-transform:none}
.plan-save{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg)}
/* Which one, before how often: the grade cards use the same two-up comparison
   as the plan cards, so the page asks its two questions the same way twice. */
/* Which ball, as a segmented control. Two names sharing one rule reads as a
   single choice; two stacked cards read as two products. The note underneath
   carries whichever is selected, so the difference is stated once in a
   sentence rather than twice in furniture. */
.grade{border:0; margin:var(--s-4) 0 0; padding:0}
.grade-seg{display:grid; grid-template-columns:1fr 1fr; gap:2px; background:var(--bg)}
.grade-opt{
  position:relative; display:grid; gap:3px; cursor:pointer; text-align:center;
  padding:var(--s-3) var(--s-3);
  transition:background var(--t-detent) var(--ease-detent), color var(--t-detent) var(--ease-detent);
}
.grade-opt{transition:background-color .5s var(--ease-detent), color .5s var(--ease-detent)}
.grade-opt:hover:not(:has(input:checked)){background:color-mix(in oklab, var(--cream) 85%, var(--ink))}
.grade-opt input{position:absolute; opacity:0; width:1px; height:1px; margin:0}
.grade-opt:has(input:checked){background:var(--fg); color:var(--bg)}
.grade-opt:has(input:focus-visible){outline:2px solid var(--fg); outline-offset:2px}
.grade-n{font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.06em; text-transform:uppercase}
.grade-p{font-family:var(--mono); font-size:var(--fs-mono); font-variant-numeric:tabular-nums; text-transform:uppercase; letter-spacing:.08em}
.grade-note{font-size:var(--fs-label); line-height:1.6; color:var(--fg-2); margin:var(--s-3) 0 0}

/* ---- reviews -------------------------------------------------------------
   Quiet by design. It states the absence in the same register as the rest of
   the buy column and does not dress it up as a feature. */
.revs{margin-top:var(--s-6); padding-top:var(--s-5); border-top:1px solid var(--rule-soft)}
.rev-k{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg-2); margin:0}
.rev-none{font-family:var(--display); font-weight:700; font-size:var(--fs-body); margin:var(--s-3) 0 0; text-transform:none}
.rev-note{font-size:var(--fs-label); line-height:1.6; color:var(--fg-2); margin:var(--s-2) 0 0; max-width:44ch}
.rev{margin:var(--s-4) 0 0}
.rev blockquote{font-size:var(--fs-label); line-height:1.7; margin:0; font-family:var(--display); text-transform:none}
.rev figcaption{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em;
  text-transform:uppercase; color:var(--fg-2); margin-top:var(--s-2)}
/* the delivery controls, beneath both cards since 2026-10-27 (they sat inside
   the plan card): a label, the unit switch and the stepper on one line where
   there is room. While One-off is chosen they rest at half strength, and
   touching one chooses the plan, so nothing has to be ticked twice. */
.plan-ctl{margin-top:var(--s-4); display:grid; gap:var(--s-2); transition:opacity .5s var(--ease-detent)}
.plan-ctl-k{margin:0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
[data-accbuy]:has(.plan input[value="once"]:checked) .plan-ctl{opacity:.55}
.plan-ctl .freq{grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); align-items:stretch}
/* 2026-10-16: the unit as a two-way switch, the count as a stepper that reads
   as its sentence. Ink marks the chosen unit, the glass the other; the
   stepper is the quantity counter's shape. */
.freq{display:grid; gap:6px}
.freq-seg{display:grid; grid-template-columns:1fr 1fr; gap:2px}
.freq-u{min-height:40px; padding:0 10px; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em;
  text-transform:uppercase; color:var(--fg-2);
  transition:color .5s var(--ease-detent), background-color .5s var(--ease-detent)}
.freq-u:hover{color:var(--fg)}
.freq-u[aria-pressed="true"]{background:var(--fg); color:var(--bg)}
.freq-step{display:grid; grid-template-columns:40px 1fr 40px; align-items:center; min-height:40px; overflow:hidden}
.freq-b{height:40px; font-size:16px; line-height:1; color:var(--fg-2);
  transition:background-color .5s var(--ease-detent), color .5s var(--ease-detent)}
.freq-b:hover:not(:disabled){background:color-mix(in oklab, var(--ink) 7%, transparent); color:var(--fg)}
.freq-b:disabled{opacity:.3; cursor:default}
.freq-v{text-align:center; font-family:var(--display); font-weight:700; font-size:var(--fs-label); text-transform:none; color:var(--fg)}
.freq button:focus-visible{outline:2px solid var(--fg); outline-offset:2px}

/* ---- the quantity counter ------------------------------------------------
   The same 44px controls the bag uses, so a racquet page and an accessory
   page reach the cart the same way. */
.qty{display:inline-flex; align-items:center; --glass-a:34%}
.qty button{
  width:44px; height:44px; border-radius:0; font-size:16px; line-height:1; color:var(--fg-2);
  transition:background-color .5s var(--ease-detent), color .5s var(--ease-detent);
}
.qty button:hover{background:color-mix(in oklab, var(--ink) 7%, transparent); color:var(--fg)}
.qty button:active{background:color-mix(in oklab, var(--ink) 12%, transparent); transition-duration:.18s}
.qty span{min-width:30px; text-align:center; font-family:var(--mono); font-size:var(--fs-mono); font-variant-numeric:tabular-nums; text-transform:uppercase; letter-spacing:.08em}
/* One buy row on every product page: the price on its own line above it, then
   the counter at a fixed width and a button that takes whatever is left. The
   button used to float right at some widths and hug the counter at others, and
   an inline top margin on it sat it lower than the counter it stands beside. */
.buyrow{display:flex; align-items:center; gap:var(--s-3); margin-top:var(--s-4)}
.buyrow .qty{flex:0 0 auto}
.buyrow .btn{flex:1 1 auto; margin-top:0; min-width:0}
/* on a product page the row owns the column; elsewhere it stays content-sized */
.buy .buyrow,.acc-buy--pdp .buyrow{width:100%; max-width:360px}
.acc-buy--pdp{display:block; margin-top:var(--s-5)}
.acc-buy--pdp .acc-price{margin:0 0 var(--s-2)}
/* a product page's button is the same size whatever it sells */
.pdp .acc-buy .btn{min-height:48px; padding:13px 26px; font-size:var(--fs-label)}

/* ---- the subscription offer ----------------------------------------------
   A suggestion at the point of checkout, not a second product: it switches the
   eligible lines already in the bag on to a plan. Same collapse mechanic as the
   bulk notice, and it sits above it so the cheaper suggestion reads first. */
.subo{
  display:grid; grid-template-rows:0fr; opacity:0;
  transition:grid-template-rows var(--t-slow) var(--ease-expo),
             opacity var(--t-slow) var(--ease-expo);
}
.subo[data-on]{grid-template-rows:1fr; opacity:1}
.subo-in{overflow:hidden; min-height:0}
.subo-box{margin:0 var(--s-6); padding:var(--s-5) 0; border-top:1px solid var(--rule-soft)}
.subo-h{
  font-family:var(--display); font-weight:800; font-size:var(--fs-name); line-height:1.25;
  letter-spacing:-.015em; margin:var(--s-3) 0 var(--s-2); text-transform:uppercase}
.subo-p{font-size:var(--fs-label); line-height:1.65; color:var(--fg-2); margin:0 0 var(--s-4); max-width:40ch; font-family:var(--display); text-transform:none}
.subo-btn{min-height:44px; padding:11px 20px; font-size:var(--fs-label)}
/* a line that is on a plan says so, in the bag */
.line-plan{
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg-2); margin:var(--s-2) 0 0;
}
@media (prefers-reduced-motion:reduce){ .subo{transition:none} }

.acc-mark{display:block; flex:none; color:var(--fg-2)}
.acc-mark svg{width:34px; height:48px; display:block}
.acc-note{
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg-2); margin:0; white-space:nowrap;
}

/* ---- product page: description, what it suits, and the easy part -------- */
.pdp-desc{margin:var(--s-5) 0 0; font-size:var(--fs-label); line-height:1.85; color:var(--fg-2); max-width:44ch}
.playline{
  /* the names are written as their marks (TORq keeps its small q, the
     user 2026-10-29), so no uppercase for them: .tm */
  margin:var(--s-4) 0 0; padding-top:var(--s-4); border-top:1px solid var(--rule-soft);
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2);
}

/* the reassurance block. Hairlines, not badges: this is a list of facts, and
   a row of icons would read as a trust-badge strip from another brand. */
.svc{list-style:none; margin:var(--s-5) 0 0; padding:0; border-top:1px solid var(--rule-soft)}
.svc li{
  display:flex; align-items:baseline; gap:var(--s-4); padding:11px 0;
  border-bottom:1px solid var(--rule-soft); font-size:var(--fs-label); line-height:1.6;
}
.svc .k{font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.02em;
  flex:none; min-width:112px; color:var(--fg); text-transform:none}
.svc .v{color:var(--fg-2)}

/* ---- the buy column, in the reference's order ----------------------------
   Name, verdict, price, Add to Bag, a short description, the three scales,
   then everything else behind a row with a plus: Tech, Usage, Shipping &
   Returns, the specialist. Same order on every device — the split behaviour
   from ledger #01 and the phone-only collapse from #05 are both superseded by
   the user's instruction of 2026-09-10 to follow this format throughout. */
.pdp-short{font-family:var(--display); font-size:var(--fs-body); line-height:1.65; color:var(--fg);
  margin:var(--s-5) 0 0; max-width:44ch; text-transform:none}
.fold{margin-top:var(--s-6); border-top:1px solid var(--rule-soft)}
.fold-row{border-bottom:1px solid var(--rule-soft)}
.fold-body{padding-bottom:var(--s-5)}
.fold-body > :first-child{margin-top:0}
.fold-body > .tech-row{margin-top:var(--s-2)}
.tm{text-transform:none}
.fold-body > .playline{border-top:0; margin-top:0; padding-top:var(--s-2)}
.fold-body > .svc{margin-top:var(--s-2)}
.fold-body > .pdp-desc + .pdp-desc{margin-top:var(--s-4)}
/* a grade's own description (ORB Pro): a name line, the story, mono keys over
   the grade and the features, a closing line */
.grade-desc[hidden]{display:none}
.grade-desc-t{margin:0; font-family:var(--display); font-weight:800; font-size:var(--fs-name); line-height:1.25; letter-spacing:-.01em; text-transform:uppercase}
.grade-desc .pdp-short{margin-top:var(--s-3)}
.grade-desc-k{margin:var(--s-5) 0 0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.grade-desc .pdp-desc{margin-top:var(--s-2)}
.grade-feats{list-style:none; margin:var(--s-2) 0 0; padding:0; display:grid; gap:6px; max-width:44ch}
.grade-feats li{position:relative; padding-left:20px; font-family:var(--display); font-size:var(--fs-label); line-height:1.6; letter-spacing:.01em; text-transform:none; color:var(--fg-2)}
.grade-feats li::before{content:""; position:absolute; left:0; top:.8em; width:9px; height:1px; background:var(--fg)}
.grade-desc-close{margin:var(--s-5) 0 0; font-family:var(--display); font-weight:700; font-size:var(--fs-body); color:var(--fg); text-transform:none}
/* how it plays, in words — the scales above carry the same three headings */
.use{list-style:none; margin:var(--s-4) 0 0; padding:0}
.use li{display:grid; grid-template-columns:124px minmax(0,1fr); gap:2px var(--s-4); padding:9px 0}
.u-k{font-family:var(--display); font-weight:700; font-size:var(--fs-label); text-transform:none}
.u-v{font-family:var(--display); font-size:var(--fs-label); line-height:1.65; color:var(--fg-2); max-width:44ch; text-transform:none}
@media (max-width:639.98px){ .use li{grid-template-columns:1fr} }
.talk{border-bottom:1px solid var(--rule-soft)}
.fold > .talk{margin-top:0}
.talk summary,.fold-row summary,.planmore summary{
  display:flex; align-items:center; justify-content:space-between; gap:var(--s-4);
  min-height:52px; cursor:pointer; list-style:none;
  font-family:var(--display); font-weight:700; font-size:var(--fs-label); letter-spacing:.01em;
  transition:color var(--t-detent) var(--ease-detent); text-transform:none}
.talk summary::-webkit-details-marker,.fold-row summary::-webkit-details-marker,.planmore summary::-webkit-details-marker{display:none}
/* the glyph is a plus drawn from two hairlines, which turns into a cross when
   the row is open — the reference's convention, in the site's own line weight */
.talk summary::after,.fold-row summary::after,.planmore summary::after{
  content:""; width:11px; height:11px; flex:none; position:relative;
  background:
    linear-gradient(currentColor,currentColor) center/11px 1px no-repeat,
    linear-gradient(currentColor,currentColor) center/1px 11px no-repeat;
  transition:transform var(--t-detent) var(--ease-detent);
}
.talk[open] summary::after,.fold-row[open] summary::after,.planmore[open] summary::after{transform:rotate(45deg)}
.talk summary:hover,.fold-row summary:hover,.planmore summary:hover{color:var(--fg-2)}
.talk summary:focus-visible,.fold-row summary:focus-visible,.planmore summary:focus-visible{outline:2px solid var(--fg); outline-offset:3px}

/* ---- ledger #08: the ORB plan folds on a phone ---------------------------
   Eight controls stood between a buyer and a 6.99 ball. The two-card plan
   comparison stays exactly as specified from 640 up — it exists because the
   user asked for it — and below that it folds behind one line. The price
   steps out above the fold-away, because it lives inside the one-off card
   and must not disappear with it. On a phone the frequency select waits
   until the plan itself is chosen: nobody needs to be asked how often
   before they have said yes. */
.acc-price--phone{display:none}
@media (min-width:640px){ .planmore > summary{display:none} }
@media (max-width:639.98px){
  .acc-price--phone{display:block; margin:var(--s-5) 0 0}
  .planmore{margin-top:var(--s-4)}
  .planmore-body .plan{margin-top:var(--s-4)}
  .plan-opt input[data-plan-sub]:not(:checked) ~ .plan-freq{display:none}
}
.talk-body{padding-bottom:var(--s-5)}
.talk-body p{margin:0 0 var(--s-4); font-size:var(--fs-label); line-height:1.8; color:var(--fg-2); max-width:42ch}
.talk-body .btn{min-height:46px; padding:12px 24px; font-size:var(--fs-label)}

/* the way back out of a product page. Its vertical margin came from a
   second .crumb rule in the old accessory block; kept here as margin-block so
   .wrap can still centre it past the 1560 page measure. */
.crumb{
  display:flex; align-items:center; gap:10px; padding-block:var(--s-5) 0; margin-block:var(--s-7) var(--s-5);
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2);
}
@media (max-width:639.98px){
  /* 2026-10-25, from the user's phone: 48px of margin and 24px of padding
     above SHOP / DROP left a hole under the masthead */
  .crumb{padding-block:0; margin-block:var(--s-5) var(--s-4)}
}
.crumb a{transition:color var(--t-detent) var(--ease-detent)}
.crumb a:hover{color:var(--fg)}
.crumb span[aria-current]{color:var(--fg)}

/* the decision block: one frame leads, the other is recessed behind it */
.decide{padding-block:var(--sec-h)}
.decide-foot{display:flex; align-items:center; justify-content:space-between; gap:var(--s-5);
  flex-wrap:wrap; margin-top:var(--s-6); padding-top:var(--s-5); border-top:1px solid var(--rule-soft)}
.decide-foot p{margin:0; max-width:52ch; font-size:var(--fs-label); line-height:1.75; color:var(--fg-2); font-family:var(--display); text-transform:none}
.decide-foot .ul{font-family:var(--display); font-weight:700; font-size:var(--fs-body); white-space:nowrap; text-transform:none}

/* The endline closes the page. One line, a lot of air, no ornament. */
.endline{padding-block:var(--sec-h); border-top:1px solid var(--rule-soft)}
.endline .d2{max-width:none}

/* ---- footer columns ------------------------------------------------------
   .cols was used on every page and never defined, so the footer stacked. */
.cols{display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,180px),1fr));
  gap:var(--s-6) var(--s-5); align-items:start}
.cols p{margin:0}
.cols nav{display:grid; gap:6px; margin-top:8px}
.cols nav a{color:var(--fg-2); transition:color var(--t-detent) var(--ease-detent)}
.cols nav a:hover{color:var(--fg)}

/* 2026-10-08, the user: less space between the pages in the footer. On a
   phone the columns pair up (Shop beside Support, then the name beside the
   origin) instead of stacking four deep, and a link takes a 32px row rather
   than 44: the one place below the Forty-Four Rule, still clear of WCAG
   2.5.8's 24px, with no two targets touching. */
footer .cols nav a{display:flex; align-items:center; min-height:32px}
footer .cols nav{gap:0; margin-top:var(--s-2)}
@media (max-width:639.98px){
  footer .cols{grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s-6) var(--s-4)}
  footer .cols > :nth-child(1){order:3}
  footer .cols > :nth-child(4){order:4}
}

/* ---- support documents ---------------------------------------------------
   Delivery, terms, privacy, FAQ. One measure, hairline rules, no ornament:
   these are read for an answer, not for a mood. */
.doc{padding-block:var(--s-8) var(--sec-h); max-width:70ch}
/* the support pages' titles are page titles, not the hero's display size */
.doc .page-title{font-size:var(--fs-title); line-height:.95}
.doc .lead{font-family:var(--display); font-weight:400; font-size:var(--fs-lead);
  line-height:1.55; color:var(--fg-2); margin:var(--s-5) 0 0; max-width:52ch; text-transform:none}
.doc h2{font-family:var(--display); font-weight:800; font-size:var(--fs-body); letter-spacing:.13em;
  text-transform:uppercase; margin:var(--s-8) 0 var(--s-4); padding-top:var(--s-5);
  border-top:1px solid var(--rule-soft)}
.doc h2:first-of-type{margin-top:var(--s-7)}
.doc h3{font-family:var(--display); font-weight:800; font-size:var(--fs-body); letter-spacing:-.005em;
  margin:var(--s-5) 0 6px; text-transform:uppercase}
.doc p{margin:0 0 var(--s-4); font-size:var(--fs-body); line-height:1.75; color:var(--fg-2)}
.doc p strong{color:var(--fg); font-weight:500}
.doc ul{margin:0 0 var(--s-4); padding:0; list-style:none}
.doc li{position:relative; padding-left:20px; margin-bottom:9px; font-size:var(--fs-body);
  line-height:1.85; color:var(--fg-2)}
.doc li::before{content:""; position:absolute; left:0; top:.85em; width:9px; height:1px;
  background:var(--rule)}
/* in running text a link is told apart by weight and contrast, not a line */
.doc a:not(.btn){color:var(--fg); font-weight:700; transition:color var(--t-detent) var(--ease-detent)}
.doc a:not(.btn):hover{color:var(--fg-2)}
.doc .updated{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase;
  color:var(--fg-2); margin-top:var(--s-7)}
/* anything a lawyer or the company still has to supply */
.tbc{display:inline-block; padding:2px 8px; background:var(--cream); border-radius:0;
  font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; color:var(--slate); text-transform:uppercase}

.vh{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap}
@media (prefers-reduced-motion:reduce){ .bag-drawer,.bag-scrim{transition:none} }

/* ---- the Set01 launch price (2026-10-27): the full price struck and quiet
   beside the launch price; on the product page a mono line names the offer */
s.was{text-decoration:line-through; text-decoration-thickness:1px; color:var(--fg-2); font-weight:400; margin-right:.25em}
.launch-note{margin:var(--s-2) 0 0; font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}

/* ---- the hero film, 2026-10-27: played once per load, held on its last
   frame, filling the screen behind the masthead ("a screen height version
   ... so it's immersive"). An upright screen plays the 9:16 cut, so the
   endline is never cropped. The opening title sits over the first shot on a
   soft shade and clears as the rally gets going (.hv-go); the button comes
   in when the film lands (.hv-done). */
.hero-video{position:relative; margin-top:calc(-1 * var(--header-h)); background:var(--ink); overflow:hidden}
.hero-video .hv{display:block; width:100%; height:100svh; object-fit:cover; background:var(--ink)}
.hv-shade{position:absolute; inset:auto 0 0; height:70%; pointer-events:none;
  background:linear-gradient(0deg, rgba(18,23,29,.6), rgba(18,23,29,.2) 55%, rgba(18,23,29,0))}
.hv-head{position:absolute; bottom:clamp(88px, 15svh, 168px); left:var(--gutter); right:var(--gutter); max-width:var(--page); color:var(--paper)}
.hv-head .page-title{color:var(--paper)}
.hv-head .series,.hv-head .standfirst{color:color-mix(in oklab,var(--paper) 86%,transparent)}
.hv-head .series::before{background:color-mix(in oklab,var(--paper) 60%,transparent)}
.hv-head,.hv-shade{transition:opacity .9s var(--ease-detent), transform .9s var(--ease-detent)}
.hero-video.hv-go .hv-head{opacity:0; transform:translateY(-10px); pointer-events:none}
.hero-video.hv-go .hv-shade{opacity:0}
.hv-head[hidden],.hv-shade[hidden]{display:none}
.hv-foot{position:absolute; left:var(--gutter); bottom:var(--s-7); opacity:0; transform:translateY(8px);
  transition:opacity .6s var(--ease-detent), transform .6s var(--ease-detent); pointer-events:none}
.hero-video.hv-done .hv-foot{opacity:1; transform:none; pointer-events:auto}
/* upright screens play the 9:16 cut. Its endline spans 83% of the frame and
   a tall phone shows only the middle 82% of a 9:16 film at full height, so
   the film is drawn just short of full height there (86% of its width in
   view), centred on Ink: the endline whole, the strips above and below a
   few pixels each, the top one under the masthead */
@media (max-aspect-ratio:1/1){
  .hero-video{height:100svh}
  .hero-video .hv{position:absolute; left:0; top:50%; transform:translateY(-50%); height:min(100svh, calc(100vw * 16 / 9 / .9))}
}
.hv-foot .btn{white-space:nowrap}
@media (prefers-reduced-motion:reduce){ .hv-foot,.hv-head,.hv-shade{transition:none} }

/* Newsletter, in the footer of every page (2026-10-30). The field takes tone,
   not an outline box, as .enlist did; the button is the site's own. */
/* stacked, the text above the field (2026-10-30, the user: "Move the section above that email input") */
footer .news{display:grid; grid-template-columns:minmax(0,560px); gap:var(--s-5);
  align-items:start; padding-bottom:var(--s-7); margin-bottom:var(--s-7); border-bottom:1px solid var(--rule-soft); letter-spacing:normal}
footer .news p{margin:0}
.news-t{margin-top:var(--s-2) !important; max-width:32ch; font-family:var(--display); font-weight:700; font-size:var(--fs-name);
  line-height:1.3; letter-spacing:-.005em; color:var(--fg); text-transform:none}
.news-row{display:flex; gap:var(--s-3); flex-wrap:wrap}
.news-row input{flex:1 1 220px; min-width:0; min-height:46px; padding:12px 16px; background:color-mix(in oklab, var(--cream) 55%, var(--bg)); border:0; border-radius:0;
  color:var(--fg); font-family:var(--mono); font-size:var(--fs-label); letter-spacing:.02em; text-transform:uppercase;
  transition:background-color var(--t-detent) var(--ease-detent)}
.news-row input::placeholder{color:var(--fg-2)}
.news-row input:hover,.news-row input:focus-visible{background:var(--cream)}
.news-row input:focus-visible{outline:2px solid var(--fg); outline-offset:2px}
.news-row input:disabled{opacity:.6}
.news-row .btn{flex:none}
@media (max-width:639.98px){ .news-row .btn{width:100%} }
.news-fine,.news-msg{font-family:var(--mono); font-size:var(--fs-mono); letter-spacing:.08em; text-transform:uppercase; color:var(--fg-2)}
.news-fine{margin-top:var(--s-3) !important}
.news-fine a{color:var(--fg-2); text-decoration:none; transition:color var(--t-detent) var(--ease-detent)}
.news-fine a:hover{color:var(--fg)}
.news-msg{margin-top:var(--s-2) !important; min-height:1.4em; color:var(--fg)}
/* Turnstile sits here, invisible unless Cloudflare wants a click */
.news-cap:not(:empty){margin-top:var(--s-3)}
