/* ============================================================
   DataSeeds.AI - component layer  v3.1.0  (2026-09-02)
   Requires tokens.css. Every pattern below is lifted from the shipped
   website_v5 build of the Figma homepage frame (4221:8029) - build with
   these instead of re-inventing (or re-measuring) them. Canonical file:
   design-system/components.css - website_v5/css/components.css is a symlink.
   The v4-era pieces that no live surface uses any more (search pill, purple
   band, glass cards, offering-pair cards, badge photos) stay for catalog /
   dataset pages and are marked "legacy".
   ============================================================ */

/* ---------- page shell ---------- */
/* Without this, min-height + padding stack (content-box) and every pill/card
   renders taller than production - the live site resets box-sizing globally. */
*,*::before,*::after{box-sizing:border-box}
/* an image is never stretched or squashed - width or height, never both (Ron) */
img{max-width:100%}
/* zero-weight :where() so a component's own img size (tier art 115x144, engine
   art 100%) wins - at 0,4,1 this used to override every one of them */
.ds-page :where(img:not([class*="ds-media"]):not(.ds-nav__logo):not(.ds-footer__logo)){height:auto}
figure{margin:0}
/* one caption role on every page - 12 below the image, 18 G5 */
figcaption{margin-top:var(--ds-s3);font-size:var(--ds-fs-body);color:var(--ds-muted)}
.ds-page{margin:0;font-family:var(--ds-font);font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);
  color:var(--ds-body);background:var(--ds-off-white);-webkit-font-smoothing:antialiased;
  overflow-x:clip;overflow-wrap:break-word}
/* --ds-container is the CONTENT width (1240 in the frame); the gutter sits outside
   it, so the box caps at 1240+2*100 = the frame's full 1440 */
.ds-container{max-width:calc(var(--ds-container) + 2 * var(--ds-gutter));margin:0 auto;
  padding-inline:var(--ds-gutter)}
.ds-section{padding-block:var(--ds-section-pad)}
.ds-section--grey{background:var(--ds-grey)}
.ds-section--white{background:var(--ds-white)}
.ds-section--ots{background:var(--ds-tint-blue)}      /* Off-the-Shelf */
.ds-section--mto{background:var(--ds-tint-green)}     /* Made-to-Order */
.ds-section--purple{background:var(--ds-grad-purple);color:var(--ds-on-dark)}
.ds-section--navy{background:var(--ds-navy);color:var(--ds-on-dark)}
/* LEGACY - World-map texture (the v4 "Our Edge" purple band). No live surface
   carries the purple band since website v5; the deck re-tints this map to G3
   on its grey story band instead. Kept for catalog / dataset pages.
   This is the real vector map exported from Figma (4213:3692) - dots arranged as
   continents, one path filled #C2D8FA, shown at 15% over the section gradient.
   It is NOT a repeating dot pattern: do not substitute a CSS radial-gradient.
   Sizing matches the design: map 1508.65 x 797.11 in a 1440-wide band (=105% width),
   centred, bleeding a little off both edges. */
.ds-map{position:relative;isolation:isolate}
.ds-map::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:url("assets/world-map.svg");
  background-repeat:no-repeat;background-position:center;background-size:105% auto;
  opacity:.15}
.ds-map>*{position:relative;z-index:1}
/* The band needs enough height for the continents to read - production runs ~628px
   at 1280 wide. Without this the map crops to a meaningless middle strip. */
.ds-section--purple.ds-map,.ds-section--navy.ds-map{min-height:clamp(420px,40vw,640px);
  display:flex;flex-direction:column;justify-content:center}
/* deprecated alias - .ds-dots was a wrong guess at this texture; kept so old markup
   still gets the real map. Use .ds-map. */
.ds-dots{position:relative;isolation:isolate}
.ds-dots::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:url("assets/world-map.svg");
  background-repeat:no-repeat;background-position:center;background-size:105% auto;
  opacity:.15}
.ds-dots>*{position:relative;z-index:1}

/* ---------- type ---------- */
.ds-display{font-size:var(--ds-fs-display);line-height:var(--ds-lh-display);font-weight:700;color:var(--ds-ink);margin:0}
.ds-h2{font-size:var(--ds-fs-h2);line-height:var(--ds-lh-h2);font-weight:700;color:var(--ds-ink);margin:0}
.ds-h3{font-size:var(--ds-fs-h3);line-height:var(--ds-lh-h3);font-weight:700;color:var(--ds-ink);margin:0}
.ds-h4{font-size:var(--ds-fs-h4);line-height:var(--ds-lh-h4);font-weight:700;color:var(--ds-ink);margin:0}
/* the lead is ink wherever it runs (the Figma hero and page heads set #0A0A0A) */
.ds-lead{font-size:var(--ds-fs-lead);line-height:var(--ds-lh-lead);font-weight:400;color:var(--ds-ink);margin:0;max-width:38ch}
.ds-body-lg{font-size:var(--ds-fs-body-lg);line-height:var(--ds-lh-body-lg);margin:0}
/* eyebrows: 18px 700 in G5 grey since 2026-09-02 (Ron) - the body-lg rung, not
   the 12px label; G5 is 5.5:1 on white and on the tint bands, so still AA */
.ds-eyebrow{font-size:var(--ds-fs-body-lg);line-height:var(--ds-lh-eyebrow);font-weight:700;letter-spacing:.02em;color:var(--ds-muted);margin:0}
.ds-muted{color:var(--ds-muted)}
.ds-section--purple .ds-display,.ds-section--purple .ds-h2,.ds-section--purple .ds-h3,.ds-section--purple .ds-h4,
.ds-section--navy .ds-display,.ds-section--navy .ds-h2,.ds-section--navy .ds-h3,.ds-section--navy .ds-h4,
.ds-section--purple .ds-eyebrow,.ds-section--navy .ds-eyebrow{color:var(--ds-on-dark)}
.ds-section--purple .ds-lead,.ds-section--navy .ds-lead,
.ds-section--purple p,.ds-section--navy p{color:var(--ds-on-dark)}
/* accent text - AA-safe variants, never raw green on light */
.ds-t-ots{color:var(--ds-blue)}          /* >=24px bold only */
.ds-t-ots-sm{color:var(--ds-blue-deep)}  /* body size */
.ds-t-mto{color:var(--ds-green-deep)}    /* >=24px bold only */
.ds-t-mto-sm{color:var(--ds-green-ink)}  /* body size */
.ds-t-action{color:var(--ds-purple)}
.ds-section--purple .ds-t-mto,.ds-section--navy .ds-t-mto{color:var(--ds-green)} /* brand-lint:allow green text on dark */

/* ---------- buttons ---------- */
/* 18/700 label on a 55px pill (Figma 4221:8059); the white closing-CTA pill is
   59 (4225:8221). Pills never wrap their label. */
.ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-family:inherit;font-size:var(--ds-fs-body-lg);font-weight:700;line-height:1;text-decoration:none;cursor:pointer;
  border:none;border-radius:var(--ds-r-pill);padding:15px 18px;min-height:55px;white-space:nowrap;  /* Figma 4221:8059: 135x55 */
  transition:background var(--ds-dur) var(--ds-ease),transform var(--ds-dur) var(--ds-ease)}
.ds-cta .ds-btn{min-height:59px;padding:17px 24px}  /* Figma 4225:8221: 137x59 */
.ds-btn:hover{transform:translateY(-1px)}
/* focus (guidelines §10): 2px purple ring, 2px offset - on the gradient/purple/navy
   the purple ring vanishes, so interactive elements there get a white ring.
   All three rules are zero-weight :where() + :focus-visible, so they tie and
   the later pill-radius and white-ring rules win on order. */
