/* ═══════════════════════════════════════════════════════════════════════════
   ELIIAN · ATHLETIC — a factory site, quiet edition.

   ── What changed in 1.4.0 ─────────────────────────────────────────────────
   The brief was "the colour is too busy, make it simpler". It was: four hues
   running as gradients through every kicker, numeral, chip and button, five
   dark bands on the home page alone, diagonal section edges, sheen sweeps on
   hover, a giant outlined ghost word behind most sections, a scrolling
   marquee and a full-screen loading curtain. Each of those was defensible on
   its own; together they left nothing for the photography to do.

   So: one hue instead of four, and the decoration switched off rather than
   deleted (section 17 below is a single override layer — the rules it
   neutralises are all still here, which means turning any of it back on is a
   two-line change, not a rebuild).

   ── The palette ───────────────────────────────────────────────────────────
   Two light grounds, far enough apart to read as a change of section:
   --paper is a warm off-white (#F4F2EC), --paper-2 is near-white. Warm, not
   the old cool grey-blue, because every garment in the photography sits on a
   warm or neutral studio ground and a cool page fought them.

   The dark ground is a deep olive-charcoal (#2E312A) rather than navy — it
   is the same hue family as the accent, so a dark band reads as the page
   getting quieter rather than as a different design.

   Colour proper is ONE family: sage. The four --volt-* tokens survive because
   hundreds of rules reference them, but they now hold four values of one
   green, so every "gradient" in this file resolves to a near-flat field.

   Two colours stay ring-fenced and appear nowhere else: red is the LIVE dot,
   and orange (#F5811E) is the property of Alibaba Trade Assurance.

   Every colour is a token. There are no loose hex values in the rules below.

   Contrast against the new grounds (WCAG 2.1):
     ink / paper         13.6:1     white / char        13.9:1
     body / paper         6.2:1     mist / char         12.1:1
     steel / paper        5.0:1     steel-on-dark / char 5.2:1
     ink / paper-2       14.4:1     cyan / char          6.0:1
     white / volt-blue    4.6:1     white / volt-indigo  6.4:1
   All AA or better; the reading sizes are all AAA.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── grounds ── */
  --paper:#FAFAF8;        /* warm off-white — tinted light ground          */
  --paper-2:#FFFFFF;      /* near-white light ground                       */
  --char:#2A2A28;         /* deep olive-charcoal — primary dark surface    */
  --char-2:#373734;       /* raised dark surface (cards)                   */
  --char-3:#1E1E1C;       /* deepest — photo scrims, tints, shadow colour  */
  --white:#FFFFFF;

  /* ── type colours ── */
  --ink:#1F1F1D;          /* headings on light                             */
  --body:#5C5C57;         /* body text on light                            */
  --mist:#EAEAE6;         /* body text on dark                             */
  --steel:#6A6A63;        /* secondary labels — light ground default.
                             Overridden inside dark containers below.     */

  /* ── ring-fenced ── */
  --red:#EF233C;          /* LIVE dot only — never text                    */
  --ta:#F5811E;           /* Alibaba Trade Assurance ONLY                  */

  /* ── the one colour family ──
     Retuned 1.4.0. The old system ran blue → indigo → violet → cyan, four
     hues appearing as gradients on every kicker, numeral, chip and button.
     On a page that already alternates two grounds and carries photography in
     seven categories, that was a fifth and sixth thing competing for the eye.

     There is now one hue: sage. The four tokens are kept — hundreds of rules
     reference them — but they are now four VALUES of the same green, not four
     different colours, so a "gradient" reads as a single flat field and the
     photography is the only thing on the page making a colour statement. */
  --volt-blue:#6E7A62; --volt-indigo:#57624C; --volt-violet:#8A9578;
  --volt-cyan:#A9B596;
  --grad-main:linear-gradient(120deg,#6E7A62 0%,#57624C 100%);
  --grad-hot:linear-gradient(120deg,#7E8A70 0%,#57624C 100%);
  --grad-text:linear-gradient(100deg,#B4BEA2 0%,#9FAA8C 100%);

  /* Channel triplets, so the rules below can build translucent variants
     without hard-coding a second copy of the colour. */
  --ink-rgb:31,31,29;
  --char-rgb:30,30,28;
  --mist-rgb:234,234,230;
  --volt-rgb:110,122,98;      /* --volt-blue                              */
  --volt-mid-rgb:138,149,120; /* the mid stop of --grad-text              */
  --indigo-rgb:87,98,76;      /* --volt-indigo                            */
  --cyan-rgb:169,181,150;     /* --volt-cyan                              */
  --ta-rgb:245,129,30;        /* --ta                                     */

  /* WhatsApp's own brand green. Ring-fenced like the Alibaba orange: it is
     someone else's colour and it appears only on the WhatsApp button. */
  --wa:#25D366; --wa-rgb:37,211,102;

  --line:rgba(var(--ink-rgb),.12);
  --line-d:rgba(255,255,255,.14);
  --bar:rgba(252,251,248,.92);     /* nav bar once it goes solid          */

  --display:'Archivo','Arial Narrow',sans-serif;
  --sans:'Inter','Helvetica Neue',Arial,sans-serif;

  --radius:14px; --radius-sm:10px;
  /* Shadows softened and warmed. The old set carried a 60px blue-black
     spread under every card, which on an off-white ground reads as grime
     rather than lift. */
  --shadow:0 2px 10px rgba(var(--ink-rgb),.05),0 14px 34px -20px rgba(var(--ink-rgb),.18);
  --shadow-s:0 2px 10px rgba(var(--ink-rgb),.05);
  --glow:0 10px 30px -12px rgba(var(--volt-rgb),.4);
  --hairline:1px solid rgba(var(--ink-rgb),.12);
  --cut:0px;                      /* diagonal section edges — retired     */
  --t:.22s; --t-slow:.5s;
  --out:cubic-bezier(.19,1,.22,1);
}

/* ── tone contexts ───────────────────────────────────────────────────────
   Six containers on this site are dark. Rather than write a light-ground and
   a dark-ground rule for every component that lives in both, the containers
   re-point the token once and the components inherit it. --steel is the only
   token that needs it: at 4.8:1 it is a legible label colour on paper and
   would be an illegible one on navy, so navy gets the lighter value. */
/* dark containers, and the dark cards that sit on light sections */
.asec.char,.ph,.finish,.sfoot,.ah,#curtain,
.score,.taband,.filmb,.certc,.veri>div,.leg-v,.ppost-v{--steel:#A5A59E}
/* …and the light islands that sit back inside a dark one, which have to put
   the light value back or they inherit a label colour meant for navy. */
.filmb-cap,.spect,.fsheet,.asec.char .aform input,.asec.char .aform textarea{--steel:#6A6A63}

/* ── reset-ish ── */
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
body{font-family:var(--sans);background:var(--paper);color:var(--body);
  font-size:16.5px;line-height:1.7;-webkit-font-smoothing:antialiased}
img,video{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
button{font:inherit;border:0;background:none;cursor:pointer}
h1,h2,h3{font-family:var(--display);font-weight:900;font-style:italic;
  color:var(--ink);text-transform:uppercase;line-height:1.02;
  letter-spacing:-.01em}
/* the brand's two-tone headline: first half solid, second half gradient */
.gw{background-image:var(--grad-main);-webkit-background-clip:text;
  background-clip:text;color:transparent}
.asec.char .gw,.ph .gw,.finish .gw{background-image:var(--grad-text);
  -webkit-background-clip:text;background-clip:text;color:transparent}
::selection{background:var(--volt-indigo);color:#fff}

.wrap{width:min(1280px,92vw);margin:0 auto}

/* the old theme's cursor gimmicks — retired. Athletic = direct. */
#cdot,#cring{display:none}

/* scroll progress: a lime race line */
#sprog{position:fixed;top:0;left:0;height:3px;width:0;z-index:400;
  background:var(--grad-hot)}

/* ═══ 1. CURTAIN ══════════════════════════════════════════════════════════ */
#curtain{position:fixed;inset:0;z-index:500;background:var(--char);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:10px;transition:transform .7s var(--out) .9s,visibility 0s 1.7s}
#curtain::before{content:"";position:absolute;inset:0;
  background:repeating-linear-gradient(115deg,transparent 0 72px,rgba(255,255,255,.03) 72px 74px)}
#curtain .cbrand{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(64px,11vw,150px);color:var(--white);text-transform:uppercase;
  line-height:1}
#curtain .cbrand em{font-style:italic;background-image:var(--grad-text);
  -webkit-background-clip:text;background-clip:text;color:transparent}
#curtain .cprog{width:min(300px,60vw);height:3px;border-radius:2px;
  background:rgba(255,255,255,.12);overflow:hidden;margin-top:18px}
#curtain .cprog i{display:block;height:100%;width:0;background:var(--grad-hot);
  transition:width .3s var(--out)}
#curtain .csub{font-size:12px;font-weight:700;letter-spacing:.42em;
  text-transform:uppercase;color:var(--steel)}
body.lift #curtain{transform:translateY(-101%);visibility:hidden}

/* ═══ 2. NAV ══════════════════════════════════════════════════════════════
   One bar for every page. Over the hero it is a white-on-video strip; after
   80px of scroll the JS adds .on and it becomes a solid paper bar with a hard
   bottom edge — no blur, no translucency. */
.abar{position:fixed;top:0;left:0;right:0;z-index:300;
  display:flex;align-items:center;gap:34px;
  padding:0 clamp(18px,3vw,44px);height:72px;
  color:var(--white);transition:background var(--t),color var(--t),box-shadow var(--t)}
.abar.on{background:var(--bar);color:var(--char);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  box-shadow:0 1px 0 rgba(var(--char-rgb),.1),0 10px 30px -18px rgba(var(--char-rgb),.25)}
.abar-l{display:flex;align-items:center;gap:12px;margin-right:auto}
/* floating pill nav (front page, over the video) */
.abar.pills:not(.on){background:transparent}
.abar.pills:not(.on) .abar-l{background:rgba(var(--char-rgb),.6);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border-radius:999px;padding:9px 20px 9px 12px}
.abar.pills:not(.on) .abar-l i{display:none}
.abar.pills:not(.on) .anav{position:absolute;left:50%;transform:translateX(-50%);
  background:rgba(var(--char-rgb),.6);backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);border-radius:999px;padding:7px 12px;gap:2px}
.abar.pills:not(.on) .anav a{padding:8px 15px;border-radius:999px;
  font-size:12.5px}
.abar.pills:not(.on) .anav a::after{display:none}
.abar.pills:not(.on) .anav a:hover{background:rgba(255,255,255,.12)}
.abar-l svg{width:34px;height:34px}
.abar-l b{font-family:var(--display);font-weight:400;font-size:24px;
  letter-spacing:.03em;text-transform:uppercase}
.abar-l i{font-style:normal;font-size:9.5px;font-weight:700;letter-spacing:.3em;
  text-transform:uppercase;opacity:.62;display:block;margin-top:2px}
.abar-l span{line-height:1}
.anav{display:flex;gap:clamp(14px,2vw,30px)}
.anav a{position:relative;font-size:13.5px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;padding:6px 2px}
.anav a::after{content:"";position:absolute;left:0;right:100%;bottom:0;
  height:3px;border-radius:2px;background:var(--grad-main);
  transition:right var(--t) var(--out)}
.anav a:hover::after,.anav a.on::after{right:0}
.abtn{display:inline-flex;align-items:center;gap:10px;position:relative;
  overflow:hidden;font-family:var(--display);font-weight:800;font-size:14px;
  letter-spacing:.04em;text-transform:uppercase;padding:13px 28px;
  background:var(--grad-main);color:#fff;border:0;border-radius:999px;
  box-shadow:var(--glow);
  transition:transform var(--t),box-shadow var(--t)}
.abtn::after{content:"";position:absolute;top:0;bottom:0;left:-80%;width:40%;
  transform:skewX(-20deg);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.35),transparent)}
