/* V7 · Bright — the central-app-home prototype's visual system, carried over
   as closely as the shared chrome allows. Its palette, its pill buttons, its
   rounded cards, its centred sections, its navy synthesis band. Scoped to .v7
   so nothing here reaches the other treatments.
   The status chips keep the platform's own colours: they are positioning, not
   styling, and survive in every variation. */
.v7{
  --ink:#1D1D1F; --muted:#56565C; --faint:#86868B; --white:#FFFFFF;
  --slate:#F5F5F7; --off:#F4F5F7; --navy:#0E2A4F;
  --accent:#0071E3; --accent-deep:#0059B3; --accent-tint:#EAF3FD; --hair:#E4E4E7;
  --sans:"Segoe UI",-apple-system,"Helvetica Neue",Arial,sans-serif;
  font-family:var(--sans); color:var(--ink); background:var(--white);
  line-height:1.6; font-size:17px;
}
/* Longhand on purpose. As `padding:0 28px` this was a shorthand at higher
   specificity than `.foot-inner` and `.foot-base`, both of which are `.wrap`
   elements, so it silently reset the footer's own vertical padding to zero on
   every page in this treatment: 44px above the row and 30px below it, 18px above
   the copyright and 26px under it, all gone, with the rule landing hard against
   the foot of the tallest column. Setting only the two sides it means to set
   leaves the footer's spacing to the footer. */
.v7 .wrap{max-width:1080px;padding-left:28px;padding-right:28px}
.v7 h1,.v7 h2,.v7 h3{font-family:var(--sans);line-height:1.15;margin:0;
  letter-spacing:-.015em;text-wrap:balance;font-weight:700}
.v7 p{margin:0 0 1em}
.v7 a{color:var(--accent-deep);text-decoration:none}
.v7 a:hover{text-decoration:underline}
.v7 :focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* buttons */
.v7 .btn{display:inline-block;border-radius:980px;padding:12px 26px;font-size:16px;
  font-weight:600;cursor:pointer;border:0;font-family:var(--sans);text-decoration:none}
.v7 .btn.primary{background:var(--accent);color:#fff}
.v7 .btn.primary:hover{background:var(--accent-deep);text-decoration:none}
.v7 .btn.ghost{background:transparent;color:var(--accent-deep);border:1.5px solid var(--accent)}
.v7 .btn.ghost:hover{background:var(--accent-tint);text-decoration:none}

/* the shared chrome, restyled into this system */
.v7 .site-nav{background:rgba(255,255,255,.92);backdrop-filter:blur(10px);border-bottom:1px solid var(--hair)}
.v7 .nav-inner{min-height:60px}
.v7 .brand{font-family:var(--sans);font-size:19px;font-weight:700;color:var(--ink);letter-spacing:-.01em}
.v7 .brand .mark{border-radius:0}
.v7 .nav-links a{font-family:var(--sans);color:var(--muted);font-size:15px;border-bottom:none}
.v7 .nav-links a:hover{color:var(--ink)}
.v7 .nav-links a.nav-cta{background:var(--accent);color:#fff;border-radius:980px;padding:8px 18px;font-size:14.5px}
.v7 .nav-links a.nav-cta:hover{background:var(--accent-deep)}
.v7 .nav-toggle{border-color:var(--hair);border-radius:8px}
.v7 .site-foot{background:var(--off);border-top:1px solid var(--hair)}
.v7 .foot-name,.v7 .foot-from,.v7 .foot-links a,.v7 .foot-note,.v7 .foot-base{font-family:var(--sans)}
.v7 .foot-name{color:var(--ink)}
.v7 .foot-from,.v7 .foot-links a{color:var(--muted)}
/* Both of these were --faint on the footer's own ground, which measures 3.32:1.
   The legend note and the copyright are the two smallest strings on the page and
   were the two hardest to read; --muted takes them to 6.68:1. Measured by
   _tools/motion-test.mjs rather than trusted. */
.v7 .foot-base,.v7 .foot-note{color:var(--muted);letter-spacing:0}
/* 11px mono is a size that reads as a caption. In this treatment's sans it was
   simply the smallest thing in the footer for no reason it could carry. */
.v7 .foot-note{font-size:12.5px}
.v7 .foot-base{border-top-color:var(--hair)}
/* Owner ruling, 2026-08-28: the chips may take the page's form while keeping
   their two colours. Typeface and radius only. Both colours, both fills and what
   each one says are untouched, and this is scoped to the treatment, so every
   other variation renders the chips exactly as it always has. */
.v7 .chip{font-family:var(--sans);font-size:12px;font-weight:600;letter-spacing:.01em;
  padding:3.5px 12px 4px;border-radius:980px;vertical-align:1px}

/* hero */
.v7 .hero{padding:90px 0 46px;text-align:center;background:var(--white)}
.v7 .hero h1{font-size:clamp(38px,5.6vw,58px);max-width:18em;margin:0 auto}
.v7 .hero .sub{font-size:21px;color:var(--muted);margin:22px auto 32px;max-width:40em}
/* Owner ruling, 2026-08-29: the rotating line sets three quarters of the
   headline. It is the variable half of the hero and the fixed claim beneath it is
   the constant one, so the claim leads and the examples support it. Sized in em so
   it tracks the headline's own clamp at every width. It also settles a wrap: at the
   headline's full size the three phrases set different line counts across 23 of the
   65 widths from 320 to 1600, so the hero changed height each time the rotor turned.
   At .75em that falls to 2. */
.v7 .rotor-line{display:block;min-height:1.2em;font-size:.75em;color:var(--accent-deep)}
/* Owner ruling, 2026-08-30: the fourth line is the D card’s own sentence, which is
   shorter than the other three and would have set one line where they set two across
   340 to 520, jumping the hero 32px each time the rotor turned there. Reserving the
   second line below 560 removes it: measured 0 of 65 widths against 11, and because
   the phrases are already wrapping at those widths it costs two or three pixels
   rather than the twenty-six a reserve would cost at 1280. */
@media (max-width:560px){.v7 .rotor-line{min-height:2.4em}}
.v7 .caret{display:inline-block;width:3px;height:.88em;background:var(--accent);
  margin-left:5px;vertical-align:-.06em;animation:v7blink 1.05s steps(1) infinite}
@keyframes v7blink{50%{opacity:0}}
.v7 .sr-only{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap}
.v7 .cta-row{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
.v7 .friction{font-size:14px;color:var(--faint);margin-top:14px}

/* flexibility band */
.v7 .flexband{background:var(--white);padding:0 0 80px}
.v7 .flex-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:34px 48px;
  border-top:1px solid var(--hair);padding-top:30px}
/* No container at all, still. The tile version used --slate, which is #F5F5F7, and
   the audience strip forty pixels below it is the same #F5F5F7 full-bleed, with the
   capability band beneath that on #F4F5F7: three greys within one value of each
   other doing three different jobs, so the section washed out against the thing it
   introduces. That ruling stands and nothing here is a box.

   Owner ruling, 2026-08-29: at four columns a separate two-pixel rule floating over
   each one read as a wireframe. The structure is now one hairline across the whole
   set with the columns divided from each other, which is what a rule per column was
   approximating: the section gets a single top edge, and the dividers hang from it.
   The divider before D is the accent at two pixels, so the column the section calls
   different is marked by the same system that separates the others rather than by a
   container of its own. Below 560 there is nothing to divide, so the hairline goes
   back over each item and D's turns accent. */
.v7 .flex-item{position:relative;background:transparent;border:0;border-radius:0;
  padding:0;text-align:left}
.v7 .flex-item + .flex-item::before{content:"";position:absolute;left:-24px;top:0;bottom:0;
  border-left:1px solid var(--hair)}
.v7 .flex-item:nth-child(4)::before{left:-25px;border-left:2px solid var(--accent)}
.v7 .flex-item b{display:block;font-size:19.5px;line-height:1.2;letter-spacing:-.014em;
  margin:0 0 3px;padding-right:0}
.v7 .flex-item span{font-size:15px;line-height:1.6;color:var(--muted);text-wrap:pretty}
/* Owner ruling, 2026-08-29: a two-line hanging initial on each approach, so the
   four read as A, B, C, D at a glance. One structure for all four, and colour is
   the only thing that separates them: A, B and C are the choices a committee
   already has words for and take the page's ink, D is the one this platform adds
   and takes the accent. The letter hangs in the margin the column's rule already
   defines, so it costs no vertical space, and it is sized so its cap spans the
   label line and the first line of the body. aria-hidden: the label says the word. */
/* Owner, 2026-08-29: the step between the name and the copy below it read as too
   wide. Six pixels of it was the label's own margin, which is now three. The rest
   was the initial: at 60px its cap ran a full line past the label, so the name and
   the first line of copy were both squeezed into the letter's channel while the
   second line ran the full column, and that step read as a gap. At 52px the
   channel is shallower. The letter is no longer a strict two-line initial, which
   was the earlier ruling: its cap now spans about a line and a half. */
.v7 .flex-item b::first-letter{float:left;margin:2px 8px 0 0;
  font-size:52px;font-weight:800;line-height:.72;letter-spacing:-.05em;color:var(--ink)}
.v7 .flex-item:nth-child(4) b::first-letter{color:var(--accent)}
.v7 .flex-note{text-align:center;color:var(--muted);font-size:15px;margin:22px 0 0}

/* audience strip */
.v7 .orgs{background:var(--slate);padding:30px 0}
.v7 .orgs .wrap{display:flex;gap:12px 34px;justify-content:center;flex-wrap:wrap;align-items:baseline}
.v7 .orgs .label{font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);font-weight:600}
.v7 .orgs .kind{font-size:15.5px;color:var(--muted);font-weight:600}
.v7 .orgs .kind:hover{color:var(--accent-deep)}
@media (max-width:820px){.v7 .orgs .wrap{gap:10px 22px}}
@media (max-width:560px){.v7 .orgs .label{flex-basis:100%;text-align:center}}
.v7 .orgs .kind-more{color:var(--accent-deep)}

/* The wider audience list. The strip names five and the market is wider than five,
   so the sixth entry opens the rest rather than lengthening the line: the band keeps
   one row of names and the page keeps its quiet. Closed it is hidden outright, so it has no
   box, no transform and nothing for the motion pass to measure; the sheet takes its
   offset only while the panel is open, which is also why a reader with the script
   blocked simply follows the link to the page the strip already pointed at. */
.v7 .orgpanel[hidden]{display:none}
.v7 .orgpanel{position:fixed;inset:0;z-index:60}
.v7 .orgpanel-scrim{position:absolute;inset:0;background:rgba(15,20,28,.42)}
.v7 .orgpanel-sheet{position:absolute;top:0;right:0;height:100%;width:min(440px,92vw);
  background:var(--white);padding:30px 32px 40px;overflow-y:auto;
  box-shadow:-18px 0 46px rgba(15,20,28,.18)}
.v7 .orgpanel:not(.is-open) .orgpanel-sheet{transform:translateX(100%)}
.v7 .orgpanel:not(.is-open) .orgpanel-scrim{opacity:0}
.v7 .orgpanel.is-ready .orgpanel-sheet{transition:transform .26s ease}
.v7 .orgpanel.is-ready .orgpanel-scrim{transition:opacity .26s ease}
.v7 .orgpanel-close{position:absolute;top:22px;right:26px;background:none;border:0;
  font:inherit;font-size:13px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint);cursor:pointer;padding:6px}