:where(a,button,input,summary,.ds-btn,.ds-btn-arrow,.ds-btn-circle,.ds-filter,.ds-link,.ds-faq__q,
.ds-nav__links a,.ds-footer a,.ds-search input,.ds-nav__toggle,.ds-menu-btn):focus-visible{
  outline:2px solid var(--ds-purple);outline-offset:2px;border-radius:4px}
:where(.ds-btn,.ds-btn-arrow,.ds-btn-circle,.ds-filter,.ds-search input,.ds-skip):focus-visible{border-radius:var(--ds-r-pill)}
:where(.ds-section--purple,.ds-section--navy,.ds-cta) :where(.ds-btn,a):focus-visible{outline-color:var(--ds-white)}
.ds-btn--primary{background:var(--ds-purple);color:var(--ds-on-dark)}
.ds-btn--primary:hover{background:var(--ds-purple-soft)}
.ds-btn--primary:active{background:var(--ds-purple-deep);transform:none}
.ds-btn--white{background:var(--ds-white);color:var(--ds-body)}   /* on gradient / navy / purple; frame label #474747 */
.ds-btn--secondary{background:var(--ds-tint-blue-3);color:var(--ds-ink)}
.ds-btn--ghost{background:transparent;color:var(--ds-ink);box-shadow:inset 0 0 0 1px var(--ds-line-2)}
.ds-btn--sm{font-size:var(--ds-fs-body);padding:12px 20px;min-height:40px}
.ds-btn-arrow{width:55px;height:55px;padding:0;border-radius:50%;background:var(--ds-purple);color:var(--ds-white);
  display:inline-flex;align-items:center;justify-content:center;border:none;cursor:pointer}
.ds-btn-arrow:hover{background:var(--ds-purple-soft)}
.ds-btn-circle{width:40px;height:40px;padding:0;border-radius:50%;background:var(--ds-white);color:var(--ds-ink);
  box-shadow:var(--ds-sh-card);display:inline-flex;align-items:center;justify-content:center;border:none;cursor:pointer}
.ds-btn-circle[disabled]{color:var(--ds-faint);box-shadow:var(--ds-sh-ui);cursor:default}
/* inline text link - one role: blue-deep 700 at rest (4.7:1 on white), purple +
   underline on hover, like every navigational link */
.ds-link,.ds-prose a,.ds-check a{color:var(--ds-blue-deep);font-weight:700;text-decoration:none}
.ds-link:hover,.ds-prose a:hover,.ds-check a:hover{color:var(--ds-purple);text-decoration:underline}

/* ---------- search (hero) - LEGACY, v4 hero. v5 hero uses two pills ---------- */
/* Figma 3644:899: white pill, 2px G1 border, pl 32 / pr+py 12, 586 wide */
.ds-search{display:flex;align-items:center;gap:12px;background:var(--ds-white);border:2px solid var(--ds-grey-2);
  border-radius:var(--ds-r-pill);padding:12px 12px 12px 32px;max-width:586px}
.ds-search input{flex:1;border:none;outline:none;font-family:inherit;font-size:var(--ds-fs-body-lg);color:var(--ds-ink);background:none}
.ds-search input::placeholder{color:var(--ds-faint)}

/* ---------- cards ---------- */
.ds-card{background:var(--ds-white);border-radius:var(--ds-r-lg);padding:var(--ds-s5);box-shadow:var(--ds-sh-ui)}
.ds-card--xl{border-radius:var(--ds-r-xl);padding:var(--ds-s6)}
.ds-card--flat{box-shadow:none;background:var(--ds-grey)}
.ds-card--ots{background:var(--ds-tint-blue);box-shadow:none}
.ds-card--mto{background:var(--ds-tint-green-2);box-shadow:none}
.ds-card--glass{background:var(--ds-purple-soft);color:var(--ds-on-dark);box-shadow:none;border-radius:var(--ds-r-lg)}
.ds-card--navy{background:var(--ds-navy);color:var(--ds-on-dark);box-shadow:none}
.ds-card--hover{transition:box-shadow var(--ds-dur) var(--ds-ease),transform var(--ds-dur) var(--ds-ease)}
.ds-card--hover:hover{box-shadow:var(--ds-sh-card);transform:translateY(-1px)}   /* §9: 1px lift + one shadow step */

/* ---------- chips, tags, filters ---------- */
/* chip value: 6/12 padding and a 6 gap (~30px tall) - off the 4/8 steps, kept
   and documented as the chip's own value (2026-09-29) */
.ds-chip{display:inline-flex;align-items:center;gap:6px;background:var(--ds-grey);color:var(--ds-body);
  border-radius:var(--ds-r-pill);padding:6px 12px;font-size:var(--ds-fs-label);font-weight:700;line-height:var(--ds-lh-label)}
.ds-filter{display:inline-flex;align-items:center;gap:8px;background:var(--ds-white);color:var(--ds-ink);
  border:1px solid var(--ds-line);border-radius:var(--ds-r-pill);padding:9px 18px;font-size:var(--ds-fs-body);font-weight:700;cursor:pointer}
.ds-filter.is-selected{background:var(--ds-tint-blue-3);border-color:var(--ds-tint-blue-3)}
.ds-filterbar{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;background:var(--ds-tint-blue-2);
  padding:14px var(--ds-s5)}
/* Figma "label white" 3644:910: h 42, radius 16, white @90%, shadow_2,
   pad 4/12/4/8, 12px bold ink label, optional 32px icon image */
.ds-badge{display:inline-flex;align-items:center;gap:2px;background:rgba(255,255,255,.9);color:var(--ds-ink);
  border-radius:var(--ds-r-md);min-height:42px;padding:4px 12px 4px 8px;
  font-size:var(--ds-fs-label);font-weight:700;line-height:1.83;box-shadow:var(--ds-sh-card)}  /* 1.83 = Figma 3644:910's 22px leading, not the label rung */
.ds-badge img{width:32px;height:32px;display:block}
.ds-badge--plain{padding:4px 12px;min-height:34px}   /* label-only badges (no icon slot) */
.ds-dot{width:8px;height:8px;border-radius:50%;background:var(--ds-green);flex:none;display:inline-block}
/* modality icons - the canonical badge set in assets/icons/{video,image,audio}.svg.
   Inside illustrations the modality glyphs come from hero-d instead (solid tile,
   white glyph, 5-bar audio) - see illustrations/STYLE.md. */
.ds-ico{width:18px;height:18px;display:inline-block;vertical-align:-3px;color:var(--ds-blue);flex:none}

/* ---------- media ---------- */
.ds-media{border-radius:var(--ds-r-lg);overflow:hidden;display:block;width:100%;height:auto;background:var(--ds-grey-2)}
.ds-media--xl{border-radius:var(--ds-r-xl)}
/* animated seed-lines composition (the sales-deck set): transparent, no crop, 4:3.
   The frame sizes each slot explicitly rather than letting it fill its column -
   hero 697x523, annotation 851x638, evals 800x600, tier media 333x208. */
.ds-media--illus{aspect-ratio:4/3;object-fit:contain;background:none;border-radius:0}
.ds-media--hero{width:697px;max-width:none;margin-left:auto;display:block}
.ds-media--anno{width:851px;max-width:none}
/* evals scaled down 20% on 2026-09-02 (Ron): 800 -> 640, offset scales with it */
.ds-media--evals{width:640px;max-width:none;margin-left:-58px}
.ds-media-wrap{position:relative;display:inline-block}
.ds-media-wrap .ds-badge{position:absolute}
.ds-play{position:absolute;inset:0;margin:auto;width:88px;height:88px;border-radius:50%;
  background:rgba(255,255,255,.28);backdrop-filter:blur(2px);border:none;color:var(--ds-white);
  display:flex;align-items:center;justify-content:center;cursor:pointer}

