/* The opening of an inner page. Centred, and the only place on the page with an h1. */
.intro{position:relative;background:#fff;padding:var(--section-pad) 0 calc(var(--section-pad) * .7)}
/* When a panel lifts over it, the heading holds the middle of an empty screen until you
   scroll, and the section grows by the lift so the panel never covers the copy. svh, not
   vh, so mobile browser chrome appearing does not resize the hero under the reader. */
.intro:has(+ .sld){
  min-height:100vh;min-height:100svh;
  display:flex;flex-direction:column;justify-content:center;
  padding-bottom:calc(var(--section-pad) * .7 + var(--section-lift));
}
/* On a page with no dark hero the header sits in the flow, so the screen this has to fill is
   a viewport minus the header. Without subtracting it the first screen is 120px too tall and
   the panel below never peeks above the fold, which is the whole point of the lift. */
.no-hero .intro:has(+ .sld){
  min-height:calc(100vh - var(--header-h));
  min-height:calc(100svh - var(--header-h));
}
.intro__inner{max-width:56rem;margin:0 auto;padding-inline:var(--gutter);text-align:center}
.intro__inner .eyebrow{text-align:center}
.intro__inner .sec-head{margin-top:1.25rem;max-width:none}
.intro__inner .sec-sub{margin-top:clamp(1.25rem,3vw,1.75rem);max-width:38rem}
/* The column gap has to beat the buttons' own 1.5rem inner padding, or the pair groups as
   one control. Both buttons read as outlines here — the primary's fill is white on a white
   page — so a tight gap chained them into a single bar with the orange tile wedged inside.
   Deliberately rem, not vw: the padding it has to beat is rem, and a vw gap collapses to its
   floor exactly when the viewport is small — including under browser zoom, which shrinks the
   CSS viewport and brought the fault straight back. Row gap stays 1rem for the wrapped case. */
.intro__actions{display:flex;flex-wrap:wrap;justify-content:center;gap:1rem 2rem;margin-top:clamp(1.75rem,4vw,2.5rem)}
/* min-width, not width: the label is nowrap, so a fixed width narrower than the text just
   makes the label spill past the box. Layout still reserves only 13.5rem, so the ink of one
   button ran into the next and ate the gap. A floor lets the box grow to its content. */
.intro__cta{min-width:13.5rem;max-width:100%}
@media (max-width:36em){.intro__cta{width:100%;max-width:18rem}}

/* ---- drifting pictures ----------------------------------------------------- */
/* Rounded tiles scattered around the heading, filling the space an opening like this leaves
   empty either side. Six fixed places rather than real randomness: random positions collide
   with the copy at some window width sooner or later, and "scattered" only has to LOOK
   unplanned. Three down each side, sizes and offsets deliberately uneven.

   Below 80em the heading needs the whole width, so they are not rendered at all — the copy
   never has to compete with them for the line. */
.intro__floats{display:none}
@media (min-width:80em){
  .intro--floats .intro__inner{position:relative;z-index:1}
  .intro__floats{display:block;position:absolute;inset:0;overflow:hidden;pointer-events:none}

  .intro__float{
    position:absolute;margin:0;
    width:var(--w);aspect-ratio:var(--ar,4 / 5);
    border-radius:clamp(1.25rem,1.8vw,2rem);overflow:hidden;
    background:var(--wash,#f7f8fa);
    box-shadow:0 1.5rem 3rem -1.5rem rgba(15,23,42,.22);
    opacity:0;transition:opacity .9s var(--ease) calc(var(--i) * .11s);
  }
  .intro__float.is-in{opacity:1}
  .intro__floatimg{
    width:100%;height:100%;object-fit:cover;display:block;
    transform:scale(1.07);transition:transform 1.4s var(--b2-ease) calc(var(--i) * .11s);
  }
  .intro__float.is-in .intro__floatimg{transform:none}

  /* Three down each side, none across the bottom. The section runs taller than the white you
     can see — it carries the lift the next panel rides up into — so anything below about 80%
     is behind that panel by the time you look at it. Widths in vw so the tiles shrink with the
     margin they live in, and the copy keeps its clearance at 1280 as well as at 1920.
     Left. */
  .intro__float:nth-child(1){--w:clamp(6.5rem,10vw,12.5rem);--ar:4 / 5;  left:3.5%;  top:8%}
  .intro__float:nth-child(2){--w:clamp(4rem,5.5vw,7rem);   --ar:1 / 1;  left:1%;    top:42%}
  .intro__float:nth-child(3){--w:clamp(5.5rem,8vw,10.5rem);--ar:5 / 4;  left:8%;    top:66%}
  /* Right */
  .intro__float:nth-child(4){--w:clamp(5rem,7.5vw,9.5rem); --ar:1 / 1;  right:6%;   top:5%}
  .intro__float:nth-child(5){--w:clamp(7rem,10.5vw,13rem); --ar:4 / 5;  right:1.5%; top:34%}
  .intro__float:nth-child(6){--w:clamp(4.5rem,6.5vw,8.5rem);--ar:4 / 5; right:9%;   top:62%}

  /* The drift. One transform, composited, no layout — a long slow breath rather than a loop
     you can time. Every tile gets its own duration and delay so they never march together. */
  .intro__float{animation:intro-drift var(--dur,16s) var(--lag,0s) ease-in-out infinite alternate}
  .intro__float:nth-child(1){--dur:17s; --lag:0s;    --dx:6px;  --dy:-18px; --rot:-1.2deg}
  .intro__float:nth-child(2){--dur:13s; --lag:-4s;   --dx:-8px; --dy:-12px; --rot:1.6deg}
  .intro__float:nth-child(3){--dur:19s; --lag:-9s;   --dx:5px;  --dy:-15px; --rot:1deg}
  .intro__float:nth-child(4){--dur:15s; --lag:-6s;   --dx:-6px; --dy:-16px; --rot:-1.4deg}
  .intro__float:nth-child(5){--dur:21s; --lag:-2s;   --dx:7px;  --dy:-20px; --rot:.9deg}
  .intro__float:nth-child(6){--dur:14s; --lag:-11s;  --dx:-5px; --dy:-13px; --rot:-1.7deg}
}
@keyframes intro-drift{
  to{transform:translate3d(var(--dx,0),var(--dy,-16px),0) rotate(var(--rot,0deg))}
}

@media (prefers-reduced-motion:reduce){
  .intro__float{animation:none;opacity:1;transition:none}
  .intro__floatimg{transform:none;transition:none}
}

/* ---- parallax pictures ------------------------------------------------------ */
/* Three rounded tiles under the copy, at staggered heights as drawn (Figma 296-8189), the
   outer two higher than the middle. At rest their lower parts are past the fold. As you
   scroll they rise FASTER than the page, each at its own rate, so they fly up past the hero's
   own heading and are gone before the next section arrives — the join between the two
   sections is then an ordinary one. The middle tile is the slowest and sits behind the copy;
   the outer two are quicker and pass in front of it. Nearer things move faster: that is the
   depth.

   The copy is lifted to z-index 1 inside the section so the middle tile (z 0) passes behind
   it and the outer two (z 2) in front. Transforms are written by view.js. */
.intro--px{padding-bottom:0;overflow:visible}
.intro--px .intro__inner{position:relative;z-index:1}
.intro__px{
  position:relative;height:clamp(8rem,16vh,12rem);
  max-width:var(--container-max);margin:0 auto;padding-inline:var(--gutter);
  pointer-events:none;
}
.intro__tile{
  position:absolute;margin:0;
  width:var(--w);aspect-ratio:1;
  border-radius:clamp(1.25rem,1.8vw,2rem);overflow:hidden;
  background:var(--wash,#f7f8fa);
  box-shadow:0 1.5rem 3rem -1.5rem rgba(15,23,42,.22);
  transform:translate3d(0,var(--y,0px),0) scale(var(--s,1));
  opacity:var(--o,1);filter:blur(var(--b,0px));
  will-change:transform;
}
.intro__tileimg{width:100%;height:100%;object-fit:cover;display:block}
/* The outer two sit in the margins, clear of the copy, and rise past it; the middle one sits
   under the copy and blurs out the moment its top meets the copy block (`--fade:touch`, read
   by view.js), so it is never seen through the text. --k is how much faster than the page a
   tile rises. No tilt: the pictures stay square to the page. */
.intro__tile:nth-child(1){--w:clamp(9rem,15.5vw,18.5rem);left:0;  top:44%;--k:2.7;z-index:2}
.intro__tile:nth-child(2){--w:clamp(9rem,15.5vw,18.5rem);left:50.2%;top:62%;--k:1.8;z-index:0;--s:.94;--fade:touch}
.intro__tile:nth-child(3){--w:clamp(9rem,15.5vw,18.5rem);right:0;top:22%;--k:2.6;z-index:2}
/* When a block has only two pictures the second one is not the middle of three, so it must not
   keep the middle's rules: it takes the right-hand spot, in front, with no touch-fade. */
.intro__tile:nth-child(2):last-child{left:auto;right:0;top:22%;--k:2.6;z-index:2;--s:1;--fade:none}
/* Arriving: a fade-and-settle on the picture INSIDE the tile, not on the tile. The tile's own
   transform and opacity belong to the scroll, and the site's generic reveal (`[data-reveal]`,
   which ends on transform:none) outranks them — that is exactly how the first cut froze. */
.intro__tileimg{opacity:0;transform:scale(1.07);animation:intro-tile-in 1.1s var(--b2-ease) calc(.15s + var(--i) * .12s) forwards}
@keyframes intro-tile-in{to{opacity:1;transform:none}}
/* At rest the lowest tile can reach into the top of the next section on a tall window. That
   section lets it through rather than cutting it off along a hard edge. */
/* Three classes, not two: the principles stylesheet loads after this one and sets its own
   `.intro + .pri--light` top pad at the same specificity, so a two-class rule here lost. */
.intro.intro--px + .pri--light{background:transparent;padding-top:calc(var(--section-pad) * .3)}
.intro.intro--px + .pri--light .pri__inner{position:relative;z-index:1}

@media (max-width:64em){
  .intro__px{height:clamp(8rem,22vh,12rem)}
  .intro__tile:nth-child(1){--w:26vw;left:0;top:30%}
  .intro__tile:nth-child(2){--w:26vw;left:37%;top:60%}
  .intro__tile:nth-child(3){--w:26vw;right:0;top:0}
  .intro__tile:nth-child(2):last-child{left:auto;right:0;top:0}
}
@media (prefers-reduced-motion:reduce){
  .intro__tile{transform:none;opacity:1;filter:none;transition:none}
  .intro__tileimg{animation:none;opacity:1;transform:none}
}

/* Muted line under the buttons, and the evidence card below the copy. Centred, like the rest of the block. */
.intro__note{margin:clamp(.9rem,2vw,1.25rem) auto 0;max-width:46rem;font-size:.8125rem;line-height:1.5;color:var(--muted-4)}
.intro__stage{position:relative;z-index:3;margin-top:clamp(2rem,5vw,3.25rem);padding-inline:var(--gutter)}
.intro__caption{max-width:min(100%,32.75rem);margin:clamp(.75rem,1.6vw,1.1rem) auto 0;font-size:.75rem;line-height:1.5;color:var(--muted-4);text-align:right}
/* With the card in place the tiles need their own breathing room above them. */
.intro--px .intro__stage{margin-bottom:clamp(1.5rem,4vw,2.5rem)}
@media (max-width:36em){.intro__caption{text-align:center}}

/* ---------- Split: copy left, card right. Only with a card, and only above the tablet breakpoint,
   where there is room for two columns. Below it the block falls back to the centred stack. ---------- */
@media (min-width:64.01em){
  .intro--split .intro__main{
    display:grid;
    grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);
    align-items:center;
    column-gap:clamp(1.75rem,3.5vw,3.5rem);
    max-width:86rem;margin-inline:auto;padding-inline:var(--gutter);
  }
  .intro--split .intro__inner{max-width:none;margin:0;padding-inline:0;text-align:left}
  .intro--split .intro__inner .sec-head{margin-inline:0;text-align:left;text-wrap:pretty}
  .intro--split .intro__inner .sec-sub{margin-inline:0;max-width:34rem;text-align:left}
  .intro--split .intro__inner .eyebrow{text-align:left}
  .intro--split .intro__actions{justify-content:flex-start}
  .intro--split .intro__note{margin-left:0;margin-right:0;text-align:left}
  .intro--split .intro__stage{margin-top:0;padding-inline:0}
  .intro--split .epack{margin-left:auto;margin-right:0}
  .intro--split .intro__caption{margin-right:0}
}

/* In split the pictures stop being a full-width strip. They cluster around the card on the
   right, smaller, and still rise on scroll. The card sits at z-index 3, so they pass behind
   it rather than over the rows. Two tiles here, not three: with the middle one gone the
   release still fires, because the outer pair gate it too (see view.js). */
@media (min-width:64.01em){
  /* The strip is pulled up into the card's row so the tiles sit beside it rather than under
     the whole hero. `left:auto` on both matters: the base rules set `left`, and with a width
     set an absolute box follows `left` and ignores `right` when both are given. */
  .intro--split .intro__px{
    height:clamp(5rem,9vh,8rem);
    max-width:86rem;
    margin-top:calc(-1 * clamp(6rem,12vh,11rem));
  }
  .intro--split .intro__tile{--w:clamp(6rem,9vw,10.5rem);border-radius:clamp(.9rem,1.2vw,1.4rem)}
  .intro--split .intro__tile:nth-child(1){left:auto;right:24%;top:-120%;--k:2.4;z-index:2;--s:1}
  /* With the middle picture gone this is the second of two, so it inherits the middle tile's
     rules: z-index 0 and --fade:touch. Both are wrong here, so both are reset. */
  .intro--split .intro__tile:nth-child(2){left:auto;right:-1%;top:10%;--k:2.9;z-index:2;--s:1;--fade:none}
}

/* ---- the dotted word ------------------------------------------------------ */
/* Sits across the foot of the section, under everything else. The section's own bottom
   padding is what separates it from the copy, so the word gets none of its own — giving it
   a margin here would push it away from the edge it is supposed to sit along. */
.intro__word{margin-top:clamp(2rem,6vh,5rem)}
/* Brand blue, kept faint: it is a watermark behind the page, never a heading. */
.intro__word{--dotword-rgb:0,113,188;--dotword-alpha:.3}

/* ---- the timeline card ----------------------------------------------------- */
/* The quiet sibling of the evidence card: same place in the hero, no animation and nothing
   to wait for. Rows are a fixed label column and a flowing one, so the whens line up down
   the left however long the descriptions run. */
.tcard{
  width:min(100%,32.75rem);margin-inline:auto;
  background:#fff;border:1px solid var(--hairline);border-radius:1rem;
  box-shadow:0 18px 44px rgba(10,26,48,.06);
  padding:clamp(.5rem,1.4vw,1rem) clamp(1.25rem,2.6vw,2rem);
}
/* One grid for the whole list rather than a grid per row: the label column then sizes to the
   widest label once, and every description starts at the same place down the card. A grid per
   row sizes each independently, so "Day 1" and "Every release" push their text to different
   points and the card reads as ragged. */
/* The row is a real box on a subgrid, not display:contents. Both give the same shared column
   widths, but display:contents leaves no row to draw on, so the rule has to be a border on
   each cell — two borders meeting at a fractional column edge (149.445px here), which the
   browser rounds independently and paints with a hairline seam between them. One box, one
   border, no seam. The cells keep no vertical padding of their own; the row owns it. */
.tcard__rows{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:max-content minmax(0,1fr);
}
.tcard__row{
  display:grid;grid-column:1/-1;grid-template-columns:subgrid;align-items:baseline;
  padding-block:clamp(.875rem,1.9vw,1.25rem);
  border-top:1px solid var(--hairline);
}
.tcard__row:first-child{border-top:0}
.tcard__label{padding-right:clamp(1rem,2.4vw,1.75rem)}
.tcard__label{
  font-weight:var(--w-ui);font-size:var(--cap-size);line-height:var(--cap-lh);
  letter-spacing:var(--eyebrow-track);text-transform:uppercase;color:var(--blue);
}
.tcard__text{
  font-size:var(--body-size);line-height:var(--body-lh);letter-spacing:var(--body-track);
  color:var(--muted-3);
}
.tcard__text strong{font-weight:600;color:var(--ink-heading)}

/* The rows land one after another as the hero settles, reading down the card the way the
   sequence itself does. Driven off the stage's own reveal, so the card waits for the hero
   rather than playing to nobody. The cells are animated, not the row: display:contents
   leaves no row box to move. */
.js .intro__stage[data-reveal] .tcard__label,
.js .intro__stage[data-reveal] .tcard__text{
  opacity:0;transform:translateY(.625rem);
  transition:opacity .55s var(--ease),transform .55s var(--ease);
  transition-delay:calc(var(--i,0) * 120ms + .15s);
}
.js .intro__stage[data-reveal].is-in .tcard__label,
.js .intro__stage[data-reveal].is-in .tcard__text{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  .js .intro__stage[data-reveal] .tcard__label,
  .js .intro__stage[data-reveal] .tcard__text{opacity:1;transform:none;transition:none}
}

/* Narrow: the when sits above its description rather than squeezing it to two words a line.
   One column, so the rule falls above the label and the description keeps none of its own. */
@media (max-width:36em){
  .tcard__rows{grid-template-columns:minmax(0,1fr)}
  .tcard__label{padding-right:0;padding-bottom:.25rem}
}

/* ---- phones: a hero with a card is one screen ------------------------------
   The same treatment the dark home banner gets. The copy keeps its natural height, the card
   takes what is left and is cropped by the bottom of the screen, and its rows run one at a
   time in a fixed slot. Scoped to a split hero that actually carries an evidence card, so
   the plain intros and the How it works timeline card are untouched. */
@media (max-width:36em){
  .intro--split:has(.epack){
    height:calc(100svh - var(--header-h));
    padding-block:1.5rem 0;
    overflow:hidden;
  }
  .intro--split:has(.epack) .intro__main{height:100%;min-height:0;display:flex;flex-direction:column}
  .intro--split:has(.epack) .intro__inner{flex:none}

  /* Tightened so the card gets a strip worth showing. */
  .intro--split:has(.epack) .eyebrow{margin-bottom:.625rem}
  .intro--split:has(.epack) .sec-sub{margin-top:.875rem}
  .intro--split:has(.epack) .intro__actions{margin-top:1.25rem;gap:.625rem}
  .intro--split:has(.epack) .intro__note{margin-top:.75rem}

  .intro--split:has(.epack) .intro__stage{flex:1 1 auto;min-height:0;overflow:hidden;margin-top:1.25rem}
  /* Small print about a cropped illustration, itself cropped, is noise. */
  .intro--split:has(.epack) .intro__caption{display:none}

  /* One row at a time, in a slot that never changes height. .epack .epack__row rather than
     .epack__row: the shared card stylesheet shows a waiting row at the same specificity and
     loads after this one. */
  .intro--split .epack__rowbox{height:5.25rem;min-height:0;clip-path:inset(0 -3rem)}
  .intro--split .epack__rows{gap:0}
  .intro--split .epack .epack__row{display:none}
  .intro--split .epack .epack__row.is-typing,
  .intro--split .epack .epack__row.is-current,
  .intro--split .epack:not(.is-live) .epack__row.is-done:last-child{display:flex}
}

/* ---- the rotating subtitle -------------------------------------------------
   Phones only. One grid cell holds every sentence, so the paragraph is as tall as its
   longest sentence instead of the sum of them, and nothing shifts as they change. */
@media (max-width:36em){
  .sec-sub--rot{display:grid}
  .sec-sub--rot .sub-line{grid-area:1/1;opacity:0;transition:opacity .6s var(--ease)}
  .sec-sub--rot .sub-line.is-on{opacity:1}
}
/* Anything wider, and anyone who would rather nothing moved, gets the whole paragraph. */
@media (prefers-reduced-motion:reduce){
  .sec-sub--rot{display:block}
  .sec-sub--rot .sub-line{opacity:1}
}