.abtn:hover::after{animation:btnSheen .9s var(--out)}
@keyframes btnSheen{0%,55%{left:-80%}100%{left:160%}}
.abtn:hover{transform:translateY(-3px);box-shadow:0 16px 52px rgba(var(--volt-rgb),.45)}
.abtn:active{transform:translateY(0);box-shadow:var(--glow)}
.abtn.dark{background:var(--ink);box-shadow:0 10px 30px -12px rgba(var(--ink-rgb),.5)}
.abtn.dark:hover{background:#20294B;box-shadow:0 16px 40px -14px rgba(var(--ink-rgb),.55)}
.abtn.ghost{background:transparent;box-shadow:none;color:inherit;
  border:1px solid rgba(255,255,255,.35)}
.abtn.ghost:hover{border-color:#fff;box-shadow:none}
.abar .abtn{padding:10px 22px;font-size:13.5px;box-shadow:none}
.aburg{display:none;flex-direction:column;gap:5px;padding:8px}
.aburg i{display:block;width:24px;height:3px;background:currentColor}

/* ═══ 3. HERO ═════════════════════════════════════════════════════════════ */
.ah{position:relative;height:100svh;min-height:620px;overflow:hidden;
  background:var(--char);color:var(--white);
  display:flex;flex-direction:column;justify-content:flex-end}
.ah-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:50% 28%}
/* Two scrims, not one. The vertical one alone left a bright band across the
   middle of the frame at 18% tint — and the middle of the frame is exactly
   where the hero's one line of copy and the right-hand stats sit, so on a pale
   video frame they were white-on-near-white. The second gradient darkens the
   two vertical edges where all the type actually lives and leaves the centre of
   the footage almost untouched, so the video still reads as video. */
.ah-tint{position:absolute;inset:0;background:
  linear-gradient(100deg,rgba(var(--char-rgb),.74) 0%,rgba(var(--char-rgb),.34) 36%,
                          rgba(var(--char-rgb),.06) 62%,rgba(var(--char-rgb),.46) 100%),
  linear-gradient(180deg,rgba(var(--char-rgb),.56) 0%,rgba(var(--char-rgb),.26) 42%,rgba(var(--char-rgb),.70) 100%)}
/* start-list lane lines down the left edge */
.ah-lanes{position:absolute;top:0;bottom:0;left:clamp(18px,3vw,44px);width:1px;
  background:var(--line-d)}
.ah-lanes::before{content:"";position:absolute;top:0;bottom:0;left:10px;width:1px;
  background:rgba(255,255,255,.07)}
.ah-in{position:relative;z-index:2;padding:0 clamp(18px,3vw,44px) clamp(90px,11vh,150px)}
/* scattered hero — three giant lowercase words placed around the frame,
   one small line of copy, three corner stats. Nothing else. */
.ah-in.scatter{position:absolute;inset:0;padding:0}
.ahw{position:absolute;font-family:var(--display);font-weight:800;
  font-style:normal;text-transform:lowercase;line-height:.86;color:#fff;
  font-size:clamp(84px,14.5vw,260px);letter-spacing:-.03em;overflow:hidden}
.ahw>span{display:inline-block;transform:translateY(112%);
  animation:hup .8s var(--out) forwards}
.ahw1{left:3%;top:24%}
.ahw2{right:2.5%;top:40%}.ahw2>span{animation-delay:.12s}
.ahw3{left:29%;bottom:11%}.ahw3>span{animation-delay:.22s}
.ah-in.scatter .ah-sub{position:absolute;left:3.2%;top:47%;margin:0;
  max-width:30ch;font-size:13.5px;line-height:1.6;font-weight:600;
  color:rgba(var(--mist-rgb),.92)}
.ah-st{position:absolute;display:grid;gap:3px}
.ah-st b{font-family:var(--display);font-weight:800;font-size:clamp(24px,2.4vw,38px);
  line-height:1;color:#fff}
.ah-st i{font-style:normal;font-size:11px;font-weight:600;letter-spacing:.08em;
  color:rgba(var(--mist-rgb),.8)}
.ah-st::before{content:"";position:absolute;width:56px;height:1px;
  background:rgba(255,255,255,.55);transform:rotate(-38deg)}
.st1{right:4%;top:24%;text-align:right;justify-items:end}
.st1::before{left:-72px;top:8px}
.st2{left:4%;bottom:22%}
.st2::before{right:-72px;top:8px}
.st3{right:4%;bottom:20%;text-align:right;justify-items:end}
.st3::before{left:-72px;top:8px}
.ah-kick{display:inline-flex;align-items:center;gap:12px;
  font-size:12px;font-weight:800;letter-spacing:.34em;text-transform:uppercase;
  color:var(--volt-cyan);margin-bottom:18px}
.ah-kick::before{content:"";width:34px;height:3px;border-radius:2px;background:var(--grad-text)}
.ah h1{color:var(--white);font-size:clamp(58px,9.6vw,168px);letter-spacing:.005em}
.ah h1 .row{display:block;overflow:hidden}
.ah h1 .row span{display:inline-block;transform:translateY(110%);
  animation:hup .7s var(--out) forwards}
.ah h1 .row:nth-child(2) span{animation-delay:.1s}
.ah h1 .row:nth-child(3) span{animation-delay:.2s}
@keyframes hup{to{transform:translateY(0)}}
.amark{display:inline-block;background-image:var(--grad-text);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  background-size:200% auto;animation:gradShift 6s linear infinite}
/* background-clip:text does not paint through a descendant that makes its own
   stacking context, and the word-reveal splitter wraps every word in a
   transformed span that does exactly that. The splitter re-paints the last
   word (it tags it .gw) but not the ones before it, so on the four page heroes
   whose .amark holds two words — "one thing", "our floor", "on file", "your
   collection" — the first word was rendering as transparent-on-navy, i.e.
   invisible. Each word repaints the gradient for itself instead — declared, not
   inherited: `inherit` here would resolve against .w, the immediate parent,
   which carries no background of its own, so it would resolve to `none`. */
.amark .w>span{background-image:var(--grad-text);background-size:200% auto;
  -webkit-background-clip:text;background-clip:text;color:transparent;
  animation:gradShift 6s linear infinite}
@media (prefers-reduced-motion:reduce){.amark,.amark .w>span{animation:none}}
@keyframes gradShift{0%{background-position:0% center}
  100%{background-position:200% center}}
.ah-sub{margin-top:22px;max-width:52ch;font-size:clamp(15px,1.2vw,17.5px);
  font-weight:500;color:rgba(var(--mist-rgb),.92)}
.ah-cta{display:flex;gap:16px;margin-top:30px;flex-wrap:wrap}
/* corner scoreboard */
.ah-score{position:absolute;right:clamp(18px,3vw,44px);
  bottom:clamp(90px,11vh,150px);z-index:2;display:grid;gap:14px;text-align:right}
.ah-score b{display:block;font-family:var(--display);font-weight:900;
  font-style:italic;font-size:clamp(26px,2.6vw,40px);line-height:1;
  background-image:var(--grad-text);-webkit-background-clip:text;
  background-clip:text;color:transparent}
.ah-score i{font-style:normal;display:block;font-size:10.5px;font-weight:700;
  letter-spacing:.26em;text-transform:uppercase;color:rgba(var(--mist-rgb),.72);margin-top:4px}
.ah-down{position:absolute;left:50%;bottom:26px;transform:translateX(-50%);z-index:2;
  font-size:10.5px;font-weight:800;letter-spacing:.3em;text-transform:uppercase;
  color:rgba(var(--mist-rgb),.65);animation:nudge 2.2s ease-in-out infinite}
@keyframes nudge{50%{transform:translate(-50%,6px)}}

/* ═══ 4. SECTIONS ═════════════════════════════════════════════════════════ */
.asec{position:relative;padding:clamp(84px,9vw,140px) 0}
/* The two light grounds each carry one faint wash from the colour family,
   entering from opposite corners and from opposite ends of that family. It is
   far too weak to read as "a coloured section" — it just stops a light band
   from being a dead flat rectangle, and it means the alternation between the
   two is felt as a shift in hue as well as in value. */
.asec.paper{background:var(--paper);
  background-image:radial-gradient(120% 78% at 88% 0%,rgba(var(--volt-rgb),.055),transparent 62%)}
.asec.paper2{background:var(--paper-2);
  background-image:radial-gradient(104% 70% at 8% 100%,rgba(var(--indigo-rgb),.05),transparent 64%)}
.asec.char{background:var(--char);color:rgba(var(--mist-rgb),.88)}
.asec.char h2,.asec.char h3{color:var(--white)}
.asec.char{--is-navy:1}
/* diagonal cuts: the section's own top edge slices into the one above */
.cut-t{margin-top:calc(var(--cut) * -1);
  clip-path:polygon(0 var(--cut),100% 0,100% 100%,0 100%);
  padding-top:calc(clamp(84px,9vw,140px) + var(--cut))}
.cut-t.rev{clip-path:polygon(0 0,100% var(--cut),100% 100%,0 100%)}
/* track lanes on dark sections */
.asec.char.lanes::before{content:"";position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(115deg,transparent 0 110px,rgba(255,255,255,.035) 110px 112px)}
.asec.char.lanes>.wrap{position:relative}

.ahead{margin-bottom:clamp(38px,4.5vw,64px)}
.kick{display:inline-flex;align-items:center;gap:12px;font-size:12px;
  font-weight:800;letter-spacing:.34em;text-transform:uppercase;
  color:var(--volt-indigo);margin-bottom:16px}
.asec.char .kick{color:var(--volt-cyan)}
.kick::before{content:"";width:34px;height:3px;border-radius:2px;background:var(--grad-main)}
.asec.char .kick::before{background:var(--grad-text)}
/* Indigo is the accent for light grounds only — on navy it falls to 2.9:1 and
   small uppercase labels in it stop being readable. Every place indigo is used
   as a text colour therefore has a cyan counterpart for dark containers. */
.asec.char .fsheet-cap,.ph .fsheet-cap,.finish .fsheet-cap,
.asec.char .rung .tag2,.ph .rung .tag2{color:var(--volt-cyan)}
.asec.char .rung .tag2,.ph .rung .tag2{border-color:rgba(var(--cyan-rgb),.34)}
.asec.char .rung .no,.ph .rung .no{-webkit-text-stroke-color:rgba(var(--cyan-rgb),.5)}
.asec.char .rung b,.ph .rung b{color:var(--white)}
.ahead h2{font-size:clamp(42px,5.6vw,86px)}
.ahead .lead{margin-top:18px;max-width:58ch;font-size:clamp(15.5px,1.25vw,18px)}
.ahead.split{display:flex;align-items:flex-end;justify-content:space-between;
  gap:28px;flex-wrap:wrap}
.ahead.split .lead{margin-top:0;max-width:44ch}

/* ═══ 5. CHEVRON TICKER ═══════════════════════════════════════════════════ */
.chev{overflow:hidden;background:var(--grad-main);
  padding:13px 0;box-shadow:0 12px 34px -16px rgba(var(--volt-rgb),.45);
  transform:skewY(-1.2deg);margin-block:-2px;position:relative;z-index:3}
.chev-in{display:flex;gap:44px;width:max-content;white-space:nowrap;
  animation:chevroll 26s linear infinite;will-change:transform}
.chev-in span{font-family:var(--display);font-weight:800;font-style:italic;
  font-size:17px;letter-spacing:.08em;
  text-transform:uppercase;color:#fff;display:flex;align-items:center;gap:44px}
.chev-in span::after{content:"▸▸";letter-spacing:0;font-size:15px}
@keyframes chevroll{to{transform:translateX(-50%)}}

/* ═══ 6. PRODUCT POSTERS ══════════════════════════════════════════════════ */
.pgrid2{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(16px,2vw,28px)}
.ppost{position:relative;background:var(--white);box-shadow:var(--shadow-s);
  border:.8px solid var(--line);border-radius:var(--radius);overflow:hidden;
  transition:transform var(--t) var(--out),box-shadow var(--t) var(--out)}
.ppost:hover{transform:translateY(-8px);box-shadow:var(--shadow),0 18px 48px -18px rgba(var(--volt-rgb),.35)}
.ppost-v{position:relative;aspect-ratio:.74/1;overflow:hidden;background:var(--char)}
.ppost-v video,.ppost-v img{width:100%;height:100%;object-fit:cover}
.ppost-no{position:absolute;top:14px;left:14px;z-index:2;
  font-family:var(--display);font-weight:800;font-size:12px;color:#fff;
  background:var(--grad-main);padding:6px 12px;border-radius:999px;
  letter-spacing:.08em;box-shadow:0 6px 18px -6px rgba(var(--volt-rgb),.5)}
.ppost-tag{position:absolute;top:14px;right:14px;z-index:2;font-size:9.5px;font-weight:800;
  letter-spacing:.22em;text-transform:uppercase;color:var(--white);
  background:rgba(var(--char-rgb),.82);padding:7px 12px;border-radius:999px}
.ppost-b{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:18px 18px 17px}
.ppost-b b{font-family:var(--display);font-weight:900;font-style:italic;font-size:18px;
  letter-spacing:0;text-transform:uppercase;color:var(--ink);line-height:1.08}
.ppost-b i{font-style:normal;display:block;font-size:10.5px;font-weight:700;
  letter-spacing:.18em;color:var(--steel);margin-top:5px}
.ppost-b .go{flex:none;width:36px;height:36px;display:grid;place-items:center;
  border-radius:50%;background:var(--grad-main);color:#fff;font-weight:800;
  box-shadow:0 6px 18px -6px rgba(var(--volt-rgb),.5);
  transition:transform .55s var(--spring)}
.ppost:hover .ppost-b .go{transform:translateX(4px)}
.ppost:hover .go{background:var(--ink)}
/* per-card spec chips (style gallery). The card foot already carries the name
   and a one-line descriptor; these are the three things a sourcing buyer scans
   for instead of reading it — cut, feature, finish. Kept at the same 10.5px /
   .06em as the descriptor so the foot still reads as one block, and pushed up
   4px so the chips sit against the descriptor rather than floating mid-card. */
.ppost-sp{display:flex;flex-wrap:wrap;gap:5px;padding:0 18px 17px;margin-top:-4px}
.ppost-sp span{font-size:10.5px;font-weight:700;letter-spacing:.06em;
  color:var(--steel);border:1px solid var(--line);border-radius:999px;
  padding:4px 9px;background:var(--paper);
  transition:border-color .25s ease,color .25s ease}
.ppost:hover .ppost-sp span{border-color:rgba(var(--volt-rgb),.35);color:var(--ink)}
.pmore{margin-top:clamp(30px,3.5vw,46px);display:flex;justify-content:center}

/* ═══ 7. SCOREBOARD ═══════════════════════════════════════════════════════ */
.score{display:grid;grid-template-columns:repeat(4,1fr)}
.score>div{padding:clamp(30px,3.4vw,52px) clamp(20px,2.4vw,40px);
  border-left:1px solid var(--line-d)}
.score>div:first-child{border-left:0}
/* nowrap: a browser will happily break after the hyphen in a range like
   "25-35d", which splits one figure across two lines and reads as two numbers.
   Single counters ("20,000") have no break opportunity, so this only matters
   for the ranged variant below. */
.score b{display:block;font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(52px,5.8vw,92px);line-height:1;white-space:nowrap;
  background-image:var(--grad-text);-webkit-background-clip:text;
  background-clip:text;color:transparent}
/* Ranges are twice as many glyphs as a counter, so the ranged scoreboard gets
   its own step down rather than forcing every scoreboard to size for the worst
   case. */
.score.ranges b{font-size:clamp(38px,3.9vw,62px)}
.score b em{font-style:normal}
.score i{font-style:normal;display:block;margin-top:10px;font-size:11.5px;
  font-weight:700;letter-spacing:.28em;text-transform:uppercase;color:var(--steel)}

/* ═══ 7b. MILESTONE — the anniversary block ═══════════════════════════════
   Three parts, top to bottom: a strip of the real floor, the years count set
   larger than anything else on the page, and a band of figures underneath.

   The photo strip is pulled up by exactly the section's own top padding so it
   starts at the section's true top edge. The section carries .cut-t, whose
   clip-path slices a diagonal across everything inside it — which means the
   strip gets that diagonal for free. That is the intended effect: the cut
   runs through the photography, not around it.
   ═════════════════════════════════════════════════════════════════════════ */
.mband{display:grid;grid-template-columns:repeat(3,1fr);gap:3px;position:relative;
  height:clamp(190px,25vw,360px);background:var(--char-2);
  margin-top:calc((clamp(84px,9vw,140px) + var(--cut)) * -1);
  margin-bottom:clamp(46px,5vw,84px)}
.mband figure{overflow:hidden;position:relative}
.mband img{width:100%;height:100%;object-fit:cover;filter:saturate(.82) contrast(1.06);
  scale:1.05;transition:scale 1.4s var(--out),filter .6s ease}
.mband figure:hover img{scale:1;filter:saturate(1) contrast(1)}
/* The scrim does two jobs: it keeps the strip from competing with the display
   type below it, and it lands the bottom of the photography on the section's
   own navy so there is no visible seam. */
.mband::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(var(--char-rgb),.62) 0,rgba(var(--char-rgb),.08) 40%,
    rgba(var(--char-rgb),.55) 78%,var(--char) 100%)}

.mile{display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:clamp(30px,4vw,72px);align-items:center}
.mile-yr{display:flex;align-items:flex-start;gap:clamp(12px,1.5vw,22px);
  margin-bottom:clamp(12px,1.6vw,22px)}
.mile-yr b{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(88px,12.5vw,196px);line-height:.8;letter-spacing:-.04em;
  background-image:var(--grad-text);-webkit-background-clip:text;
  background-clip:text;color:transparent}
.mile-yr i{font-style:normal;padding-top:clamp(10px,1.2vw,20px)}
.mile-yr i>span{display:block;font-family:var(--display);font-weight:900;
  font-style:italic;text-transform:uppercase;line-height:1;color:var(--white);
  font-size:clamp(22px,2.4vw,38px)}
.mile-yr i>em{display:block;font-style:normal;margin-top:10px;font-size:11.5px;
  font-weight:800;letter-spacing:.28em;text-transform:uppercase;color:var(--steel)}
.mile h2{font-size:clamp(36px,4.6vw,68px)}
.mile .lead{margin-top:18px;max-width:56ch;font-size:clamp(15.5px,1.25vw,18px)}

/* The seal replaces the medal in the reference layout. It is drawn, not
   photographed, so it carries our own wordmark and cannot be mistaken for an
   award we were not given. */
.seal{width:clamp(156px,17vw,236px);aspect-ratio:1;position:relative;
  display:grid;place-items:center}
.seal svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.seal-ring{fill:none;stroke:rgba(255,255,255,.14);stroke-width:1}
.seal text{font-family:var(--sans);font-size:9px;font-weight:800;
  letter-spacing:.3em;text-transform:uppercase;fill:var(--volt-cyan)}
.seal-c{position:relative;text-align:center}
.seal-c b{display:block;font-family:var(--display);font-weight:900;
  font-style:italic;line-height:.9;color:var(--white);
  font-size:clamp(32px,3.6vw,54px)}
.seal-c i{display:block;font-style:normal;margin-top:6px;font-size:10px;
  font-weight:800;letter-spacing:.3em;text-transform:uppercase;color:var(--steel)}
/* 41s is deliberately not a round number and not a multiple of any other
   ambient period on the page, so the seal never falls into step with the
   marquees and start reading as one synchronised animation. */
@media (prefers-reduced-motion:no-preference){
  .seal-rot{transform-origin:100px 100px;animation:sealspin 41s linear infinite}
}
@keyframes sealspin{to{transform:rotate(360deg)}}

.bignums{display:grid;grid-template-columns:repeat(4,1fr);
  margin-top:clamp(48px,5.4vw,88px);padding-top:clamp(30px,3.4vw,52px);
  border-top:1px solid var(--line-d)}
.bignums>div{padding:0 clamp(16px,2vw,34px);border-left:1px solid var(--line-d)}
.bignums>div:first-child{border-left:0;padding-left:0}
.bignums b{display:flex;align-items:flex-start;gap:9px;
  font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(40px,5vw,78px);line-height:.86;letter-spacing:-.03em;
  color:var(--white);white-space:nowrap}
.bignums b>em{font-style:normal}
/* The unit sits beside the numeral rather than under it, the way a scoreboard
   sets a unit — it keeps the numeral itself the tallest thing in the row. */
.bignums b>i{font-style:normal;font-family:var(--sans);font-weight:800;
  font-size:11.5px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--volt-cyan);line-height:1.4;padding-top:7px;white-space:nowrap}