/* ---------- numbered steps (How It Works) ---------- */
.ds-steps{display:grid;gap:var(--ds-s4)}
.ds-step{display:flex;gap:var(--ds-s4);align-items:flex-start;background:var(--ds-white);
  border:1px solid var(--ds-tint-green-3);border-radius:var(--ds-r-lg);padding:var(--ds-s5)}
.ds-step__n{flex:none;width:44px;height:44px;border-radius:50%;background:var(--ds-tint-green-3);
  color:var(--ds-green-ink);font-weight:700;font-size:var(--ds-fs-body-lg);display:flex;align-items:center;justify-content:center}
.ds-step__t{font-size:var(--ds-fs-body);font-weight:700;color:var(--ds-green-ink);margin:0 0 4px}

/* ---------- closing CTA (the signature gradient) ---------- */
/* Figma 4225:8221: a 730x326 card, centred - not full container width. The flex
   gap gives title-to-body 12; the body's 24 + gap 12 gives the frame's 36 above
   the pill. The contact illustration is absolute (see below) so it cannot
   inflate the height; min-height 316 is only a floor. */
.ds-cta{position:relative;overflow:hidden;background:var(--ds-grad-brand);border-radius:var(--ds-r-hero);
  width:730px;max-width:100%;min-height:316px;margin-inline:auto;padding:var(--ds-s8) var(--ds-s7);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--ds-s3);
  text-align:center;color:var(--ds-white)}
.ds-cta .ds-h3{color:var(--ds-white)}
.ds-cta p{color:rgba(255,255,255,.92);margin:0 auto var(--ds-s5);max-width:46ch}   /* frame 4225:8221: white 92% */
/* inline CTA card, inside a light section */
.ds-cta-inline{background:var(--ds-tint-purple);border-radius:var(--ds-r-lg);padding:var(--ds-s5)}

/* ---------- FAQ ---------- (grid columns: see the Figma homepage block) */
.ds-faq__row{border-top:1px solid var(--ds-line);padding:18px 0}
.ds-faq__q{display:flex;gap:16px;justify-content:space-between;align-items:flex-start;
  font-size:var(--ds-fs-body);font-weight:700;color:var(--ds-ink);cursor:pointer;list-style:none}
.ds-faq__q::-webkit-details-marker{display:none}
.ds-faq__a{color:var(--ds-body);margin-top:8px}  /* frame: answers run the full 936 row */

/* ---------- nav + footer ---------- */
/* Figma 4648:2342/2373/2406 nav: off-white bar, UI shadow (no border), 27px
   lockup, 18/1.6 regular links at 48px gap, 103 tall */
.ds-nav{background:var(--ds-off-white);box-shadow:var(--ds-sh-ui)}
.ds-nav__in{display:flex;align-items:center;gap:var(--ds-s6);min-height:103px;padding-block:var(--ds-s4)}
/* the lockup never gives up width to the links (its img max-width:100% made it a
   zero-min-content flex item that shrank to 0 at 901-1285) */
.ds-nav__in>a:first-child{flex:none}
/* 8px under the lockup lifts it 4px in the centred bar, bringing the logotype's
   baseline up to the nav links' baseline (Ron, 2026-09-29). Only with the full link bar. */
@media (min-width:1260px){.ds-nav__in>a:first-child{padding-bottom:8px}}
/* 2026-09-02, Ron: the lockup scaled down 20% twice - nav 42 -> 34 -> 27,
   footer 32 -> 26 -> 21, mobile nav 30 -> 24 -> 19 */
.ds-nav__logo{height:clamp(19px,2.1vw,27px);width:auto;display:block}
.ds-nav__links{display:flex;gap:clamp(16px,2.5vw,48px);margin-inline:auto;list-style:none;padding:0}
.ds-nav__links a{color:var(--ds-ink);text-decoration:none;font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);white-space:nowrap}
/* current page (build.py sets aria-current) - ink 700; purple stays hover/action */
.ds-nav__links a[aria-current="page"],.ds-menu__in a[aria-current="page"],.ds-mobile-nav a[aria-current="page"]{font-weight:700;color:var(--ds-ink)}
.ds-nav__links a:hover{color:var(--ds-purple)}
/* hamburger for pages that don't ship their own menu (templates) */
.ds-nav__toggle{display:none;background:none;border:none;cursor:pointer;padding:10px;margin-left:auto;color:var(--ds-ink)}
.ds-nav__toggle span{display:block;width:22px;height:2px;background:currentColor;border-radius:2px;margin:5px 0;
  transition:transform var(--ds-dur) var(--ds-ease),opacity var(--ds-dur) var(--ds-ease)}
.ds-nav.is-open .ds-nav__toggle span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.ds-nav.is-open .ds-nav__toggle span:nth-child(2){opacity:0}
.ds-nav.is-open .ds-nav__toggle span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.ds-nav__links .ds-nav__cta-item{display:none}
/* Footer - v2.2 (2026-09-02, Ron: "redesign footer to look like a footer").
   G1 grey surface (Figma 4225:8224 set the colour); the one-line strip it used
   to be is now a real footer: a brand column (26px lockup, one-sentence pitch,
   sales address) beside three link columns, then a hairline and a bottom bar
   with the copyright and the Zedge byline. Type stays on the ladder - 18/700
   ink column heads, 18 body links in G5, 18 bottom bar (the 16px rung is
   retired, 2026-09-29).
   .ds-footer--navy keeps the v2 deep-navy close for surfaces that want it. */
.ds-footer{background:var(--ds-grey);color:var(--ds-ink);padding-block:80px 24px}   /* 64 -> 80 with the pass-2 rhythm step (2026-09-29) */
/* measured off the Figma component (web / footer, 2026-09-02): brand 406,
   100px gutter, three 218px columns at 40px gaps = 1240 */
.ds-footer__top{display:grid;grid-template-columns:406px minmax(0,1fr);gap:clamp(32px,7vw,100px);align-items:start}
.ds-footer__brand{display:flex;flex-direction:column;align-items:flex-start;gap:16px;max-width:406px}
.ds-footer__logo{height:21px;width:auto;display:block}
.ds-footer__pitch{margin:0;font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);color:var(--ds-muted)}
.ds-footer__mail{font-weight:700}
.ds-footer__cols{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(24px,3vw,40px)}
/* column heads: the eyebrow rung (18/700/1.4) in ink, over 18/400 G5 links */
.ds-footer__col h2{font-size:var(--ds-fs-body);line-height:var(--ds-lh-eyebrow);font-weight:700;
  color:var(--ds-ink);margin:0 0 16px}
.ds-footer__col ul{list-style:none;margin:0;padding:0;display:grid;gap:10px}
.ds-footer a{color:var(--ds-muted);text-decoration:none;font-size:var(--ds-fs-body);line-height:var(--ds-lh-body)}
.ds-footer a:hover{color:var(--ds-purple)}
.ds-footer__bar{display:flex;flex-wrap:wrap;gap:16px 32px;justify-content:space-between;align-items:center;
  margin-top:48px;padding-top:24px;border-top:1px solid var(--ds-line)}
.ds-footer__legal,.ds-footer__byline{margin:0;font-size:var(--ds-fs-body);color:var(--ds-muted)}
/* legacy one-row footer (pre-2.2) - kept so older pages still render */
.ds-footer__in{display:flex;align-items:center;gap:var(--ds-s6);flex-wrap:wrap;min-height:80px;padding-block:var(--ds-s5)}
.ds-footer__links{display:flex;gap:clamp(24px,3.3vw,48px);margin-inline:auto;flex-wrap:wrap}
.ds-footer__sub{display:flex;gap:clamp(16px,2.5vw,32px);flex-wrap:wrap;justify-content:center;
  padding:0 0 var(--ds-s5);margin-top:-8px}