.v7 .orgpanel-close:hover{color:var(--ink)}
.v7 .orgpanel-sheet h2{font-size:23px;line-height:1.22;letter-spacing:-.014em;
  margin:0 70px 12px 0}
.v7 .orgpanel-lede{font-size:15px;line-height:1.6;color:var(--muted);margin:0 0 24px}
.v7 .orgpanel-sheet h3{font-size:12.5px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--accent-deep);margin:0 0 8px}
.v7 .orgpanel-sheet ul{list-style:none;margin:0 0 22px;padding:0}
.v7 .orgpanel-sheet li{font-size:15px;line-height:1.5;color:var(--ink);
  padding:7px 0 7px 15px;border-top:1px solid var(--hair);position:relative}
.v7 .orgpanel-sheet li::before{content:"";position:absolute;left:0;top:15px;
  width:5px;height:5px;border-radius:50%;background:var(--hair)}
.v7 .orgpanel-note{font-size:14.5px;line-height:1.6;color:var(--muted);margin:26px 0 0;
  padding-top:20px;border-top:1px solid var(--hair)}
.v7 body.orgs-open{overflow:hidden}
@media (prefers-reduced-motion:reduce){
  .v7 .orgpanel.is-ready .orgpanel-sheet,.v7 .orgpanel.is-ready .orgpanel-scrim{transition:none}
}

/* bands */
.v7 section.band{padding:88px 0}
.v7 section.band.alt{background:var(--off)}
.v7 .eyebrow{font-size:13.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--accent-deep);margin-bottom:12px;font-family:var(--sans)}
.v7 section h2{font-size:clamp(28px,3.8vw,40px)}
.v7 .center{text-align:center}
/* Balanced on every section deck, not just the one that was caught at it. Each of
   the three was ending on a stub: the philosophy deck at 17% of the measure, the
   capability deck at 28%. Balanced they end at 72% and 58%, and the suite deck,
   which already had it, is unchanged. */
.v7 .center .sub{color:var(--muted);max-width:42em;margin:16px auto 0;text-wrap:balance}
/* Owner, 2026-08-29: this deck set three lines with "requirements." alone on the
   third, a last line 14% of the measure. 42em is the width the three decks share
   and the other two sit well in it, so the measure is widened for this one only:
   at 46em the sentence sets two lines and the second runs 85% of the way across.
   Balanced as well as widened, because below 900 the wrap is narrower than 46em
   and the orphan would otherwise come back at 768. */
/* Two decks run to two sentences and set a line more than they need at the 42em
   the three share. Both come to three lines at 46em, so they take one wider
   measure between them rather than a width each. */
.v7 #suite .sub,.v7 #philosophy .sub{max-width:46em}

/* releases */
.v7 .family{display:grid;grid-template-columns:1.2fr 1fr 1fr;gap:18px;margin-top:42px}
.v7 .prod{border:1px solid var(--hair);border-radius:16px;padding:26px;background:#fff;
  display:flex;flex-direction:column}