.bignums b>i>em{display:block;font-style:normal;color:var(--steel);
  letter-spacing:.15em}
.bignums p{margin-top:14px;font-size:13px;line-height:1.6;color:var(--steel);
  max-width:24ch}

@media (max-width:1040px){
  .mile{grid-template-columns:1fr}
  .mile-s{justify-self:start}
}
@media (max-width:900px){
  .bignums{grid-template-columns:repeat(2,1fr);row-gap:clamp(26px,4vw,42px)}
  .bignums>div:nth-child(odd){border-left:0;padding-left:0}
}
@media (max-width:760px){
  .mband{grid-template-columns:1fr;height:clamp(150px,44vw,230px)}
  .mband figure:nth-child(n+2){display:none}
}
@media (max-width:520px){
  .bignums{grid-template-columns:1fr}
  .bignums>div{border-left:0;padding-left:0}
}

/* ═══ 8. STATIONS — the relay ═════════════════════════════════════════════ */
.relay{display:grid;gap:clamp(40px,5vw,72px)}
.leg{display:grid;grid-template-columns:1fr 1fr;gap:clamp(24px,3.4vw,60px);
  align-items:center}
.leg:nth-child(even) .leg-v{order:2}
.leg-v{position:relative;background:var(--char);overflow:hidden;
  border-radius:var(--radius);box-shadow:var(--shadow)}
.leg-v video{width:100%;aspect-ratio:16/10;object-fit:cover}
.leg-live{position:absolute;top:14px;right:14px;display:inline-flex;align-items:center;
  gap:7px;font-size:10px;font-weight:800;letter-spacing:.22em;color:var(--white);
  background:rgba(var(--char-rgb),.85);padding:7px 13px;border-radius:999px}
.leg-live i{width:7px;height:7px;border-radius:50%;background:var(--red);
  animation:blink 1.1s steps(2,jump-none) infinite}
@keyframes blink{50%{opacity:.25}}
.leg-no{font-family:var(--display);font-weight:800;font-size:12px;color:#fff;
  display:inline-block;background:var(--grad-main);padding:6px 13px;
  border-radius:999px;letter-spacing:.08em;margin-bottom:16px;
  box-shadow:0 6px 18px -6px rgba(var(--volt-rgb),.5)}
.leg-t h3{font-size:clamp(30px,3.4vw,52px)}
.leg-t p{margin-top:14px;max-width:40ch;font-size:clamp(15px,1.2vw,17px)}
.leg-t .spec{margin-top:16px;display:flex;gap:10px;flex-wrap:wrap}
.leg-t .spec span{font-size:11px;font-weight:800;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink);border:var(--hairline);
  border-radius:999px;padding:6px 13px}

/* ═══ 9. CERTIFICATE WALL (dark) ══════════════════════════════════════════ */
.certrow{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,1.8vw,26px)}
.certc{position:relative;background:var(--char-2);border:1px solid var(--line-d);
  border-radius:var(--radius);padding:14px;
  transition:transform var(--t) var(--out),border-color var(--t),box-shadow var(--t)}
.certc:hover{transform:translateY(-6px);border-color:rgba(var(--volt-mid-rgb),.55);box-shadow:0 18px 48px -14px rgba(var(--volt-rgb),.35)}
/* The document frame owns the shape; the image only fills it.
   Two deliberate choices, both learned from the live install:
   1. `padding-top` rather than `aspect-ratio` — the ratio hack cannot be
      undone by a plugin stylesheet setting `img{height:auto}` or by a parent
      theme resetting `aspect-ratio`, which is what stretched these cards into
      tall strips on the user's WordPress.
   2. `contain`, not `cover` — a certificate is a document a buyer wants to
      read. Cropping it to fill the box hides the lab name and the seal, which
      are the only reasons the picture is on the page. Letterboxing on white
      reads as a scan on paper, which is exactly what it is. */
.cert-doc{display:block;position:relative;padding-top:133.333%;overflow:hidden;
  background:#fff;border-radius:calc(var(--radius) - 8px)}
.certc img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;object-position:center;border-radius:0}
.certc b{display:block;margin-top:12px;font-size:12.5px;font-weight:800;
  letter-spacing:.08em;text-transform:uppercase;color:var(--white)}
.certc i{font-style:normal;font-size:10.5px;font-weight:700;letter-spacing:.2em;
  text-transform:uppercase;color:var(--steel)}
/* z-index: the document frame is now a positioned element that comes later in
   the DOM, so without this the tick badge paints underneath the scan. */
.certc .chk{position:absolute;z-index:2;top:22px;right:22px;width:26px;height:26px;
  display:grid;place-items:center;border-radius:50%;background:var(--grad-main);
  color:#fff;font-size:12px;font-weight:900;
  box-shadow:0 6px 16px -4px rgba(var(--volt-rgb),.55)}

/* verification cards */
.veri{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,1.8vw,26px)}
.veri>div{position:relative;background:var(--char-2);border:1px solid var(--line-d);
  border-radius:var(--radius);padding:clamp(22px,2.2vw,32px);overflow:hidden;
  transition:transform var(--t) var(--out),border-color var(--t),box-shadow var(--t)}
.veri>div::before{content:"";position:absolute;top:0;left:0;right:0;height:4px;
  background:var(--grad-main)}
.veri>div:hover{transform:translateY(-6px);border-color:rgba(var(--volt-mid-rgb),.5);box-shadow:0 18px 48px -14px rgba(var(--volt-rgb),.35)}
.veri b{display:block;font-family:var(--display);font-weight:900;font-style:italic;
  font-size:18px;letter-spacing:0;text-transform:uppercase;color:var(--white)}
.veri i{font-style:normal;display:block;font-size:10.5px;font-weight:700;
  letter-spacing:.22em;text-transform:uppercase;color:var(--volt-cyan);margin:4px 0 12px}
.veri p{font-size:14px;line-height:1.65;color:rgba(var(--mist-rgb),.78)}
.veri .tick{display:inline-flex;align-items:center;gap:8px;margin-top:14px;
  font-size:10.5px;font-weight:800;letter-spacing:.18em;text-transform:uppercase;
  color:var(--volt-cyan);border:1px solid rgba(var(--cyan-rgb),.4);
  border-radius:999px;padding:6px 13px}

/* ═══ 9B. GARMENT ANATOMY (dark) ══════════════════════════════════════════
   The picture parks while the construction notes travel past it. A buyer
   reading point 05 is still looking at the garment point 05 describes, which
   is the whole argument for a sticky column here rather than a plain
   two-up. */
.anat{display:grid;grid-template-columns:minmax(0,.82fr) minmax(0,1.18fr);
  gap:clamp(28px,4vw,64px);align-items:start}
.anat-v{margin:0;position:sticky;top:96px;border-radius:var(--radius);
  overflow:hidden;background:var(--char-2);border:1px solid var(--line-d);
  box-shadow:0 24px 60px -24px rgba(0,0,0,.55)}
/* height:auto for the same reason as .icard figure img — the width/height
   attributes map to a presentational height, which silently disables the
   aspect-ratio crop. */
.anat-v img{display:block;width:100%;height:auto;aspect-ratio:3/4;object-fit:cover}
.anat-v figcaption{display:flex;justify-content:space-between;align-items:center;
  gap:12px;padding:14px 18px;font-size:11px;font-weight:800;letter-spacing:.16em;
  text-transform:uppercase;color:var(--white)}
.anat-v figcaption i{font-style:normal;letter-spacing:.1em;color:var(--steel)}
.anat-list{list-style:none;margin:0;padding:0}
.anat-list li{display:grid;grid-template-columns:clamp(46px,4.4vw,68px) 1fr;
  gap:clamp(12px,1.8vw,24px);padding:clamp(17px,2vw,25px) 0;
  border-top:1px solid var(--line-d);
  transition:transform .3s var(--out),border-color .3s}
.anat-list li:last-child{border-bottom:1px solid var(--line-d)}
.anat-list li:hover{transform:translateX(6px);border-color:rgba(var(--volt-mid-rgb),.45)}
/* Same stroke-to-fill trick as .rung .no — outlined at rest, flooded with the
   gradient on hover, so the number reads as a marker rather than as text. */
.an-no{font-family:var(--display);font-weight:900;font-style:italic;line-height:1;
  font-size:clamp(25px,2.5vw,38px);color:transparent;
  -webkit-text-stroke:1.4px rgba(var(--volt-mid-rgb),.5)}
.anat-list li:hover .an-no{background-image:var(--grad-text);
  -webkit-background-clip:text;background-clip:text;color:transparent;
  -webkit-text-stroke:0px transparent}
.anat-list b{display:block;font-family:var(--display);font-weight:900;
  font-style:italic;font-size:clamp(17px,1.6vw,23px);text-transform:uppercase;
  color:var(--white);line-height:1.14}
.anat-list p{margin-top:7px;font-size:14.5px;max-width:56ch;
  color:rgba(var(--mist-rgb),.7)}

/* ═══ 9C. CUSTOMISATION MATRIX (light) ════════════════════════════════════ */
.custg{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,1.6vw,22px)}
.custc{display:flex;flex-direction:column;background:var(--white);
  border:.8px solid var(--line);border-radius:var(--radius);
  padding:clamp(18px,2vw,26px);box-shadow:var(--shadow-s);
  transition:transform .3s var(--out),box-shadow .3s var(--out),border-color .3s}
.custc:hover{transform:translateY(-6px);border-color:rgba(var(--indigo-rgb),.35);
  box-shadow:var(--shadow),0 18px 48px -18px rgba(var(--volt-rgb),.3)}
.cust-b{align-self:flex-start;font-family:var(--display);font-weight:800;
  font-size:12px;color:#fff;background:var(--grad-main);padding:5px 12px;
  border-radius:var(--radius-sm);letter-spacing:.08em;
  box-shadow:0 6px 18px -6px rgba(var(--volt-rgb),.45)}
.custc b{display:block;margin-top:14px;font-family:var(--display);font-weight:900;
  font-style:italic;font-size:19px;text-transform:uppercase;color:var(--ink);
  line-height:1.12}
.custc ul{list-style:none;margin:13px 0 0;padding:0;display:grid;gap:6px}
.custc ul li{position:relative;padding-left:16px;font-size:13.5px;color:var(--body);
  transition:transform .2s ease,color .2s ease}
.custc ul li::before{content:"";position:absolute;left:0;top:.62em;width:7px;
  height:1.5px;background:var(--volt-indigo);opacity:.55}
.custc ul li:hover{transform:translateX(4px);color:var(--ink)}
/* margin-top:auto pins the caveat to the card floor, so eight cards of unequal
   option counts still line their footnotes up along one row. */
.custc i{margin-top:auto;padding-top:14px;font-style:normal;font-size:11.5px;
  line-height:1.45;color:var(--steel);border-top:1px solid var(--line)}

/* ═══ 10. ALIBABA BAND — the one orange thing ═════════════════════════════ */
.taband{display:grid;grid-template-columns:1.2fr .8fr;gap:clamp(24px,3vw,54px);
  align-items:center;background:linear-gradient(120deg,var(--char-2) 0%,var(--char-3) 100%);
  border-top:3px solid var(--ta);border-radius:var(--radius);
  box-shadow:0 24px 60px -24px rgba(0,0,0,.5);
  padding:clamp(26px,3.4vw,54px)}
.taband h3{color:var(--white);font-size:clamp(26px,3vw,44px)}
.taband h3 em{font-style:normal;color:var(--ta)}
.taband p{margin-top:12px;font-size:15px;color:rgba(var(--mist-rgb),.8);max-width:52ch}
.taband .abtn.ta{background:var(--ta);color:var(--ink);box-shadow:0 12px 40px rgba(var(--ta-rgb),.4)}
.taband .abtn.ta:hover{background:#FFA655;box-shadow:0 16px 52px rgba(var(--ta-rgb),.5)}
.taband-r{display:grid;gap:10px;justify-items:end}
.taband-r .abtn{width:max-content}
.taband-r small{font-size:11px;font-weight:700;letter-spacing:.2em;
  text-transform:uppercase;color:var(--steel)}

/* ═══ 11. EXPO STRIP ══════════════════════════════════════════════════════ */
.expo2{overflow:hidden;margin-top:clamp(28px,3vw,44px)}
.expo2-in{display:flex;gap:clamp(14px,1.6vw,22px);width:max-content;
  animation:chevroll 44s linear infinite;will-change:transform}
.expo2-in img{height:clamp(200px,24vw,320px);width:auto;
  box-shadow:var(--shadow-s)}


/* ═══ 12. FINISH-LINE CTA ═════════════════════════════════════════════════ */
.finish{background:
  radial-gradient(44% 40% at 88% 4%,rgba(var(--indigo-rgb),.28),transparent 62%),
  radial-gradient(40% 36% at 6% 92%,rgba(var(--cyan-rgb),.14),transparent 60%),
  linear-gradient(170deg,var(--char-3) 0%,var(--char-2) 55%,var(--char-3) 100%);
  position:relative;overflow:hidden;color:rgba(var(--mist-rgb),.85)}
.finish .wrap{position:relative;display:grid;gap:22px;justify-items:start}
.finish h2{font-size:clamp(46px,6.4vw,100px);color:var(--white)}
.finish p{max-width:56ch;font-weight:500;font-size:clamp(15.5px,1.3vw,18px)}
.finish .kick{color:var(--volt-cyan)}
.finish .fmeta{display:flex;gap:26px;flex-wrap:wrap;font-size:12px;font-weight:800;
  letter-spacing:.2em;text-transform:uppercase;color:var(--steel)}

/* ═══ 13. PAGE HERO (inner pages) ═════════════════════════════════════════ */
.ph{position:relative;background:var(--char);color:var(--white);
  padding:calc(72px + clamp(60px,8vw,110px)) 0 clamp(60px,8vw,110px);overflow:hidden}
.ph::before{content:"";position:absolute;inset:0;
  background:repeating-linear-gradient(115deg,transparent 0 110px,rgba(255,255,255,.035) 110px 112px)}
.ph .wrap{position:relative}
.ph h1{color:var(--white);font-size:clamp(52px,8vw,132px)}
.ph .lead{margin-top:18px;max-width:56ch;font-size:clamp(15.5px,1.3vw,18px);
  color:rgba(var(--mist-rgb),.85)}
.ph .bc{display:flex;gap:10px;align-items:center;font-size:11px;font-weight:800;
  letter-spacing:.26em;text-transform:uppercase;color:var(--volt-cyan);margin-bottom:18px}
.ph .bc::before{content:"";width:34px;height:3px;border-radius:2px;background:var(--grad-text)}

/* ── 13.1 hero media panel ────────────────────────────────────────────────
   Every inner-page hero used to be a flat navy block with a stripe pattern —
   six pages that each opened on nothing but type. .ph-m fills the right side
   of the frame with that page's own footage. .ph already carries
   overflow:hidden and .ph .wrap is already positioned, so the panel drops in
   underneath the copy without touching a single existing rule. */
.ph-m{position:absolute;top:0;right:0;bottom:0;width:48%;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 34%);
          mask-image:linear-gradient(90deg,transparent 0,#000 34%)}
.ph-m img,.ph-m video{display:block;width:100%;height:100%;object-fit:cover;
  transform:scale(1.03)}
/* The scrim is horizontal, not vertical. All the type sits on the left, so the
   left edge of the panel is darkened hard and the right two thirds are left
   almost alone — the footage still reads as footage rather than as texture. */
.ph-m::after{content:"";position:absolute;inset:0;background:
  linear-gradient(90deg,var(--char) 0%,rgba(var(--char-rgb),.80) 22%,
                        rgba(var(--char-rgb),.30) 58%,rgba(var(--char-rgb),.52) 100%)}
.ph.has-m .wrap>*{max-width:min(58%,760px)}
.ph.has-m .lead{max-width:min(58%,46ch)}
@media (max-width:1000px){
  /* Below the split point the panel goes full-bleed and sits behind the copy,
     so the scrim has to become a flat wash — a horizontal gradient under
     full-width type would leave the right half of every line bright. */
  .ph-m{width:100%;-webkit-mask-image:none;mask-image:none}
  .ph-m::after{background:rgba(var(--char-rgb),.84)}
  .ph.has-m .wrap>*,.ph.has-m .lead{max-width:none}
}

/* ═══ 14. GENERIC KIT ═════════════════════════════════════════════════════ */
/* fact rows with jersey numbers */
.facts{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2vw,28px)}
.fact{background:var(--white);padding:clamp(22px,2.4vw,34px);
  border:.8px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-s);
  transition:transform var(--t) var(--out),box-shadow var(--t) var(--out)}