.ds-footer--navy{background:var(--ds-navy-deep);color:var(--ds-on-dark)}
.ds-footer--navy a,.ds-footer--navy .ds-footer__legal,.ds-footer--navy .ds-footer__byline,
.ds-footer--navy .ds-footer__pitch{color:var(--ds-on-dark-muted)}
.ds-footer--navy .ds-footer__col h2{color:var(--ds-on-dark)}
.ds-footer--navy .ds-footer__bar{border-top-color:rgba(255,255,255,.15)}
@media (max-width:900px){
  .ds-footer{padding-block:64px 24px}
  .ds-footer__top{grid-template-columns:minmax(0,1fr);gap:40px}
  .ds-footer__brand{max-width:none}
  .ds-footer__cols{gap:32px}
}
@media (max-width:520px){
  .ds-footer__cols{grid-template-columns:repeat(2,minmax(0,1fr));gap:32px}
  .ds-footer__bar{margin-top:32px}
}

/* ---------- layout helpers ---------- */
.ds-grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--ds-s5)}
.ds-grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--ds-s5)}
.ds-split{display:grid;grid-template-columns:1fr 1fr;gap:clamp(24px,5vw,64px);align-items:center}
.ds-stack>*+*{margin-top:var(--ds-s4)}
.ds-center{text-align:center;margin-inline:auto}
@media (max-width:900px){
  .ds-grid-2,.ds-grid-3,.ds-split{grid-template-columns:1fr}
}
/* the full bar (27px lockup + six links + pill + gutters) needs ~1260px - below
   that the nav collapses to the hamburger (site.css switches its menu at the
   same width) */
@media (max-width:1259px){
  .ds-nav__links{display:none}
  .ds-nav__toggle{display:block}
  .ds-nav__links .ds-nav__cta-item{display:block}
  .ds-nav{position:relative}
  .ds-nav.is-open .ds-nav__links{display:flex;flex-direction:column;gap:4px;
    position:absolute;left:0;right:0;top:100%;margin:0;padding:12px var(--ds-gutter) 20px;
    background:var(--ds-off-white);border-bottom:1px solid var(--ds-line);box-shadow:var(--ds-sh-card);z-index:50}
  .ds-nav.is-open .ds-nav__links a{display:block;padding:12px 0;font-size:var(--ds-fs-body-lg)}
}
@media (max-width:600px){ .ds-nav__in>.ds-btn{display:none} }

/* ---------- scroll reveal (opt-in; add .is-in via IntersectionObserver) ---------- */
.ds-reveal{opacity:0;transform:translateY(16px);
  transition:opacity var(--ds-dur-slow) var(--ds-ease),transform var(--ds-dur-slow) var(--ds-ease)}
.ds-reveal.is-in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.ds-reveal{opacity:1;transform:none}}

/* ==========================================================================
   Figma homepage (node 4221:8029) - components introduced by that design
   ========================================================================== */

/* the frame runs two heading sizes: 42px for the stats and evals headings, 28px
   for tiers / how-it-works / annotation / Q&A */
.ds-h2--xl{font-size:var(--ds-fs-display);line-height:var(--ds-lh-display);max-width:734px}
/* G4 faint heading - homepage only: §3 sanctions it because frame 4221:8029 sets
   it; 2.44:1 on the page, a recorded §13 AA deviation. Nowhere else. */
.ds-h2--faint{color:var(--ds-faint)}

/* stat - a coloured rule above the number, left-aligned, 4-up.
   Frame: row 1206 wide at x=117, cards 239 with a 44 gap, 50px of top padding */
/* frame 4441:22581: stats row sits 64 below the heading with 50px of its own
   padding above AND below the cards (2026-08-31 re-audit); the 64 is one section
   pad since pass 2 (2026-09-29) - 96 desktop, 64 phones */
/* columns size to their content (not 239 fixed) so "NYSE: ZDGE" and the labels stay
   inside the row - with space-between the block then centres under the heading */
.ds-statrow{display:grid;grid-template-columns:repeat(4,auto);gap:44px;
  margin-top:var(--ds-section-pad);padding-block:50px;
  justify-content:space-between;margin-inline:auto;max-width:1206px}
.ds-stat__rule{width:52px;height:7px;border-radius:4px;display:block}
.ds-stat__rule--blue{background:var(--ds-blue)}
.ds-stat__rule--green{background:var(--ds-green)}
.ds-stat__rule--grey{background:var(--ds-faint)}
.ds-stat__rule--purple{background:var(--ds-purple)}
.ds-statrow .ds-stat__n{font-size:var(--ds-fs-stat);font-weight:800;line-height:var(--ds-lh-stat);color:var(--ds-ink);margin:12px 0 0;white-space:nowrap}
.ds-statrow .ds-stat__l{font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);color:var(--ds-muted);margin:12px 0 0}   /* frame 4441:22581: 18/400/1.6 */

/* def - 32px monoline icon, bold title, body */
.ds-defs{display:grid;gap:48px}
.ds-defs--4{grid-template-columns:repeat(4,1fr)}
.ds-defs--2{grid-template-columns:repeat(2,1fr);gap:var(--ds-s6) var(--ds-s7)}
.ds-defs--1{grid-template-columns:1fr;gap:32px;max-width:344px}
/* The icons are inlined <svg> stroked with currentColor, NOT a CSS mask on an
   external file - an external-SVG mask-image fails silently in Safari and over
   file://, which is exactly how it went missing the first time. */
/* def icons are G5 grey, never purple, and 32px square (Ron, 2026-09-02 -
   was 44 with negative margins). The 28-unit monoline glyphs scale to the box. */
.ds-def__icon{display:block;width:32px;height:32px;margin:0;
  color:var(--ds-muted);flex:none}
/* the frame keeps every def title on one line (whitespace-nowrap on a 278 column);
   web / def 4647:2222 sets it 20/700 - the h4 rung */
.ds-def__t{font-size:var(--ds-fs-h4);font-weight:700;line-height:var(--ds-lh-h4);color:var(--ds-ink);margin:8px 0 0;
  white-space:nowrap}
.ds-def__b{font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);color:var(--ds-muted);margin:8px 0 0}

/* our story - frame 4223:227 (re-read 2026-09-02): the engines sit on the BARE
   page inside a 1376 column with 64px of padding; only the community band below
   (4625:8905) is a grey r80 panel, and it carries the world map at G3 15%. */
/* width: 1376 at the frame, 24px side gutters below it - the same formula as
   .ds-tiergrid, so band and tier row line up at every width */
/* padding: frame 64 = section pad - 16; pass 2 keeps that relation (80 desktop, 48 phones) */
.ds-story{background:none;border-radius:0;padding:calc(var(--ds-section-pad) - var(--ds-s4)) 0;margin-inline:auto;
  width:min(var(--ds-container-wide),calc(100% - 48px))}
.ds-story__h{font-size:var(--ds-fs-h3);line-height:var(--ds-lh-h3);font-weight:700;color:var(--ds-ink);text-align:center;margin:0}
.ds-engines{display:grid;grid-template-columns:1fr 1fr;gap:56px;margin:48px 54px 0;padding-block:33px}   /* heading -> engines 32 -> 48 (pass 2) */
.ds-engine{display:flex;gap:26px;align-items:center}
/* without this the 222px art plus the text's min-content width overflows the column */
.ds-engine > div:last-child{min-width:0}
.ds-engine__art{flex:0 0 auto;width:222px;height:220px;border-radius:var(--ds-r-lg);display:flex;
  align-items:center;justify-content:center;overflow:hidden}
