/* ============================================================
   VERNIAS - Glass design system: site layer
   Spec: docs/design-system.html

   LOAD ORDER (this matters):
     1. css/editorial.css     legacy geometry and layout
     2. css/ontology.css      the isometric scene
     3. css/glass-tokens.css  the token layer, overriding 1 and 2
     4. css/glass-site.css    this file

   Migration stage, not the end state. The old sheets are kept
   for their geometry while the theme is inverted through the
   variable map in part A; part B replaces the surfaces that a
   variable swap cannot reach. Sections get rebuilt against the
   tokens alone as they are reworked, and editorial.css shrinks
   until it can be dropped.
   ============================================================ */

/* ============================================================
   PART A - legacy variable map
   The table in section 12 of the spec, expressed as CSS. Every
   name on the left belongs to editorial.css or ontology.css.
   ============================================================ */
:root{
  /* ground: light inverts to dark */
  --paper:var(--ink-950);
  --paper-2:var(--ink-900);
  --surface:var(--ink-850);
  --paper-tint:transparent;      /* section tints are deleted, not recoloured */
  --line-tint:rgba(255,255,255,.09);

  /* text: direct one-to-one */
  --ink:var(--text-hi);
  --ink-2:var(--text);
  --ink-3:var(--text-lo);

  /* borders become light-on-dark and belong to the glass tier */
  --line:rgba(255,255,255,.09);
  --line-2:rgba(255,255,255,.16);

  /* accent: --accent itself already comes from the token layer */
  --accent-2:var(--accent-hover);
  --accent-tint:var(--accent-soft);
  --accent-bright:var(--accent);
  --amber:#E0A64A;               /* non-text only, on dark */
  --danger:#E8705A;

  --r:var(--r-lg);

  /* ontology.css caption, which sits on the dark page and needs the
     bright accent. The scene INSIDE the SVG is handled separately, in
     its own scope further down: it stays light. */
  --pine:var(--accent);
  --pine-2:var(--accent-press);
  --pine-bright:var(--accent-hover);
  --pine-tint:var(--accent-soft);
}

/* ============================================================
   PART B - surfaces a variable swap cannot reach
   ============================================================ */

*,*::before,*::after{box-sizing:border-box}

body{
  margin:0;min-height:100dvh;            /* never 100vh: mobile chrome */
  background:var(--ink-950);
  font-family:var(--font-sans);
  line-height:var(--lh-body);letter-spacing:var(--tr-body);
  color:var(--text);
  -webkit-font-smoothing:antialiased;
}

h1,h2,h3,h4{
  color:var(--text-hi);text-wrap:balance;
  font-weight:var(--w-heading);
  letter-spacing:var(--tr-heading);
}
h1{font-size:var(--t-display);line-height:var(--lh-display);letter-spacing:var(--tr-display)}
h2{font-size:var(--t-h2);line-height:var(--lh-heading)}
h3{font-size:var(--t-h3);line-height:var(--lh-heading)}

/* Lead paragraphs - the sentence under a heading - all read at the same
   size and leading as the hero's, wherever they appear. */
.sec-head p,.hero .sub,.step-text p,.lead{
  font-size:var(--t-lead);line-height:var(--lh-lead);
}

/* Instrument Serif is retired: the emphasis span becomes the
   accent in the same weight as its heading. */
/* The accent phrase in a heading.
   CONVENTION, and the reason this comment exists: .em wraps the CLOSING
   one or two words of an h1 - the phrase that carries the claim - never
   the predicate and never most of the sentence. At four or five words
   the green stops being emphasis and becomes the heading's colour, and
   it starts mid-line rather than landing at the end. Every page lead on
   the site is written to this: "every record." / "one place." /
   "deal team." / "seriously." / "use." */
.em,.serif{font-family:inherit;font-style:normal;font-weight:inherit;color:var(--accent)}

.wrap{max-width:var(--maxw);padding-inline:var(--gutter)}
.block{position:relative;z-index:var(--z-content);padding-block:var(--section-y)}

.skip{
  position:absolute;left:-9999px;top:0;z-index:calc(var(--z-nav) + 1);
  padding:var(--s-3) var(--s-4);border-radius:var(--r-md);
  background:var(--accent);color:var(--accent-ink);font-weight:600;text-decoration:none;
}
.skip:focus{left:var(--s-4);top:var(--s-4)}

/* ---------- eyebrow -> label ------------------------------
   Keep the styling, drop the leading rule. */
.eyebrow{
  font-family:var(--font-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--text-lo);
}
.eyebrow::before,.eyebrow::after{display:none!important}

.sec-head p{color:var(--text);font-size:var(--t-lead)}

/* ---------- nav -------------------------------------------
   Tier 1 glass, transparent until scrolled. The blur is switched
   by a class, never tweened: glass rule 5. */
/* One appearance, at every scroll position. The spec originally called
   for a transparent bar at the top; a photographic hero killed that,
   because a transparent bar over a bright frame leaves the links
   unreadable exactly where a visitor first looks. The lighter-at-rest
   variant that replaced it had the same problem in miniature - the bar
   changed the instant the page moved, which read as a glitch rather
   than as a response to anything.

   .is-scrolled is still set by js/glass-motion.js and is deliberately
   left unstyled: the hook stays available without the bar acting on it. */
.nav{
  position:fixed;inset:0 0 auto;z-index:var(--z-nav);height:68px;
  background:var(--nav-fill);border-bottom:1px solid var(--g1-border);
  -webkit-backdrop-filter:var(--g1-blur);backdrop-filter:var(--g1-blur);
}
/* editorial.css:97 sets .nav.scrolled to rgba(255,255,255,.88) - the light
   theme's scrolled bar - and editorial.js still adds that class on scroll.
   Loading later is not enough to beat it, because .nav.scrolled outranks
   .nav on specificity, so the state has to be answered explicitly rather
   than left to cascade order. Both spellings are covered: editorial.js
   sets `scrolled`, glass-motion.js sets `is-scrolled`. */
.nav.scrolled,.nav.is-scrolled{
  background:var(--nav-fill);
  border-bottom-color:var(--g1-border);
}
.brand{color:var(--text-hi)}
.nav-links a,.drop-toggle{color:var(--text)}
.nav-links a:hover,.drop-toggle:hover{color:var(--text-hi)}
.nav-links a.active{color:var(--accent)}

/* Opaque enough to be independent of what is behind it: see --menu-fill.
   The blur stays, so it still reads as glass rather than a flat card. */
.drop-menu{
  background:var(--menu-fill);border:1px solid var(--menu-border);
  border-radius:var(--r-lg);
  -webkit-backdrop-filter:var(--g3-blur);backdrop-filter:var(--g3-blur);
  box-shadow:var(--g3-inset),0 18px 40px rgba(2,8,12,.55);
}
.drop-menu a{color:var(--text);border-radius:var(--r-md)}
.drop-menu a:hover{background:var(--accent-soft);color:var(--text-hi)}

.nav-burger{background:var(--g1-fill);border:1px solid var(--g1-border);border-radius:var(--r-pill)}
.nav-burger span{background:var(--text-hi)}

/* The mobile panel must not live inside the blurred bar: an
   element with backdrop-filter is a containing block, so a fixed
   descendant anchors to it instead of the viewport. Glass rule 9. */
.nav-panel,.mobile-menu{
  background:var(--ink-900);border-top:1px solid var(--g1-border);
}

/* ---------- buttons ---------------------------------------
   .btn-outline gains a tier 1 frosted backing. */
.btn{border-radius:var(--r-pill)}
.btn-outline{
  background:var(--g1-fill);border:1px solid var(--g1-border);color:var(--text-hi);
  -webkit-backdrop-filter:var(--g1-blur);backdrop-filter:var(--g1-blur);
}
.btn-outline:hover{background:rgba(255,255,255,.09);border-color:rgba(255,255,255,.20)}
.btn-sm{padding:.625rem 1.125rem;font-size:var(--t-xs)}

.link{color:var(--accent)}
.link:hover{color:var(--accent-hover)}

/* ---------- hero ------------------------------------------
   The old .hero-glow is replaced by the document mesh. */
.hero{position:relative;isolation:isolate;overflow:hidden;min-height:100dvh}
.hero-glow{display:none}
.hero>.wrap{position:relative;z-index:var(--z-content)}
.hero .sub{color:var(--text);font-size:var(--t-lead)}

.scroll-cue{
  background:var(--g1-fill);border:1px solid var(--g1-border);color:var(--text-lo);
  border-radius:var(--r-pill);
  -webkit-backdrop-filter:var(--g1-blur);backdrop-filter:var(--g1-blur);
}
.scroll-cue:hover{color:var(--accent)}

/* ---------- product frames --------------------------------
   Lane 2: real UI, never graded. Raised glass. */
.frame,.media-product{
  background:var(--g3-fill);border:1px solid var(--g3-border);
  border-radius:var(--r-xl);overflow:hidden;
  box-shadow:var(--g3-inset),var(--g3-shadow);
}
.frame img,.media-product>img{filter:var(--product-prep)}

/* ---------- cards -----------------------------------------
   Tier 2 content glass. These are inside a section that already
   sits over the mesh, so they are the only glass in their
   subtree: no nesting. Glass rule 2. */
.ind-card,.why-item,.agent-node{
  background:linear-gradient(158deg,rgba(255,255,255,.07),rgba(255,255,255,.02) 46%),var(--g2-fill);
  border:1px solid var(--g2-border);
  border-radius:var(--r-lg);
  color:var(--text);
  box-shadow:var(--g2-inset),var(--g2-shadow);
}
/* Blur budget: six to eight blurred elements per viewport is the
   ceiling. The agent web alone puts six nodes plus a core on
   screen at once, so nodes take a flat translucent fill and the
   real backdrop-filter is spent on the cards. Glass rule 6. */
.ind-card,.why-item{
  -webkit-backdrop-filter:var(--g2-blur);backdrop-filter:var(--g2-blur);
}