.fact:hover{transform:translateY(-6px);box-shadow:var(--shadow),0 18px 48px -18px rgba(var(--volt-rgb),.3)}
.fact .no{font-family:var(--display);font-weight:800;font-size:12px;color:#fff;
  display:inline-block;background:var(--grad-main);padding:6px 13px;
  border-radius:var(--radius-sm);letter-spacing:.08em;margin-bottom:16px;
  box-shadow:0 6px 18px -6px rgba(var(--volt-rgb),.45)}
.fact b{display:block;font-family:var(--display);font-weight:900;font-style:italic;
  font-size:19px;letter-spacing:0;text-transform:uppercase;color:var(--ink);line-height:1.12}
.fact p{margin-top:10px;font-size:14.5px}

/* photo frames */
.frame{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow)}
.frame img,.frame video{width:100%;object-fit:cover}

/* big video block with caption bar */
.filmb{border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);background:var(--char)}
.filmb video{width:100%;aspect-ratio:16/9;object-fit:cover}
.filmb-cap{display:flex;justify-content:space-between;align-items:center;
  background:var(--white);padding:13px 18px;
  font-size:11px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;color:var(--char)}
.filmb-cap i{font-style:normal;color:var(--steel)}

/* two-column story */
.story{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,4vw,64px);align-items:center}
.story h3{font-size:clamp(28px,3.2vw,48px)}
.story p{margin-top:14px}
.story .spec{margin-top:18px;display:flex;gap:10px;flex-wrap:wrap}
.story .spec span{font-size:11px;font-weight:800;letter-spacing:.14em;
  text-transform:uppercase;color:var(--char);border:var(--hairline);padding:6px 11px}
.asec.char .story .spec span{color:var(--white);border-color:var(--line-d)}

/* process ladder (OEM steps) */
.ladder{display:grid;gap:0;counter-reset:leg}
.rung{display:grid;grid-template-columns:110px 1fr auto;gap:clamp(16px,2.4vw,40px);
  align-items:center;padding:clamp(20px,2.4vw,30px) 0;border-top:var(--hairline);
  transition:background var(--t),padding-left var(--t)}
.rung:last-child{border-bottom:var(--hairline)}
.rung:hover{background:var(--white);padding-left:14px}
.rung .no{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(34px,3.6vw,54px);
  color:transparent;-webkit-text-stroke:1.5px rgba(var(--indigo-rgb),.55);line-height:1}
.rung:hover .no{background-image:var(--grad-main);-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-stroke:0px transparent}
.rung b{display:block;font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(19px,1.9vw,28px);letter-spacing:0;text-transform:uppercase;color:var(--ink)}
.rung p{margin-top:4px;font-size:14.5px;max-width:60ch}
.rung .tag2{font-size:10.5px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;
  color:var(--volt-indigo);border:1px solid rgba(var(--indigo-rgb),.3);
  border-radius:999px;padding:6px 14px;white-space:nowrap}
/* The Alibaba chip. Trade Assurance orange as TEXT on a light ground is
   2.3:1 — unreadable. Filled, with ink on the orange, it is 6.5:1 and it
   still reads unmistakably as the Alibaba colour. */
.rung .tag2.ta{background:var(--ta);border-color:var(--ta);color:var(--ink)}

/* ── 14.1 image-topped cards ──────────────────────────────────────────────
   The generic .fact card is text-only, and four of them in a three-column
   grid leave a third of the last row empty — which is most of what the
   factory page's blank space was. .icard is the same card with the picture
   carrying the top of it, so a set of four sits 2×2 with nothing left over. */
.icards{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(16px,2vw,26px)}
.icards.c3{grid-template-columns:repeat(3,1fr)}
.icard{position:relative;display:flex;flex-direction:column;background:var(--white);
  border:.8px solid var(--line);border-radius:var(--radius);overflow:hidden;
  box-shadow:var(--shadow-s);
  transition:transform var(--t) var(--out),box-shadow var(--t) var(--out)}
.icard:hover{transform:translateY(-6px);
  box-shadow:var(--shadow),0 18px 48px -18px rgba(var(--volt-rgb),.3)}
.icard figure{position:relative;margin:0;overflow:hidden;background:var(--char)}
/* height:auto is load-bearing. eliian_img_attrs() prints the real width and
   height attributes for CLS, and an <img height> maps to a presentational
   `height` declaration — aspect-ratio is only consulted when one axis is auto,
   so without this the crop is ignored and a 1000×1333 certificate renders
   1333px tall inside a 407px card. */
.icard figure img,.icard figure video{display:block;width:100%;height:auto;
  aspect-ratio:16/10;object-fit:cover;transition:scale .6s var(--out)}
.icard:hover figure img,.icard:hover figure video{scale:1.05}
/* Top-down scrim only. The number and the tag both sit in the top corners; the
   bottom of the frame is left clean so the picture meets the card body without
   a dark seam across it. */
.icard figure::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(var(--char-rgb),.50) 0%,transparent 46%)}
.icard .no{position:absolute;z-index:2;top:14px;left:14px;
  font-family:var(--display);font-weight:800;font-size:12px;color:#fff;
  background:var(--grad-main);padding:6px 13px;border-radius:var(--radius-sm);
  letter-spacing:.08em;box-shadow:0 6px 18px -6px rgba(var(--volt-rgb),.45)}
.icard .tag2{position:absolute;z-index:2;top:14px;right:14px;
  font-size:10.5px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;
  color:#fff;border:1px solid rgba(255,255,255,.42);border-radius:999px;
  padding:6px 13px;white-space:nowrap;backdrop-filter:blur(6px)}
.icard .tag2.ta{background:var(--ta);border-color:var(--ta);color:var(--ink)}
.icard-b{flex:1;display:flex;flex-direction:column;gap:9px;
  padding:clamp(18px,2vw,26px)}
.icard-b b{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(18px,1.6vw,21px);letter-spacing:0;text-transform:uppercase;
  color:var(--ink);line-height:1.12}
.icard-b p{font-size:14.5px}
/* On a dark section the white card would punch a hole in the tone, so it
   re-points to a translucent panel instead and the type follows. */
.asec.char .icard{background:rgba(255,255,255,.05);border-color:var(--line-d)}
.asec.char .icard-b b{color:var(--white)}
.asec.char .icard-b p{color:rgba(var(--mist-rgb),.82)}

/* ── 14.2 media + list split ──────────────────────────────────────────────
   For sections that are a short numbered argument rather than a card set.
   The media column is sticky, so the picture stays in frame while the reader
   works down the list beside it — the list can grow without ever reopening
   the blank column the old three-up grid left behind. */
.duo{display:grid;grid-template-columns:.92fr 1.08fr;gap:clamp(26px,3.4vw,58px);
  align-items:start}
.duo-m{position:sticky;top:96px;border-radius:var(--radius);overflow:hidden;
  box-shadow:var(--shadow);background:var(--char)}
.duo-m img,.duo-m video{display:block;width:100%;height:auto;aspect-ratio:4/5;
  object-fit:cover}
.duo-cap{position:absolute;left:0;right:0;bottom:0;
  display:flex;justify-content:space-between;align-items:center;gap:12px;
  padding:16px 18px;font-size:11px;font-weight:800;letter-spacing:.2em;
  text-transform:uppercase;color:#fff;
  background:linear-gradient(180deg,transparent,rgba(var(--char-rgb),.86))}
.duo-cap i{font-style:normal;color:rgba(var(--mist-rgb),.8)}
.ditem{display:grid;grid-template-columns:auto 1fr;gap:clamp(14px,1.6vw,22px);
  padding:clamp(18px,2vw,26px) 0;border-top:var(--hairline)}
.ditem:last-child{border-bottom:var(--hairline)}
.ditem .no{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(30px,3vw,44px);line-height:1;
  color:transparent;-webkit-text-stroke:1.5px rgba(var(--indigo-rgb),.55)}
.ditem b{display:block;font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(18px,1.8vw,25px);letter-spacing:0;text-transform:uppercase;
  color:var(--ink);line-height:1.12}
.ditem p{margin-top:6px;font-size:14.5px;max-width:58ch}
.asec.char .ditem b{color:var(--white)}
.asec.char .ditem .no{-webkit-text-stroke-color:rgba(var(--cyan-rgb),.55)}
@media (max-width:960px){
  .icards,.icards.c3{grid-template-columns:1fr}
  .duo{grid-template-columns:1fr}
  .duo-m{position:static}
  .duo-m img,.duo-m video{aspect-ratio:16/10;max-height:60vh}
}

/* FAQ ladder — a real <dl>, styled to read exactly like a .rung.
   The semantics are for crawlers; the look is unchanged for people. */
.faql{margin:0}
.faqr{grid-template-columns:1fr;align-items:start}
.faql dt{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(19px,1.9vw,28px);letter-spacing:0;text-transform:uppercase;color:var(--ink)}
.faql dd{margin:6px 0 0;font-size:14.5px;max-width:68ch;color:var(--ink-soft,inherit)}

/* spec table (moq etc.) */
.spect{width:100%;border-collapse:separate;border-spacing:0;background:var(--white);
  border-radius:var(--radius-sm);overflow:hidden;box-shadow:var(--shadow-s)}
.spect thead th{font-family:var(--display);font-weight:800;font-style:italic;
  text-transform:uppercase;letter-spacing:.03em;font-size:14px;text-align:left;
  color:var(--white);background:var(--ink);padding:13px 18px}
/* Explicit colour, never inherited: the table paints itself a white card, so
   inside a dark .char section the inherited light text turns every cell into
   near-invisible grey on white. */
.spect td{padding:13px 18px;border-top:1px solid var(--line);font-size:14.5px;
  color:var(--body)}
.spect tr td:first-child{font-weight:800;color:var(--char);white-space:nowrap}

/* GEO fact sheet — scoped row headers so a parser knows which cell labels which.
   Styled to match the first column of every other spec table on the site. */
.fsheet tbody th{padding:13px 18px;border-top:1px solid var(--line);font-size:14.5px;
  text-align:left;font-weight:800;color:var(--char);white-space:nowrap;background:none}
.fsheet tbody td b{font-weight:800;color:var(--ink)}
.fsheet-cap{caption-side:top;text-align:left;font-size:11px;font-weight:800;
  letter-spacing:.2em;text-transform:uppercase;color:var(--volt-indigo);
  padding:0 0 12px}