/* Figma "Purple Tint 4" / "Blue Tint 3" - which are --ds-tint-purple / --ds-tint-blue
   here; the site's numbered tints do NOT line up with Figma's, so name them explicitly */
.ds-engine__art--zedge{background:var(--ds-tint-purple)}
.ds-engine__art--gurushots{background:var(--ds-tint-blue)}
.ds-engine__art img{width:100%;height:100%;object-fit:contain;display:block}
.ds-engine__mark{height:33px;width:auto;display:block}
.ds-engine__mark--zedge{height:39px}
.ds-engine__b{font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);color:var(--ds-muted);margin:16px 0 0;max-width:36ch}
/* frame 4625:8692: grey r80 panel, py 32; inner band px 96 py 48 with the map
   centred behind (1824x964, G3 at 15%, bleeding off the panel). The heading
   ("Elegant content creation, not brute force") is a 24px h3 ABOVE the panel,
   48px clear since pass 2 (frame 32) - use .ds-story__h for it. */
.ds-story__band{position:relative;isolation:isolate;overflow:clip;
  margin:48px 0 0;padding:80px 96px;border-radius:var(--ds-r-panel);background:var(--ds-grey)}
.ds-story__band::before{content:"";position:absolute;left:50%;top:50%;width:1824px;height:964px;
  transform:translate(-50%,-50%);pointer-events:none;z-index:0;opacity:.15;
  background:var(--ds-line-2);
  -webkit-mask:url("assets/world-map.svg") center/1824px 964px no-repeat;
  mask:url("assets/world-map.svg") center/1824px 964px no-repeat}
.ds-story__band>*{position:relative;z-index:1}

/* card / tier - flat grey card, spot illustration beside the copy
   (Figma 4223:8104, 2026-08-31 re-audit: g1 bg, r32, pad 32, gap 14,
   media 100x125 left; OTS title G6, MTO titles green-shade; no chips) */
/* the row matches the community band's 1376 (Ron, 2026-09-02) and the cards
   stretch to one height - 442px each at 1376 instead of 397 at 1240 */
/* pass 2 (2026-09-29): intro -> cards 40 -> 64, card gap 24 -> 32 */
.ds-tiergrid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px;margin-top:64px;
  align-items:stretch;width:min(var(--ds-container-wide),calc(100% - 48px));margin-inline:auto}
.ds-tiercard{background:var(--ds-grey);border-radius:var(--ds-r-xl);
  padding:32px;display:flex;gap:14px;align-items:flex-start}
/* no hover lift or shadow on the tier cards (Ron, 2026-09-02) - they are not links */
.ds-tiercard,.ds-tiercard:hover{box-shadow:none;transform:none;transition:none}
/* spot art scaled up 15% on 2026-09-02 (Ron): 100x125 -> 115x144 */
.ds-tiercard__art{flex:none;width:115px;height:144px;object-fit:contain;display:block}
.ds-tiercard__body{min-width:0;display:flex;flex-direction:column;gap:8px}
.ds-tiercard .ds-h3{font-size:var(--ds-fs-h4);margin:0}
.ds-tiercard .ds-h3--mto{color:var(--ds-green-shade)}
.ds-tiercard p{font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);color:var(--ds-muted);margin:0}

/* how it works - numbered track with dashed connectors, then the pipeline illustration */
/* heading -> track: frame 98, one rung up to 128 in pass 2 (64 on <=1000) */
.ds-track{display:grid;grid-template-columns:repeat(6,1fr);gap:16px;margin-top:var(--ds-s10);
  max-width:1058px;margin-inline:auto}
.ds-track__s{display:flex;flex-direction:column;align-items:center;gap:14px;position:relative}
/* frame 4221:8029 + §3: Green Tint #BBE6D9 chips, numerals and labels in Green Shade 700 */
.ds-track__n{width:44px;height:44px;border-radius:50%;background:var(--ds-tint-green-3);
  display:grid;place-items:center;font-weight:700;font-size:var(--ds-fs-body-lg);color:var(--ds-green-shade);position:relative;z-index:2}
.ds-track__t{font-size:var(--ds-fs-body);font-weight:700;color:var(--ds-green-shade);text-align:center;margin:0;
  white-space:nowrap}
/* the connector spans the GAP between two circles - 22px radius + 8px of air on
   each side - and sits behind them (z-index 0 vs the numeral's 2), so it can
   never cross a numbered circle. 100% - 44px = pitch - diameter - 2*8px air. */
.ds-track__s + .ds-track__s::before{content:"";position:absolute;top:22px;right:calc(50% + 30px);
  width:calc(100% - 44px);border-top:2px dashed var(--ds-green);opacity:.55;z-index:0}

/* Annotation and Evals are deliberately asymmetric in the frame and their artwork
   bleeds past the column - annotation's 851 runs off the right edge, evals' 800 is
   clipped 73px off the left. Both sections clip rather than scroll. */
/* the frame gives annotation a full-bleed #F5F5F5 band with square edges; every
   other section is transparent on the #FBFBFB page */
#annotation{background:var(--ds-grey);padding-block:var(--ds-s7)}  /* frame 37 (the 638 illus 37 from both edges), up a rung to 48 in pass 2 */
#annotation,#evals,.ds-hero{overflow:clip}
/* the frame seats the 851 illustration at x=644, i.e. flush against the 544 text
   column with no gap - the artwork's own margin supplies the breathing room */
#annotation .ds-split{grid-template-columns:544px 1fr;gap:0;align-items:center}

/* hero: 479 text column, 697 illustration starting at x=643 */
.ds-hero .ds-split{grid-template-columns:479px 1fr;gap:64px;align-items:center}
.ds-hero .ds-display{max-width:479px}
#evals .ds-container{max-width:calc(1340px + 2 * 50px);padding-inline:50px}
#evals .ds-split{grid-template-columns:654px 654px;gap:32px}
#evals .ds-split > div:first-child{overflow:clip;height:567px}  /* frame "illus wrap" 654x567 crops the 600-tall canvas */

/* Q&A - the frame puts the rows straight on the page, no grey card, and the first
   row keeps a hairline above it */
/* frame: heading column 240 at x=100, rows 936 at x=404 - so a 64px gap */
.ds-faq{display:grid;grid-template-columns:240px 936px;gap:64px}
.ds-faq__row:first-of-type{border-top:1px solid var(--ds-line)}

/* the frame shows pipeline-d cropped to its stage band (800x242 of the 800x600
   canvas, offset -179px) rather than the whole canvas and its empty margins */
/* air under the numbered steps and under the crop (Ron, 2026-09-02, twice):
   32 -> 64 -> 96 above, 24 -> 48 below */
.ds-pipecrop{width:800px;max-width:100%;aspect-ratio:800/242;overflow:hidden;position:relative;
  margin:96px auto 48px}
@media (max-width:1000px){.ds-pipecrop{margin:56px auto 32px}}
.ds-pipecrop img{position:absolute;left:0;top:-74%;width:100%;height:auto;display:block;background:none}

/* closing CTA (card rule: see "closing CTA" above) - the art sits behind the copy */
.ds-cta > *{position:relative;z-index:1}
/* declared AFTER `.ds-cta > *` on purpose - same specificity, and if it came first
   the universal child rule would reset it to relative and the art would push the
   card to double height */
.ds-cta > .ds-cta__art{position:absolute;left:-54px;top:22px;width:407px;height:305px;
  pointer-events:none;opacity:.9;z-index:0}
.ds-cta__art img{width:100%;height:100%;object-fit:contain;display:block}

/* egocentric - mirrors the deck's slide 05 (s-physical): the whole band is Blue
   Tint 3, the headline and lead sit left (637 of 1668 on the deck), the
   composition runs large on the right, and the four defs sit in one row on a
   Blue Tint 2 card. The deck is the reference; this is the web rendering of it. */