.ind-card h3,.why-item h3,.agent-node b{color:var(--text-hi)}
.ind-card .no,.why-item .no{
  font-family:var(--font-mono);font-size:var(--t-label);
  letter-spacing:var(--tr-label);color:var(--text-lo);
}
.ind-card .go{color:var(--accent)}
.why-item p,.agent-node .d{color:var(--text)}

/* ---------- agent web -------------------------------------
   Geometry stays in editorial.css; only the surfaces change. */
.agent-web::before{
  background:radial-gradient(circle,var(--accent-soft),rgba(62,214,155,.05) 45%,transparent 68%);
}
.agent-node:hover{border-color:var(--accent-line)}
.agent-node::after{color:var(--text-lo)}
.agent-node .agent-ico{background:rgba(255,255,255,.05);border-radius:var(--r-md)}
.agent-links .jdot{fill:var(--accent)}

/* ---------- footer ----------------------------------------- */
.footer{
  position:relative;isolation:isolate;overflow:hidden;
  background:none;border-top:1px solid rgba(255,255,255,.09);
}
.footer>.wrap{position:relative;z-index:var(--z-content)}
.footer-brand p{color:var(--text-lo)}
/* editorial.css paints both of these with var(--paper), which was the
   white page under the light theme and is now --ink-950. On the dark
   footer that is near-black on near-black: the wordmark was invisible
   on every page of the site, and the link hover state took a link from
   visible to gone. The migration map cannot fix this by remapping
   --paper - it is the page ground everywhere else - so the two rules
   are answered directly. */
.footer-brand .brand span{color:var(--text-hi)}
.footer-links a:hover{color:var(--text-hi)}
.footer-links h3{
  font-family:var(--font-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--text-lo);
}
.footer-links a{color:var(--text-lo);text-decoration:none}
.footer-links a:hover{color:var(--text-hi)}
.footer-social a{
  color:var(--text-lo);
  background:var(--g1-fill);border:1px solid var(--g1-border);border-radius:var(--r-pill);
}
.footer-social a:hover{color:var(--accent);border-color:var(--accent-line)}
.footer-bottom{border-top-color:rgba(255,255,255,.09);color:var(--text-lo)}

/* ---------- lightbox ---------------------------------------
   Outside any glass panel in the DOM, per glass rule 9. */
.lightbox{background:rgba(4,9,12,.86)}
.lightbox img{border-radius:var(--r-xl);border:1px solid var(--g3-border)}

/* The isometric scene styles itself: see css/ontology.css. It used to be
   re-themed from here through editorial.css's variables, which is why that
   block is gone rather than merely shortened. */

/* ============================================================
   AGENT DECK
   Slides advancing on a timer, and on click. Copy on the left
   over white, the agent's own screen on the right.

   Every slide is stacked at inset:0 and cross-faded, so the
   panel height is set once by the longest slide rather than
   changing under the reader between slides.
   ============================================================ */
/* The deck splits the page exactly in half. The ontology band uses 4/8
   because a diagram needs the room; here both halves carry equal weight,
   so the seam sits on the centre line. */
.bandsplit.deck{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}

/* the rail across the top: counter, rule, eyebrow */
.deck-rail{
  display:flex;align-items:center;gap:var(--s-4);
  padding-bottom:var(--s-5);
  border-bottom:1px solid rgba(22,25,27,.12);
}
.deck-numstrip{display:flex;flex-direction:column;transition:transform var(--d-slow) var(--ease)}
.deck-num{height:1.3em;line-height:1.3em;color:var(--accent)}
.deck-slide{
  position:absolute;inset:0;
  display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-3);
  opacity:0;visibility:hidden;
  transition:opacity var(--d-slow) var(--ease),visibility 0s linear var(--d-slow);
}
.deck-slide.is-on{opacity:1;visibility:visible;transition-delay:0s}
.deck-slide h3{font-size:var(--t-h2);margin:0}
.deck-slide .deck-kicker{
  font-family:var(--font-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--accent);
}
.deck-slide p{font-size:var(--t-lead);line-height:var(--lh-lead);color:var(--text);max-width:46ch}
.deck-slide .band-link{margin-top:var(--s-4)}

/* dots: a control, so they are real buttons */
.deck-dots{display:flex;gap:var(--s-2);margin-top:var(--s-6)}
.deck-dot{
  width:10px;height:12px;padding:0;cursor:pointer;
  background:transparent;border:1px solid rgba(22,25,27,.38);
  transition:background var(--d-base) var(--ease),border-color var(--d-base) var(--ease);
}
.deck-dot:hover{border-color:var(--accent)}
.deck-dot.is-active{background:var(--accent);border-color:var(--accent)}
.deck-shot{
  position:absolute;inset:0;opacity:0;
  transition:opacity var(--d-slow) var(--ease);
}
.deck-shot.is-on{opacity:1}
.deck-shot img{
  display:block;width:100%;height:100%;
  object-fit:cover;object-position:top left;
  filter:var(--product-prep);
}


/* ============================================================
   AGENT GRID
   The eight agents as a ruled grid of cards on the dark ground,
   with the section copy alongside. No light band here: the grid
   IS the artwork, and it is drawn for dark.
   ============================================================ */
.agents{
  position:relative;z-index:var(--z-content);
  display:grid;grid-template-columns:minmax(0,4.2fr) minmax(0,7.8fr);
  align-items:stretch;overflow:hidden;

  /* How far the light panel reaches across the seam. ONE value, used by
     both sides: the panel's negative margin and the grid's clearance.
     Set independently they drift apart, and the grid ends up underneath
     the panel that is painted over it. */
  --seam-overlap:clamp(60px,9vw,160px);
  /* Black on the section, not on the grid column: the white panel is
     pulled left across the seam and then clipped, so whatever the clip
     cuts away shows THIS background. Painting the column instead would
     leave the page mesh showing through the curve.

     --ink-900 rather than pure black: the token layer keeps every ground
     off true black on purpose, and it also stops the seam with the
     neighbouring sections reading as a hard step. */
  background:var(--ink-900);
}

/* The grid keeps the page's right margin; its left edge runs under the
   white panel, which is clipped over the top of it. */
.agents-grid{
  display:flex;flex-direction:column;justify-content:center;
  padding-block:var(--section-y);
  /* clear the overlap, then breathe */
  padding-inline:calc(var(--seam-overlap) + clamp(20px,2vw,44px))
                 max(var(--gutter),calc((100vw - var(--maxw)) / 2));
}

/* The copy panel is the light one, and the smaller one. It reaches back
   across the seam so the curve has room to bulge into the dark side
   rather than being flattened against the column edge. */
.agents-copy{
  position:relative;
  /* the whole ramp flips inside the panel */
  --text-hi:#16191B;
  --text:#3B4145;
  --text-lo:#6E767B;
  --accent:#0E5A41;          /* the original brand pine reads on white */
  --accent-hover:#0A4733;
  --accent-ink:#FFFFFF;      /* 7.2:1 on pine */
  background:#FFFFFF;color:var(--text);

  margin-right:calc(var(--seam-overlap) * -1);
  display:flex;flex-direction:column;justify-content:center;align-items:flex-start;
  gap:var(--s-4);
  padding-block:var(--section-y);
  padding-inline:max(var(--gutter),calc((100vw - var(--maxw)) / 2)) clamp(78px,10vw,168px);

  /* clipPathUnits="objectBoundingBox" on the path, so the curve is
     expressed as fractions of this element and rescales with it instead
     of needing a fixed path per breakpoint. */
  clip-path:url(#agentsCurve);
}
/* Warm ambient art runs hotter than the cool blooms: at --art-opacity it
   disappears against the ink ground. It also sits at z-index -1 inside
   .agents, which is a stacking context, so it paints above the section's
   own background and below the panels. */
.art-warm{opacity:.55}

.agents-copy .eyebrow{color:var(--text-lo)}
.agents-copy p{font-size:var(--t-lead);line-height:var(--lh-lead);max-width:34ch}
.agents-copy .btn{margin-top:var(--s-4)}
.agents-copy .btn-primary{background:var(--accent);color:var(--accent-ink);box-shadow:none}
.agents-copy .btn-primary:hover{background:var(--accent-hover)}

.agent-cell img{
  width:clamp(46px,4.6vw,66px);height:clamp(46px,4.6vw,66px);
  object-fit:contain;display:block;
  transition:scale var(--d-base) var(--ease);
}
a.agent-cell:hover img{scale:1.08}
.agent-cell span{
  font-size:clamp(1rem,.94rem + .24vw,1.1875rem);
  color:var(--text-hi);line-height:1.25;letter-spacing:-.015em;
}

@media (max-width:1024px){
  /* Stacked, so there is no seam for a curve to sit on. */
  .agents{grid-template-columns:1fr}
  .agents-copy{
    margin-right:0;clip-path:none;
    padding-block:var(--s-8);
    padding-inline:max(var(--gutter),calc((100vw - var(--maxw)) / 2));
  }
  .agents-grid{padding-block:var(--s-8)}
}

.gridrule.is-grid{
  grid-auto-flow:row;
  grid-template-columns:repeat(4,minmax(0,1fr));
}
.gridrule.is-grid .gridrule-cell{
  /* Horizontal padding stays tight: at four columns the cell is about
     180px wide, and every pixel spent on padding comes straight off the
     label, which then wraps mid-word on the longer agent names. */
  min-height:0;padding:var(--s-6) var(--s-3);
  align-items:center;justify-content:center;text-align:center;gap:var(--s-5);
  aspect-ratio:1 / 1.06;
  border-top:1px solid var(--rule);
}
/* the container already draws the outer box */
.gridrule.is-grid .gridrule-cell:nth-child(-n+4){border-top:0}
.gridrule.is-grid .gridrule-cell:nth-child(4n+1){border-left:0}

.agent-cell img{
  width:clamp(34px,3.2vw,44px);height:clamp(34px,3.2vw,44px);
  object-fit:contain;display:block;
  transition:scale var(--d-base) var(--ease);
}
a.agent-cell:hover img{scale:1.08}
.agent-cell span{
  font-size:var(--t-sm);color:var(--text-hi);line-height:1.25;
  letter-spacing:-.01em;
}

.agents-copy{display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-4)}
.agents-copy p{font-size:var(--t-lead);line-height:var(--lh-lead);max-width:44ch}
.agents-copy .btn{margin-top:var(--s-4)}