/* inquiry form */
.aform{display:grid;gap:14px}
.aform .frow{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.aform label{font-size:11px;font-weight:800;letter-spacing:.2em;
  text-transform:uppercase;color:var(--char);display:grid;gap:7px}
.asec.char .aform label{color:rgba(var(--mist-rgb),.85)}
.aform input,.aform select,.aform textarea{width:100%;font:500 15px/1.4 var(--sans);
  color:var(--ink);background:var(--white);border:1px solid rgba(var(--ink-rgb),.18);
  padding:13px 15px;border-radius:10px;
  transition:box-shadow var(--t),border-color var(--t)}
.aform input:focus,.aform select:focus,.aform textarea:focus{outline:none;
  border-color:var(--volt-indigo);box-shadow:0 0 0 3px rgba(var(--indigo-rgb),.18)}
.aform textarea{min-height:130px;resize:vertical}
.aform .abtn{justify-self:start}
.fnote{font-size:12.5px;color:var(--steel)}

/* contact info list */
.clist{display:grid;gap:0}
.clist>div{display:grid;grid-template-columns:150px 1fr;gap:18px;
  padding:16px 0;border-top:1px solid var(--line-d);font-size:15px}
.clist b{font-size:11px;font-weight:800;letter-spacing:.22em;text-transform:uppercase;
  color:var(--volt-cyan);padding-top:3px}
.clist a{border-bottom:2px solid var(--volt-cyan)}
.clist a:hover{color:var(--white)}

/* ═══ 15. FOOTER ══════════════════════════════════════════════════════════ */
.sfoot{background:var(--char);color:rgba(var(--mist-rgb),.72);padding:0 0 34px;
  border-top:3px solid transparent;
  border-image:var(--grad-main) 1}
.sfoot .fcols{display:grid;grid-template-columns:2fr 1fr 1fr 1.2fr;
  gap:clamp(24px,3vw,54px);padding:clamp(44px,5vw,70px) 0 34px}
.sfoot b{color:var(--white)}
.fbrand>b{font-family:var(--display);font-weight:900;font-style:italic;font-size:30px;
  text-transform:uppercase;display:block;margin:10px 0 10px}
.fbrand p{font-size:13.5px;line-height:1.7;max-width:38ch}
.fsoc{display:flex;gap:10px;margin-top:16px}
.fsoc a{width:36px;height:36px;display:grid;place-items:center;border-radius:50%;
  border:1px solid var(--line-d);font-weight:800;font-size:13px;color:var(--white);
  transition:background var(--t),border-color var(--t),color var(--t)}
.fsoc a:hover{background:var(--grad-main);border-color:transparent;color:#fff}
.sfoot h5{font-size:11px;font-weight:800;letter-spacing:.26em;text-transform:uppercase;
  color:var(--steel);margin-bottom:14px}
.sfoot .fcol a{display:block;padding:5px 0;font-size:14px;
  transition:color var(--t),padding-left var(--t)}
.sfoot .fcol a:hover{color:var(--volt-cyan);padding-left:6px}
/* The footer's bottom bar. The markup calls it .fbot; this rule used to be
   written for .fbase alone, a name nothing emits, so the copyright line and the
   trust badges fell back to raw block layout and printed on top of each other.
   Both selectors are kept so either name lays out correctly. */
.fbot,.fbase{display:flex;justify-content:space-between;align-items:center;
  gap:14px 24px;flex-wrap:wrap;
  border-top:1px solid var(--line-d);padding-top:22px;font-size:12px;color:var(--steel)}
.fbadges{display:flex;flex-wrap:wrap;gap:8px}
/* Small trust chips. The Alibaba one is the only place in the footer allowed to
   use the Trade Assurance orange, and it uses it as a border and a dot rather
   than as text: orange type this small on navy is unreadable. */
.fbadges i{font-style:normal;display:inline-flex;align-items:center;gap:7px;
  padding:5px 12px;border-radius:999px;border:1px solid var(--line-d);
  font-size:11px;font-weight:700;letter-spacing:.04em;
  color:rgba(var(--mist-rgb),.82);white-space:nowrap}
.fbadges i::before{content:"";width:6px;height:6px;border-radius:50%;
  background:currentColor;opacity:.9}
.fbadges i.ora{border-color:rgba(var(--ta-rgb),.55);color:rgba(var(--mist-rgb),.95)}
.fbadges i.ora::before{background:var(--ta);opacity:1}
.fbadges i.grn{border-color:rgba(var(--cyan-rgb),.45)}
.fbadges i.grn::before{background:var(--volt-cyan);opacity:1}

/* ═══ 16. INNER TOP BAR fallback (header.php .tbar) ═══════════════════════
   Inner pages emit .tbar from header.php; restyle it to match .abar.on. */
.tbar{position:fixed;top:0;left:0;right:0;z-index:300;display:flex;
  align-items:center;gap:34px;padding:0 clamp(18px,3vw,44px);height:72px;
  background:var(--bar);color:var(--char);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  box-shadow:0 1px 0 rgba(var(--char-rgb),.1)}
.tbar-l{display:flex;align-items:center;gap:12px;margin-right:auto;line-height:1}
.tbar-l svg{width:34px;height:34px}
.tbar-l b{font-family:var(--display);font-weight:400;font-size:24px;text-transform:uppercase}
.tbar-l i{font-style:normal;font-size:9.5px;font-weight:700;letter-spacing:.3em;
  text-transform:uppercase;opacity:.62;display:block;margin-top:2px}
.tnav{display:flex;gap:clamp(14px,2vw,30px)}
.tnav a{position:relative;font-size:13.5px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;padding:6px 2px}
.tnav a::after{content:"";position:absolute;left:0;right:100%;bottom:0;height:3px;
  border-radius:2px;background:var(--grad-main);transition:right var(--t) var(--out)}
.tnav a:hover::after,.tnav a.on::after{right:0}
.tbtn{display:inline-flex;align-items:center;gap:10px;font-family:var(--display);
  font-weight:800;font-size:13px;letter-spacing:.04em;text-transform:uppercase;
  padding:10px 22px;background:var(--grad-main);color:#fff;border:0;
  border-radius:999px;box-shadow:0 8px 26px -8px rgba(var(--volt-rgb),.45);
  transition:transform var(--t),box-shadow var(--t)}
.tbtn:hover{transform:translateY(-2px);box-shadow:0 14px 40px -8px rgba(var(--volt-rgb),.5)}
.tburg{display:none;flex-direction:column;gap:5px;padding:8px}
.tburg i{display:block;width:24px;height:3px;background:var(--char)}

/* mobile menu drop-down: the same links, stacked under the bar */
/* top:100%, not top:72px — the bar shrinks to 60px once .on is set, and a
   hard-coded 72 left a 12px gap the menu floated below. */
.abar.menu-open .anav,.tbar.menu-open .tnav{display:grid;position:absolute;
  top:100%;left:0;right:0;max-width:none;transform:none;
  background:var(--paper);color:var(--char);
  padding:16px clamp(18px,3vw,44px) 22px;gap:2px;
  box-shadow:0 20px 40px -20px rgba(var(--char-rgb),.35)}
.abar.menu-open .anav a,.tbar.menu-open .tnav a{padding:11px 2px;
  border-bottom:1px solid var(--line)}
.abar.menu-open.pills:not(.on) .anav{background:var(--paper);border-radius:0}

/* ═══ 17. REVEALS ═════════════════════════════════════════════════════════ */
.rv{opacity:0;transform:translateY(26px);
  transition:opacity var(--t-slow) var(--out),transform var(--t-slow) var(--out);
  transition-delay:var(--d,0s)}
.rv.on{opacity:1;transform:none}

/* ── one-shot cinema reveals ──────────────────────────────────────────────
   These fire ONCE when the IntersectionObserver flips .rv to .on, then the
   element is completely still. Nothing here is linked to scroll position,
   so wheel steps can never make the cards judder.
   The inner zoom uses the standalone `scale` property so it composes with
   the transform-based hover zooms instead of fighting them. */
.ppost.rv .ppost-v,.leg.rv .leg-v,.leg-v.rv,.filmb.rv{
  clip-path:inset(0 0 96% 0);
  transition:clip-path 1s var(--out);transition-delay:var(--d,0s)}
.ppost.rv.on .ppost-v,.leg.rv.on .leg-v,.leg-v.rv.on,.filmb.rv.on{
  clip-path:inset(0 0 0% 0)}
.ppost.rv .ppost-v video,.ppost.rv .ppost-v img,
.leg.rv .leg-v video,.leg-v.rv video,.filmb.rv video{
  scale:1.12;transition:scale 1.3s var(--out);transition-delay:var(--d,0s)}
.ppost.rv.on .ppost-v video,.ppost.rv.on .ppost-v img,
.leg.rv.on .leg-v video,.leg-v.rv.on video,.filmb.rv.on video{scale:1}
.certc.rv img{scale:1.08;transition:scale 1.1s var(--out);
  transition-delay:var(--d,0s)}
.certc.rv.on img{scale:1}

/* scoreboard: children rise once, staggered */
.score.rv>div{opacity:0;transform:translateY(30px);
  transition:opacity .7s var(--out),transform .7s var(--out)}
.score.rv>div:nth-child(2){transition-delay:.08s}
.score.rv>div:nth-child(3){transition-delay:.16s}
.score.rv>div:nth-child(4){transition-delay:.24s}
.score.rv.on>div{opacity:1;transform:none}

/* ═══ 18. RESPONSIVE ══════════════════════════════════════════════════════ */
@media (max-width:1060px){
  .pgrid2{grid-template-columns:repeat(2,1fr)}
  .score{grid-template-columns:repeat(2,1fr)}
  .score>div:nth-child(odd){border-left:0}
  .score>div:nth-child(n+3){border-top:1px solid var(--line-d)}
  .certrow,.veri{grid-template-columns:repeat(2,1fr)}
  .custg{grid-template-columns:repeat(2,1fr)}
  .facts{grid-template-columns:1fr 1fr}
  .taband{grid-template-columns:1fr}
  .taband-r{justify-items:start}
}
/* Six uppercase links plus a wordmark plus a pill button need about 1100px
   before they stop competing for the same space. Collapsing at 820 meant that
   between 820 and 1100 the nav won and the logo and the button paid for it —
   the squeeze this whole block exists to prevent. Collapse earlier instead. */
@media (max-width:1080px){
  .anav,.tnav{display:none}
  .aburg,.tburg{display:flex}
}
@media (max-width:820px){
  .leg{grid-template-columns:1fr}
  .leg:nth-child(even) .leg-v{order:0}
  .story{grid-template-columns:1fr}
  /* Below this width the sticky picture would eat the whole screen before the
     first note is readable, so it goes back to being an ordinary block. */
  .anat{grid-template-columns:1fr}
  .anat-v{position:static;max-width:420px}
  .aform .frow{grid-template-columns:1fr}
  .sfoot .fcols{grid-template-columns:1fr 1fr}
  .ah-score{display:none}
  .rung{grid-template-columns:70px 1fr}
  .rung .tag2{display:none}
  .clist>div{grid-template-columns:1fr;gap:4px}
}
@media (max-width:560px){
  .pgrid2,.certrow,.veri,.facts,.custg,.sfoot .fcols{grid-template-columns:1fr}
  .score{grid-template-columns:1fr 1fr}
}

/* On a phone the inner-page bar carries one more thing than the home-page bar
   does — the "Xiamen · since 1999" subline — and that extra width was enough
   to push the quote button off the right edge at 390px. The subline is the
   least useful item in the row at this size, so it is the one that goes. */
@media (max-width:620px){
  .abar,.tbar{gap:12px;padding:0 14px}
  .tbar-l i{display:none}
  .abar-l b,.tbar-l b{font-size:19px}
  .abar .abtn,.tbar .tbtn{padding:9px 15px;font-size:11.5px;letter-spacing:.02em}
}

/* ═══ 20. PREMIUM MOTION ══════════════════════════════════════════════════
   The interaction layer. Two principles keep it fast on a page full of
   video: everything animates transform/opacity/clip-path only (compositor
   work, no repaint), and every scroll-linked effect runs on CSS
   scroll-driven animations behind @supports — browsers without them simply
   get the IntersectionObserver reveals, which already work. */

:root{
  /* physics-based spring for hovers — overshoots ~8% then settles */
  --spring:linear(0,.016 .5%,.06 1%,.226 2%,1.116 5.4%,1.375 6.6%,1.527 7.7%,
    1.565 8.2%,1.585 8.8%,1.581 9.3%,1.559 9.8%,1.458 10.9%,.937 14.3%,
    .784 15.5%,.693 16.6%,.67 17.1%,.657 17.7%,.671 18.7%,.729 19.8%,
    1.042 23.3%,1.13 24.5%,1.182 25.6%,1.201 26.7%,1.192 27.7%,1.156 28.8%,
    .977 32.2%,.925 33.4%,.894 34.5%,.882 35.6%,.887 36.6%,.907 37.7%,
    1.045 42.4%,1.069 44.5%,1.059 46.3%,.979 50.9%,.96 53.4%,.966 55.3%,
    1.013 59.9%,1.024 62.3%,.986 71.2%,1.008 79.9%,.995 88.9%,1);
}

/* springy hovers replace the plain ones */
.abtn,.tbtn{transition:transform .55s var(--spring),box-shadow .3s var(--out),
  background var(--t)}
.ppost,.fact{transition:transform .6s var(--spring),box-shadow .35s var(--out)}
/* card media breathes on a very slow drift, so even a video waiting for its
   playback slot never reads as frozen. transform-only, compositor work. */
@media (prefers-reduced-motion:no-preference){
  .ppost-v video,.ppost-v img{animation:mediaBreath 16s ease-in-out infinite alternate}
  .ppost:nth-child(2) .ppost-v video{animation-delay:-4s}
  .ppost:nth-child(3) .ppost-v video{animation-delay:-8s}
  .ppost:nth-child(4) .ppost-v video{animation-delay:-12s}
  @keyframes mediaBreath{from{transform:scale(1) translateY(0)}
    to{transform:scale(1.08) translateY(-1.5%)}}
}
.leg-v video{transition:transform .9s var(--spring)}
.leg-v:hover video{transform:scale(1.04)}
.certc img{transition:transform .7s var(--spring)}
.certc:hover img{transform:scale(1.045)}

/* word-stagger heading reveal: JS wraps each word of .ahead h2 / .ph h1 in
   <span class=w><span>word</span></span>; the outer clips, the inner rises. */
.ahead h2 .w,.ph h1 .w{display:inline-block;overflow:hidden;
  vertical-align:bottom;margin-bottom:-.14em;padding-bottom:.14em}
.ahead h2 .w>span,.ph h1 .w>span{display:inline-block;
  transform:translateY(115%)}
.wsplit.on .w>span{animation:wup .7s var(--out) forwards;
  animation-delay:calc(var(--wi) * 70ms)}
/* words tagged .gw by the splitter render in brand gradient */
.wsplit .w>span.gw{background-image:var(--grad-main);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.asec.char .wsplit .w>span.gw,.ph .wsplit .w>span.gw{
  background-image:var(--grad-text);
  -webkit-background-clip:text;background-clip:text;color:transparent}
@keyframes wup{to{transform:translateY(0)}}

/* ticker: pause when the pointer is over it, so it can be read */
.chev:hover .chev-in,.expo2:hover .expo2-in{animation-play-state:paused}

/* nav condenses after scroll */
.abar,.tbar{transition:background var(--t),color var(--t),
  box-shadow var(--t),height .35s var(--out)}
.abar.on{height:60px}

/* ── bar geometry guards ──────────────────────────────────────────────────
   The bar is one row that must never break, whatever the viewport is and
   whatever a plugin stylesheet does to it. This block is why the header
   stopped deforming on the live install:

   1. `box-sizing:border-box` — the clamp() side padding has to live INSIDE
      the full-bleed width. A plugin reset that hands everything back to
      `content-box` otherwise makes the bar 88px wider than the viewport, and
      the right-hand button falls off the screen.
   2. `flex:0 0 auto` on the two ends. The mark is an inline `<svg>` carrying
      only a viewBox — no intrinsic pixel size — so its `min-width:auto`
      resolves to 0. As a shrinkable flex item it therefore collapses to
      nothing under the slightest pressure, which is exactly how the logo
      vanished while the wordmark slid to the very edge. Pinning the basis at
      34px makes that impossible.
   3. The nav is the ONLY item allowed to shrink, and it never wraps — it
      hands space back to the logo and the CTA instead of pushing them out.
   `max-width:100vw` is a last-ditch clamp for the same reason: a fixed
   element sized off left/right:0 can still be widened by a stray transform
   ancestor, and a bar wider than the screen is what "变形" looks like. */
.abar,.tbar{box-sizing:border-box;max-width:100vw}
.abar>*,.tbar>*{flex:0 0 auto}
.abar-l,.tbar-l{flex:0 0 auto;min-width:0;white-space:nowrap}
.abar-l svg,.tbar-l svg,
.abar-l .elmark,.tbar-l .elmark,
.abar-l .eliian-logo-img,.tbar-l .eliian-logo-img{
  flex:0 0 34px;width:34px;height:34px;max-width:none;display:block}

/* ── the EL monogram ──────────────────────────────────────────────────────
   Two cuts of the same mark, one navy and one white, stacked in the markup and
   swapped by CSS. Which one is correct depends entirely on what is behind it,
   and that changes as you scroll: over the hero video the home-page bar wraps
   the logo in a dark translucent pill, then .on turns the whole bar to paper.
   `display` rather than `opacity` so the hidden cut is not painted at all and
   never bleeds a halo through the visible one. */
.elmark{position:relative;line-height:0}
.elmark img{display:block;width:100%;height:100%;object-fit:contain}
.elmark .elmark-l{display:none}
.abar:not(.on) .elmark .elmark-d,
.sfoot .fbrand .elmark .elmark-d{display:none}
.abar:not(.on) .elmark .elmark-l,
.sfoot .fbrand .elmark .elmark-l{display:block}
/* `display:block` is load-bearing, not tidiness. .elmark inherits `inline` and
   an inline box ignores width/height, so the mark was laying out at the full
   width of the footer's first column and the inner img's height:100% resolved
   against that — a 40px monogram rendering at 442×680 and shoving the whole
   bottom bar out of place. The header cuts get display:block from the rule
   above; the footer cut never did. */
.sfoot .fbrand .elmark{display:block;flex:0 0 40px;width:40px;height:40px}
.anav,.tnav{flex:0 1 auto;min-width:0;flex-wrap:nowrap;justify-content:center}
.anav a,.tnav a{white-space:nowrap}
.abar .abtn,.tbar .tbtn{flex:0 0 auto;white-space:nowrap}

/* The floating pill on the home page is centred with absolute positioning, so
   flex cannot protect it. Cap it to the gap actually left between the logo
   pill and the quote button, and let it scroll internally rather than run
   underneath them. */
.abar.pills:not(.on) .anav{max-width:calc(100% - 2 * clamp(18px,3vw,44px) - 430px)}

/* WordPress admin bar. When you are logged in, WP paints a 32px bar over the
   top of the document — and a `position:fixed;top:0` header lands underneath
   it. Every logged-in view of the site had the top of the nav covered; a
   visitor never saw it, which is why it reads as "it deforms in the backend". */
body.admin-bar .abar,body.admin-bar .tbar{top:32px}
@media screen and (max-width:782px){
  body.admin-bar .abar,body.admin-bar .tbar{top:46px}
}

/* giant ghost word — sections carrying data-ghost get a huge outlined
   watermark drifting slower than the content (parallax by view-timeline). */
.asec[data-ghost]{overflow:clip}
.asec[data-ghost]::after{content:attr(data-ghost);position:absolute;
  right:-.06em;bottom:-.14em;z-index:0;pointer-events:none;
  font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(120px,16vw,280px);
  line-height:1;text-transform:uppercase;letter-spacing:-.01em;
  color:transparent;-webkit-text-stroke:1.5px rgba(var(--ink-rgb),.07)}
.asec.char[data-ghost]::after{-webkit-text-stroke:1.5px rgba(255,255,255,.05)}
.asec[data-ghost]>.wrap{position:relative;z-index:1}

/* cursor: a dot that grows into a lime ring over anything clickable.
   Pointer-fine only; the system cursor stays visible underneath. */
@media (pointer:fine){
  #cdot{display:block;position:fixed;left:0;top:0;width:9px;height:9px;
    border-radius:50%;background:var(--volt-cyan);
    z-index:600;pointer-events:none;transform:translate(-50%,-50%);
    transition:width .3s var(--out),height .3s var(--out),opacity .2s,
      background .3s,border-color .3s;
    opacity:0;border:1.5px solid transparent}
  body.cur #cdot{opacity:1}
  body.cur.cur-link #cdot{width:38px;height:38px;background:rgba(var(--cyan-rgb),.12);
    border-color:var(--volt-cyan)}
}

/* ── scroll-driven layer (progressive enhancement) ── */
@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline:view()){

    /* hero: video and text part company as you scroll away (depth) */
    @keyframes hero-sink{to{transform:translateY(9%) scale(1.06)}}
    .ah-bg{animation:hero-sink auto linear both;
      animation-timeline:view();animation-range:exit}
    @keyframes hero-lift{to{transform:translateY(-60px);opacity:.1}}
    .ah-in,.ah-score{animation:hero-lift auto linear both;
      animation-timeline:view();animation-range:exit 0% exit 70%}

    /* inner-page titles drift up slightly faster than the page */
    @keyframes ph-drift{to{transform:translateY(-46px);opacity:.25}}
    .ph .wrap{animation:ph-drift auto linear both;
      animation-timeline:view();animation-range:exit}

    /* ghost words move slower than their section — that lag is the
       parallax that sells the depth */
    @keyframes ghost-drift{
      from{transform:translateY(.22em)}
      to{transform:translateY(-.1em)}
    }
    .asec[data-ghost]::after{animation:ghost-drift auto linear both;
      animation-timeline:view()}

  }
}