#egocentric{background:var(--ds-tint-blue)}
#egocentric .ds-ego__top{display:grid;grid-template-columns:minmax(0,38fr) minmax(0,58fr);
  gap:clamp(24px,3vw,48px);align-items:center}
#egocentric .ds-ego__art{margin-inline:-2%}
/* the 800x600 canvas carries ~85 units of empty ground under the scene, which read
   as a hole above the defs strip. Crop it with the wrapper (same trick as
   .ds-pipecrop) so the artwork itself is never scaled out of ratio. */
#egocentric .ds-ego__art{overflow:hidden;aspect-ratio:800/515}
#egocentric .ds-ego__art img{width:100%;height:auto;display:block}
/* the defs strip sits close under the copy (Ron, 2026-09-02) - the composition
   carries its own bottom whitespace, so 48px read as a hole */
#egocentric .ds-ego__defs{margin-top:0;background:var(--ds-tint-blue-2);border-radius:var(--ds-r-hero);
  padding:48px;gap:clamp(24px,3.4vw,48px)}
#egocentric .ds-ego__top{margin-bottom:-24px}
#egocentric .ds-ego__defs .ds-def__b{max-width:284px}
@media (max-width:1000px){
  #egocentric .ds-ego__top{grid-template-columns:minmax(0,1fr)}
  #egocentric .ds-ego__defs{grid-template-columns:repeat(2,1fr);padding:32px;margin-top:0}
  #egocentric .ds-ego__top{margin-bottom:0}
}
@media (max-width:640px){
  #egocentric .ds-ego__defs{grid-template-columns:1fr;padding:24px}
}

/* Everything above is measured off the 1440 frame and uses fixed pixel widths.
   Below the frame width they all have to unwind, or the fixed columns overflow. */
@media (max-width:1439px){
  .ds-hero .ds-split{grid-template-columns:minmax(0,479px) minmax(0,1fr)}
  .ds-hero .ds-display{max-width:none}
  .ds-media--hero{width:100%;max-width:697px}
  #annotation .ds-split{grid-template-columns:minmax(0,544px) minmax(0,1fr);gap:32px}
  .ds-media--anno{width:100%;max-width:851px}
  #evals .ds-container{max-width:calc(var(--ds-container) + 2 * var(--ds-gutter));
    padding-inline:var(--ds-gutter)}
  #evals .ds-split{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .ds-media--evals{width:100%;max-width:640px;margin-left:0}
  #evals .ds-split > div:first-child{height:auto}
  .ds-faq{grid-template-columns:240px minmax(0,1fr)}
  .ds-statrow{max-width:none}
  .ds-statrow .ds-stat__l{max-width:11em}  /* too narrow for one-line labels - wrap all four to two, evenly */
  .ds-engines{margin-inline:0}
  .ds-def__t{white-space:normal}   /* one-line titles only fit the 1440 columns; nowrap clipped the band's 4th def at 1001-1370 */
}
@media (max-width:1000px){
  .ds-hero .ds-split,#annotation .ds-split,#evals .ds-split,#egocentric .ds-split{grid-template-columns:minmax(0,1fr)}
  .ds-track__t{white-space:normal}
  .ds-cta{width:auto;padding:48px 24px}
  .ds-statrow{grid-template-columns:repeat(2,auto);gap:32px}
  .ds-statrow .ds-stat__l{max-width:none}
  .ds-defs--4{grid-template-columns:repeat(2,1fr);gap:32px}
  .ds-engines{grid-template-columns:1fr;gap:40px}
  .ds-tiergrid{grid-template-columns:1fr}
  .ds-track{grid-template-columns:repeat(3,1fr);row-gap:32px;margin-top:var(--ds-s8)}
  .ds-track__s + .ds-track__s::before{display:none}
  .ds-story__band{padding:40px 24px;margin-top:32px;border-radius:var(--ds-r-hero)}
  .ds-story__band::before{width:1200px;height:634px;-webkit-mask-size:1200px 634px;mask-size:1200px 634px}
  .ds-cta__art{display:none}
}
@media (max-width:700px){
  .ds-engine{flex-direction:column;align-items:flex-start;gap:20px}
  .ds-engine__art{width:100%;max-width:280px;height:200px}
  .ds-engine__b{max-width:none}
  .ds-faq{grid-template-columns:1fr;gap:32px}
}
@media (max-width:640px){
  .ds-statrow{grid-template-columns:1fr}
  .ds-tiercard{flex-direction:column}  /* the 100px art + text row is too tight on phones */
  .ds-defs--4,.ds-defs--2{grid-template-columns:1fr}
  .ds-track{grid-template-columns:repeat(2,1fr)}
}

/* ==========================================================================
   v2.1 (2026-09-02) - subpage components: contact, research, about, articles,
   legal. Same tokens, same ladder rungs. Built for website_v5/{contact,
   research,about,articles,privacy,terms,consent}.html and articles/*.html.
   ========================================================================== */

/* page header - subpages open like the hero: eyebrow, display title, lead.
   .ds-pagehead--center when there is no composition beside the copy */
.ds-pagehead{padding-block:var(--ds-section-pad) 0}
.ds-pagehead .ds-display{max-width:24ch}
.ds-pagehead .ds-lead{max-width:48ch}
.ds-pagehead--center{text-align:center}
.ds-pagehead--center .ds-display,.ds-pagehead--center .ds-lead{margin-inline:auto}
/* breadcrumbs - the pages under Company (about, research, articles) carry a quiet
   Home / Company / Page strip at the top of the page, under the nav and apart from
   the page head (Ron, 2026-09-29: "attach to page top area ... more minimalist").
   Body size (not the 12px label - "not too small"), 400, G5; the current page only
   steps up to ink; hairline slashes. */
.ds-crumbbar{padding-top:var(--ds-s5)}
.ds-crumbs ol{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;align-items:center;gap:var(--ds-s2);
  font-size:var(--ds-fs-body);line-height:var(--ds-lh-eyebrow);font-weight:400;color:var(--ds-muted)}
.ds-crumbs li{display:flex;align-items:center;gap:var(--ds-s2)}
.ds-crumbs li+li::before{content:"/";content:"/" / "";color:var(--ds-line-2)}   /* decorative - not read out */
.ds-crumbs a{color:inherit;text-decoration:none}
.ds-crumbs a:hover{color:var(--ds-purple)}
.ds-crumbs [aria-current]{color:var(--ds-ink)}
/* more air under the head of these pages (128 / 96). Research keeps its full section
   break - its first section opens on a heading, and that rule is more specific */
.ds-crumbbar ~ .ds-pagehead + .ds-section{padding-top:clamp(var(--ds-s9),9vw,var(--ds-s10))}

/* meta row - date · read time · author, as grey chips */
.ds-meta{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:0;padding:0;list-style:none}

/* article cards - grey r32, art on white, 20/700 title, G5 excerpt */
/* feature card spans the row, the rest sit two-up (three posts, not four); cards
   stretch to one height, gap 32 (pass 2, 2026-09-29) */
.ds-postgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:32px;margin-top:64px;align-items:stretch}   /* every post is the same card, three up (Ron, 2026-09-29) */
.ds-post{display:flex;flex-direction:column;background:var(--ds-grey);border-radius:var(--ds-r-xl);overflow:clip;
  text-decoration:none;color:inherit}
/* every thumbnail is the same 16:9 cover crop, so the three cards match (Ron,
   2026-09-29 - replaces "images keep their own ratio"). The DSD cover's text sits
   in its middle 76%, which the crop keeps. */