@media (max-width:1024px){
  .agents{grid-template-columns:1fr;gap:var(--s-7)}
  .agents-copy{order:-1}
}
@media (max-width:600px){
  .gridrule.is-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .gridrule.is-grid .gridrule-cell:nth-child(-n+4){border-top:1px solid var(--rule)}
  .gridrule.is-grid .gridrule-cell:nth-child(-n+2){border-top:0}
  .gridrule.is-grid .gridrule-cell:nth-child(4n+1){border-left:1px solid var(--rule)}
  .gridrule.is-grid .gridrule-cell:nth-child(2n+1){border-left:0}
}

/* The ontology band is only a ground and some room: the diagram draws its
   own labels, so there is no copy column to lay out beside it. */
.onto-band{padding-block:var(--section-y)}
.onto-band .wrap{max-width:1320px}

/* ============================================================
   FULL-BLEED SECTION BACKING
   The image is overscanned by 10% top and bottom so parallax
   travel never exposes an edge, and z-index is explicit: making
   the image absolute promotes it into the positioned layer,
   where it would otherwise paint OVER the grade and scrim.
   ============================================================ */
.media-bleed{overflow:hidden}
.media-bleed>img,.media-bleed>picture>img{
  position:absolute;inset:-12% 0 auto 0;z-index:0;
  width:100%;height:124%;object-fit:cover;
}
/* The section owns the stacking context; the backing sits behind on its
   own z-index rather than every sibling being pushed forward, which would
   overwrite the positioning of any child that sets its own. */
.has-bleed{position:relative;isolation:isolate;overflow:hidden}

@media (max-width:900px){
  /* Corner art at phone width covers most of the viewport and starts
     competing with the copy rather than sitting behind it. */
  .art{opacity:var(--art-opacity-lo)}
}

/* ============================================================
   FULL-BLEED HERO
   Contrast here does NOT come from the glass tiers - a photograph
   has no contrast contract of its own. It comes from the scrim,
   which is why the scrim class is not optional on this layout.
   ============================================================ */
.hero-bleed{
  display:grid;place-items:center;
  min-height:100dvh;padding-block:calc(68px + var(--s-8)) var(--s-9);
  text-align:center;
}
.hero-bleed .hero-lead{
  display:flex;flex-direction:column;align-items:center;gap:var(--s-5);
  max-width:60ch;margin-inline:auto;
  /* Nudge below the optical centre. A translate rather than margin: on a
     centred grid item a margin only moves it by half the value. GSAP
     animates the children, so nothing fights over the transform. */
  --hero-shift:56px;translate:0 var(--hero-shift);
}
.hero-bleed .sub{max-width:52ch;color:var(--text-hi)}
.hero-bleed .hero-actions{justify-content:center;margin-top:var(--s-2)}
/* SplitText wraps each line in its own block; without this the lines lose
   their centring mid-animation. */
.hero-bleed .split-line{text-align:center}

/* Show the WHOLE frame. The default bleed crops hard: cover plus the
   parallax overscan throws away most of a wide photograph in a tall hero.
   Here the section takes the image's own aspect ratio and the image is
   contained. Parallax comes off for the same reason - it can only travel
   by overscanning, which is a crop. */
.hero-bleed.is-photo{min-height:0;aspect-ratio:1776 / 886;max-height:100dvh}
/* The section matches the photograph's ratio, so "cover" crops nothing
   horizontally - the extra height is the only crop, and it exists solely
   to give the parallax somewhere to travel.

   The two numbers are linked and easy to get wrong: yPercent moves the
   image by a share of ITS OWN height, not the container's. With 16%
   overscan the slack is 8% either side, and 12% travel reaches
   6% x 1.16 = 6.96%. Raising travel to 14% reaches 8.12% and the frame
   edge shows. If this overscan changes, recheck the travel with it. */
.hero-bleed.is-photo .media-bleed>img,
.hero-bleed.is-photo .media-bleed>picture>img{
  inset:-8% 0 auto 0;width:100%;height:116%;object-fit:cover;
}

/* Hero type: lighter and larger than the rest of the site, and no colour
   break in the headline. */
.hero-bleed h1 .em{color:inherit}
.hero-bleed .btn-primary{
  background:#FFFFFF;color:var(--ink-950);
  text-transform:uppercase;letter-spacing:.10em;font-weight:600;
  font-size:var(--t-xs);padding:1rem 1.75rem;
  box-shadow:0 8px 24px rgba(2,8,12,.28);
}
.hero-bleed .btn-primary:hover{background:var(--text-hi);box-shadow:0 12px 32px rgba(2,8,12,.34)}
.hero-bleed .btn-primary:active{background:#DCE7E4}

@media (max-width:900px){
  .hero-bleed{min-height:auto;padding-block:calc(68px + var(--s-7)) var(--s-7)}
  .hero-bleed .hero-lead{--hero-shift:24px}
  /* A 2:1 frame is too short to hold the copy on a phone, so the image
     goes back to covering and the section reclaims its own height. */
  .hero-bleed.is-photo{aspect-ratio:auto;min-height:88svh}
  .hero-bleed.is-photo .media-bleed>img,
  .hero-bleed.is-photo .media-bleed>picture>img{object-fit:cover}
}

/* ============================================================
   RULED GRID
   Hairline box with a cross at every intersection. The crosses
   are what make it read as a drawing rather than as a table, so
   they are brighter than the rules themselves.
   ============================================================ */
.gridrule{
  --rule:rgba(255,255,255,.13);
  --mark-size:11px;
  /* The intersection mark. Its colour is baked into the data URI, so a
     light-ground grid overrides the whole token rather than a colour.

     A diamond rather than a cross: the isometric planes in the ontology
     diagram are the same shape, so the grid reuses a mark the site
     already has instead of introducing a second one. */
  --cross:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11'%3E%3Cpath d='M5.5 1.4 9.6 5.5 5.5 9.6 1.4 5.5Z' fill='none' stroke='%23ffffff' stroke-opacity='.5' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
  position:relative;
  display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
  border:1px solid var(--rule);
}
.gridrule-cell{
  position:relative;
  display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-4);
  min-height:min(44vh,400px);
  padding:var(--s-6) var(--s-6) var(--s-7);
  border-left:1px solid var(--rule);
  text-decoration:none;color:var(--text);
  transition:background var(--d-base) var(--ease);
}
.gridrule-cell:first-child{border-left:0}   /* the container draws it */
a.gridrule-cell:hover{background:rgba(255,255,255,.03)}

/* Every cell draws a cross at all FOUR of its own corners, from one
   pseudo-element carrying four background layers. The element is inset by
   half a mark on every side so a mark placed in its corner centres exactly
   on the cell's corner rather than being clipped in half. Cells share
   corners, so the marks overlap exactly and the figure closes itself: any
   number of rows and columns is marked correctly with no nth-child
   bookkeeping, and it stays correct across breakpoints. */
.gridrule-cell::before{
  content:"";position:absolute;z-index:1;pointer-events:none;
  inset:calc(var(--mark-size) / -2);
  background-image:var(--cross),var(--cross),var(--cross),var(--cross);
  background-repeat:no-repeat;
  background-size:var(--mark-size) var(--mark-size);
  background-position:0 0,100% 0,0 100%,100% 100%;
}

.gridrule-cell h3{
  font-family:var(--font-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--text-hi);
}
.gridrule-cell p{color:var(--text);font-size:var(--t-sm);max-width:38ch}
.gridrule-cell .go{margin-top:auto;color:var(--accent);font-size:var(--t-sm)}
.gridrule-cell .go svg{width:14px;height:14px;transition:translate var(--d-base) var(--ease)}
a.gridrule-cell:hover .go svg{translate:3px 0}

@media (max-width:768px){
  /* One column: each cell now draws its crosses on the edges of a
     full-width row, which is the correct figure for a stacked list.

     Scoped away from .is-grid, which manages its own column count and
     borders: without the :not() these rules strip the agent grid's
     internal column borders between 601px and 768px, where it is still
     four columns wide. */
  .gridrule:not(.is-grid){grid-auto-flow:row}
  .gridrule:not(.is-grid) .gridrule-cell{
    min-height:0;border-left:0;border-top:1px solid var(--rule);
  }
  .gridrule:not(.is-grid) .gridrule-cell:first-child{border-top:0}
}

/* ============================================================
   STEPPED SCROLL
   Side-by-side text and media, pinned, advancing on scroll.

   Desktop: the panel pins for one viewport per step, every step
   stacked at inset:0 and cross-faded, with a counter, a rule and
   dots tracking position.
   Below 901px it unpins completely and becomes a plain stacked
   list: pinning a full-height panel on a phone traps the scroll,
   and the media ends up taller than the viewport it is pinned in.
   ============================================================ */
.steps{position:relative;z-index:var(--z-content)}
.steps-pin{
  position:relative;overflow:hidden;
  min-height:100dvh;display:flex;align-items:center;
}

.step{
  position:absolute;inset:0;
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  align-items:center;gap:var(--s-8);
  /* centres to --maxw without needing a .wrap child, so the two halves
     can still reach the full panel height */
  padding-inline:max(var(--gutter),calc((100% - var(--maxw)) / 2));
  padding-block:calc(68px + var(--s-8)) var(--s-9);
}
.step[aria-hidden="true"]{opacity:0;pointer-events:none}

/* Media is a raised frame with NO backdrop-filter: an opaque screenshot
   covers the panel completely, so the blur would cost a composite layer
   per step and paint nothing. */
.step-media{
  position:relative;overflow:hidden;
  height:min(62vh,560px);
  border-radius:var(--r-xl);
  background:var(--g3-fill);border:1px solid var(--g3-border);
  box-shadow:var(--g3-inset),var(--g3-shadow);
}