/* ═══ 19. REDUCED MOTION ══════════════════════════════════════════════════ */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;
    animation-iteration-count:1 !important;transition-duration:.01ms !important;
    scroll-behavior:auto !important}
  .rv{opacity:1;transform:none}
  .ppost.rv .ppost-v,.leg.rv .leg-v,.leg-v.rv,.filmb.rv{clip-path:none}
  .ppost.rv .ppost-v video,.ppost.rv .ppost-v img,.leg.rv .leg-v video,
  .leg-v.rv video,.filmb.rv video,.certc.rv img{scale:1}
  .score.rv>div{opacity:1;transform:none}
  .ah h1 .row span,.ahw>span{transform:none;animation:none}
  .wsplit .w>span,.ahead h2 .w>span,.ph h1 .w>span{transform:none;animation:none}
  #cdot{display:none}
  body.lift #curtain{display:none}
}

/* footer logo: cap the vector and paint it for navy ground */
.sfoot .fbrand svg,.sfoot .fbrand .eliian-logo-img{width:40px;height:40px}
.sfoot .fbrand svg path{fill:#fff}
.tbar-l svg path,.abar-l svg path{fill:currentColor}

/* pill nav: lowercase, like the reference frame */
.abar.pills:not(.on) .abar-l b{text-transform:lowercase;font-weight:800;
  font-style:normal;font-size:21px;letter-spacing:0}
.abar.pills:not(.on) .anav a{text-transform:lowercase;letter-spacing:.02em;
  font-weight:600;font-size:14px}
.abar.pills:not(.on) .abtn{text-transform:lowercase;letter-spacing:.01em;
  font-weight:700;font-size:14.5px}

/* ═══ WHATSAPP ════════════════════════════════════════════════════════════ */
/* WhatsApp green is a light colour: white text on it is 2.0:1, unreadable, and
   that is what this button was doing. Dark ink on the green is 7.5:1 and is
   also how WhatsApp itself sets the colour on light interfaces. */
.abtn.wa{background:var(--wa);color:#0A2E1C;
  box-shadow:0 12px 34px -10px rgba(var(--wa-rgb),.55)}
.abtn.wa:hover{box-shadow:0 16px 44px -10px rgba(var(--wa-rgb),.65)}

/* global floating bubble — every page, above everything but the curtain */
.wafab{position:fixed;right:22px;bottom:22px;z-index:360;width:58px;height:58px;
  border-radius:50%;background:var(--wa);display:grid;place-items:center;
  box-shadow:0 14px 34px -8px rgba(var(--wa-rgb),.55);
  transition:transform .35s var(--out),box-shadow .35s var(--out)}
.wafab svg{width:31px;height:31px;fill:#fff}
.wafab:hover{transform:translateY(-4px) scale(1.05);
  box-shadow:0 20px 44px -8px rgba(var(--wa-rgb),.65)}
.wafab::before{content:"";position:absolute;inset:0;border-radius:50%;
  border:2px solid rgba(var(--wa-rgb),.7);animation:waPing 2.4s var(--out) infinite}
@keyframes waPing{0%{transform:scale(1);opacity:.8}
  70%,100%{transform:scale(1.55);opacity:0}}
.wafab-tip{position:absolute;right:70px;top:50%;transform:translateY(-50%);
  white-space:nowrap;font-size:12.5px;font-weight:700;color:var(--ink);
  background:#fff;border-radius:999px;padding:9px 16px;
  box-shadow:var(--shadow-s);opacity:0;pointer-events:none;
  translate:8px 0;transition:opacity .3s var(--out),translate .3s var(--out)}
.wafab:hover .wafab-tip{opacity:1;translate:0 0}
@media (max-width:560px){.wafab{right:14px;bottom:14px;width:52px;height:52px}
  .wafab-tip{display:none}}
@media (prefers-reduced-motion:reduce){.wafab::before{animation:none;display:none}}

/* ═══ 12. MOTION CALIBRATION ═══════════════════════════════════════════════
   Measured against the live eliian.com (theme eliian-volt, volt.css 3.7.3)
   and codified in the eliian-style skill. Everything below is here to make
   this site behave exactly like the main brand site. */

/* ── 12.1 fail-safe progressive enhancement ────────────────────────────────
   The live site hangs every hidden-initial-state on classes that JavaScript
   itself adds (js-anim / fx-anim). If the script never runs, the page is
   simply visible instead of blank. We now do the same. */
html:not(.js-anim) .rv,
html:not(.js-anim) .score.rv > div,
html:not(.js-anim) .wsplit .w > span,
html:not(.js-anim) .ahw > span{opacity:1 !important;transform:none !important}
html:not(.js-anim) .ppost.rv .ppost-v,
html:not(.js-anim) .leg.rv .leg-v,
html:not(.js-anim) .leg-v.rv,
html:not(.js-anim) .filmb.rv{clip-path:none !important}
html:not(.js-anim) .ppost.rv .ppost-v video,
html:not(.js-anim) .ppost.rv .ppost-v img,
html:not(.js-anim) .leg.rv .leg-v video,
html:not(.js-anim) .leg-v.rv video,
html:not(.js-anim) .filmb.rv video,
html:not(.js-anim) .certc.rv img{scale:1 !important}

/* the curtain gets a pure-CSS escape hatch too: 3.4s and it goes, script or
   no script. Plus the brand's curved exit edge on the way out. */
#curtain{animation:curtainAuto .5s ease 3.4s forwards}
@keyframes curtainAuto{to{opacity:0;visibility:hidden;pointer-events:none}}
body.lift #curtain{transform:translateY(-101%);
  border-radius:0 0 50% 50% / 0 0 8% 8%}
#curtain{transition:transform .7s cubic-bezier(.7,0,.3,1) .9s,
  border-radius .7s cubic-bezier(.7,0,.3,1) .9s,visibility 0s 1.7s}

/* ── 12.2 hover sheen on the product cards ─────────────────────────────────
   A skewed light band crosses the whole card once per hover. Compositor-only,
   no layout, no judder. */
.ppost::before{content:"";position:absolute;z-index:3;top:-10%;left:-55%;
  width:42%;height:120%;pointer-events:none;opacity:0;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.45),transparent);
  transform:skewX(-18deg) translateX(0);
  transition:transform .7s var(--out),opacity .3s var(--out)}
.ppost:hover::before{opacity:1;transform:skewX(-18deg) translateX(430%)}
@media (prefers-reduced-motion:reduce){.ppost::before{display:none}}

/* ── 12.3 reveals keep their corners ───────────────────────────────────────
   inset(... round R) means the clip wipes in with the radius already on it,
   rather than a square rectangle growing rounded corners at the last frame. */
.leg.rv .leg-v,.leg-v.rv,.filmb.rv{clip-path:inset(0 0 96% 0 round var(--radius))}
.leg.rv.on .leg-v,.leg-v.rv.on,.filmb.rv.on{clip-path:inset(0 round var(--radius))}

/* ── 12.4 no layout thrash on list hovers ──────────────────────────────────
   padding-left animation reflows the row; the brand site slides with
   transform instead and adds a coloured shadow. */
.rung{transition:background var(--t),transform var(--t) var(--out),
  box-shadow var(--t) var(--out)}
.rung:hover{background:var(--white);padding-left:0;
  transform:translateX(6px);box-shadow:0 12px 36px -12px rgba(var(--volt-rgb),.28)}
.sfoot .fcol a{transition:color var(--t),transform var(--t) var(--out)}
.sfoot .fcol a:hover{padding-left:0;transform:translateX(6px)}

/* ── 12.5 the eyebrow's pulsing rule ──────────────────────────────────────*/
@media (prefers-reduced-motion:no-preference){
  .kick::before{animation:tagPulse 2.6s ease-in-out infinite alternate}
  @keyframes tagPulse{from{width:26px;opacity:.7}to{width:40px;opacity:1}}
}

/* ── 12.6 ambient periods must not be integer multiples ───────────────────
   Same-duration-with-a-delay still means every card turns around on the same
   frame. Give each one its own prime-ish period and they never sync up. */
@media (prefers-reduced-motion:no-preference){
  .ppost:nth-child(1) .ppost-v video,
  .ppost:nth-child(1) .ppost-v img{animation-duration:16s}
  .ppost:nth-child(2) .ppost-v video,
  .ppost:nth-child(2) .ppost-v img{animation-duration:19s;animation-delay:-5s}
  .ppost:nth-child(3) .ppost-v video,
  .ppost:nth-child(3) .ppost-v img{animation-duration:13s;animation-delay:-9s}
  .ppost:nth-child(4) .ppost-v video,
  .ppost:nth-child(4) .ppost-v img{animation-duration:22s;animation-delay:-3s}
  /* The style gallery runs eight cards, so the period table has to run to
     eight as well — otherwise cards 5–8 inherit card 1's 16s and half the grid
     breathes in lockstep, which is far more noticeable than no drift at all.
     17/23/14/20 keeps every pair of periods co-prime-ish against the first four. */
  .ppost:nth-child(5) .ppost-v video,
  .ppost:nth-child(5) .ppost-v img{animation-duration:17s;animation-delay:-11s}
  .ppost:nth-child(6) .ppost-v video,
  .ppost:nth-child(6) .ppost-v img{animation-duration:23s;animation-delay:-6s}
  .ppost:nth-child(7) .ppost-v video,
  .ppost:nth-child(7) .ppost-v img{animation-duration:14s;animation-delay:-2s}
  .ppost:nth-child(8) .ppost-v video,
  .ppost:nth-child(8) .ppost-v img{animation-duration:20s;animation-delay:-14s}
  .leg-v video,.filmb video{animation:mediaBreath 21s ease-in-out infinite alternate}
  .leg:nth-child(even) .leg-v video{animation-duration:17s;animation-delay:-6s}
}

/* ── 12.7 the brand's card/image speed gap ────────────────────────────────
   Card moves at .3s, the picture inside follows at .5s. That half-beat lag is
   most of what reads as "expensive". */
.certc,.veri > div{transition:transform .3s var(--out),border-color .3s,
  box-shadow .3s var(--out)}
.certc img,.leg-v video{transition:transform .5s var(--out)}

/* 12.8 the drifting videos own `transform`, so their hover zoom has to travel
   on the standalone `scale` property — otherwise the running animation wins
   the cascade and the hover silently does nothing. */
.leg-v video,.filmb video{transition:scale .5s var(--out)}
.leg-v:hover video{transform:none;scale:1.05}
.filmb:hover video{scale:1.04}

/* ── 15. editorial ────────────────────────────────────────────────────────
   The Insights section. Everything here is new surface: before this the blog
   templates printed .psec / .eliian-prose / .eliian-list, none of which had a
   single rule in this file, so an article rendered as raw browser default in
   the middle of a designed site.

   The reading column is deliberately narrower than .wrap. A long-form answer
   about AQL sampling is read, not scanned, and 74ch is the widest measure that
   still tracks comfortably at this body size. */
.entry{max-width:74ch;margin:0 auto;color:var(--body);
  font-size:clamp(16px,1.32vw,18.5px);line-height:1.76}
.entry > *:first-child{margin-top:0}
.entry > *:last-child{margin-bottom:0}
.entry p{margin:0 0 1.15em}
/* The opening paragraph carries the weight of a standfirst without needing a
   class on it, so the writer can paste plain HTML and still get the ramp. */
.entry > p:first-of-type{font-size:clamp(17.5px,1.55vw,21px);line-height:1.62;
  color:var(--ink);font-weight:500}
.entry h2{font-family:var(--display);font-weight:900;font-style:italic;
  text-transform:uppercase;letter-spacing:-.01em;line-height:1.08;
  font-size:clamp(27px,3.1vw,42px);color:var(--ink);
  margin:1.85em 0 .5em;padding-top:.55em;border-top:var(--hairline)}