.ds-post__art{aspect-ratio:16/9;overflow:hidden;background:var(--ds-white)}
.ds-post__art img{width:100%;height:100%;object-fit:cover;display:block}
.ds-post__art--illus img{object-fit:contain;padding:6%}
.ds-post__body{padding:24px 32px 32px;display:flex;flex-direction:column;gap:12px}
/* cards in a post grid share one set of rows (subgrid): art | chips | title | excerpt
   line up across the row even when a chip row or a title wraps in one card only */
.ds-postgrid>.ds-post{display:grid;grid-row:span 4;grid-template-rows:subgrid;row-gap:0}
.ds-postgrid .ds-post__body{display:grid;grid-row:span 3;grid-template-rows:subgrid;row-gap:12px;align-content:start}
.ds-post__t{font-size:var(--ds-fs-h4);line-height:var(--ds-lh-h4);font-weight:700;color:var(--ds-ink);margin:0}
.ds-post:hover .ds-post__t{color:var(--ds-purple)}
.ds-post__x{color:var(--ds-muted);margin:0}
.ds-post .ds-chip{background:var(--ds-white)}   /* chips sit on the grey card, so they go white */
/* prose - article and legal bodies: body-lg at 1.6, h2/h3 on the ladder. The
   vertical rhythm (24 / 32 / 48 / 12) is at the end of the file. */
.ds-prose{max-width:760px;margin-inline:auto;font-size:var(--ds-fs-body-lg);line-height:var(--ds-lh-body-lg);color:var(--ds-body)}
.ds-prose h2{font-size:var(--ds-fs-h2);line-height:var(--ds-lh-h2);font-weight:700;color:var(--ds-ink)}
.ds-prose h3{font-size:var(--ds-fs-h4);line-height:var(--ds-lh-h4);font-weight:700;color:var(--ds-ink)}
.ds-prose strong,.ds-prose b{color:var(--ds-ink)}   /* 700 is always ink */
.ds-prose ul,.ds-prose ol{padding-left:1.3em}
.ds-prose ul li::marker{color:var(--ds-green)}   /* the check colour, as a bullet */
.ds-prose blockquote{margin-inline:0;padding:24px 32px;border-left:4px solid var(--ds-green);background:var(--ds-grey);
  border-radius:0 var(--ds-r-lg) var(--ds-r-lg) 0;color:var(--ds-ink);font-weight:700}
.ds-prose blockquote p{margin:0}
.ds-prose blockquote cite{display:block;margin-top:8px;font-style:normal;font-weight:400;color:var(--ds-muted);font-size:var(--ds-fs-body)}
.ds-prose figure img{width:100%;height:auto;display:block;border-radius:var(--ds-r-lg)}
.ds-prose--legal{font-size:var(--ds-fs-body);line-height:var(--ds-lh-body)}
/* frame 4648:2406: legal heads 20/700/1.4 (the denser legal register) */
.ds-prose--legal h2{font-size:var(--ds-fs-h4);line-height:var(--ds-lh-h4)}
/* grey, not the Off-the-Shelf blue wash - pages segment with grey */
.ds-prose--legal .ds-note{background:var(--ds-grey);border-radius:var(--ds-r-md);padding:var(--ds-s4) var(--ds-s5);color:var(--ds-ink)}

/* tables - hairline rows, ink header, tabular numerals, first column left */
.ds-tablewrap{overflow-x:auto}
.ds-table{width:100%;border-collapse:collapse;font-size:var(--ds-fs-body);line-height:var(--ds-lh-body)}
.ds-table caption{text-align:left;font-weight:700;color:var(--ds-ink);padding:0 0 12px}
.ds-table th,.ds-table td{padding:12px 16px;text-align:left;border-bottom:1px solid var(--ds-line);white-space:nowrap}
.ds-table th{font-weight:700;color:var(--ds-ink);border-bottom:2px solid var(--ds-line-2)}
.ds-table td{color:var(--ds-body);font-variant-numeric:tabular-nums}
.ds-table td:not(:first-child),.ds-table th:not(:first-child){text-align:right}
.ds-table td:first-child{white-space:normal;min-width:12ch}  /* long row labels wrap so every column fits the 760 prose column */
.ds-table tr.is-hi td{color:var(--ds-ink);font-weight:700}
.ds-table td.is-up{color:var(--ds-green-ink)}
.ds-table td.is-down{color:var(--ds-red-ink)}
.ds-table--text th,.ds-table--text td{text-align:left;white-space:normal}   /* prose tables wrap and read left */

/* forms - 55px fields (r16), G2 hairline, 400 labels, purple focus ring, red-ink errors */
.ds-form{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ds-s5)}   /* minmax(0): a wide child (the 300px captcha) cannot widen the card */
.ds-form__row{display:grid;grid-template-columns:1fr 1fr;gap:var(--ds-s5)}
.ds-field{display:flex;flex-direction:column;gap:8px}
.ds-field label,.ds-field__l{font-size:var(--ds-fs-body);font-weight:400;color:var(--ds-ink)}
.ds-field .req{color:var(--ds-muted)}   /* status, not action - never purple */
/* 12/16 padding + 28.8 line + 2 border = 54.8, so min-height 55 binds (§6) */
.ds-input,.ds-select,.ds-textarea{font:inherit;font-size:var(--ds-fs-body);line-height:var(--ds-lh-body);color:var(--ds-ink);
  background:var(--ds-white);border:1px solid var(--ds-line);border-radius:var(--ds-r-md);padding:var(--ds-s3) var(--ds-s4);min-height:55px;width:100%;
  transition:border-color var(--ds-dur) var(--ds-ease),box-shadow var(--ds-dur) var(--ds-ease)}
.ds-textarea{min-height:150px;resize:vertical}
.ds-input::placeholder,.ds-textarea::placeholder{color:var(--ds-faint)}
.ds-input:focus-visible,.ds-select:focus-visible,.ds-textarea:focus-visible{outline:none;border-color:var(--ds-purple);box-shadow:0 0 0 3px var(--ds-tint-purple)}
.ds-select{appearance:none;padding-right:44px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none' stroke='%2366696A' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right var(--ds-s4) center}
.ds-field.is-invalid .ds-input,.ds-field.is-invalid .ds-select,.ds-field.is-invalid .ds-textarea{border-color:var(--ds-red-ink);background:var(--ds-tint-red)}
.ds-field__err{display:none;font-size:var(--ds-fs-body);color:var(--ds-red-ink);margin:0}
.ds-field.is-invalid .ds-field__err{display:block}
.ds-check{display:flex;gap:12px;align-items:flex-start;font-size:var(--ds-fs-body);font-weight:400}   /* ink, like the other labels */
.ds-check input{width:22px;height:22px;flex:none;margin:1px 0 0;accent-color:var(--ds-purple)}
.ds-form__status{display:none;padding:var(--ds-s4) var(--ds-s5);border-radius:var(--ds-r-md);font-weight:700}
.ds-form__status.is-ok{display:block;background:var(--ds-tint-green);color:var(--ds-green-ink)}
.ds-form__status.is-err{display:block;background:var(--ds-tint-red);color:var(--ds-red-ink)}
.ds-form__foot{display:flex;gap:16px;align-items:center;flex-wrap:wrap}
.ds-btn[disabled]{opacity:.6;cursor:progress;transform:none}
.ds-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}  /* honeypot */

/* contact layout - short intro left (h1, lead, one "prefer to talk" line), white
   form card right (Ron, 2026-09-29: no call card, no def, no form title) */
.ds-contact{display:grid;grid-template-columns:minmax(0,479px) minmax(0,1fr);gap:64px;align-items:start}
/* form card pads 32 (Ron's softer form: 400 labels, G2 borders, sh-ui) */
.ds-formcard{background:var(--ds-white);border-radius:var(--ds-r-xl);padding:var(--ds-s6);box-shadow:var(--ds-sh-ui)}