/* ---------- overlay: counter, rule, dots ------------------- */
.steps-overlay{
  position:absolute;z-index:2;pointer-events:none;
  inset:auto auto var(--s-7) 0;
  width:100%;
  padding-inline:max(var(--gutter),calc((100% - var(--maxw)) / 2));
  display:flex;align-items:center;gap:var(--s-4);
}
/* one line tall, so the strip inside reads as an odometer */
.steps-mask{display:block;overflow:hidden;height:1.25em}
.steps-numstrip{display:flex;flex-direction:column}
.steps-num{height:1.25em;line-height:1.25em;color:var(--accent)}
.steps-dot{
  width:26px;height:3px;padding:0;border:0;border-radius:2px;cursor:pointer;
  background:rgba(255,255,255,.18);
  transition:background var(--d-base) var(--ease);
}
.steps-dot:hover{background:rgba(255,255,255,.32)}
.steps-dot.is-active{background:var(--accent)}

@media (max-width:900px){
  .steps-pin{min-height:0;display:block;overflow:visible}
  .step{
    position:relative;inset:auto;opacity:1!important;
    grid-template-columns:1fr;gap:var(--s-5);
    padding-block:var(--s-7);
    border-top:1px solid rgba(255,255,255,.09);
  }
  .step[aria-hidden="true"]{opacity:1;pointer-events:auto}
}

/* ============================================================
   HEADING RESET
   editorial.css sets size, weight and tracking per component
   (.sec-head h2, .hero h1, .ind-card h3 and so on). Those class
   selectors outrank the element rule, so the type tokens never
   reach them. This hands every heading back to the tokens, in
   one place, at matching specificity.

   Deliberately NOT included: .footer-links h3 and
   .gridrule-cell h3, which are tracked uppercase mono labels
   rather than headings and keep their own treatment.
   ============================================================ */
.hero h1,.page-hero h1{
  font-size:var(--t-display);line-height:var(--lh-display);
  letter-spacing:var(--tr-display);font-weight:var(--w-heading);
  color:var(--text-hi);
}
.sec-head h2,.coming h2,.mission-text h2,.contact-info h2,.cta h2{
  font-size:var(--t-h2);line-height:var(--lh-heading);
  letter-spacing:var(--tr-heading);font-weight:var(--w-heading);
  color:var(--text-hi);
}
.ind-card h3,.pillar h3,.founder h3,.why-item h3{
  font-size:var(--t-h3);line-height:var(--lh-heading);
  letter-spacing:var(--tr-heading);font-weight:var(--w-heading);
  color:var(--text-hi);
}
/* size is set by the component; only the treatment is unified */
.step h3,.step-text h3,.agents-copy h2{
  line-height:var(--lh-heading);letter-spacing:var(--tr-heading);
  font-weight:var(--w-heading);color:var(--text-hi);
}
/* body copy under a heading, wherever it lands */
.sec-head p,.split .copy>p,.mission-text p,.contact-info p{
  font-size:var(--t-lead);line-height:var(--lh-lead);color:var(--text);
}

/* The ontology flow needs more width than the reading measure: five
   columns below about 200px each start wrapping their card rows. */
.onto .wrap{max-width:1440px}
.sprite{position:absolute;width:0;height:0;overflow:hidden}

/* ============================================================
   CLAIMS
   Four blocks. Each is a heading held on the left while its
   rows scroll past on the right, separated by hairlines.

   The left column is sticky rather than simply top-aligned: the
   rows are the content and the heading is their label, so the
   label should stay with them instead of scrolling away and
   leaving a list of unattributed statements.
   ============================================================ */
.claims{display:flex;flex-direction:column;gap:clamp(72px,9vw,150px)}

.claim{
  display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);
  gap:clamp(32px,5vw,96px);align-items:start;
}
.claim-head{position:sticky;top:calc(68px + var(--s-6))}
/* Number, rule, label on one line. The rule between them does the work
   a bare gap cannot: it ties the number to the label so the pair reads
   as one mark rather than two stray fragments. */
.claim-meta{display:flex;align-items:center;gap:var(--s-3)}
.claim-num{
  font-family:var(--font-num);font-variant-numeric:tabular-nums lining-nums;
  font-size:var(--t-sm);font-weight:500;
  color:var(--accent);letter-spacing:.06em;
}
.claim-rule{width:26px;height:1px;background:rgba(255,255,255,.22);flex:none}
/* A meta row with no number starts with the rule, so it needs no gap
   before it. */
.claim-meta > .claim-rule:first-child{margin-left:0}
.claim-head .eyebrow{color:var(--text-lo)}
.claim-head h2{margin-top:var(--s-5);max-width:14ch}
/* Two-tone: the subject in full, the qualifier stepped back, so a long
   headline still reads as one line of thought. */
.claim-head h2 .dim{color:var(--text-lo)}

.claim-rows{display:flex;flex-direction:column}
.claim-row{
  display:grid;gap:var(--s-4) var(--s-5);
  padding-block:var(--s-6);
  border-top:1px solid rgba(255,255,255,.10);
}
.claim-row:first-child{border-top:0;padding-top:0}