.entry h3{font-family:var(--display);font-weight:900;font-style:italic;
  text-transform:uppercase;letter-spacing:0;line-height:1.16;
  font-size:clamp(18px,1.75vw,23px);color:var(--ink);margin:1.6em 0 .45em}
.entry h4{font-weight:800;font-size:1.02em;color:var(--ink);margin:1.4em 0 .4em}
.entry strong,.entry b{font-weight:800;color:var(--ink)}
.entry em{font-style:italic}
.entry a{color:var(--volt-indigo);font-weight:600;
  text-decoration:underline;text-decoration-thickness:1.5px;
  text-underline-offset:3px;text-decoration-color:rgba(var(--indigo-rgb),.35);
  transition:text-decoration-color var(--t) var(--out),color var(--t) var(--out)}
.entry a:hover{color:var(--volt-blue);text-decoration-color:currentColor}
.entry ul,.entry ol{margin:0 0 1.25em;padding-left:1.25em}
.entry li{margin:0 0 .55em}
.entry li::marker{color:var(--volt-indigo);font-weight:800}
.entry blockquote{margin:1.5em 0;padding:2px 0 2px clamp(16px,2vw,26px);
  border-left:3px solid var(--volt-indigo);color:var(--ink);font-weight:500}
.entry hr{border:0;border-top:var(--hairline);margin:2em 0}
.entry code{font-size:.92em;background:rgba(var(--ink-rgb),.06);
  padding:2px 6px;border-radius:6px}
.entry img{display:block;width:100%;height:auto;border-radius:var(--radius);
  margin:1.5em 0;box-shadow:var(--shadow-s)}
.entry figcaption{font-size:12.5px;color:var(--body);text-align:center;
  margin-top:-.9em;margin-bottom:1.6em}
/* Tables inside prose get the same .spect skin the spec pages use, whether or
   not the writer remembered the class — and they break the reading measure on
   purpose, because a four-column sampling table at 74ch is unreadable. */
.entry table{width:100%;max-width:none;border-collapse:separate;border-spacing:0;
  background:var(--white);border-radius:var(--radius-sm);overflow:hidden;
  box-shadow:var(--shadow-s);margin:1.6em 0;font-size:14.5px}
.entry thead th{font-family:var(--display);font-weight:800;font-style:italic;
  text-transform:uppercase;letter-spacing:.03em;font-size:14px;text-align:left;
  color:var(--white);background:var(--ink);padding:13px 18px}
.entry td{padding:13px 18px;border-top:1px solid var(--line);color:var(--body)}
.entry tr td:first-child{font-weight:800;color:var(--char)}
@media(min-width:1100px){
  /* Let a wide table use the room the prose column gives up, centred on it. */
  .entry table{width:min(96ch,92vw);
    margin-left:calc((74ch - min(96ch,92vw))/2)}
}
@media(max-width:760px){
  .entry table{display:block;overflow-x:auto;-webkit-overflow-scrolling:touch}
}

/* 15.1 article meta — date, reading time, byline */
.imeta{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;
  font-size:11px;font-weight:800;letter-spacing:.18em;text-transform:uppercase;
  color:var(--volt-indigo)}
.imeta i{font-style:normal;color:var(--body);display:flex;align-items:center;gap:14px}
/* The dot is a separator, so it only exists between two items. A related-post
   card carries reading time and nothing else; a leading dot there reads as a
   bullet that lost its list. */
.imeta i:not(:first-child)::before{content:"";width:4px;height:4px;border-radius:50%;
  background:rgba(var(--ink-rgb),.28)}
.ph .imeta-h{margin-top:22px;color:var(--volt-cyan)}
.ph .imeta-h i{color:rgba(var(--mist-rgb),.72)}
.ph .imeta-h i:not(:first-child)::before{background:rgba(var(--mist-rgb),.4)}
.ph .bc a{color:inherit;text-decoration:none;opacity:.86;
  transition:opacity var(--t) var(--out)}
.ph .bc a:hover{opacity:1}

/* 15.2 the card title link — the whole card is the hit area, but only the
   title is in the tab order, so a keyboard user gets one stop per article
   rather than one per card plus one per image. */
.icard-b b a{color:inherit;text-decoration:none}
.icard-b b a:hover{color:var(--volt-indigo)}
.asec.char .icard-b b a:hover{color:var(--volt-cyan)}
.istretch::after{content:"";position:absolute;inset:0;z-index:3}

/* 15.3 keep-reading strip */
.mnext{max-width:none;margin-top:clamp(46px,5.4vw,80px);
  padding-top:clamp(26px,3vw,42px);border-top:var(--hairline)}
.mnext .kick{display:block;margin-bottom:clamp(16px,2vw,24px)}
.mnext .icard-b{gap:7px}
.mnext .icard-b b{font-size:clamp(16px,1.35vw,18px)}

/* 15.4 pagination */
.pgn .nav-links{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.pgn .screen-reader-text{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}
.pgn .page-numbers{display:inline-flex;align-items:center;justify-content:center;
  min-width:42px;height:42px;padding:0 14px;border-radius:999px;
  border:1px solid var(--line);background:var(--white);color:var(--ink);
  font-size:12.5px;font-weight:800;letter-spacing:.06em;text-decoration:none;
  transition:transform var(--t) var(--out),border-color var(--t),color var(--t)}
.pgn a.page-numbers:hover{transform:translateY(-2px);
  border-color:var(--volt-indigo);color:var(--volt-indigo)}
.pgn .page-numbers.current{background:var(--grad-main);border-color:transparent;
  color:#fff}

@media(max-width:860px){
  .entry{max-width:none}
}

/* 15.5 long-headline hero
   A page title is a slogan — two or three words that can carry 132px. An
   article headline, or the 404 apology, is a sentence, and at that size a real
   one runs to four or five lines inside the 58% column the media panel leaves
   it. Same frame, same scrim, a type size a sentence can actually live at, and
   a little of the width taken back from the panel. */
.ph.ph-long h1{font-size:clamp(34px,4.3vw,66px);line-height:1.03}
.ph.ph-long.has-m .wrap>*{max-width:min(66%,900px)}
.ph.ph-long.has-m .lead{max-width:min(60%,54ch)}
@media (max-width:1000px){
  .ph.ph-long.has-m .wrap>*,.ph.ph-long.has-m .lead{max-width:none}
}

/* ═══ 16. THE LOOKBOOK, THE NINE STEPS, THE DOCUMENT WALL ═════════════════
   Three photo components added once the factory's own photography arrived.
   All three reuse tokens and the existing .ppost card rather than inventing a
   second card system — the only genuinely new pieces here are the filter row
   and the two figure captions. */

/* ── 16.1 lookbook filter ────────────────────────────────────────────────
   Chips, not a <select>. A select hides the range behind a tap, and the range
   is the argument this section is making. The count in each chip is doing
   real work too: "Jackets 11" says more about the factory than the word
   "Jackets" on its own. */
.lbfil{display:flex;flex-wrap:wrap;gap:8px;
  margin:0 0 clamp(22px,2.6vw,34px)}
.lbf{display:inline-flex;align-items:center;gap:7px;
  font-family:var(--sans);font-size:12px;font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;color:var(--body);
  background:var(--white);border:.8px solid var(--line);
  border-radius:999px;padding:9px 16px;
  transition:color var(--t) var(--out),border-color var(--t) var(--out),
    background var(--t) var(--out),transform var(--t) var(--out)}
.lbf i{font-style:normal;font-size:10.5px;font-weight:800;color:var(--steel);
  background:var(--paper);border-radius:999px;padding:2px 7px;line-height:1.5;
  transition:inherit}
.lbf:hover{border-color:rgba(var(--volt-rgb),.4);color:var(--ink);
  transform:translateY(-2px)}
.lbf.on{background:var(--grad-main);border-color:transparent;color:#fff;
  box-shadow:0 8px 24px -10px rgba(var(--volt-rgb),.55)}
.lbf.on i{background:rgba(255,255,255,.22);color:#fff}
.lbf:focus-visible{outline:3px solid var(--volt-indigo);outline-offset:3px}

/* Hidden by the filter. Kept in the DOM — removing and re-inserting 49 nodes
   on every chip press throws away the browser's decoded images. */
.lb-item[hidden]{display:none}

.lbnote{margin-top:clamp(22px,2.4vw,34px);font-size:14.5px;color:var(--body);
  max-width:70ch}

/* ── 16.2 home-page strip ────────────────────────────────────────────────
   Six across on desktop. Caption sits over the image because a strip with
   captions underneath becomes a second grid of cards competing with the
   product-line grid directly above it. */
.lbstrip{display:grid;grid-template-columns:repeat(6,1fr);
  gap:clamp(10px,1.2vw,18px)}
.lbs{position:relative;display:block;overflow:hidden;
  border-radius:var(--radius-sm);background:var(--char);
  box-shadow:var(--shadow-s);
  transition:transform var(--t) var(--out),box-shadow var(--t) var(--out)}
.lbs img{width:100%;aspect-ratio:.72/1;object-fit:cover;
  transition:transform var(--t-slow) var(--out)}
.lbs::after{content:"";position:absolute;inset:auto 0 0 0;height:56%;
  background:linear-gradient(to top,rgba(var(--char-rgb),.86),transparent);
  opacity:0;transition:opacity var(--t) var(--out)}
.lbs-c{position:absolute;z-index:2;left:12px;right:12px;bottom:11px;
  opacity:0;transform:translateY(7px);
  transition:opacity var(--t) var(--out),transform var(--t) var(--out)}
.lbs-c b{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:13.5px;line-height:1.15;color:#fff;text-transform:uppercase;
  display:block}
.lbs:hover{transform:translateY(-6px);
  box-shadow:var(--shadow),0 18px 44px -18px rgba(var(--volt-rgb),.4)}
.lbs:hover img{transform:scale(1.06)}
.lbs:hover::after,.lbs:hover .lbs-c{opacity:1}
.lbs:hover .lbs-c{transform:none}
.lbs:focus-visible{outline:3px solid var(--volt-indigo);outline-offset:3px}

/* ── 16.3 the nine steps ─────────────────────────────────────────────────
   Landscape frames, three across, numbered in the corner the way the factory
   numbered them. */
.st9grid{display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(16px,2vw,28px)}
.st9{background:var(--white);border:.8px solid var(--line);
  border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-s);
  transition:transform var(--t) var(--out),box-shadow var(--t) var(--out)}
.st9:hover{transform:translateY(-6px);
  box-shadow:var(--shadow),0 18px 48px -18px rgba(var(--volt-rgb),.32)}
.st9-i{position:relative;background:var(--char)}
.st9-i img{width:100%;aspect-ratio:5/4;object-fit:cover}
.st9-no{position:absolute;top:13px;left:13px;z-index:2;
  font-family:var(--display);font-weight:800;font-size:12px;color:#fff;
  background:var(--grad-main);padding:6px 12px;border-radius:999px;
  letter-spacing:.08em;box-shadow:0 6px 18px -6px rgba(var(--volt-rgb),.5)}
.st9 figcaption{padding:clamp(18px,2vw,26px)}
.st9 figcaption b{display:block;font-family:var(--display);font-weight:900;
  font-style:italic;font-size:19px;text-transform:uppercase;color:var(--ink);
  line-height:1.12}
.st9 figcaption p{margin-top:9px;font-size:14px;line-height:1.65}

/* ── 16.4 two wide figures ───────────────────────────────────────────────
   The document wall on /factory/ and the mockup board on /oem-odm/. Same
   shape, opposite grounds, so each inherits its section's tone. */
.honwall,.mockup{margin-top:clamp(28px,3.4vw,48px);
  border-radius:var(--radius);overflow:hidden;
  border:.8px solid var(--line-d);background:rgba(255,255,255,.04)}
.mockup{border-color:var(--line);background:var(--white);
  box-shadow:var(--shadow-s);display:grid;grid-template-columns:1.15fr 1fr;
  align-items:center}
.honwall img,.mockup img{width:100%;display:block}
.honwall figcaption{padding:clamp(18px,2vw,26px);
  border-top:1px solid var(--line-d)}
.honwall figcaption b{display:block;font-family:var(--display);font-weight:900;
  font-style:italic;font-size:18px;text-transform:uppercase;color:var(--white)}
.honwall figcaption span{display:block;margin-top:7px;font-size:14px;
  color:rgba(var(--mist-rgb),.8);max-width:72ch}
.mockup figcaption{padding:clamp(22px,2.6vw,40px)}
.mockup figcaption b{display:block;margin-top:14px;font-family:var(--display);
  font-weight:900;font-style:italic;font-size:clamp(22px,2.4vw,32px);
  text-transform:uppercase;color:var(--ink);line-height:1.08}
.mockup figcaption p{margin-top:11px;font-size:14.5px}

@media(max-width:1180px){
  .lbstrip{grid-template-columns:repeat(4,1fr)}
}
@media(max-width:1000px){
  .st9grid{grid-template-columns:repeat(2,1fr)}
  .mockup{grid-template-columns:1fr}
}
@media(max-width:700px){
  .lbstrip{grid-template-columns:repeat(3,1fr)}
  .st9grid{grid-template-columns:1fr}
  /* On a phone the chip row is the only way to make 49 cards usable, so it
     stays visible while it scrolls sideways rather than wrapping to four
     rows and pushing the grid off screen. */
  .lbfil{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    margin-inline:calc(50% - 50vw);padding-inline:4vw;
    scroll-snap-type:x proximity}
  .lbfil::-webkit-scrollbar{display:none}
  .lbf{flex:none;scroll-snap-align:start}
}

/* ═══ 17. THE QUIET LAYER ═════════════════════════════════════════════════
   One override block, loaded last, that switches off the decoration rather
   than deleting the rules that draw it. Everything neutralised here is still
   defined above; re-enabling any of it is a matter of deleting a rule from
   this section, which is why it is a section and not a hundred edits
   scattered through the file.

   The ordering principle: keep motion that tells you where you are (reveal on
   scroll, hover lift), remove motion that is only there to be noticed. */

/* 17.1 the giant outlined ghost word behind most sections.
   It was set in the display face at 22vw. At that size it is not a texture,
   it is a second headline competing with the real one. */
.asec[data-ghost]::after{display:none}

/* 17.2 diagonal section edges.
   --cut is already 0, which makes the clip-path a no-op, but the negative
   margin and the padding compensation are separate rules. Zeroed explicitly
   so a section boundary is a straight line and two adjacent grounds meet
   cleanly. */
.cut-t{margin-top:0;clip-path:none;padding-top:clamp(84px,9vw,140px)}
.cut-t.rev{clip-path:none}

/* 17.3 lane stripes over dark sections, and the vertical rules in the hero. */
.asec.char.lanes::before,.ah-lanes{display:none}

/* 17.4 sheen sweeps. A white diagonal highlight fired across buttons and
   product cards on hover. Two of them can fire at once on a grid. */
.abtn::after,.ppost::before{display:none!important}

/* 17.5 the loading curtain.
   A full-screen brand splash with a progress bar, on a site whose visitor
   arrived from an Alibaba listing and wants to see garments. It delayed first
   contact with the page by roughly a second and a half for no information. */
#curtain{display:none!important}
body{opacity:1!important}

/* 17.6 the scrolling trust marquee becomes a static line.
   The facts in it are worth having; sliding them past the reader is not. On a
   narrow screen it stays scrollable by finger, but nothing moves on its own. */
.chev{background:var(--paper-2);border-block:1px solid var(--line);
  padding:15px 0;overflow-x:auto;scrollbar-width:none}
.chev::-webkit-scrollbar{display:none}
.chev-in{animation:none!important;width:auto;justify-content:center;
  gap:clamp(18px,3vw,52px);padding-inline:4vw;margin:0 auto}
/* The markup prints the list twice so a marquee can loop seamlessly. With the
   marquee gone the second copy is duplicate content read out by screen
   readers, so it is dropped. */
.chev-in span:nth-child(n+7){display:none}
.chev-in span{color:var(--steel);font-family:var(--sans);font-weight:700;
  font-style:normal;font-size:11.5px;letter-spacing:.1em;text-transform:uppercase}
.chev-in span::after{content:none}

/* 17.7 quieter cards.
   Radius down, border kept, lift halved. A grid of forty-nine cards each
   rising 8px with a coloured shadow is a lot of weather. */
.ppost:hover,.fact:hover,.st9:hover,.lbs:hover{transform:translateY(-3px);
  box-shadow:var(--shadow)}

/* 17.8 numerals and kickers stop being gradient-filled.
   With one hue the gradient bought nothing but a second rendering path. */
.kick::before{background:var(--volt-indigo)}
.asec.char .kick::before{background:var(--volt-cyan)}
.ppost-no,.fact .no,.st9-no{background:var(--volt-indigo);
  box-shadow:none;font-weight:700;letter-spacing:.1em}
.lbf.on{background:var(--ink);box-shadow:none}

/* 17.9 headline two-tone.
   .amark held an animated gradient sweep. It keeps the accent colour and
   loses the animation and the sweep. */
.amark,.amark .w>span{background-image:none!important;-webkit-text-fill-color:currentColor;
  color:var(--volt-indigo);animation:none!important}
.asec.char .amark,.ph .amark,.finish .amark,.ah .amark{color:var(--volt-cyan)}
.gw{background-image:none;color:var(--volt-indigo)}
.asec.char .gw,.ph .gw,.finish .gw{background-image:none;color:var(--volt-cyan)}

/* 17.10 the scroll progress line at the top of the window.
   Kept — it is genuinely useful on pages this long — but hairline and in the
   accent rather than a three-stop gradient. */
#sprog{height:2px;background:var(--volt-indigo)}

/* ── 17.11 CATEGORY TILES ────────────────────────────────────────────────
   The seven category images arrive with their English labels already burned
   into the artwork, which is why these tiles carry no text of their own: a
   caption underneath would say "Sports Bra" directly below an image that
   already says "Sports Bra". The accessible name lives in the alt text and
   in a visually-hidden span, so the link is still announced properly. */
.cats{display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(10px,1.2vw,16px)}
.cat{position:relative;display:block;overflow:hidden;
  border-radius:var(--radius-sm);background:var(--paper);
  transition:transform var(--t) var(--out)}
.cat img{width:100%;aspect-ratio:3/4;object-fit:cover;
  transition:transform var(--t-slow) var(--out)}
.cat:hover{transform:translateY(-3px)}
.cat:hover img{transform:scale(1.04)}
.cat:focus-visible{outline:3px solid var(--volt-indigo);outline-offset:3px}
/* The seventh tile used to span two columns to fill the row. It does not any
   more — see 18.3; the eighth cell is now a text link instead. */

/* ── 17.12 BRANDING / CRAFT TILES ────────────────────────────────────────
   Same idea, square, with the process name underneath because these images
   do NOT carry their own label once the mockup border is cropped off. */
.crafts{display:grid;grid-template-columns:repeat(6,1fr);
  gap:clamp(10px,1.2vw,16px)}
.craft{text-align:center}
.craft img{width:100%;aspect-ratio:4/3;object-fit:cover;
  border-radius:var(--radius-sm);border:1px solid var(--line)}
.craft b{display:block;margin-top:10px;font-size:11.5px;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;color:var(--steel)}

.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
  clip-path:inset(50%);white-space:nowrap}

@media(max-width:1000px){
  .cats{grid-template-columns:repeat(2,1fr)}
  .crafts{grid-template-columns:repeat(3,1fr)}
}
@media(max-width:600px){
  .crafts{grid-template-columns:repeat(2,1fr)}
}

/* ── 17.13 THE HERO ──────────────────────────────────────────────────────
   Reverted in the review pass: the scattered three-word hero is back. It was
   the one piece of the old design that was doing a job nothing else could —
   it reads as a poster rather than a landing page, which on a factory site is
   the difference between "supplier" and "brand". Only the stat chips are
   touched, to sit on the quieter palette. */
.ah-st b{font-weight:800}
.ah-st::before{background:rgba(255,255,255,.45)}
.ah-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}