.v7 .prod.live{border:2px solid var(--accent)}
.v7 .prod .tag{margin-bottom:12px}
.v7 .prod h3{font-size:21px;margin-bottom:8px}
.v7 .prod p{color:var(--muted);font-size:15.5px;flex:1}
.v7 .prod a.go{font-weight:600}
.v7 .family-note{text-align:center;color:var(--muted);font-size:15.5px;margin-top:26px}

/* capability grid: twelve, in the prototype's three columns */
.v7 .caps{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
/* The section's own deck says "four groups of three". The groups were in the
   content spine all along and every other treatment renders them; this one had
   flattened to a single twelve. A quiet rule-and-label subordinates to the
   section head rather than competing with its eyebrow. */
.v7 .cap-group{margin-top:38px}
.v7 .cap-group:first-of-type{margin-top:44px}
/* Owner ruling, 2026-08-29: the group headings were a 12.5px faint uppercase label
   with a hairline running off them, which read as almost forgotten. They now take
   the form the owner named, the heading over a row of cards in the Apple store: the
   name at the page's ink in sentence case, the ground it covers under it in grey,
   and no rule, because a heading this size does not need one to hold the row.

   Sized against what it labels rather than by eye. The card titles are 19.5px, and
   a 20px heading was tried and rejected: it cannot outrank the cards it introduces
   when it matches them. At 30px it outranks them clearly and still sits a step under
   the section's own h2, which is 40px here, so the section still reads as one thing
   with four parts rather than as four sections. It costs 186px across the four
   groups, which is under a tenth of the 2084px they already stood at. */
.v7 .cap-label{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 14px;margin:0 0 20px;
  font-size:clamp(22px,2.4vw,30px);font-weight:700;letter-spacing:-.02em;
  text-transform:none;color:var(--ink);line-height:1.15}
/* Beside the name on one baseline, not under it. The size difference does the
   separating, so the two need no punctuation between them, and at 16px against
   30px the longest of the four, Requirements and oversight, still holds one line
   at 1280. Matching the name's size instead is the truer Apple line but wraps
   that group onto a second. It wraps rather than overflows when the width runs
   out, which is the narrow widths' business. */
.v7 .cap-scope{font-size:clamp(14px,1.25vw,16px);font-weight:400;letter-spacing:0;
  color:var(--muted)}
/* A column, so the pairing anchors to the foot of the card. The grid stretches
   every card in a row to the tallest, and this gives that stretch something to
   push against instead of leaving up to 136px of nothing under the short ones.
   Drawn in the store-card idiom the owner asked for: a large radius, no rule, the
   card lifted by a soft shadow rather than outlined, the glyph sitting on its own
   tinted tile, and the pairing reading as the card's action. */
/* The card is a body over a photograph, on one ground. --ground is set per card
   from the photograph's own top edge, so the image dissolves into the card and the
   tile reads as a single surface with the object sitting in it rather than a
   picture stuck to a panel.

   The padding lives on .cap-body, not .cap, so the image can run edge to edge, and
   the text children sit inside that wrapper: .cap-see anchors with margin-top:auto
   and needs its own flex column to push against. Leaving <b>, <span> and <a> as
   siblings of the image would have put the image in the same flow as the text. */
.v7 .cap{background:var(--ground,#fff);border:0;border-radius:18px;padding:0;overflow:hidden;
  display:flex;flex-direction:column;box-shadow:0 1px 2px rgba(16,32,64,.06)}
.v7 .cap-body{flex:1;display:flex;flex-direction:column;padding:28px 26px 22px}
/* The photograph closes the card. Its own ground and the card's are the same
   colour, so the only thing that would give the join away is the shadow and
   vignette a lens leaves at the frame edge: a short wash of the ground colour
   over the top of the image takes that out, and the tile reads as one surface
   with the object sitting in it. */
.v7 .cap-shot{position:relative;background:var(--ground,#fff)}
.v7 .cap-shot::before{content:"";position:absolute;left:0;right:0;top:0;height:16%;z-index:1;
  background:linear-gradient(var(--ground,#fff),rgba(255,255,255,0))}
.v7 .cap-img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  object-position:center}
/* Text tone follows the ground rather than the treatment. */
.v7 .cap[data-ground="dark"] b{color:#fff}
.v7 .cap[data-ground="dark"] span{color:#C4CBD2}
.v7 .cap[data-ground="dark"] .cap-see{color:#8FC2FF}
.v7 .cap-see{margin-top:auto;padding-top:20px;font-size:14px;font-weight:600;
  color:var(--accent-deep);align-self:flex-start;transition:transform .18s ease}
.v7 .cap-see::after{content:" →";display:inline}
.v7 .cap:hover .cap-see{transform:translateX(3px)}
@media (prefers-reduced-motion:reduce){.v7 .cap:hover .cap-see{transform:none}}
/* 18.5px is the largest step up from 17px that costs no card a line: at 19px the
   sixth title goes to three lines and starts its body 24px below its neighbours'.
   Card 1 is the one title in twelve that runs to three lines, as it always has. */
.v7 .cap b{display:block;font-size:18.5px;margin-bottom:9px;line-height:1.24;text-wrap:balance;
  letter-spacing:-.014em;min-height:2.48em}
.v7 .cap span{font-size:15.5px;line-height:1.55;color:var(--muted);text-wrap:pretty}

/* foundations */
.v7 .ppt{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:42px}
/* Inherited from the implementation band that used to sit below this one. */
.v7 .pillar{border-top:3px solid var(--accent);background:#fff;border-radius:0 0 14px 14px;
  padding:24px;box-shadow:0 1px 3px rgba(0,0,0,.05)}
.v7 .pillar h3{font-size:19px;margin-bottom:8px}
.v7 .pillar p{color:var(--muted);font-size:15.5px;margin:0}

/* navy synthesis */
/* The band was a flat navy field. Two soft lifts, one from the top left and one
   from the bottom right, give it depth without touching a single text colour.
   Both reach exactly --synth-lift and no further, which is the value
   _tools/motion-test.mjs measures every ratio against, so the case the gate
   checks is the real worst case rather than the flat background. */
.v7 .synth{--synth-lift:#133055;background-color:var(--navy);color:#fff;text-align:center;
  padding:96px 0;
  background-image:radial-gradient(115% 95% at 12% 0%, var(--synth-lift) 0%, rgba(14,42,79,0) 62%),
    radial-gradient(78% 68% at 92% 106%, var(--synth-lift) 0%, rgba(14,42,79,0) 58%)}
.v7 .synth .eyebrow{color:#9FC1E8}
.v7 .synth h2{color:#fff;max-width:24em;margin:0 auto}
.v7 .synth .traits{display:flex;gap:14px 40px;justify-content:center;flex-wrap:wrap;margin:34px 0 22px}
/* Balanced, because the third trait is the one long enough to wrap on a phone and
   it left "which" alone on a line of its own. */
.v7 .synth .trait{font-size:18px;font-weight:600;color:#DCE6F3;text-wrap:balance}
/* Owner, 2026-08-30: at 38em this ended on the single word "happen.", a last line
   9% of the measure. 46em is the width the suite and philosophy decks already share,
   so it sets two lines here without adding a fifth measure to the page, and balance
   keeps the pair even. */
.v7 .synth p.note{color:#B9C4D4;max-width:46em;margin:0 auto;text-wrap:balance}

/* close */
.v7 .final{text-align:center;padding:96px 0;background:var(--white)}
.v7 .final h2{font-size:clamp(30px,4.4vw,46px)}
/* Owner, 2026-08-30: the shorter closing paragraph ended on a stub, 26% of the
   measure, which is the third time this page has done it. Balanced and widened four
   ems it sets two full lines instead of three and a tail. */
.v7 .final p{color:var(--muted);margin:16px auto 30px;max-width:40em;text-wrap:balance}

@media (max-width:900px){
  .v7 .family{grid-template-columns:1fr}
}
@media (max-width:860px){
  .v7 .ppt{grid-template-columns:1fr}
  .v7 .p-fig{max-width:520px}
}
/* A group of three in two columns leaves a hole in every group, so the columns
   go straight from three to one. */
@media (max-width:820px){
  .v7 .caps{gap:14px}
  .v7 .cap-body{padding:20px 18px 18px}
  /* The desktop title is 19px, which is right in a 276px card and costs a line in
     a 190px one: measured at 768, three titles run to 3 and 4 lines at 19px and
     the whole set holds at 2 and 3 from 17.5px down. */
  .v7 .cap b{font-size:17.5px}
}
@media (max-width:660px){.v7 .caps{grid-template-columns:1fr}}
@media (max-width:1040px){
  .v7 .flex-grid{grid-template-columns:repeat(2,1fr)}
  .v7 .flex-item:nth-child(odd)::before{display:none}
}
@media (max-width:560px){
  .v7 .flex-grid{grid-template-columns:1fr;gap:26px 0;border-top:0;padding-top:0}
  .v7 .flex-item + .flex-item::before{display:none}
  .v7 .flex-item{border-top:1px solid var(--hair);padding:18px 0 0}
  .v7 .flex-item:nth-child(4){border-top:2px solid var(--accent)}
}
@media (max-width:560px){
  .v7 .hero{padding:60px 0 36px}
}
@media (prefers-reduced-motion:reduce){
  .v7 .caret{display:none}
}

/* ---- hero video ----------------------------------------------------------
   Decorative background footage behind the hero. Muted, looping, inline, and
   aria-hidden, because it carries no information. The poster paints first, so
   the hero is never empty while the file loads, and it is all the reader gets
   when they have asked for reduced motion. */
.v7 .hero.has-video{position:relative;isolation:isolate;background:var(--navy);
  padding:132px 0 96px;overflow:hidden;
  /* The hero was a fixed 682px at every viewport height, so on a 1920x1080 desktop
     398px of the next band sat above the fold, its eyebrow and heading included.
     The page's one cinematic asset was boxed. It now takes most of the fold and
     leaves a sliver of what follows, which is the affordance to keep, and the
     cap stops it running away on a very tall window. */
  min-height:min(88vh,900px);display:flex;flex-direction:column;justify-content:center}
.v7 .hero-media{position:absolute;inset:0;z-index:-1}
.v7 .hero-video{width:100%;height:100%;object-fit:cover;display:block;
  /* The footage is generated, and at 2x its screens carry warped lettering: a
     flowchart caption and two course titles resolve into near-words. On a site
     whose argument is that every claim can be checked, text that almost reads is
     a liability. Three pixels of blur takes it back to shape and colour, which is
     all a background is being asked for, and the headline gains contrast for it.
     The slight scale keeps the blur from feathering the edges of the frame. */
  filter:blur(max(3px,0.3vw));transform:scale(1.03)}
.v7 .hero-scrim{position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(9,26,48,.86) 0%,rgba(9,26,48,.72) 45%,rgba(9,26,48,.88) 100%)}
/* Owner ruling, 2026-08-30: fading the scrim up from nothing read as a page still
   loading rather than as a page arriving. It now opens with the overlay already in
   place and heavier than it ends, and settles out to the shipped value.

   The gradient is untouched and the resting state is byte-identical to what it was;
   the settle rides background-color, which paints behind the gradient, so the extra
   darkness is additive at the start and gone at the end. Starting heavier also means
   the headline opens with MORE contrast than it rests at, so there is no window where
   the type is under the floor, which is what the fade up from nothing cost. */
.js .v7 .hero.has-video .hero-scrim{animation:scrim-settle 1.7s cubic-bezier(.33,1,.68,1) both}
@keyframes scrim-settle{from{background-color:rgba(9,26,48,.58)}to{background-color:rgba(9,26,48,0)}}

.v7 .hero.has-video h1{color:#fff;max-width:20em}
.v7 .hero.has-video .rotor-line{color:#8FC2FF}
.v7 .hero.has-video .caret{background:#8FC2FF}
.v7 .hero.has-video .sub{color:#D7E2F0}
.v7 .hero.has-video .friction{color:#A9BACD}
.v7 .hero.has-video .btn.primary{background:#fff;color:var(--navy)}
.v7 .hero.has-video .btn.primary:hover{background:#E8F1FC}
.v7 .hero.has-video .btn.ghost{color:#fff;border-color:rgba(255,255,255,.65)}
.v7 .hero.has-video .btn.ghost:hover{background:rgba(255,255,255,.12)}

@media (prefers-reduced-motion:reduce){
  .js .v7 .hero.has-video .hero-scrim{animation:none}
  .v7 .hero-video{display:none}
  /* The poster is the same frame, so it carries the same warped lettering and is
     softened the same way. The scrim blurs with it, which a gradient does not
     mind, and the three pixels it feathers at the edge fall on the band's own
     navy. */
  .v7 .hero-media{background:url("/assets/video/hero-poster.jpg") center/cover no-repeat;
    filter:blur(max(3px,0.3vw))}
}
@media (max-width:560px){
  .v7 .hero.has-video{padding:76px 0 56px}
}
.v7 .flexband{padding-top:84px}
.v7 .flex-head{margin-bottom:34px}
.v7 .flex-head h2{font-size:clamp(24px,3vw,34px)}
.v7 .eyebrow.abcd{text-transform:none;font-size:15px;letter-spacing:.005em;
  color:var(--muted);font-weight:600}
.v7 .eyebrow.abcd b{font-weight:800;color:var(--ink)}
.v7 .flex-guide{text-align:center;color:var(--muted);max-width:54em;margin:34px auto 0;
  font-size:16.5px;text-wrap:balance}

/* the suite coverage band: what a requirements list scans for */
/* Split explicitly into 7, 6 and 6 rather than left to the browser, which balanced
   nineteen items to 7, 7 and 5 and left the last column visibly short. Nothing is
   added or dropped to make the shape work. */
/* Owner, 2026-08-29: the columns ended eighty-five pixels apart, because the
   subtitles wrap at different lengths and each column was its own list carrying
   its own total. They are now one list laid out as a grid of seven rows filled
   column by column, so the alphabet still reads downwards and each row is a grid
   row that shares one height across all three columns. It costs nothing: the row
   maxima total 546px, which is exactly what the tallest column already stood at,
   so the columns come level without the section growing. Giving every item the
   same height instead would have made all seven rows the tall one, 588px, which
   is 42px more for the same alignment. */
/* Owner, 2026-08-30: the band came to spine.html, then Ballots was held back on the
   owner's word. Credentials is gone, being in the source of record nowhere, and
   Compliance is back, so the band is twenty-one again and the three columns of
   seven end level, which is the only count in this range that does. It went to
   twenty in between, where the best split is 7, 7 and 6 and the last column ended
   63px high; that is worth knowing because any future add or drop that does not
   land on a multiple of three costs the level bottom edge. Band height is 954px at
   1280 and 951px at 1000 either way, since the grid is seven rows tall regardless. */
.v7 .suite-cols{margin-top:42px;text-align:left}
.v7 .suite-list{list-style:none;margin:0;padding:0;display:grid;
  grid-auto-flow:column;grid-template-rows:repeat(7,auto);gap:0 42px}
.v7 .suite-list li{break-inside:avoid;display:grid;grid-template-columns:20px 1fr auto;gap:0 12px;
  padding:11px 0;border-bottom:1px solid var(--hair);align-items:start}
.v7 .suite-list .ico{width:20px;height:20px;color:var(--accent-deep)}
/* A ring with a check for 2027, the same ring open for planned. Both carry the
   platform's own status colours, the ones the footer chips already use, so the
   site holds one visual language for the status rather than two. */
.v7 .st{width:17px;height:17px;flex:none}
.v7 .st.c{color:var(--committed)}
.v7 .st.p{color:var(--planned)}
.v7 .suite-list .st{margin-top:2px}
.v7 .suite-key .st,.v7 .cat-key .st{display:inline-block;vertical-align:-3.5px;margin-right:4px}
.v7 .applist .st{margin-top:2px}
/* The catalog row carries two lines under the name: the site’s own sentence,
   the same one the home band sets for that app, and under it the spine’s "also
   called" term. The term is set in the mono face, which is this design’s voice
   for a reference label rather than a sentence, so the eye reads it as the word
   to match against a requirements list and not as a second description. */
.v7 .applist .cat-term{display:block;font-family:var(--mono);font-size:12.5px;
  line-height:1.45;letter-spacing:.01em;color:var(--faint);margin-top:4px}
.v7 .lane-head .st{margin-top:3px}
.v7 .suite-list b{display:block;font-size:16px;font-weight:700;line-height:1.25}
.v7 .suite-list span{display:block;font-size:14px;color:var(--muted);margin-top:2px}
.v7 .suite-also{max-width:46em;margin:30px auto 0;text-align:center;color:var(--muted);font-size:16px}
.v7 .suite-key{display:flex;align-items:center;justify-content:center;gap:8px 22px;flex-wrap:wrap;
  margin-top:22px;font-size:14px;color:var(--faint)}
.v7 .key-item{white-space:nowrap}
/* Below 900 the three columns become two and the list goes back to flowing, which
   balances itself; a grid of seven rows would leave a hole at the foot of one. */
@media (max-width:900px){
  .v7 .suite-list{display:block;columns:2;column-gap:42px;break-inside:auto}
}
@media (max-width:560px){.v7 .suite-list{columns:1}}
.v7 .p-no{display:block;font-size:12px;font-weight:700;letter-spacing:.12em;
  color:var(--accent-deep);margin-bottom:8px}

/* the philosophy sequence */
.v7 .p-fig{margin-bottom:20px;max-width:340px}
.v7 .p-fig .fig{display:block}

/* ---- the fifty ------------------------------------------------------------
   A ghosted numeral bleeding off the right edge of the synthesis band, which
   opens "Fifty years in these organizations". Decorative and aria-hidden: the
   heading and the first trait both state the fact in words, so the numeral is
   never the only place it appears and a screen reader is spared reading it
   twice. Its contrast cost is measured by _tools/synth-test.mjs rather than
   eyeballed, worst case being white text sitting directly on the numeral. */
.v7 .synth{position:relative;isolation:isolate;overflow:hidden}
.v7 .synth .wrap{position:relative;z-index:1}
.v7 .synth-mark{position:absolute;z-index:0;right:0;top:50%;transform:translateY(-50%);
  /* Owner, 2026-08-30: the mark is 1976 rather than 50, and four digits run about
     twice the width of two. The floor was 230px, sized for two, and at 375 it put the
     leading 1 past the left edge, so the watermark read 976. The vw term never
     overflows; only the floor did, so it comes down to 145px, which keeps all four
     digits inside 320. The ceiling is unchanged. */
  font-size:clamp(145px,32vw,440px);font-weight:800;line-height:.78;letter-spacing:-.05em;
  color:rgba(255,255,255,.105);pointer-events:none;user-select:none}

/* ---- sections transitioning into place -------------------------------------
   Three attempts got this wrong in the same way, so the record of why is worth
   more than the rules underneath it.

   The first was CSS scroll-driven animation on `animation-timeline: view()`. It
   sits behind `@supports`, so a browser without scroll timelines showed a
   completely static page, which is what the owner saw. Do not go back to it.

   The second made it an IntersectionObserver plus a transition, which runs
   everywhere, and then composed it: forty-one elements, each with its own offset,
   curve and delay, varied section by section. It measured as eight distinct
   characters and still read as nothing happening.

   The third added scale and more distance. It measured better and still read as
   basic, and the measurement that explained it is the one that mattered: a scroll
   IS a vertical translation, so a reveal built out of vertical translation is
   competing with the reader's own gesture. A tile rising 14px was 7.8% of its own
   apparent motion at a slow scroll, 2.4% at a normal one, 1.4% at a fast one. The
   rest was the page going by. More pixels cannot win that, because every pixel
   added to the rise is added to the thing it is losing to.

   What was wrong was never the numbers. It was three choices underneath them,
   and all three are different now.

   1. THE UNIT. It was the element: fifty-one of them, each moving on its own.
      It is the section now: eleven, and nothing inside one moves relative to
      anything else. A band arrives as one object. Where a section is taller than
      the screen it cannot be seen to arrive at all, so there the unit is the
      group instead: the capability band is 2,448px against a 900px screen, so
      its head and its four labelled groups are five units rather than one.
   2. THE RELATIONSHIP TO THE SCROLL. It was one-shot: cross a line, run a fixed
      transition, finish, and be finished long before the reader has scrolled to
      you. It is scroll-linked now. A unit's progress is a function of where it
      sits in the viewport, so it is still arriving while you bring it up and it
      goes back if you scroll back. That is what "transitions into place" means,
      and it is why the motion no longer competes with the scroll: it is coupled
      to it rather than racing it.
   3. THE CHANNEL. Transform only was the rule, and it capped how much any of
      this could register. These fade now, on the owner's ruling, and opacity is
      doing more work than distance and scale together.

   None of that needs a scroll timeline. site.js reads each unit's position once a
   frame and writes two numbers; it runs in every browser that has
   requestAnimationFrame, and _tools/motion-test.mjs measures the result by
   scrolling the page rather than by reading these rules.

   The two numbers, both set on the unit and inherited by whatever moves with it:
     --pe   eased progress, 0 at the foot of the screen and 1 at rest
     --po   opacity progress, which reaches 1 well before --pe does, so a section
            is fully readable while it is still settling

   Both default to 1, which is rest. That is the safety property, and it is
   stronger than the one it replaces: with no script at all, nothing is offset,
   nothing is faded and nothing is hidden, because the resting state is what the
   stylesheet says and the script only ever moves things away from it. */

/* What moves, and how. A section marked data-rev="wraps" moves its own .wrap
   children, so the band's background colour stays where it is and only its content
   travels. A unit marked data-rev="self" is the moving thing itself, used where a
   section is too tall to arrive in one piece.

   Only the fade is shared. Every section states its own movement in full below,
   because a section that arrives the way every other section arrives is a section
   that has told the reader nothing about itself. The vocabulary is deliberately
   small: translate, uniform scale, and a degree or two of rotation, all of it two
   dimensional. Nothing here reaches for perspective or rotateX. Those resolve to a
   matrix3d, which the gate's matrix reader would have read as no motion at all,
   which is the same class of mistake as reading translateY for an animation that
   moves sideways. */
.js .v7 [data-rev="wraps"] > .wrap,
.js .v7 [data-rev="self"]{opacity:var(--po,1)}

/* The adoption triad. Three ordered columns under a head that says "three ways", so
   it rises as one block and grows into place: the section is a proposition, and a
   proposition arrives squarely. */
.js .v7 .flexband > .wrap{
  transform:translateY(calc((1 - var(--pe,1)) * 120px))
            scale(calc(1 - (1 - var(--pe,1)) * .06));
}

/* The audience strip. 85px of punctuation and a single horizontal row of names, so
   it slides in along its own axis rather than up: the one band whose shape is a
   line gets the one motion that runs along a line. Its own overflow keeps it off
   the side of the page while it waits. */
.v7 .orgs{overflow:hidden}
.js .v7 .orgs > .wrap{
  transform:translateX(calc((1 - var(--pe,1)) * -150px))
            translateY(calc((1 - var(--pe,1)) * 18px));
}

/* The suite. Nineteen apps in three columns, the page's densest object and its
   claim to breadth. It comes up furthest of anything that rises, and grows, because
   what it has to say is "there is a lot of this". */
.js .v7 section.band#suite > .wrap{
  transform:translateY(calc((1 - var(--pe,1)) * 150px))
            scale(calc(1 - (1 - var(--pe,1)) * .06));
}

/* The capability head, which introduces the four groups below it. It rises alone
   and plainly, so that the groups have something still to arrive against. */
.js .v7 .band.alt > .wrap[data-rev]{
  transform:translateY(calc((1 - var(--pe,1)) * 90px));
}

/* The twelve capabilities, four rows of three. The rows sweep in from alternating
   sides, which is the motion the section's own shape asks for: four bands of cards
   stacked down 2,448px, and a page that just lifts them all the same way says
   nothing about there being four of them. Alternating says it without a word.

   Each row is one object, so its three cards arrive together and hold their
   relationship to each other and to their label. The rotation is two and a half
   degrees, which is enough for the row to read as swinging in rather than sliding,
   and little enough that no card looks broken if the reader stops mid-sweep.

   They come in on a diagonal rather than flat along the horizontal, because a row
   that only slides sideways reads as a panel being pushed, and one that also rises
   reads as a card being dealt. Eighty pixels of it, against 280 across.

   The section clips, because 280px of sideways travel would otherwise put a row
   off the edge of the page and give the whole document a horizontal scrollbar. */
.v7 section.band.alt{overflow:hidden}
.js .v7 .cap-group{transform-origin:50% 50%}
.js .v7 .cap-group:nth-of-type(odd){
  transform:translate(calc((1 - var(--pe,1)) * -280px), calc((1 - var(--pe,1)) * 80px))
            rotate(calc((1 - var(--pe,1)) * 2.5deg))
            scale(calc(1 - (1 - var(--pe,1)) * .1));
}
.js .v7 .cap-group:nth-of-type(even){
  transform:translate(calc((1 - var(--pe,1)) * 280px), calc((1 - var(--pe,1)) * 80px))
            rotate(calc((1 - var(--pe,1)) * -2.5deg))
            scale(calc(1 - (1 - var(--pe,1)) * .1));
}

/* The philosophy pillars. People, then process, then technology, in a fixed order,
   and the section's own word for them is pillars. So they stand up: the block
   pivots from its foot rather than sliding, which is what a pillar being raised
   looks like. A degree and a half, from an origin at the bottom of the block.

   It clips for the same reason the two sideways sections do, and for a reason that
   is easy to miss: rotating a block widens its bounding box by its own height times
   the sine of the angle, so a degree and a half across 734px of pillars is twenty
   pixels added to each side. At 1280 that is room the page has. At 375 it is not,
   and it put the band 22px past the left edge of the screen. */
.v7 section.band[data-rev="wraps"]:not(#suite):not(.alt){overflow:hidden}
.js .v7 section.band[data-rev="wraps"]:not(#suite):not(.alt) > .wrap{
  transform-origin:50% 100%;
  transform:translateY(calc((1 - var(--pe,1)) * 110px))
            rotate(calc((1 - var(--pe,1)) * -1.6deg))
            scale(calc(1 - (1 - var(--pe,1)) * .07));
}

/* The synthesis band. The page's one dark ground and its one claim about itself, so
   it grows into place from further out of scale than anything else and rises least.
   A statement should settle rather than travel. */
.js .v7 .synth > .wrap{
  transform:translateY(calc((1 - var(--pe,1)) * 70px))
            scale(calc(1 - (1 - var(--pe,1)) * .1));
}

/* The close. The hero's gesture again at the other end of the page, so the last
   thing the reader sees arrives the way the first thing did. */
.js .v7 .final > .wrap{
  transform:translateY(calc((1 - var(--pe,1)) * 120px))
            scale(calc(1 - (1 - var(--pe,1)) * .05));
}

/* The synthesis numeral rides its band's progress rather than carrying a reveal of
   its own, which keeps it part of the unit instead of a fifty-second moving thing.
   Its resting transform is the translateY(-50%) that centres it, so the drift has
   to compose with that rather than replace it. */
.js .v7 .synth-mark{transform:translateY(-50%) translateX(calc((1 - var(--pe,1)) * 7%))}

/* The hero. It is above the fold on load, so there is no scroll to link it to and
   nothing to compete with either: it arrives once, as one unit, on the clock.

   A plain keyframe animation rather than a class the script adds, because this is
   the one reveal that needs no script at all and should not wait for one. The flag
   and the driver come from different places: an inline script in the head sets
   `.js` before first paint, and site.js arrives separately and deferred, so a page
   where the driver fails to load still has the flag. Under the class it was written
   with first, that page kept a hero sitting thirty-four pixels low for ever, which
   _tools/motion-test.mjs now catches by blocking the script and loading the page.
   An animation runs on its own and finishes on its own.

   It is also the one thing here that must never be blank, so it is the one reveal
   that does not fade. Every other unit rests by default and is only ever moved away
   from rest by the driver; the hero is the one whose offset lives in the stylesheet,
   and a stylesheet cannot un-hide anything. */
.js .v7 .hero .wrap{animation:hero-in 1.3s cubic-bezier(.22,.75,.3,1) both}
@keyframes hero-in{from{transform:translateY(80px) scale(.93)}to{transform:none}}

/* Asked for calm, nothing moves and nothing is faded. Two belts, and one of them
   turned out to be a single point of failure.

   The first is that the driver declines to run at all under this setting. The second
   is meant to hold every unit at rest whatever the driver writes, and as first
   written it did not: now that every section states its own movement,
   `.js .v7 .cap-group:nth-of-type(odd)` out-specifies `.js .v7 [data-rev="self"]`,
   so `transform:none` lost to the sweep. It only ever looked safe because the driver
   was silent, which is to say the two belts were one. Breaking the driver's own
   guard on purpose is what showed it, and nothing else would have.

   Pinning the two progress values is what actually holds. Every movement on this
   page is a function of them, so at 1 the calc in every rule resolves to identity
   whatever that rule's specificity, and !important is what beats the inline values
   the driver would otherwise be writing onto each unit. */
@media (prefers-reduced-motion:reduce){
  .js .v7 [data-rev],
  .js .v7 [data-rev] > .wrap{--pe:1 !important;--po:1 !important}
  .js .v7 [data-rev="wraps"] > .wrap,
  .js .v7 [data-rev="self"],
  .js .v7 .hero .wrap{opacity:1;transform:none;transition:none;animation:none}
  .js .v7 .synth-mark{transform:translateY(-50%)}
}

/* Micro-transitions. Hover only, so nothing moves unbidden, and all of it is
   dropped when reduced motion is asked for. */
.v7 .btn,.v7 .orgs .kind,.v7 .cap,.v7 .pillar,
.v7 .suite-list li,.v7 .suite-list .ico{transition:background-color .18s ease,border-color .18s ease,
  color .18s ease,box-shadow .22s ease,transform .22s ease}
.v7 .cap:hover{box-shadow:0 10px 30px rgba(16,32,64,.10);transform:translateY(-3px)}
.v7 .pillar:hover{box-shadow:0 8px 24px rgba(16,32,64,.09);transform:translateY(-2px)}
.v7 .suite-list li:hover{background:var(--accent-tint);border-bottom-color:#CFE0F6}
.v7 .suite-list li:hover .ico{color:var(--accent)}
@media (prefers-reduced-motion:reduce){
  .v7 .btn,.v7 .orgs .kind,.v7 .cap,.v7 .pillar,
  .v7 .suite-list li,.v7 .suite-list .ico{transition:none}
  .v7 .cap:hover,.v7 .pillar:hover{transform:none}
}

/* The header is sticky at 61px, so an anchor has to stop short of it. */
html:has(body.v7){scroll-padding-top:78px}

/* The headline set "Freedom" alone on a line at phone width. */
@media (max-width:430px){
  .v7 .hero h1{font-size:clamp(30px,8.4vw,38px)}
  .v7 .cta-row{flex-direction:column;align-items:stretch}
  .v7 .cta-row .btn{width:100%;text-align:center}
}


/* ---- the inner pages, in this treatment -----------------------------------
   The funnel used to change brand at the click: the home page is blue, sans and
   white, and every page it links to was green, serif and cream, down to the logo
   mark in the same nav bar. All six now carry this treatment. The components
   below keep their base styling in site.css, so reverting is one line of front
   matter per page rather than a rewrite. */
.v7 .kicker{font-family:var(--sans);font-size:13px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--accent-deep)}
.v7 .page-head{border-bottom:1px solid var(--hair)}
.v7 .page-head h1{font-size:clamp(34px,5vw,54px);font-weight:700;letter-spacing:-.015em}
.v7 .page-head .lede{color:var(--muted);font-size:19.5px}
.v7 h2,.v7 h3{font-family:var(--sans);font-weight:700}

/* the shared rules, numerals and body tone across all six */
.v7 .venues li,.v7 .kindlist li,.v7 .applist li,.v7 .honlist li,.v7 .qtheme,
.v7 .req-note,.v7 .qs-close,.v7 .kinds-note,.v7 .lanes-note,.v7 .hon-note,
.v7 .lane,.v7 .lane-head{border-color:var(--hair)}
.v7 .venues li::before,.v7 .kindlist li::before,.v7 .honlist li::before,
.v7 .q-no{font-family:var(--sans);color:var(--accent-deep)}
.v7 .venues span,.v7 .kindlist span,.v7 .applist span,.v7 .honlist span,
.v7 .q-why,.v7 .qt-head p,.v7 .cat-sub,.v7 .lane li,.v7 .why p,
.v7 .req-lede,.v7 .req-note,.v7 .qs-close,.v7 .kinds-note,.v7 .lanes-note,
.v7 .hon-note,.v7 .lane-head span,.v7 .cat-key{color:var(--muted)}
.v7 .venues b,.v7 .kindlist b,.v7 .applist b,.v7 .honlist b,.v7 .q-body h3{font-family:var(--sans)}

/* what a strong answer looks like, in this palette */
.v7 .q-strong{background:var(--accent-tint);color:var(--ink)}
.v7 .q-strong b{font-family:var(--sans);color:var(--accent-deep)}
.v7 .qs-take .take-link{font-family:var(--sans);color:var(--accent-deep)}
.v7 .qs-take .take-note{color:var(--faint)}

/* the one form on the site */
.v7 .req-form form{background:#fff;border:1px solid var(--hair);border-radius:14px}
.v7 .req-form .f{font-family:var(--sans);font-size:11px;font-weight:700;color:var(--faint)}
.v7 .req-form input,.v7 .req-form select{font-family:var(--sans);border-color:var(--hair);
  border-radius:9px;color:var(--ink)}
.v7 .req-form input:focus,.v7 .req-form select:focus{border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-tint)}
.v7 .req-form .btn{background:var(--accent);color:#fff;font-family:var(--sans);font-weight:600;
  border-radius:980px}
.v7 .req-form .btn:hover{background:var(--accent-deep)}
.v7 .req-form .f-note{color:var(--faint)}
.v7 .lane{border-radius:14px}

/* ---- the app overview pages --------------------------------------------------
   /apps/<name>/: one app, end to end, in the pattern a product page takes. A navy
   opening states the claim, in the ground the home hero rests on once its footage is
   gone. A deck on the off ground says what the app covers and carries the status,
   because the hero's navy is not a ground the status colours can sit on. The app's
   parts follow as bands of text beside an illustration, alternating sides down the
   page and divided by the hairline the rest of this treatment already uses. Three
   pillars say what it is built for, the journey takes the synthesis band's navy as
   the page's second dark moment, which is the home page's own rhythm, and the close
   is the home page's close. Applications is the first; the others take these same
   classes. */
.v7 .app-hero{background-color:var(--navy);color:#fff;padding:96px 0 84px;
  background-image:radial-gradient(115% 95% at 12% 0%,#133055 0%,rgba(14,42,79,0) 62%),
    radial-gradient(78% 68% at 92% 106%,#133055 0%,rgba(14,42,79,0) 58%)}
.v7 .app-hero .eyebrow{color:#9FC1E8}
.v7 .app-hero h1{color:#fff;font-size:clamp(34px,5vw,54px);max-width:17em;margin:0 auto}
/* Owner, 1 Sep: the first paragraph set four lines and ended on "and consistently.",
   a last line 18% of the measure. Measured before it was changed: narrowing the box
   to 36 or 34em moves that stub to the second paragraph instead, which goes to three
   short lines ending on 17%, at every width from 768 to 1280. The measure is not the
   lever. Balanced, the first paragraph sets four lines within 13px of each other at
   68 characters a line, and the second holds its two, so the box stays at 40em. */
.v7 .app-hero .sub{color:#D7E2F0;font-size:19px;max-width:40em;margin:22px auto 0;text-wrap:balance}
.v7 .app-hero .sub + .sub{margin-top:14px}
.v7 .app-hero .cta-row{margin-top:32px}
.v7 .app-hero .btn.primary{background:#fff;color:var(--navy)}
.v7 .app-hero .btn.primary:hover{background:#E8F1FC}
/* The second call, on the owner's word of 1 Sep: the home hero's own pair, a white
   primary and a white-ruled ghost, the ghost taking the reader down to the first band
   rather than to the form. */
.v7 .app-hero .btn.ghost{color:#fff;border-color:rgba(255,255,255,.65)}
.v7 .app-hero .btn.ghost:hover{background:rgba(255,255,255,.12)}

.v7 .app-intro{padding:80px 0 72px;background:var(--off)}
/* The catalog's own status pairing, the ring and its words, so the page says when
   the app arrives in the vocabulary the rest of the site uses for it. */
.v7 .app-status{display:inline-flex;align-items:center;gap:7px;font-size:14px;font-weight:600;
  color:var(--muted);margin:0 0 16px}
.v7 .app-status .st{width:17px;height:17px}
.v7 .app-intro h2{font-size:clamp(26px,3.4vw,36px);max-width:22em;margin:0 auto}
.v7 .app-intro .sub{color:var(--muted);max-width:44em;margin:18px auto 0;text-wrap:pretty}
.v7 .app-intro .sub + .sub{margin-top:12px}

.v7 .app-feat{padding:84px 0;background:var(--white)}
.v7 .app-feat + .app-feat{padding-top:0}
.v7 .app-feat + .app-feat > .wrap{border-top:1px solid var(--hair);padding-top:84px}
.v7 .app-feat-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:56px;
  align-items:center}
.v7 .app-feat.flip .app-feat-media{order:-1}
.v7 .app-feat-text h2{font-size:clamp(26px,3.2vw,34px);margin-bottom:16px;text-wrap:pretty}
.v7 .app-feat-text p{color:var(--muted);font-size:16.5px;margin:0 0 14px;text-wrap:pretty}
.v7 .app-list{list-style:none;margin:8px 0 0;padding:0}
/* A check, drawn in the page's accent and nothing more: the status marks are a ring
   in the platform's own green and mean "arriving", and a list of what an app does
   must not borrow that shape. */
.v7 .app-list li{position:relative;padding:5px 0 5px 27px;font-size:15.5px;line-height:1.5;
  color:var(--ink)}
.v7 .app-list li::before{content:"";position:absolute;left:1px;top:9px;width:15px;height:15px;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230059B3' stroke-width='2.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center/contain no-repeat}
.v7 .app-note{margin-top:18px;padding-top:16px;border-top:1px solid var(--hair)}
.v7 .app-feat-text .app-note{margin-bottom:0}
.v7 .app-feat-media{margin:0}
.v7 .app-feat-media img{width:100%;height:auto}

.v7 .app-values .center h2{max-width:18em;margin:0 auto}

.v7 .app-journey h2{margin:0 auto 18px}
.v7 .app-journey p.note + p.note{margin-top:12px}
.v7 .app-journey .cta-row{margin-top:32px}
.v7 .app-journey .btn.primary{background:#fff;color:var(--navy)}
.v7 .app-journey .btn.primary:hover{background:#E8F1FC}

/* The app's own glyph over the opening, the mark the suite band gives this app, so
   the page and the band that names it share one sign. It sits on a tile of the
   navy's own lift, which is the only other surface the opening has. */
.v7 .app-mark{display:inline-grid;place-items:center;width:56px;height:56px;border-radius:16px;
  background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.14);color:#8FC2FF;
  margin-bottom:22px}
.v7 .app-mark .ico{width:28px;height:28px}

/* Owner, 1 Sep: the pillars take an illustration each rather than a glyph on a
   tile, centred, after the reference's grid, and the title and body centre under
   it. 100px tall on a 120 by 100 drawing, close to the reference's own size. */
.v7 .app-values .pillar{text-align:center}
.v7 .app-values .pillar .p-ico{display:block;width:120px;height:100px;margin:2px auto 14px}
.v7 .app-values .pillar .p-ico .ico.rich{display:block;width:120px;height:100px}

/* The illustration arrives from its own side of the band while the text rises with
   the wrap, so a band whose picture sits on the right is seen to be that band. The
   driver writes the two progress values onto the wrap and they inherit from there,
   which is what lets one unit move in two ways at once. Clipped, because 48px of
   sideways travel at the wrap's edge would otherwise widen the document. */
.v7 .app-feat{overflow:hidden}
.js .v7 .app-feat .app-feat-media{transform:translateX(calc((1 - var(--pe,1)) * 48px))}
.js .v7 .app-feat.flip .app-feat-media{transform:translateX(calc((1 - var(--pe,1)) * -48px))}

/* Owner, 1 Sep: the pictures sit beside the text on desktop, and as large as the
   geometry allows. The boards are screens drawn at 1040px, and in two equal columns
   they showed at 47% with their labels at five pixels. The text column holds at
   420px; the picture runs from a 24px gap beside it to the viewport's own edge on
   its side. Measured at 1280, 1440 and 1920 the picture shows at 68, 76 and 99% of
   its drawn size. Stacking the picture under the text would give 98% everywhere and
   was ruled out, pictures beside. Below 1100 the band keeps its two equal columns,
   and below 860 it stacks as it always did.

   The text is plain, on no plate. Four treatments were built and compared on 1 Sep:
   a white plate with a shadow on the picture side only, which read as a smudge
   because a plate whose other three edges are white on white has no edge for the
   shadow to belong to; a hairline card and a floating panel, both of which made the
   text look like one more of the white cards the pictures are drawn from; and this,
   which keeps the site's own rule that prose is never boxed and leaves the depth to
   the picture's own shadows. */
@media (min-width:1100px){
  .v7 .app-feat-grid{grid-template-columns:minmax(0,420px) minmax(0,1fr);gap:0}
  .v7 .app-feat.flip .app-feat-grid{grid-template-columns:minmax(0,1fr) minmax(0,420px)}
  .v7 .app-feat-media{margin-right:calc(-1 * ((100vw - min(100vw,1080px)) / 2 + 28px));padding-left:24px}
  .v7 .app-feat.flip .app-feat-media{margin-right:0;padding-left:0;padding-right:24px;
    margin-left:calc(-1 * ((100vw - min(100vw,1080px)) / 2 + 28px))}
  .v7 .app-feat-media img{width:100%;max-width:1040px}
  .v7 .app-feat.flip .app-feat-media img{margin-left:auto}
}

/* The bands rise plainly, as the capability head does. The hero arrives on the
   clock through the rule every .hero already carries, and the journey and the close
   take the synthesis band's settle and the close's own gesture, which their classes
   already state. */
.js .v7 .app-intro > .wrap,
.js .v7 .app-feat > .wrap,
.js .v7 .app-values > .wrap{transform:translateY(calc((1 - var(--pe,1)) * 80px))}

@media (max-width:860px){
  .v7 .app-feat-grid{grid-template-columns:1fr;gap:36px}
  .v7 .app-feat.flip .app-feat-media{order:0}
  .v7 .app-feat-media{max-width:560px;margin:0 auto}
  .v7 .app-feat{padding:64px 0}
  .v7 .app-feat + .app-feat > .wrap{padding-top:64px}
  .v7 .app-intro{padding:64px 0 56px}
}
@media (max-width:560px){
  .v7 .app-hero{padding:64px 0 56px}
}
@media (max-width:430px){
  .v7 .app-hero h1{font-size:clamp(28px,8vw,34px)}
}