/* --- check rows: one statement, its qualifier trailing inline --- */
.claim-row.is-check{grid-template-columns:auto minmax(0,1fr);align-items:start}
.claim-tick{
  flex:none;width:26px;height:26px;border-radius:7px;
  border:1px solid var(--accent-line);background:var(--accent-soft);
  display:grid;place-items:center;
}
.claim-tick svg{width:14px;height:14px;fill:none;stroke:var(--accent);
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.claim-row.is-check p{font-size:var(--t-body);line-height:1.5;color:var(--text-lo)}
.claim-row.is-check b{color:var(--text-hi);font-weight:500;margin-right:.4em}

/* --- stat rows: a display term, then its explanation --- */
/* A fixed term column, not a fraction. At .28fr the column collapsed to
   about 120px on a laptop, which is narrower than the words in it, so a
   two-word term wrapped and then spilled into the description beside it.
   A min of 210px holds the longest term on one or two clean lines, and
   the column can grow but never shrink under it. */
.claim-row.is-stat{
  grid-template-columns:minmax(210px,.26fr) minmax(0,1fr);
  column-gap:clamp(24px,3vw,56px);
  align-items:start;
}
.claim-term{
  display:block;
  font-size:clamp(1.5rem,1.05rem + 1.4vw,2.125rem);
  line-height:1.06;letter-spacing:-.03em;color:var(--accent);
  text-wrap:balance;
  /* never let a term hyphenate or break mid-word into the next column */
  overflow-wrap:normal;hyphens:none;
  padding-right:var(--s-3);
}
.claim-row.is-stat h3{font-size:var(--t-body);font-weight:500;color:var(--text-hi)}
.claim-row.is-stat p{margin-top:var(--s-3);color:var(--text);line-height:1.55;max-width:52ch}

@media (max-width:900px){
  .claim{grid-template-columns:1fr;gap:var(--s-6)}
  .claim-head{position:static}
  .claim-head h2{max-width:none}
  .claim-row.is-stat{grid-template-columns:1fr;gap:var(--s-2)}
  .claim-term{font-size:clamp(1.5rem,1.2rem + 1.4vw,2rem)}
}

/* The claims run inside the pinned stepper, so each block gets the whole
   panel instead of sharing a two-column step layout. */
.step.is-claim{grid-template-columns:minmax(0,1fr);align-content:center;gap:0}
.step.is-claim .claim{width:100%}
/* Sticky is meaningless inside a pinned panel that does not scroll. */
.step.is-claim .claim-head{position:static}

@media (max-width:900px){
  .step.is-claim{align-content:start}
}

/* ============================================================
   CLAIM TABS
   The reader chooses a panel. No timer, no pinning.
   ============================================================ */
.claim-tablist{
  display:flex;flex-wrap:wrap;gap:var(--s-2);
  padding-bottom:var(--s-6);
  border-bottom:1px solid rgba(255,255,255,.10);
}
.claim-tab{
  display:inline-flex;align-items:baseline;gap:var(--s-3);
  padding:var(--s-3) var(--s-5);border-radius:var(--r-pill);
  background:transparent;border:1px solid transparent;cursor:pointer;
  font-family:var(--font-sans);color:var(--text-lo);
  transition:background var(--d-base) var(--ease),
             border-color var(--d-base) var(--ease),
             color var(--d-base) var(--ease);
}
.claim-tab:hover{color:var(--text-hi);background:rgba(255,255,255,.04)}
.claim-tab.is-on{
  color:var(--text-hi);
  background:var(--accent-soft);border-color:var(--accent-line);
}
.claim-tab-num{
  font-family:var(--font-mono);font-size:var(--t-label);
  letter-spacing:.06em;color:var(--text-lo);
}
.claim-tab.is-on .claim-tab-num{color:var(--accent)}
.claim-tab-label{font-size:var(--t-sm);letter-spacing:-.01em}

/* Panels share one grid cell rather than being absolutely positioned, so
   the container is always as tall as the TALLEST panel. Switching tabs
   therefore never changes the page height, and no fixed min-height has
   to be guessed for the longest block. */
.claim-panels{display:grid;margin-top:clamp(40px,5vw,72px)}
.claim-panels>.claim{
  grid-area:1 / 1;
  opacity:0;visibility:hidden;pointer-events:none;z-index:0;
  /* the outgoing panel fades rather than vanishing */
  transition:opacity 200ms var(--ease),visibility 0s linear 200ms;
}
/* The outgoing panel is hidden at once and the incoming one animates in,
   rather than the two cross-fading.

   Cross-fading stacked panels looks broken in both directions: for the
   420ms both are painted, and which sits on top is decided by DOM order,
   not by which one you picked. Going backwards through the tabs, the new
   panel therefore faded in BEHIND the old one, and clicking quickly left
   several mid-fade at once. Animating only the arrival removes the
   overlap entirely, and it replays on every selection because the class
   is toggled off and on. */
/* A hand-off, not a cross-fade: the incoming panel waits out the 200ms
   the old one takes to leave, then eases in over 460ms. `both` fill mode
   holds it at the from-state during that delay, so the two are never
   visible at once and there is no double image - which is what made
   overlapping them look broken. Total is a little over half a second,
   slow enough to read as a change of subject rather than a cut. */
.claim-panels>.claim.is-on{
  opacity:1;visibility:visible;pointer-events:auto;z-index:1;
  transition:none;
  animation:panelIn 460ms var(--ease) 200ms both;
}
@keyframes panelIn{
  from{opacity:0;translate:0 14px}
  to{opacity:1;translate:0 0}
}
/* [hidden] would remove the panel from the grid and collapse the height
   the stack depends on, so the attribute is present for assistive tech
   and neutralised for layout. */
.claim-panels>.claim[hidden]{display:grid}

.claims-tabs .claim-head{position:static}

@media (max-width:700px){
  .claim-tab{padding:var(--s-2) var(--s-4)}
  .claim-tab-label{font-size:var(--t-xs)}
}

/* ---------- agent cards as tabs -------------------------
   They were links; they are buttons now, so the browser's own button
   styling has to be cleared before the cell rules can take effect. */
button.gridrule-cell{
  appearance:none;background:transparent;font:inherit;color:var(--text);
  cursor:pointer;text-align:center;
}
button.gridrule-cell:hover{background:rgba(255,255,255,.03)}
.agent-cell.is-on{background:var(--accent-soft)}
.agent-cell.is-on span{color:var(--text-hi)}
.agent-cell.is-on img{scale:1.06}
/* The selected card is marked by more than a fill: a tinted panel alone
   is easy to miss against eight others, and colour is not available to
   every reader. */
.agent-cell.is-on::after{
  content:"";position:absolute;left:50%;bottom:0;translate:-50% 0;
  width:34px;height:2px;background:var(--accent);
}

/* ---------- the copy panel it drives ---------------------
   Same one-grid-cell stack as the claim tabs: the container takes the
   height of the LONGEST agent description, so choosing an agent never
   shifts the button below it. */
.agent-panels{display:grid;margin-block:var(--s-3) var(--s-2)}
.agent-panels>.agent-panel{
  grid-area:1 / 1;
  opacity:0;visibility:hidden;pointer-events:none;z-index:0;
  transition:opacity 180ms var(--ease),visibility 0s linear 180ms;
}
/* Same hand-off as the claim panels, a little quicker: this one also
   advances on a timer, so it should not still be settling when the next
   rotation is due. */
.agent-panels>.agent-panel.is-on{
  opacity:1;visibility:visible;pointer-events:auto;z-index:1;
  transition:none;
  animation:panelIn 400ms var(--ease) 180ms both;
}
.agent-panels>.agent-panel[hidden]{display:block}

.agent-panel-kicker{
  display:block;
  font-family:var(--font-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--accent);
}
.agent-panel h3{margin-top:var(--s-3);font-size:var(--t-h3)}
.agent-panel p{
  margin-top:var(--s-4);font-size:var(--t-lead);line-height:var(--lh-lead);
  color:var(--text);max-width:34ch;
}

/* Legal links sit on the copyright line rather than in the Company
   column: they are boilerplate every site carries in the same place, and
   listing them beside About and Contact gives them a weight they have
   not earned. */
.footer-bottom{
  display:flex;flex-wrap:wrap;align-items:center;
  justify-content:space-between;gap:var(--s-3) var(--s-5);
}
.footer-legal{display:flex;align-items:center;gap:var(--s-3)}
/* Inherit rather than restate: these should read as part of the same
   line as the copyright beside them, so colour and type come from the
   parent and only the hover differs. */
.footer-legal a{color:inherit;font:inherit;text-decoration:none}
.footer-legal a:hover{color:var(--text-hi)}
/* A real element, not a pseudo on the link: a separator drawn inside an
   <a> becomes part of that link's hit area and its accessible name. */
.footer-sep{color:inherit;opacity:.45;user-select:none}

/* ============================================================
   TILE GRID
   A variant of .gridrule for cells that are CONTROLS rather than
   table cells.

   The ruled version draws a cross at every intersection, which is
   right for a figure - it reads as a drawing. For eight pressable
   cards it reads as clutter: eight cells make fifteen crosses, and
   each one sits exactly where two cards meet, so the marks pile up
   along the seams instead of framing anything.

   Discrete tiles say what the ruled grid could not: each of these
   is a separate thing you can press.
   ============================================================ */
.gridrule.is-tiles{
  border:0;
  gap:clamp(8px,.9vw,14px);
}
.gridrule.is-tiles .gridrule-cell{
  border:1px solid rgba(255,255,255,.10);
  border-radius:var(--r-lg);
  background:rgba(255,255,255,.022);
  transition:background var(--d-slow) var(--ease),
             border-color var(--d-slow) var(--ease),
             box-shadow var(--d-slow) var(--ease),
             translate var(--d-fast) var(--ease);
}
/* the crosses belong to the ruled variant only */
.gridrule.is-tiles .gridrule-cell::before{content:none}

.gridrule.is-tiles .gridrule-cell:hover{
  background:rgba(255,255,255,.05);
  border-color:rgba(255,255,255,.20);
  translate:0 -2px;
}
.gridrule.is-tiles .agent-cell.is-on{
  background:var(--accent-soft);
  border-color:var(--accent-line);
}
/* The underline the ruled version used sat on a shared edge; on a tile
   it would float off the rounded corner, so selection is marked by the
   fill, the border and a ring instead. */
.gridrule.is-tiles .agent-cell.is-on::after{content:none}
.gridrule.is-tiles .agent-cell.is-on{
  box-shadow:0 0 0 1px var(--accent-line), 0 10px 30px rgba(2,8,12,.35);
}

/* ============================================================
   MOTION SUPPORT
   ============================================================ */

/* Masked line reveal. SplitText's mask:"lines" supplies the clipping
   box; this only has to stop it clipping descenders and accents. */
.split-line{padding-bottom:.08em}

/* Claim rows arrive as a list, not as one block. The rows are the
   content of a panel, so they should land in sequence the way a reader
   takes them in. Delays are per row rather than a JS stagger: the panel
   is already toggled by a class, so the whole thing stays in CSS. */
@keyframes rowIn{
  from{opacity:0;translate:0 10px}
  to{opacity:1;translate:0 0}
}
.claim-panels>.claim.is-on .claim-row{
  animation:rowIn var(--d-base) var(--ease) both;
}
.claim-panels>.claim.is-on .claim-row:nth-child(1){animation-delay:260ms}
.claim-panels>.claim.is-on .claim-row:nth-child(2){animation-delay:315ms}
.claim-panels>.claim.is-on .claim-row:nth-child(3){animation-delay:370ms}
.claim-panels>.claim.is-on .claim-row:nth-child(4){animation-delay:425ms}
.claim-panels>.claim.is-on .claim-row:nth-child(5){animation-delay:480ms}
.claim-panels>.claim.is-on .claim-row:nth-child(6){animation-delay:535ms}

/* The connectors draw left to right once the row is in view, in step
   order, so the eye is led along the flow the diagram describes. */
@keyframes drawRight{
  from{clip-path:inset(0 100% 0 0)}
  to{clip-path:inset(0 0 0 0)}
}
.onto-step .onto-art::after{clip-path:inset(0 100% 0 0)}
.onto-flow.is-in .onto-step .onto-art::after{
  animation:drawRight .5s var(--ease) both;
}
.onto-flow.is-in .onto-step:nth-child(1) .onto-art::after{animation-delay:.45s}
.onto-flow.is-in .onto-step:nth-child(2) .onto-art::after{animation-delay:.60s}
.onto-flow.is-in .onto-step:nth-child(3) .onto-art::after{animation-delay:.75s}
.onto-flow.is-in .onto-step:nth-child(4) .onto-art::after{animation-delay:.90s}

/* If the motion layer never runs - no GSAP, reduced motion, a blocked
   CDN - .is-in is never set, and the arrows would stay clipped to
   nothing. Both cases are covered explicitly. */
@media (prefers-reduced-motion:reduce){
  .onto-step .onto-art::after{clip-path:none;animation:none}
  .claim-panels>.claim.is-on .claim-row{animation:none}
}
html.no-motion .onto-step .onto-art::after{clip-path:none}
/* Arrived from below: the arrows are simply there, undrawn. */
.onto-flow.is-done .onto-step .onto-art::after{clip-path:none;animation:none}

/* Connectors leaving. The delays run right to left, the reverse of the
   draw order, so the row unwinds from the end it was built toward. */
@keyframes undrawRight{
  from{clip-path:inset(0 0 0 0)}
  to{clip-path:inset(0 100% 0 0)}
}
.onto-flow.is-out .onto-step .onto-art::after{
  animation:undrawRight .4s var(--ease) both;
}
.onto-flow.is-out .onto-step:nth-child(4) .onto-art::after{animation-delay:0s}
.onto-flow.is-out .onto-step:nth-child(3) .onto-art::after{animation-delay:.08s}
.onto-flow.is-out .onto-step:nth-child(2) .onto-art::after{animation-delay:.16s}
.onto-flow.is-out .onto-step:nth-child(1) .onto-art::after{animation-delay:.24s}

/* ============================================================
   INTERIOR PAGES
   Shared shapes for the Solutions pages: a page lead, a tile
   row, agent cards and a closing call to action.
   ============================================================ */

/* The lead block that opens an interior page. Deliberately not the
   homepage hero: no photograph, no full viewport. An interior page is
   reached on purpose, so it should start with what it is about. */
.page-lead{
  padding-block:calc(68px + var(--s-9)) var(--s-9);
  text-align:left;
}
.page-lead .eyebrow{display:block;margin-bottom:var(--s-4)}
.page-lead h1{max-width:18ch}
/* The lead paragraph, matching the home hero's .sub exactly: same size,
   same measure, and --text-hi rather than --text. The dimmer ramp is for
   body copy further down a page; the sentence under an h1 is still part
   of the headline and reads as a hedge when it is greyed off. */
.page-lead .lead,.claim-note{
  margin-top:var(--s-5);max-width:52ch;
  font-size:var(--t-lead);line-height:var(--lh-lead);color:var(--text-hi);
}

/* Three tiles across, dropping to one. A shorter variant of the agent
   grid: text rather than a logo, so the cells are wider and read left
   aligned instead of centred. */
.gridrule.is-grid.is-thirds{grid-template-columns:repeat(3,minmax(0,1fr))}
.gridrule.is-thirds .gridrule-cell{
  aspect-ratio:auto;min-height:0;
  align-items:flex-start;justify-content:flex-start;text-align:left;
  gap:var(--s-4);padding:var(--s-6);
}
.gridrule.is-thirds .gridrule-cell h3{
  font-family:var(--font-sans);font-size:var(--t-h3);text-transform:none;
  letter-spacing:var(--tr-heading);color:var(--text-hi);
}
.gridrule.is-thirds .gridrule-cell p{font-size:var(--t-sm);max-width:none}

/* ---------- closing call to action ------------------------ */
.page-cta{position:relative;isolation:isolate;overflow:hidden;text-align:center}
.page-cta h2{max-width:20ch;margin-inline:auto}
.page-cta p{
  margin:var(--s-5) auto 0;max-width:48ch;
  font-size:var(--t-lead);line-height:var(--lh-lead);color:var(--text);
}
.page-cta-actions{
  display:flex;flex-wrap:wrap;gap:var(--s-3);justify-content:center;
  margin-top:var(--s-7);
}
.page-cta-actions .btn-outline svg{width:14px;height:14px;margin-left:.15rem}

@media (max-width:900px){
  .page-lead{padding-block:calc(68px + var(--s-7)) var(--s-7)}
  .page-lead h1{max-width:none}
  .gridrule.is-grid.is-thirds{grid-template-columns:1fr}
}

/* Four across for the platform's surfaces. */
.gridrule.is-grid.is-quarters{grid-template-columns:repeat(4,minmax(0,1fr))}
.gridrule.is-quarters .gridrule-cell{
  aspect-ratio:auto;min-height:0;
  align-items:flex-start;justify-content:flex-start;text-align:left;
  gap:var(--s-3);padding:var(--s-6) var(--s-5);
}
.gridrule.is-quarters .gridrule-cell h3{
  font-family:var(--font-sans);font-size:var(--t-h3);text-transform:none;
  letter-spacing:var(--tr-heading);color:var(--text-hi);
}
.gridrule.is-quarters .gridrule-cell p{font-size:var(--t-sm);max-width:none}
/* The route is the honest label for a surface: it is what the thing is
   called in the product, not a name invented for the website. */
.surface-route{
  font-family:var(--font-mono);font-size:var(--t-label);
  letter-spacing:.04em;color:var(--accent);
}

/* A line under a figure, explaining what is being looked at. */
.figure-note{
  margin:var(--s-5) auto 0;max-width:64ch;text-align:center;
  font-size:var(--t-sm);line-height:1.6;color:var(--text-lo);
}

@media (max-width:1024px){
  .gridrule.is-grid.is-quarters{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:600px){
  .gridrule.is-grid.is-quarters{grid-template-columns:1fr}
}

/* ---------- lead with a picture beside it -------------------
   The image takes the wider column and runs off the left edge of the
   screen; the copy sits in the narrower one, aligned to the same right
   gutter as every other section, so the page still has a straight edge
   to read against. */
.page-split{
  position:relative;z-index:var(--z-content);
  display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);
  align-items:center;gap:clamp(28px,4vw,72px);
  padding-block:calc(68px + var(--s-8)) var(--s-9);
  padding-inline:0 max(var(--gutter),calc((100vw - var(--maxw)) / 2));
}
.page-split-copy .eyebrow{display:block;margin-bottom:var(--s-4)}
.page-split-copy .lead{
  margin-top:var(--s-5);max-width:46ch;
  font-size:var(--t-lead);line-height:var(--lh-lead);color:var(--text);
}


/* Copy laid over a full-bleed figure, right-hand side. The figure keeps
   its own height; this is positioned over it rather than sizing it. */
/* ============================================================
   FEATURE BAND
   A statement split across the measure, then the product shown
   large beneath it and running off the edge.
   ============================================================ */
/* Tighter than a standard .block: this section is mostly picture, and
   the picture already carries plenty of air inside it. */
.feature-band{padding-block:clamp(56px,5.5vw,100px)}

.feature-head{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(28px,4vw,80px);align-items:start;
}
/* Bigger than a section heading and deliberately broken onto two lines
   in the markup: the break is part of the composition, not a wrap. */
.feature-head h2{
  font-size:clamp(2.25rem,1.4rem + 3.1vw,3.75rem);
  line-height:1.04;letter-spacing:-.03em;max-width:none;
}
.feature-aside{padding-top:var(--s-2)}
.feature-aside p{
  font-size:var(--t-lead);line-height:var(--lh-lead);color:var(--text);
  max-width:46ch;
}
.feature-link{
  display:inline-flex;align-items:center;gap:.55rem;
  margin-top:var(--s-6);
  color:var(--text-lo);text-decoration:none;font-size:var(--t-sm);
  transition:color var(--d-base) var(--ease);
}
.feature-link svg{width:15px;height:15px;transition:translate var(--d-base) var(--ease)}
.feature-link:hover{color:var(--text-hi)}
.feature-link:hover svg{translate:4px 0}

/* The board itself. Indented on the left to the page gutter and allowed
   to run past the right edge, then faded out on the right and bottom -
   so it reads as a window onto a larger board rather than a picture that
   has been cropped and stopped. */
.feature-shot{
  margin-top:clamp(28px,3.2vw,56px);
  padding-left:max(var(--gutter),calc((100vw - var(--maxw)) / 2));
  -webkit-mask-image:
    linear-gradient(to right,#000 72%,transparent 99%),
    linear-gradient(to bottom,#000 66%,transparent 99%);
  mask-image:
    linear-gradient(to right,#000 72%,transparent 99%),
    linear-gradient(to bottom,#000 66%,transparent 99%);
  -webkit-mask-composite:source-in;
  mask-composite:intersect;
}
/* Cropped to the head of the board rather than shown whole. The lower
   half is mostly empty columns, so at full height it cost several hundred
   pixels of page to show very little - and the bottom fade already says
   the board continues. */
.feature-shot img{
  display:block;width:100%;
  height:clamp(320px,32vw,520px);
  object-fit:cover;object-position:top left;
  border-top-left-radius:var(--r-lg);
  filter:var(--product-prep);
}
/* Without mask-composite the two gradients would stack rather than
   intersect and the fade would be wrong; one edge is better than none. */
@supports not ((mask-composite:intersect) or (-webkit-mask-composite:source-in)){
  .feature-shot{
    -webkit-mask-image:linear-gradient(to bottom,#000 66%,transparent 99%);
    mask-image:linear-gradient(to bottom,#000 66%,transparent 99%);
  }
}

/* ---------- the card laid over the board -------------------
   A sibling of the picture, not a child: .feature-shot carries a mask,
   and a mask applies to descendants too, so an overlay nested inside it
   would fade out along with the image it is meant to sit on. */
.feature-stage{position:relative}

.feature-overlay{
  position:absolute;z-index:2;
  /* Pulled out past the board's own left edge and a little below centre,
     so it hangs over the corner rather than sitting inside it. The offset
     scales with the viewport so the relationship holds rather than
     drifting apart on a wide screen. */
  left:calc(max(var(--gutter),calc((100vw - var(--maxw)) / 2)) - clamp(16px,2vw,44px));
  top:calc(50% + clamp(34px,4.4vw,88px));translate:0 -50%;
  width:min(30rem,34vw);
  padding:clamp(22px,2vw,32px);
  border-radius:var(--r-xl);
  /* Solid. Dark rather than the usual white-on-glass, because a light
     card over a dark board would lift exactly where the text sits.

     Opaque means no backdrop-filter is needed at all, which is also the
     cheapest option: a blurred panel over a picture re-rasterises what
     is behind it, and this one sits on a large image. */
  background:var(--ink-950);
  border:1px solid rgba(255,255,255,.14);
  box-shadow:0 28px 70px rgba(2,8,12,.55);
}
.feature-overlay .eyebrow{display:block;margin-bottom:var(--s-5)}
.feature-overlay dl{margin:0;display:flex;flex-direction:column}
.feature-overlay dl > div{
  padding-block:var(--s-4);
  border-top:1px solid rgba(255,255,255,.10);
}
.feature-overlay dl > div:first-child{border-top:0;padding-top:0}
.feature-overlay dl > div:last-child{padding-bottom:0}
.feature-overlay dt{
  color:var(--text-hi);font-size:var(--t-body);font-weight:500;
  letter-spacing:-.01em;
}
.feature-overlay dd{
  margin:var(--s-2) 0 0;
  color:var(--text);font-size:var(--t-sm);line-height:1.5;
}

@media (max-width:1100px){
  /* Below this the card covers more board than it leaves visible, so it
     stops floating and sits under the picture as a plain list. */
  .feature-overlay{
    position:static;translate:none;width:auto;
    margin:var(--s-7) auto 0;max-width:var(--maxw);
    background:none;border:0;box-shadow:none;padding:0 var(--gutter);
    -webkit-backdrop-filter:none;backdrop-filter:none;
  }
  .feature-overlay dl{
    display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
    gap:0 clamp(24px,4vw,56px);
  }
  .feature-overlay dl > div:nth-child(2){border-top:0;padding-top:0}
}
@media (max-width:900px){
  .feature-head{grid-template-columns:1fr;gap:var(--s-5)}
  .feature-head h2 br{display:none}
  .feature-aside{padding-top:0}

}
@media (max-width:600px){
  .feature-overlay dl{grid-template-columns:1fr}
  .feature-overlay dl > div:nth-child(2){border-top:1px solid rgba(255,255,255,.10);padding-top:var(--s-4)}
}

/* Claim rows used on their own, outside a tab panel: they need the space
   above them that the panel's heading block would otherwise have given. */
.claim-rows.is-standalone{margin-top:clamp(56px,6vw,104px)}

/* ============================================================
   FEATURE GRID
   A ruled grid whose cells carry an icon, a title and a short
   paragraph. Uses the .gridrule component, so the crosses at the
   intersections come for free and stay correct as the column
   count changes at each breakpoint.
   ============================================================ */
/* Every cell the same height. Grid stretches cells within a row by
   default but sizes each ROW to its own tallest cell, so two rows of
   different content came out different heights. 1fr rows make them
   equal, and the cells stretch to fill. */
.gridrule.is-feature{grid-auto-rows:1fr}
.gridrule.is-feature .gridrule-cell{
  aspect-ratio:auto;min-height:0;
  align-items:flex-start;justify-content:flex-start;text-align:left;
  gap:var(--s-3);
  padding:clamp(22px,1.9vw,32px) clamp(20px,1.8vw,30px);
}
.cell-icon{
  width:22px;height:22px;flex:none;
  fill:none;stroke:var(--text-lo);stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;
  transition:stroke var(--d-base) var(--ease);
}
.gridrule.is-feature .gridrule-cell:hover .cell-icon{stroke:var(--accent)}
.gridrule.is-feature .gridrule-cell h3{
  margin-top:0;
  font-family:var(--font-sans);text-transform:none;
  font-size:clamp(1.0625rem,.98rem + .3vw,1.25rem);
  letter-spacing:var(--tr-heading);color:var(--text-hi);
}
.gridrule.is-feature .gridrule-cell p{
  font-size:var(--t-sm);line-height:1.5;color:var(--text);max-width:40ch;
}

/* The grid is the section: its own heading needs less air above it than
   a standard block, since the cells carry their own padding. */
#how.block{padding-block:clamp(52px,5vw,92px)}
#how .gridrule{margin-top:clamp(28px,3vw,52px)}

/* The section heading carries the link, so it needs room under it. */
.sec-head .pill-link{margin-top:var(--s-5)}
.pill-link{
  display:inline-flex;align-items:center;gap:.55rem;
  padding:.7rem 1.25rem;border-radius:var(--r-pill);
  border:1px solid rgba(255,255,255,.22);
  color:var(--text-hi);text-decoration:none;
  font-size:var(--t-sm);letter-spacing:-.01em;
  transition:background var(--d-base) var(--ease),
             border-color var(--d-base) var(--ease);
}
.pill-link svg{width:14px;height:14px;transition:translate var(--d-base) var(--ease)}
.pill-link:hover{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.38)}
.pill-link:hover svg{translate:3px 0}

@media (max-width:1024px){
  .gridrule.is-grid.is-thirds.is-feature{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:640px){
  .gridrule.is-grid.is-thirds.is-feature{grid-template-columns:1fr}
}

/* ============================================================
   AGENT TILES
   The eight cards, shown as artwork. Each is a square captured
   from the product, so the grid is square cells with a gap - no
   border or padding of our own, since the card already has both.
   ============================================================ */
/* ============================================================
   AGENT CARDS
   The platform's own card, rebuilt in HTML: square, artwork
   behind, the agent's name at the head in its own accent, and an
   icon, subtitle and description at the foot.

   Nine cells - the lead plus eight agents - make a square mosaic
   at three across, with the copy placed in the middle.
   ============================================================ */
.agent-cards-grid{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(16px,1.5vw,26px);
}
.agent-card{
  position:relative;isolation:isolate;overflow:hidden;
  aspect-ratio:1;
  border-radius:var(--r-lg);
  border:1px solid rgba(255,255,255,.10);
  background:var(--ink-900);
  transition:border-color var(--d-base) var(--ease),
             translate var(--d-base) var(--ease),
             box-shadow var(--d-base) var(--ease);
}
.agent-card:hover{
  translate:0 -3px;
  border-color:color-mix(in srgb,var(--agent) 55%,transparent);
  box-shadow:0 22px 48px rgba(2,8,12,.5);
}
.agent-card-art{
  position:absolute;inset:0;z-index:0;
  width:100%;height:100%;object-fit:cover;
}
/* Clear through the middle so the artwork is what you see, dark at both
   ends where the words are. */
.agent-card::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(180deg,
    rgba(4,9,12,.55) 0%, rgba(4,9,12,.08) 22%,
    rgba(4,9,12,.30) 46%, rgba(4,9,12,.92) 82%, rgba(4,9,12,.96) 100%);
}
.agent-card-body{
  position:absolute;inset:0;z-index:2;
  display:flex;flex-direction:column;justify-content:space-between;
  padding:clamp(15px,1.25vw,20px);
}
.agent-card-body h3{
  font-family:var(--font-mono);font-weight:500;
  font-size:clamp(.8125rem,.76rem + .2vw,.9375rem);
  letter-spacing:.1em;text-transform:uppercase;
  white-space:nowrap;
  color:var(--agent);
}
/* The foot sits at the bottom of the square, so with descriptions
   running three to six lines the subtitle band - and the icon with it -
   landed anywhere from 83px to 148px down the card. Reserving the tallest
   case for both parts fixes the band, and the icons with it. Shorter
   copy leaves empty space over the artwork, which costs nothing; a line
   clamp would have aligned them by throwing sentences away. */
.agent-card-sub{
  display:flex;align-items:flex-start;gap:var(--s-2);
  min-height:calc(2 * 1.3em);
  font-size:var(--t-sm);font-weight:500;line-height:1.3;color:var(--text-hi);
  letter-spacing:-.01em;
}
.agent-card-sub img{width:20px;height:20px;object-fit:contain;flex:none}
.agent-card-foot p{
  margin-top:var(--s-3);
  min-height:calc(6 * 1.55em);
  font-size:var(--t-xs);line-height:1.55;color:var(--text);
}
/* Stacked, the cards are wide enough that nothing needs the reserve,
   and holding it open would leave a gap under every description. */
@media (max-width:980px){
  .agent-card-sub{min-height:0}
  .agent-card-foot p{min-height:0}
}

/* The lead cell, in the MIDDLE of the 3x3. It stays first in the DOM -
   it holds the h1, so it must be first for reading and tab order - and
   is placed explicitly; the eight agents then auto-flow into the cells
   around it.

   No artwork, no border: a hole in the mosaic that the copy sits in,
   which is what makes the grid read as one object rather than a heading
   plus a gallery. */
.agent-card.is-lead{
  grid-column:2;grid-row:2;
  /* The deepest ground in the scale, so the middle of the mosaic reads
     as a well rather than a lit panel. Glass borrowed the section's
     gradient and came out grey-blue, which put a colour in the one
     cell that has no artwork to justify one. */
  background:var(--ink-975);
  border-color:rgba(255,255,255,.10);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.04);
  aspect-ratio:1;
  display:grid;place-items:center;text-align:center;
}
.agent-card.is-lead::after{content:none}
.agent-card.is-lead:hover{translate:none;box-shadow:inset 0 1px 0 rgba(255,255,255,.04);border-color:rgba(255,255,255,.10)}
.agent-card.is-lead .agent-card-body{
  position:relative;inset:auto;height:auto;
  display:block;padding:clamp(15px,1.25vw,20px);
}
.agent-card.is-lead .eyebrow{display:block;margin-bottom:var(--s-4)}
.agent-lead-line{
  margin:var(--s-5) 0 0;
  font-size:clamp(1.25rem,1rem + .85vw,1.625rem);
  line-height:1.2;letter-spacing:var(--tr-heading);color:var(--text-hi);
}

@media (max-width:980px){
  .agent-cards-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* Two columns makes nine cells ragged, and there is no middle to sit
     in, so the lead returns to the top and spans the row. */
  .agent-card.is-lead{
    grid-column:1 / -1;grid-row:auto;aspect-ratio:auto;text-align:left;
    background:none;border-color:transparent;box-shadow:none;
  }
  .agent-lead-line{margin-inline:0}
  .agent-card.is-lead .agent-card-body{padding:0 0 var(--s-5)}
}
@media (max-width:560px){
  .agent-cards-grid{grid-template-columns:1fr}
  .agent-card{aspect-ratio:4/3}
  .agent-card.is-lead{aspect-ratio:auto}
}

/* The section opens the page, so it carries the lead padding a header
   would have given it. Four square cards need more than the reading
   measure. */
#agents{padding-block:calc(68px + var(--s-8)) var(--section-y)}
/* Capped at 1460 rather than wider. The cards are square, so every extra
   pixel of width buys the same in height while the copy inside stays the
   same length - past ~285px the surplus stops being an artwork window and
   starts reading as a hole. Below the cap the rails take the slack. */
#agents .wrap{max-width:1460px}

/* ---------- the stage --------------------------------------
   The four claims that used to be a section below the mosaic now
   flank it, two a side. They are read against the cards rather
   than after them, which is the whole point of the pairing: the
   grid shows what the agents are, the rails say why they differ.

   The mosaic gives up about 40px a card to make room. That is the
   trade - the alternative is rails too narrow to hold a sentence,
   which is how side notes turn into decoration. */
.agent-stage{
  --rail-l:clamp(196px,16vw,248px);
  --rail-r:clamp(170px,13vw,204px);
  display:grid;
  grid-template-columns:var(--rail-l) minmax(0,1fr) var(--rail-r);
  grid-template-rows:auto 1fr;
  grid-template-areas:
    "head mid right"
    "left mid right";
  column-gap:clamp(16px,1.4vw,20px);
  row-gap:clamp(28px,3vw,44px);
}
/* The head is a grid item rather than the first child of the left rail.
   Nested, it could only ever appear where the left rail appears - which
   below the breakpoint is UNDER the mosaic, with the page's h1 arriving
   after the thing it introduces. */
.agent-rail-head{grid-area:head;align-self:start}
.agent-stage .is-left{grid-area:left;align-self:start}
.agent-stage .agent-cards-grid{grid-area:mid;align-self:center}
.agent-stage .is-right{grid-area:right;align-self:center}

.agent-rail{
  display:flex;flex-direction:column;
  gap:clamp(28px,3vw,44px);
}
.agent-note-list{
  list-style:none;margin:0;padding:0;
  display:flex;flex-direction:column;
  gap:clamp(26px,2.8vw,40px);
}

/* The page heading lives at the head of the left rail rather than inside
   the middle cell. Boxed in the mosaic it read as a caption to the cards;
   here the cards are the subject and the heading introduces them. */
.agent-rail-head h1{
  margin:var(--s-3) 0 var(--s-4);
  font-size:clamp(1.75rem,1.2rem + 1.5vw,2.5rem);
  font-weight:var(--w-heading);line-height:1.06;
  letter-spacing:var(--tr-heading);color:var(--text-hi);
}
/* Same ramp as every other lead paragraph (--text-hi, not --text). The
   SIZE cannot match: this heading sits in a ~230px rail beside the
   mosaic, where the site's 56px h1 would break to four characters a
   line. The scale steps down; the colour does not. */
.agent-rail-head p{margin:0;font-size:var(--t-sm);line-height:1.6;color:var(--text-hi)}
/* Scattered, not tabulated: the right pair drops below the left so
   the four never read as two tidy columns bracketing the grid. */
.agent-rail.is-right{margin-top:clamp(40px,7vw,104px)}

.agent-note{position:relative}
/* An icon rather than the tick that was here: same job of marking where
   a note starts, but it names the claim instead of only pointing at it. */
.agent-note-ico{
  display:block;width:24px;height:24px;
  color:var(--accent);margin-bottom:var(--s-4);
}
.agent-note-term{
  display:block;
  font-family:var(--font-mono);font-size:var(--t-label);font-weight:500;
  letter-spacing:var(--tr-label);text-transform:uppercase;color:var(--text-lo);
}
.agent-note h3{
  margin:var(--s-2) 0 var(--s-3);
  font-size:var(--t-sm);font-weight:var(--w-heading);line-height:1.28;
  letter-spacing:var(--tr-heading);color:var(--text-hi);
}
.agent-note p{margin:0;font-size:var(--t-xs);line-height:1.62;color:var(--text)}

/* Below this the centre column cannot hold three legible cards and two
   rails at once, so the rails fall under the mosaic as a pair. */
@media (max-width:1240px){
  .agent-stage{
    grid-template-columns:repeat(2,minmax(0,1fr));
    grid-template-rows:auto auto auto;
    grid-template-areas:"head head" "mid mid" "left right";
    row-gap:var(--s-7);
  }
  .agent-rail-head{max-width:64ch}
  .agent-rail.is-right{margin-top:0}
}
@media (max-width:640px){
  .agent-stage{grid-template-columns:1fr;grid-template-areas:"head" "mid" "left" "right"}
  .agent-rail{gap:var(--s-6)}
}

/* ============================================================
   SPLIT LEAD
   Copy on the left, the product frame on the right, lit from behind.
   The light is the reason the frame reads as an object rather than a
   picture pasted on: the glow sits BEHIND it and spills past its
   edges, so the frame has somewhere to cast a shadow onto.

   The screenshot is light-on-white and never graded (lane 2: its own
   colour is the product claim), which is why the ground under it is
   the darkest on the site - anything lighter and the frame stops
   being the brightest thing in view.
   ============================================================ */
.split-lead{
  position:relative;isolation:isolate;overflow:hidden;
  display:grid;grid-template-columns:minmax(0,6fr) minmax(0,6fr);
  align-items:center;
  column-gap:clamp(24px,3vw,60px);
  background:linear-gradient(180deg,var(--ink-950) 0%,var(--ink-975) 62%);
  padding-block:calc(68px + var(--s-8)) var(--s-9);
}
.split-lead::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:
    radial-gradient(58% 62% at 74% 34%,rgba(126,158,168,.24),transparent 66%),
    radial-gradient(34% 38% at 70% 26%,rgba(62,214,155,.11),transparent 70%);
}
.split-lead-copy{
  display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-4);
  padding-left:max(var(--gutter),calc((100vw - var(--maxw)) / 2));
}
.split-lead-copy h1{max-width:14ch}
.split-lead-copy .lead{
  font-size:var(--t-lead);line-height:var(--lh-lead);
  color:var(--text-hi);max-width:44ch;margin:0;
}

/* Wholly inside the page. The frame carries the same margin on the right
   that the copy carries on the left, so the two halves are inset by the
   same amount and the screenshot is never cut off. */
.split-lead-media{
  padding-right:max(var(--gutter),calc((100vw - var(--maxw)) / 2));
}
.split-lead-media .showcase-frame{max-width:none;margin:0;width:100%}

.showcase-frame{
  position:relative;
  border-radius:var(--r-xl);overflow:hidden;
  border:1px solid rgba(255,255,255,.12);
  /* Two shadows, not one: a tight contact shadow so the frame sits ON
     the page, and a wide soft one so it sits ABOVE it. A single blur
     can do one or the other. */
  box-shadow:
    0 2px 6px rgba(0,0,0,.5),
    0 40px 90px -20px rgba(0,0,0,.75);
}
.showcase-frame img{display:block;width:100%;height:auto}

@media (max-width:1080px){
  /* Stacked, there is no right edge to run off: a frame hanging past
     the page with nothing beside it just looks misaligned. */
  .split-lead{
    grid-template-columns:1fr;row-gap:var(--s-7);
    padding-block:calc(68px + var(--s-7)) var(--s-8);
  }
  .split-lead-copy{padding-inline:var(--gutter)}
  .split-lead-copy h1,.split-lead-copy .lead{max-width:none}
  .split-lead-media{padding-inline:var(--gutter)}
  .showcase-frame{border-radius:var(--r-lg)}
}

/* ============================================================
   SPLIT WITH A CURVED SEAM
   The same device as .agents on the home page, mirrored: there the
   curve cuts the copy panel and it reaches right across the seam;
   here it cuts the media panel, which reaches LEFT.

   As on the home page, ONE --seam-overlap drives both sides - the
   panel's negative margin and the other column's clearance. Set
   independently they drift and the copy ends up under the photo.

   The background is on the SECTION, not the media column: the photo
   is pulled left and then clipped, so whatever the clip cuts away
   shows this ground. Painting the column instead would leave the
   page mesh showing through the curve.
   ============================================================ */
.split-curve{
  position:relative;z-index:var(--z-content);
  /* 4.6fr left the copy roughly 310px of measure once the page margin
     and the seam clearance were taken out, which broke the h1 over five
     lines. The copy column has to be sized to what is left AFTER both,
     not to how wide it looks. */
  display:grid;grid-template-columns:minmax(0,5.5fr) minmax(0,6.5fr);
  align-items:stretch;overflow:hidden;
  min-height:clamp(460px,60vh,660px);
  --seam-overlap:clamp(60px,9vw,160px);
  /* WHITE on the section, not just on the copy column. The photo is
     pulled left and then clipped, so whatever the clip cuts away shows
     this ground - on ink it left a dark wedge between the white panel
     and the curve. */
  background:#FFFFFF;
}
.split-copy{
  /* The same flipped ramp as .agents-copy on the home page, values and
     all, so the site has one white panel treatment rather than two that
     nearly match. Measured on #FFF: 17.7:1 / 10.4:1 / 4.6:1, and the
     brand pine at 8.2:1. The nav is a translucent dark bar that now sits
     over white for its left half - it composites to rgb(69,73,75), where
     its own text still holds 8.0:1 and its links 5.1:1. */
  --text-hi:#16191B;
  --text:#3B4145;
  --text-lo:#6E767B;
  --accent:#0E5A41;          /* the original brand pine reads on white */
  --accent-hover:#0A4733;
  --accent-ink:#FFFFFF;
  background:#FFFFFF;color:var(--text);

  display:flex;flex-direction:column;justify-content:center;align-items:flex-start;
  gap:var(--s-4);
  /* The lead sits under the fixed nav, so the top padding clears it. */
  padding-block:calc(68px + var(--s-8)) var(--s-8);
  padding-inline:max(var(--gutter),calc((100vw - var(--maxw)) / 2))
                 calc(var(--seam-overlap) + clamp(20px,2vw,44px));
}
/* Reaches the site's h1 size at full width - measured, the longest line
   takes 381px of the 385px the column holds, and it costs no extra line
   over the smaller size, so there is no reason for this lead to be the
   one that looks different. It falls away faster below ~1300 because the
   copy column keeps what is left after the page margin AND the seam
   clearance, and the clearance grows with the viewport. */
.split-copy h1{
  max-width:15ch;
  font-size:clamp(1.875rem,0.5rem + 3.4vw,3.5rem);
}
.split-copy .lead{
  font-size:var(--t-lead);line-height:var(--lh-lead);
  color:var(--text-hi);max-width:42ch;margin:0;
}
.split-media{
  position:relative;
  margin-left:calc(var(--seam-overlap) * -1);
  clip-path:url(#platformCurve);
}
/* Square off the framed-media radius: this panel IS the edge of the
   section, and a rounded corner inside a clip path reads as a mistake. */
.split-media .media{position:absolute;inset:0;border-radius:0}
/* Overscanned so the parallax has somewhere to travel. The motion layer
   moves the image by +/- half of data-parallax-amount and scales it to
   data-parallax-scale, so at 4% and 1.03 the worst case is 2.2% of drift
   plus 1.65% of growth - a flush 100% would pull the photo's own edge
   into the panel at the ends of the range. Same convention as
   .media-bleed, which overscans by twice its shift. */
.split-media img{
  position:absolute;inset:-5% 0 auto 0;
  display:block;width:100%;height:110%;object-fit:cover;
}

@media (max-width:1080px){
  /* Stacked, there is no seam to curve. Clipping a full-width panel
     would just take a bite out of the photograph for no reason. */
  .split-curve{grid-template-columns:1fr;min-height:0}
  .split-copy{
    padding-block:calc(68px + var(--s-7)) var(--s-7);
    padding-inline:var(--gutter);
  }
  .split-copy h1,.split-copy .lead{max-width:none}
  .split-media{
    margin-left:0;clip-path:none;
    aspect-ratio:16 / 9;
  }
}

/* ---------- figures ----------------------------------------
   Every numeral on the site, wherever editorial.css had put it in
   the mono face. See --font-num: the mono's zero is slashed and
   cannot be unslashed. Tabular so stacked figures stay in column. */
.value .n,.split .copy li .n,.coming-feats .n,.tbl td.num,.stat-num{
  font-family:var(--font-num);
  font-variant-numeric:tabular-nums lining-nums;
  font-weight:500;
}