/* ═══ 18. QUIETER STILL ═══════════════════════════════════════════════════
   Second pass, from the review: the ground read too yellow, the green was
   doing too much work, and the dark band was heavy. Three fixes, in that
   order. Everything here overrides section 17, which overrides the design
   system proper — the layering is deliberate so each pass can be read (and
   reversed) on its own. */

/* 18.1 the green retreats to two jobs: primary buttons, and the 34px rule in
   front of a section kicker. Everything that used to carry it — headline
   accents, numerals, badges, the filter chip, the scroll line — goes to ink.
   A single accent used twice reads as a decision; the same accent used on
   nine different kinds of element reads as a theme. */
.kick{color:var(--steel)}
.asec.char .kick,.ph .kick,.finish .kick{color:rgba(var(--mist-rgb),.72)}
.amark,.amark .w>span,.gw{color:var(--ink)!important;background-image:none!important}
.asec.char .amark,.ph .amark,.finish .amark,.ah .amark,
.asec.char .gw,.ph .gw,.finish .gw{color:#fff!important}
.ppost-no,.fact .no,.st9-no,.rung .no{background:var(--ink);color:#fff}
#sprog{background:var(--ink)}

/* 18.2 the dark band, de-weighted.
   --char is now a neutral warm grey rather than an olive-charcoal, so the one
   dark area on the page reads as "quiet" rather than as a second brand
   colour. The home page's verification section moves to the light ground
   entirely (see index.php) — the only dark surfaces left are the hero video
   scrim and the footer, which are both structural. */
.asec.char{background:var(--char)}
.taband{background:rgba(255,255,255,.05);border:1px solid var(--line)}
.asec.char .taband{border-color:var(--line-d)}

/* 18.3 image boxes, pinned.
   The category and lookbook tiles set their aspect on the IMG, which works
   until a grid row stretches the anchor taller than the image — then
   object-fit:cover crops the sides, and these particular photographs carry
   their category name burned into the artwork, so cropping the sides eats the
   label. The ratio now lives on the box, the image fills the box absolutely,
   and the grid is told not to stretch its rows. */
.cats,.crafts,.lbstrip{align-items:start}
.cat,.lbs{position:relative;aspect-ratio:3/4;display:block;overflow:hidden}
.cat>img,.lbs>img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;aspect-ratio:auto}
.craft{position:relative}

/* 18.4 the eighth cell.
   Seven tiles in a four-column grid leaves a hole. Rather than stretch a tile
   to fill it, the hole becomes the "everything else" link — which is the one
   thing a category grid of seven is missing anyway. */
.cat-more{display:flex;flex-direction:column;justify-content:flex-end;gap:10px;
  aspect-ratio:3/4;padding:clamp(18px,2vw,26px);border:1px solid var(--line);
  border-radius:var(--radius-sm);background:var(--paper-2);
  transition:border-color var(--t) var(--out),transform var(--t) var(--out)}
.cat-more b{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(20px,2vw,27px);text-transform:uppercase;color:var(--ink);
  line-height:1.05}
.cat-more span{font-size:13px;color:var(--body)}
.cat-more i{font-style:normal;font-size:12px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--volt-indigo)}
.cat-more:hover{border-color:var(--volt-indigo);transform:translateY(-3px)}

@media(max-width:1000px){
  .cat-more{aspect-ratio:auto;min-height:200px}
}

/* ═══ 19. FRAMING ═════════════════════════════════════════════════════════
   From the review: "a lot of the images look wrong". Nothing was being
   stretched — every image box on the site carries object-fit — but several
   boxes were shaped differently from the photographs inside them, and
   object-fit:cover pays for that mismatch by cutting the picture. On a
   full-length studio shot the cut lands on somebody's head.

   The audit, box ratio against picture ratio:

     .lbs / .ppost-v   0.750 vs 0.632   16% off the top and bottom  ← worst
     .leg-v            1.600 vs 1.928   17% off both sides
     .ppost-v (video)  0.740 vs 0.562   24% off the top and bottom
     .cat              0.750 vs 0.750   exact
     .craft            1.333 vs 1.333   exact
     .st9-i            1.250 vs 1.250   exact
     .certc            contain — never cropped

   Two of those are fixed at the source (the 49 lookbook frames are now cut to
   3:4 when they are generated, so the box and the picture agree and nothing is
   lost), and two are fixed here. */

/* 19.1 the lookbook cards on /products/ take the same 3:4 as the home strip.
   .ppost-v stays at 0.74 for the cards that hold VIDEO, because the video
   files are a different shape again and re-encoding them is a bigger job than
   this fix. */
.ppost.lb-item .ppost-v{aspect-ratio:3/4}

/* 19.2 the four station videos.
   Two of them are 1.93:1 and two are 1.6:1 — no single box fits both, so the
   box splits the difference at 16:9 and each loses under ten percent instead
   of one pair losing seventeen. */
.leg-v video{aspect-ratio:16/9}

/* 19.3 where the crop falls on the remaining portrait videos.
   Centre-cropping a 0.56:1 clip into a 0.74:1 card removes twelve percent from
   the top and twelve from the bottom — and the top twelve percent is the
   model's head. Shifting the focal point up means the whole cut comes off the
   floor instead. */
.ppost-v video{object-position:50% 28%}

/* ═══ 20. BOARDS, GALLERY, ARTICLE CARDS ══════════════════════════════════ */

/* 20.1 the mockup figure, unstacked.
   It was a two-column grid: a 2.5:1 board squeezed into a 55% column, which
   made a wide, flat image look compressed next to a tall block of text. Wide
   artwork wants the full measure. Image on top at its own ratio, caption
   underneath. */
.mockup{display:block}
.mockup img{width:100%;height:auto;display:block}
.mockup figcaption{padding:clamp(20px,2.4vw,32px);max-width:78ch}

/* 20.2 the four detail boards on /oem-odm/.
   Each is a wide composite with its own labels already set into the artwork,
   so it runs full measure with no crop at all — height:auto, no aspect-ratio,
   nothing for object-fit to do. The text sits underneath rather than beside,
   for the same reason as 20.1. */
.board{margin-top:clamp(22px,2.6vw,36px);border:1px solid var(--line);
  border-radius:var(--radius);overflow:hidden;background:var(--paper-2)}
.board img{width:100%;height:auto;display:block}
.board figcaption{padding:clamp(18px,2vw,28px) clamp(20px,2.4vw,32px)}
.board figcaption b{display:block;font-family:var(--display);font-weight:900;
  font-style:italic;font-size:clamp(19px,1.9vw,25px);text-transform:uppercase;
  color:var(--ink);line-height:1.1}
.board figcaption p{margin-top:8px;font-size:14.5px;max-width:80ch}

/* 20.3 the workshop gallery on /factory/.
   Twenty-nine photographs of the real floor were sitting unused while the page
   ran long stretches of table and prose. Twelve of them, captioned, is the
   cheapest possible answer to "does this factory exist". */
.wsgrid{display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(10px,1.2vw,16px)}
.wsx{position:relative;aspect-ratio:3/2;overflow:hidden;
  border-radius:var(--radius-sm);background:var(--char)}
.wsx img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform var(--t-slow) var(--out)}
.wsx::after{content:"";position:absolute;inset:auto 0 0 0;height:58%;
  background:linear-gradient(to top,rgba(var(--char-rgb),.82),transparent);
  opacity:0;transition:opacity var(--t) var(--out)}
.wsx b{position:absolute;z-index:2;left:12px;right:12px;bottom:10px;
  font-family:var(--display);font-weight:800;font-style:italic;font-size:13px;
  text-transform:uppercase;color:#fff;opacity:0;transform:translateY(6px);
  transition:opacity var(--t) var(--out),transform var(--t) var(--out)}
.wsx:hover img{transform:scale(1.05)}
.wsx:hover::after,.wsx:hover b{opacity:1}
.wsx:hover b{transform:none}

/* 20.4 article cards.
   The blog index was the default WordPress list — a title, a date, a link.
   Cards with the cover image, the category and the standfirst are what makes
   a visitor read a second one. */
.acards{display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(16px,2vw,26px)}
.acard{display:flex;flex-direction:column;background:var(--paper-2);
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
  transition:transform var(--t) var(--out),border-color var(--t) var(--out)}
.acard:hover{transform:translateY(-3px);border-color:var(--volt-indigo)}
.acard-m{position:relative;aspect-ratio:3/2;overflow:hidden;background:var(--char)}
.acard-m img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.acard-c{padding:clamp(18px,2vw,26px);display:flex;flex-direction:column;gap:9px;
  flex:1}
.acard-k{font-size:10.5px;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--volt-indigo)}
.acard h3{font-family:var(--display);font-weight:900;font-style:italic;
  font-size:clamp(18px,1.8vw,23px);text-transform:none;letter-spacing:0;
  line-height:1.18;color:var(--ink)}
.acard p{font-size:14px;line-height:1.6;color:var(--body)}
.acard .go2{margin-top:auto;font-size:12px;font-weight:800;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink)}

@media(max-width:1000px){
  .wsgrid{grid-template-columns:repeat(2,1fr)}
  .acards{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:640px){
  .acards{grid-template-columns:1fr}
}

/* ═══ 21. THE RATIO GOES ON THE BOX ═══════════════════════════════════════
   The bug this fixes, in one line: `aspect-ratio` set on an <img> or <video>
   that also carries width and height HTML attributes does not win in every
   browser — the intrinsic size does. So a 4:3 photograph with width="680"
   height="510" inside `width:100%; aspect-ratio:4/3` renders 200px wide and
   510px tall, and object-fit:cover then eats the sides of the picture. That is
   what "the images are distorted" has been describing all along.

   Setting the ratio on the PARENT and letting the media fill it absolutely has
   no such ambiguity: the box is sized by CSS alone, and the media has nothing
   to be intrinsic about because both its dimensions are pinned to the box.
   Section 18.3 already did this for the category and lookbook tiles, which is
   why those two were the only image grids that came out right.

   Every remaining media box on the site is converted here. The old
   ratio-on-the-media rules are left in place above and simply overridden —
   they do no harm once the parent controls the height, and removing them would
   mean touching eleven rules across four sections. */

.craft-i,.st9-i,.leg-v,.filmb-v,.anat-v,.duo-m,.icard figure{
  display:block;position:relative;overflow:hidden}

.craft-i{aspect-ratio:4/3;border-radius:var(--radius-sm);
  border:1px solid var(--line)}
.st9-i{aspect-ratio:5/4}
.leg-v{aspect-ratio:16/9}
.icard figure{aspect-ratio:16/10}
.anat-v{aspect-ratio:3/4}
.duo-m{aspect-ratio:16/10}

.craft-i>img,.st9-i>img,.leg-v>video,.filmb-v>video,
.anat-v>img,.duo-m>img,.duo-m>video,.icard figure>img{
  position:absolute;inset:0;width:100%;height:100%;
  aspect-ratio:auto;object-fit:cover}

/* .filmb keeps its caption strip, so the ratio sits on an inner .filmb-v
   wrapper rather than on .filmb itself — otherwise the caption would be
   inside the 16:9 box and eat the bottom of the footage. */
.filmb-v{aspect-ratio:16/9}

/* .craft's border moved to the box, so the image must not draw its own. */
.craft img{border:0;border-radius:0}