/* link list - label + right-hand meta (downloads, links, addresses) */
.ds-linklist{list-style:none;margin:0;padding:0;display:grid;gap:12px}
.ds-linklist a{display:flex;justify-content:space-between;gap:16px;padding:var(--ds-s4) var(--ds-s5);border-radius:var(--ds-r-md);
  background:var(--ds-grey);color:var(--ds-ink);font-weight:700;text-decoration:none;
  transition:box-shadow var(--ds-dur) var(--ds-ease),color var(--ds-dur) var(--ds-ease),transform var(--ds-dur) var(--ds-ease)}
.ds-linklist a:hover{box-shadow:var(--ds-sh-card);color:var(--ds-purple);transform:translateY(-1px)}
.ds-linklist span{color:var(--ds-muted);font-weight:400;white-space:nowrap}
.ds-prose .ds-linklist{padding-left:0}   /* not a bullet list: .ds-prose ul's 1.3em indent pushed the cards off the text edge (and 1px off a 320 screen) */

/* numbered result cards (research findings) */
/* one section pad under the heading and 12 numeral-to-text, as the homepage stat row;
   card gap 32 (pass 2) */
.ds-findings{display:grid;grid-template-columns:repeat(3,1fr);gap:32px;margin-top:var(--ds-section-pad)}
.ds-finding{background:var(--ds-grey);border-radius:var(--ds-r-xl);padding:32px}
.ds-finding .ds-stat__n{font-size:var(--ds-fs-stat);font-weight:800;line-height:var(--ds-lh-stat);color:var(--ds-ink);margin:var(--ds-s3) 0}
.ds-finding p{margin:0;color:var(--ds-muted)}

@media (max-width:1000px){
  .ds-postgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .ds-contact{grid-template-columns:minmax(0,1fr);gap:var(--ds-s8)}   /* intro -> form, as page head -> content */
  .ds-findings{grid-template-columns:1fr}
}
@media (max-width:640px){
  .ds-postgrid{grid-template-columns:1fr}
  .ds-form__row{grid-template-columns:1fr}
  /* every grey/white r32 card steps down to 24 on phones */
  .ds-tiercard,.ds-finding,.ds-formcard{padding:var(--ds-s5)}
  .ds-post__body{padding:var(--ds-s4) var(--ds-s5) var(--ds-s5)}
}

/* ==========================================================================
   v3.6 normalization (2026-09-29) - the shared rhythm, measures and utilities
   that replace the per-page inline styles. Declared LAST on purpose: several
   rules tie on specificity with component rules above (.ds-linklist{margin:0},
   .ds-prose a, .ds-pagehead .ds-display) and must win on order.
   ========================================================================== */

/* skip link - first thing in the header, parked above the sticky bar until it
   takes keyboard focus; white pill, purple ring, above the nav */
.ds-skip{position:absolute;left:var(--ds-s4);top:var(--ds-s4);z-index:60;transform:translateY(-200%);
  padding:var(--ds-s3) var(--ds-s5);border-radius:var(--ds-r-pill);background:var(--ds-white);color:var(--ds-ink);
  font-weight:700;text-decoration:none;box-shadow:var(--ds-sh-card)}
.ds-skip:focus-visible{transform:none}

/* button row - hero, page heads, the call card; 48 under the lead */
.ds-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ds-s4);margin-top:var(--ds-s7)}
.ds-pagehead--center .ds-actions{justify-content:center}

/* measures: 38ch lead (page heads 48ch, above), 48ch intro beside art, 688 centred intro */
.ds-intro{max-width:688px;margin-inline:auto}   /* frame 4221:8029: the tiers intro column */
.ds-split .ds-body-lg,#egocentric .ds-ego__head .ds-body-lg{max-width:48ch}

/* page-head / section-intro rhythm: eyebrow 12, heading to body 16 (pass 2,
   2026-09-29 - was 8 / 12) */
.ds-eyebrow + :is(.ds-display,.ds-h2){margin-top:var(--ds-s3)}
.ds-meta + .ds-display{margin-top:var(--ds-s4);max-width:28ch}   /* frame 4648:2373 article head */
.ds-pagehead--center .ds-meta{justify-content:center}
:is(.ds-display,.ds-h2,.ds-story__h) + p{margin-top:var(--ds-s4)}
.ds-body-lg + .ds-body-lg{margin-top:var(--ds-s4)}   /* consecutive intro paragraphs never touch */

/* page head to first content: 64 when the next section continues the head (post
   grid, legal prose, article lead figure, about stat row). A section that opens
   with its OWN heading is a new topic: the head closes like a section and the next
   one opens with the full pad - a normal 192/128 section break, so the heading sits
   with its content, not midway (research: "The bottleneck is no longer compute" sat
   too close to the head - Ron, 2026-09-29). A head with nothing after it (404)
   closes like a section too. */
.ds-pagehead + .ds-section{padding-top:var(--ds-s8)}
.ds-pagehead:has(+ .ds-section>.ds-container>:is(.ds-h2,.ds-eyebrow):first-child),
.ds-pagehead:last-child{padding-bottom:var(--ds-section-pad)}
.ds-pagehead + .ds-section:has(>.ds-container>:is(.ds-h2,.ds-eyebrow):first-child){padding-top:var(--ds-section-pad)}
.ds-container>:first-child{margin-top:0}
.ds-container>.ds-statrow:first-child{padding-top:0;max-width:none}   /* a stat row with no heading above (about) */

/* story sections: .ds-story carries its own padding, so the section adds none; a
   second heading inside a story opens one section pad below the block above */
.ds-section:has(>.ds-story){padding-block:0}
.ds-story>*+.ds-story__h{margin-top:var(--ds-section-pad)}
.ds-story>:last-child{margin-bottom:0}

/* section gaps: intro to defs one section pad (64 -> 96, pass 2), defs to a closing line 48 */
.ds-body-lg + .ds-defs{margin-top:var(--ds-section-pad)}
.ds-defs + p{margin-top:var(--ds-s7)}

/* figures - inline media r20; the 960 article lead image r32, 64 above the prose
   (frame 4648:2373 set 48; pass 2 steps it up with the prose blocks) */
.ds-figure img{width:100%;height:auto;display:block;border-radius:var(--ds-r-lg)}
.ds-figure--hero{max-width:960px;margin-inline:auto}
.ds-figure--hero img{border-radius:var(--ds-r-xl)}
.ds-figure--hero + .ds-prose{margin-top:var(--ds-s8)}

/* prose rhythm on the space scale (pass 2, 2026-09-29): 24 between paragraphs, 48
   around media/tables/quotes/link lists, 64 above h2, 48 above h3, 16 heading to
   body. :where() keeps the media rule below the heading rules, so an h2 after a
   figure still gets 64. Legal h2s run at the h4 rung, so they take the h3 gap.
   First and last children carry no margin, so a prose column sits exactly on its
   section padding. */
.ds-prose--start{margin-inline:0}   /* left-aligned inner pages keep one left edge */
.ds-prose>*{margin-block:0}
.ds-prose>*+*{margin-top:var(--ds-s5)}
.ds-prose>*+:where(figure,blockquote,.ds-tablewrap,.ds-linklist),
.ds-prose>:where(figure,blockquote,.ds-tablewrap)+*{margin-top:var(--ds-s7)}
.ds-prose>*+h2{margin-top:var(--ds-s8)}
.ds-prose>*+h3,.ds-prose--legal>*+h2{margin-top:var(--ds-s7)}
.ds-prose>:is(h2,h3)+*{margin-top:var(--ds-s4)}
.ds-prose li+li{margin-top:var(--ds-s2)}
.ds-prose figure>.ds-grid-2{gap:var(--ds-s4)}

