/* ====== / (home) ======
   From the Figma frames 251:1998 (desktop) and 278:3020 (mobile).
   Mobile-first: base is the 402 frame, 768 relaxes a little, 1024 is the
   1440 frame. Section 4 (diagram) has no mobile frame yet — it stacks. */

/* ── 1 · Hero ─────────────────────────────────────────────────────── */
.hero {
    position: relative;
    display: flex;
    align-items: flex-end;              /* mobile frame: text sits at the bottom */
    min-height: 583px;
    padding-block: var(--space-10) var(--space-8);
    background: var(--c-primary-dark) url('../../images/home/hero-poster.webp') center / cover no-repeat;
    overflow: hidden;
}
.hero__video { position: absolute; inset: 0; }   /* the canvas mounts here, every width */
/* ── The split hero — what / serves ───────────────────────────────────────
   Each lane is an element in the copy's own flow, directly under the line it
   names, so the hero is a normal stack rather than copy floating over a
   canvas. Nothing here applies to the VoiceHero rollback.                 */
.hero.hero--split { min-height: 0; align-items: flex-start; padding-block: var(--space-15) var(--space-10);
    background: linear-gradient(105deg, #13001A 0%, #1D0026 46%, #2C003E 100%); }
/* The ground atmosphere, which this layout would otherwise lose. VoiceHero
   draws a vignette in-canvas; split turned its canvas vignette off
   because in a 54-180px strip it drew a lit rectangle around each lane, so the
   section carries it instead. Only the radial half is taken: the shipped hero
   also lays a heavy scrim down the left edge, and that exists to keep copy
   legible ON TOP of the wave, which is not what happens here (2026-10-01). */
.hero--split::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(ellipse 90% 70% at 50% 55%, rgba(8, 0, 12, 0) 45%, rgba(8, 0, 12, .45) 100%);
}
/* above the vignette, so it darkens the ground and never the copy or the lanes */
.hero--split .hero__inner { gap: var(--space-3); padding-bottom: 0; position: relative; z-index: 1; }
.hero--split .hero__line2 { color: var(--c-accent-cyan); }
.hero__lane { width: 100%; }
.hero__lane--words { height: 54px; margin-bottom: var(--space-4); }
.hero__lane--voice { height: 128px; }
@media (min-width: 1024px) {
    /* .hero.hero--split, not .hero--split: .hero sets 775px at this width and
       the two would otherwise tie on specificity and be decided by source
       order, leaving the last lane hanging past the fold. The stack sizes
       itself from its own content. */
    .hero.hero--split { min-height: 0; padding-block: var(--space-16) var(--space-10); }
    .hero--split::after { background: radial-gradient(ellipse 78% 68% at 56% 58%, rgba(8, 0, 12, 0) 38%, rgba(8, 0, 12, .55) 100%); }
    .hero--split .hero__inner { gap: var(--space-4); }
    .hero__lane--words { height: 68px; }
    .hero__lane--voice { height: 180px; }
}
/* Home had no lit phrase outside its own components until 2026-10-08. Same
   roles as every other page: cyan the capability, pink the payoff.        */
.home-cyan { color: var(--c-accent-highlight); }
.home-pink { color: var(--c-accent-pink); }

/* A lit shelf under the hero, not a continuation of it. The first attempt used
   the flat colour the hero GRADIENT ends on and the seam was visible across the
   whole width: the hero also lays a vignette over that gradient, which darkens
   the ground to roughly half, and the gradient runs at 105deg so its bottom edge
   is 35 points lighter on the right than the left. A flat colour cannot meet a
   diagonal gradient under a vignette, so this stops trying and becomes its own
   surface — the site's own card material, scaled up to a band: a hairline
   catching light along the top, the sheen fading out of it, and the ground
   settling to primary-800 before the teal stats band cuts in.             */
.intro {
    background: var(--gloss-sheen), linear-gradient(180deg, #2C003E 0%, var(--primary-800) 100%);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14);
}
.intro__inner { display: flex; flex-direction: column; gap: var(--space-4); padding-block: var(--space-15); }
.intro__inner p { max-width: var(--measure); font-size: var(--t-h4); line-height: var(--lh-heading); color: var(--c-text-muted); }

/* The band ranks its three sentences by brightness, not by size. A size step
   here would put a near-H3 line 800px down the page and start a second hero
   competing with the rotating H1; full brightness against two muted paragraphs
   buys the same hierarchy for no vertical space. The extra margin is on top of
   the flex gap, so the statement of fact reads as its own beat before the two
   sentences that qualify it.                                               */
/* Qualified, not bare: `.intro__inner p` above is (0,1,1) and would win the
   colour against a lone class.                                          */
.intro__inner p.intro__lead { margin-bottom: var(--space-2); color: var(--c-text); }

.hero__inner { position: relative; display: flex; flex-direction: column; gap: var(--space-2); }
.hero__line1 { margin-top: var(--space-2); font-size: var(--t-h3); line-height: var(--lh-heading); font-weight: var(--fw-medium); }
.hero__line2 { font-size: var(--t-h2); line-height: var(--lh-heading); color: var(--c-accent-pink); }

/* ── Carousel (mobile only; desktop lays the cards out in a row) ──── */
.carousel__track {
    display: flex;
    gap: var(--space-5);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    /* bleed to the viewport edge so the next card peeks in */
    margin-inline: calc(var(--page-gutter) * -1);
    padding-inline: var(--page-gutter);
    scroll-padding-inline: var(--page-gutter);
}
.carousel__track::-webkit-scrollbar { display: none; }
.carousel__track > li { flex: 0 0 auto; scroll-snap-align: start; }
/* Dots replaced the prev/next arrows 2026-09-17 (designer's mobile frame).
   The lit dot carries the gloss glow, like the orbit's signal dot.        */
.carousel__dots { display: flex; justify-content: center; gap: var(--space-3); margin-top: var(--space-6); }
.carousel__dot {
    position: relative;
    width: 8px; height: 8px; border-radius: 50%;
    background: currentColor; opacity: .25;   /* currentColor: dark on the teal stats band, cream on segments */
    transition: opacity .25s ease, box-shadow .25s ease, transform .25s ease;
}
.carousel__dot::before { content: ''; position: absolute; inset: -10px; }   /* 28px tap target on an 8px dot */
.carousel__dot[aria-current="true"] { opacity: 1; box-shadow: 0 0 10px currentColor; transform: scale(1.25); }
.segments .carousel__dot[aria-current="true"] { color: var(--c-accent-cyan); }

/* ── 2 · Stats ────────────────────────────────────────────────────── */
.stats { background-color: var(--c-accent-band); color: var(--c-primary-dark); }
.stats__inner { padding-block: var(--space-8); }
.stats__title { max-width: 468px; color: var(--c-surface-1); margin-bottom: var(--space-6); }

/* The closing line, below BOTH the title and the tiles. It sits outside
   .carousel on purpose — as a sibling it cannot scroll with the track, and
   below 1024 it simply follows the dots.
   Emphasis inside it is weight, never a hue: this is the page's one light
   band and its text colour is already the contrast ceiling against it
   (14.7:1), so every accent in the ramp is LIGHTER and would read as
   de-emphasis. Measured in docs/V2-DESIGN-DEVIATIONS.md.               */
.stats__note {
    max-width: var(--measure);
    margin-top: var(--space-8);
    font-size: var(--t-p1);
    line-height: var(--lh-body);
    color: var(--c-surface-1);
}
.home-strong { font-weight: var(--fw-semibold); }
/* Three words, so it can never overflow — and it must not break, because
   at 1150 the line fell as “The voice / can.” and the closer lost its
   snap. Scoped: nowrap is right for this clause, not for the class.   */
.stats__note .home-strong { white-space: nowrap; }
.stat {
    position: relative;
    /* 319 from the frame; below that, whatever leaves a 28px peek of the next card */
    width: min(319px, calc(100vw - var(--page-gutter) - var(--space-5) - 28px)); height: 269px;
    padding: var(--space-8) var(--space-6);
    border-radius: var(--radius-card);
    background: var(--gloss-sheen), linear-gradient(180deg, var(--primary-500) 0%, var(--primary-800) 100%);   /* Figma: Background/gradient 1 */
    box-shadow: var(--gloss-edge), 0 16px 40px rgba(19, 0, 26, .45);
    color: var(--c-text);
    text-align: center;
    transition: transform .25s ease, box-shadow .25s ease;
}
.stat:hover { transform: translateY(-3px); box-shadow: var(--gloss-edge), 0 24px 48px rgba(19, 0, 26, .5), var(--gloss-glow); }
.stat__said  { font-size: var(--t-h3); line-height: var(--lh-heading); font-weight: var(--fw-medium); }
.stat__meant { margin-top: var(--space-6); font-size: var(--t-h4); line-height: var(--lh-heading); color: var(--c-accent-pink); }
/* The play button — the designer's component sheet (Figma 521:7651,
   2026-09-18): a 28px gradient disc inside a 1.5px dark ring, a soft cyan
   halo behind, states for hover / pressed / disabled / loading / playing
   (pause glyph + expanding rings). Timing is ours: Figma has none.        */
.stat__play {
    position: absolute; left: 50%; bottom: var(--space-8); transform: translateX(-50%);
    width: 38px; height: 38px; color: var(--c-accent-cyan);   /* sheet says 32; 38 per the 2026-09-18 review — the disc and glyphs scale with it */
}
.stat__play::before {                                   /* the halo */
    content: ''; position: absolute; inset: -8px; border-radius: 50%;
    background: radial-gradient(circle, rgba(171, 235, 240, .10) 0%, rgba(171, 235, 240, 0) 70%);
    transition: opacity .3s ease; opacity: 1;
}
.stat__play::after {                                    /* the 1.5px dark ring */
    content: ''; position: absolute; inset: -1px; border-radius: 50%;
    border: 1.5px solid #0D0012;
}
.stat__play-disc {
    position: absolute; inset: 2px; z-index: 1; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, #320041 0%, #1A0028 45%, #0D2A2E 75%, #ABEBF0 100%);
    box-shadow: 0 1px 4px rgba(50, 0, 65, .6);
    transition: transform .18s ease, box-shadow .25s ease;
}
.stat__play-icon { position: absolute; width: 13px; height: 13px; color: #FFF9F5; transition: opacity .15s ease; }
.stat__play-icon--play  { margin-left: 1px; }
.stat__play-icon--pause, .stat__play-icon--load { opacity: 0; }

.stat__play:hover .stat__play-disc, .stat__play:focus-visible .stat__play-disc { transform: scale(1.1); box-shadow: 0 2px 6px rgba(50, 0, 65, .6), 0 0 9px rgba(171, 235, 240, .3); }
.stat__play:active .stat__play-disc { transform: scale(.92); }
.stat__play:disabled { cursor: default; }
.stat__play:disabled::before { opacity: 0; }
.stat__play:disabled::after { opacity: .25; }
.stat__play:disabled .stat__play-disc { opacity: .4; background: linear-gradient(135deg, rgba(50, 0, 65, .5) 0%, rgba(13, 42, 46, .5) 100%); box-shadow: none; transform: none; }

/* loading: the play glyph gives way to a turning arc */
.stat__play.is-loading .stat__play-icon--play { opacity: 0; }
.stat__play.is-loading .stat__play-icon--load { opacity: 1; animation: stat-spin 1s linear infinite; }
@keyframes stat-spin { to { transform: rotate(360deg); } }

/* playing: pause glyph, brighter halo, glow, and two rings rippling outward */
.stat__play.is-playing .stat__play-icon--play  { opacity: 0; }
.stat__play.is-playing .stat__play-icon--pause { opacity: 1; }
.stat__play.is-playing::before { background: radial-gradient(circle, rgba(171, 235, 240, .28) 0%, rgba(171, 235, 240, 0) 70%); }
.stat__play.is-playing .stat__play-disc { box-shadow: 0 0 7px rgba(171, 235, 240, .5); }
.stat__play.is-playing:hover .stat__play-disc { box-shadow: 0 0 11px rgba(171, 235, 240, .7), inset 0 0 4px rgba(171, 235, 240, .3); }
.stat__ring { position: absolute; inset: 0; border-radius: 50%; border: 1.5px solid rgba(171, 235, 240, .55); opacity: 0; pointer-events: none; }
.stat__play.is-playing .stat__ring { animation: stat-ripple 1.8s ease-out infinite; }
.stat__play.is-playing .stat__ring--2 { animation-delay: .9s; }
@keyframes stat-ripple {
    0%   { transform: scale(1);    opacity: .55; }
    100% { transform: scale(1.85); opacity: 0; }
}

/* ── 3 · AVI-OS ───────────────────────────────────────────────────── */
.avios { background: linear-gradient(180deg, var(--primary-900) 0%, var(--primary-800) 100%); }
.avios__sub { margin-top: var(--space-3); max-width: 585px; font-size: var(--t-h5); line-height: var(--lh-body); color: var(--c-text-muted); }
/* H5, 20px (2026-10-09, up from P1/18). Still on the scale, so the rule that
   everything 20 and under stays fixed is unaffected. It now matches the other
   section sub-lines on Home — §2's note, §4's standfirst and §8's body are all
   20px — so the old "P1, as every other section's sub-line" was the thing that
   had become untrue. The class is Home-only; nothing else reads it.        */
.avios__row { display: flex; flex-direction: column; gap: var(--space-6); margin-top: var(--space-10); }
.avios__img {
    position: relative; overflow: hidden; isolation: isolate;   /* isolate: the screen blend stops at this card */
    min-height: 260px;
    border-radius: var(--radius-card);
    box-shadow: var(--gloss-edge), 0 20px 60px rgba(0, 0, 0, .4);
    background:
        var(--gloss-sheen),
        linear-gradient(-85deg, rgba(0, 0, 0, 0) 82%, rgba(94, 55, 110, 1) 100%),
        linear-gradient(132deg, rgba(0, 0, 0, 0) 45%, rgba(171, 235, 240, .7) 100%),
        var(--primary-900);
}
/* The orbit render is white on black. `contain` keeps the whole composition —
   cover-cropping a 500px square into this wide card magnified it past its own
   resolution — and `screen` drops the black so the card's gradient shows through. */
.avios__anim {
    position: absolute; inset: 0; margin: auto;
    width: 100%; height: 100%;
    object-fit: contain;
    transform: scale(1.25);   /* contain leaves the square small in a wide card; 1.25 fills it without magnifying past the render's own 500px */
    mix-blend-mode: screen;
    pointer-events: none;
}
.avios__panel { padding: var(--space-8) var(--space-6); border-radius: var(--radius-card); background: var(--gloss-sheen), var(--c-bg-raised); box-shadow: var(--gloss-edge); }
.avios__lines { display: flex; flex-direction: column; }
.avios__lines li { padding-block: var(--space-4); font-size: var(--t-h4); line-height: var(--lh-heading); border-bottom: .5px solid rgba(235, 230, 236, .6); }
.avios__lines li:nth-child(2) { border-bottom-color: rgba(235, 230, 236, .5); }
.avios__lines li:nth-child(3) { border-bottom-color: rgba(235, 230, 236, .4); }
.avios__lines li:last-child   { border-bottom: 0; }

/* ── 4 · Diagram ──────────────────────────────────────────────────── */
.diagram { position: relative; background-color: var(--c-surface-1); overflow: hidden; }
.diagram::before {                                   /* the cyan glow ellipse */
    /* Centred on the AVI row's pipeline. 27% put it inside the FIRST row, not the
       second, despite the note that used to sit here claiming otherwise. Row 2's
       pipeline centre measures 73.7% to 76.7% of the section across 500 to 1440,
       so 75% holds it on the AVI half at every width (2026-10-01).           */
    content: ''; position: absolute; left: 12%; right: 12%; top: calc(75% - 73px); height: 146px;
    border-radius: 50%; background: rgba(171, 235, 240, .33);
    filter: blur(50px); box-shadow: 20px 0 250px 50px rgba(171, 235, 240, .4);
    pointer-events: none;
    /* Breathes: opacity and a little scale, 9s in/out, forever. transform and
       opacity only, so it composites without repainting the blur. base.css
       zeroes animation-duration under prefers-reduced-motion.             */
    animation: glow-breathe 9s ease-in-out infinite;
    will-change: transform, opacity;
}
@keyframes glow-breathe {
    0%, 100% { opacity: .45; transform: translateX(-4%) scale(.9, .8); }
    50%      { opacity: 1;   transform: translateX(4%)  scale(1.15, 1.4); }
}
.diagram__inner { position: relative; display: flex; flex-direction: column; padding-block: var(--space-15); }
/* Equal breathing room either side of the dashed divider: the first row
   pads its bottom by the same amount the second pads its top.            */

/* ── Section heading, standfirst and closer (2026-10-09) ──────────────
   §4 opened straight into the diagram until now: no h2, so it was absent
   from the document outline and the two row h3s had no parent heading.
   All three sit outside .diagram__rows — see index.php.
   Nothing is lit. The colour budget here is already spent on the diagram
   (two cyan eyebrows, `just speech`, `Voice Intelligence`, the lit
   Decision Signal node); the closer carries its weight by being the one
   full-brightness line against a muted standfirst, and sits directly under
   that cyan node, where a pink payoff would have collided.            */
/* Heading and standfirst both run the FULL container width, stacked
   (Akshay, 2026-10-09 — the two-column header and the 77px inset were both
   tried and rejected). At 1440 the heading is 1247px against a 1320px
   container, so it lands on one line; from about 1280 down it takes two.
   `balance` rather than a ch cap: left to itself the full-width line fits
   everything except the last word and orphans "voice" on its own.      */
.diagram__header { margin-bottom: var(--space-15); }
.diagram__head   { margin-bottom: var(--space-4); text-wrap: balance; }
.diagram__sub    { font-size: var(--t-h5); line-height: var(--lh-body); color: var(--c-text-muted); text-wrap: balance; }
/* §4's cyan is --c-accent-band (#ADEFF2), not the --c-accent-highlight
   (#98EBEF) that .home-cyan carries, and the two are 21 points apart in
   red — side by side in one section that reads as a mistake. The rows set
   the section's cyan via .diagram__hl, so the standfirst matches them.  */
.diagram__sub .home-cyan { color: var(--c-accent-band); }

/* RESTORED, not invented. The class was deleted on 2026-10-06 along with the old
   closing line; its treatment is a recorded decision and the doc says so in
   terms — V2-DESIGN-DEVIATIONS, 2026-10-01: "Section 4's closing line is
   centred at every width... Centring is what makes it read as the conclusion of
   BOTH rows; left-aligned under row 2 it reads as row 2's own line, which is the
   opposite of what it is for. Do not 'fix' this one back."
   H3 is the desktop size, the same step as the two row headings.           */
.diagram__tagline { margin-top: var(--space-16); font-size: var(--t-h5); line-height: var(--lh-body); text-align: center; text-wrap: balance; }
/* Centred is BASE, not desktop-only — that is how the original rules sat (both
   unindented, outside every media query) and it is what the doc means by
   "centred at every width", V2-DESIGN-DEVIATIONS 2026-10-01.
   SIZE IS 20px, NOT THE H3 THE ORIGINAL CARRIED (Akshay, 2026-10-09). The H3
   was set for a 56-character aphorism; this copy is 150 characters and at 32px
   it competed with the row headings it is supposed to conclude.
   space-16 above it, not space-10: it needs to read as a verdict delivered
   after the exhibit, not as a caption hanging off the last box.         */

.diagram__divider { display: block; width: 100%; height: 2px; margin: var(--space-10) 0; overflow: visible; }
.diagram__divider line { stroke: rgba(255, 255, 255, .6); stroke-width: 2; }   /* 33 dashes, ~20px each at 1440, ends on a dash at both bars */
/* the site's eyebrow, as .prod-eyebrow / .why-eyebrow / .products__eyebrow */
.diagram__eyebrow { margin-bottom: var(--space-3); font-family: var(--font-secondary); font-size: var(--t-p3); letter-spacing: var(--ls-cta); text-transform: uppercase; color: var(--c-accent-cyan); }
.diagram__title { font-size: var(--t-h3); font-weight: var(--fw-medium); margin-bottom: var(--space-3); }
/* the lede states the comparison the two flows make; P2 so it stays under the H3 */
.diagram__lede { margin-bottom: var(--space-6); font-size: var(--t-p2); color: var(--c-text-muted); }
.diagram__hl { color: var(--c-accent-band); }
.pipeline { display: flex; flex-direction: column; gap: var(--pipe-gap); }
.pipeline__node {
    position: relative;
    padding: var(--space-6) var(--space-4);
    border-radius: var(--radius-card);
    background: var(--gloss-sheen), var(--c-surface-1);
    font-size: var(--t-h5);
    line-height: var(--lh-heading);
    text-align: center;
    box-shadow: var(--gloss-edge), 0 10px 100px rgba(171, 235, 240, .2);
}
/* --pipe-gap lives on the ROW, not on .pipeline: the marks row is a sibling of
   the pipeline and has to use the same gap to stay in step, and a custom
   property only inherits downwards (2026-10-01).                            */
.diagram__row { position: relative; --pipe-gap: var(--space-6); }   /* (the per-row floor reflection was removed 2026-09-18 — the section glow does the work) */
.pipeline__node::before {                            /* 1px gradient border */
    content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
    background: linear-gradient(49deg, rgba(103, 66, 107, 1) 2%, rgba(171, 235, 240, .8) 98%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
    pointer-events: none;
}
/* Connector between nodes = an ARROW that follows the layout. Stacked
   (base): a vertical shaft from the node above, chevron pointing down.
   In a row (768+): horizontal shaft from the node before, chevron right.
   Shaft length is the flex gap, so it always exactly bridges the two.   */
.pipeline__node + .pipeline__node::after {
    content: ''; position: absolute; left: 50%; bottom: 100%; width: 2px; height: var(--pipe-gap);
    margin-left: -1px;
    background: linear-gradient(180deg, var(--teal-600), var(--cream-50));
    box-shadow: 0 2px 10px rgba(255, 250, 246, .25);
}
.pipeline__arrow {
    position: absolute; left: 50%; bottom: 100%; width: 10px; height: 10px; margin: 0 0 2px -5px;
    border-right: 2px solid var(--cream-50); border-bottom: 2px solid var(--cream-50);
    transform: rotate(45deg);
    filter: drop-shadow(0 0 4px rgba(255, 250, 246, .5));
}
.pipeline__node--accent { background: linear-gradient(46deg, var(--teal-500) 0%, var(--cream-50) 100%); color: var(--c-primary-dark); }
.pipeline__node--accent::before { background: linear-gradient(90deg, #fff 0%, var(--primary-200) 100%); }

/* The marker names the box where the signal is discarded. A row of cells
   mirroring the pipeline's, so it lands under its own column; --pipe-gap is
   inherited from .pipeline, so the two stay in step as the gap changes across
   768 and 1024. Stacked there is nothing to align to, so the one filled cell
   stands as a line under the column instead (2026-10-01).                   */
/* Stacked, the marker sits INSIDE its box, in the 24px of bottom padding that is
   already empty under a single-line label, so the box height does not move. It
   only works here: from 768 the boxes narrow to about 115px of inner width and
   stand 96px tall (Speech Recognition wraps and the row stretches to match), so
   there is no room in them and the caption row below takes over.            */
.pipeline__mark {
    position: absolute; left: var(--space-4); right: var(--space-4); bottom: 6px;
    font-family: var(--font-secondary); font-size: 11px; line-height: 1.2;
    letter-spacing: var(--ls-cta); text-transform: uppercase; text-align: center;
    color: var(--c-text-muted);
}
.pipeline-marks { display: none; }
.pipeline-marks li { font-family: var(--font-secondary); font-size: var(--t-p3); letter-spacing: var(--ls-cta); text-transform: uppercase; color: var(--c-text-muted); }
.pipeline-marks li:empty { display: none; }

/* The two rows were identical but for one lit box, and identical reads as 'or'
   rather than 'then'. Both rows already carried the cyan border, glow and
   arrows, so the work is taking the cyan OFF the first row, not adding it to
   the second: the boxes are what RK and Viji singled out, and the row that
   lands the argument is left untouched (2026-10-01).                        */
.diagram__row--muted .pipeline__node { box-shadow: var(--gloss-edge); }
.diagram__row--muted .pipeline__node::before { background: linear-gradient(49deg, rgba(103, 66, 107, 1) 2%, rgba(140, 125, 145, .8) 98%); }
.diagram__row--muted .pipeline__node + .pipeline__node::after { background: linear-gradient(180deg, var(--grey-700), var(--grey-600)); box-shadow: none; }
.diagram__row--muted .pipeline__arrow { border-color: var(--grey-600); filter: none; }

/* ── 5 · Products ─────────────────────────────────────────────────── */
.products {
    /* Section 4 is flat purple with a cyan glow; this was nearly the same
       and the two read as one room. Now: the designer's own
       "Background/gradient 1" (purple-500 -> purple-800, top to bottom —
       the stats-card gradient) as the ground, lighter behind the headline
       and deepening toward the products, with a PINK glow (the product
       tags' colour) where the arc bottoms out. Same palette, different
       room.                                                               */
    background:
        radial-gradient(ellipse 55% 55% at 50% 60%, rgba(232, 182, 240, .16) 0%, rgba(232, 182, 240, 0) 70%),
        linear-gradient(180deg, var(--primary-500) 0%, var(--primary-600) 100%);
    /* Ends light (purple-600) so the seam into section 6, which starts at
       purple-900, is a clear step rather than a blend.                    */
}
.products__inner { padding-block: var(--space-15); }
.products__eyebrow {
    margin-bottom: var(--space-3);
    font-family: var(--font-secondary);
    font-size: var(--t-p3);
    letter-spacing: var(--ls-cta);
    text-transform: uppercase;
    text-align: center;
    color: var(--c-accent-cyan);
}
.products__title { text-align: center; }
.orbit { display: none; }   /* desktop only — below 1024 the products stack */
.products__arc { display: flex; flex-direction: column; gap: var(--space-5); margin-top: var(--space-10); }
/* Stacked (below 1024): the arc is gone, so each product becomes a card
   with a dot marker on its left edge — the same dots the arc carries on
   desktop — and the markers pulse in sequence, Sales -> Callback -> Collections,
   so the "one signal, three products" idea survives without the arc.    */
.product {
    position: relative;
    display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-start;
    padding: var(--space-6) var(--space-6) var(--space-6) calc(var(--space-6) + 22px);
    border-radius: var(--radius-card);
    background: var(--gloss-sheen), rgba(255, 255, 255, .04);
    border: 1px solid rgba(171, 235, 240, .18);
    box-shadow: var(--gloss-edge);
}
.product::before {
    content: ''; position: absolute; left: var(--space-6); top: calc(var(--space-6) + .55em);
    width: 11px; height: 11px; border-radius: 50%; background: #fff;
    transform-origin: center;
    animation: product-pulse 9s ease-in-out infinite;
}
.product--left::before   { animation-delay: 0s; }
.product--bottom::before { animation-delay: -6s; }   /* order on the page: Sales, Collections, Callback */
.product--right::before  { animation-delay: -3s; }
/* One slow swell per dot every 9s (in over ~1.3s, out over ~1.3s), the three
   staggered 3s apart. The first cut peaked in half a second and read as a
   flicker on a phone.                                                       */
@keyframes product-pulse {
    0%, 28%, 100% { transform: scale(1);   box-shadow: none; }
    14%           { transform: scale(1.6); box-shadow: 0 0 10px #ABEBF0; background: #ABEBF0; }
}
.product__name { font-size: var(--t-h2); line-height: var(--lh-heading); font-weight: var(--fw-regular); color: var(--c-accent-highlight); }
.product__tag  { color: var(--c-accent-pink); }
/* Stacked, the detail is simply the rest of the card. It only becomes a
   separate thing at 1024, where it is lifted out onto a shared row.      */
.product__detail { display: flex; flex-direction: column; align-items: flex-start; }
.product__desc   { font-size: var(--t-p2); line-height: var(--lh-body); color: var(--c-text-muted); }
/* space-5, not space-2. Name -> tag and tag -> description are both 12px
   (the card's gap); at 8px the button read as part of the paragraph.   */
.product .btn    { margin-top: var(--space-5); }

/* ── 6 · Segments ─────────────────────────────────────────────────── */
.segments { background: linear-gradient(180deg, var(--primary-900) 0%, var(--primary-800) 100%); }   /* darkest at the top edge, against section 5's light bottom */
/* No bottom padding: .segments ends on primary-800 and .certs STARTS on
   primary-800, so the seam is invisible and the two sections read as one
   surface. Stacked, their paddings gave 60px above the lede and 120 below
   it at 1440. The next section's padding is the gap; this adds nothing. */
.segments__inner { padding-block: var(--space-8) 0; }
.segments__title { max-width: 370px; margin-bottom: var(--space-8); }
.segments__hl { color: var(--c-accent-highlight); }

/* The lede sits BELOW the tiles. The 28% heading column is 370px and sized for
   a heading; this copy is seven lines in it, which grows the row past the
   tiles' 360px and stops them meeting the band's top and bottom edges — and
   that bleed is the section. Brightness on the lead sentence, no hue: an
   audience is neither a capability nor a payoff (see the §1 intro band), and
   `THE` in the heading is already §6's one lit thing.                     */
.segments__lede { margin-top: var(--space-8); font-size: var(--t-h5); line-height: var(--lh-body); color: var(--c-text-muted); text-wrap: pretty; }
.home-lead { color: var(--c-text); }
.segment {
    display: flex; flex-direction: column; justify-content: center; gap: var(--space-3);
    width: min(325px, calc(100vw - var(--page-gutter) - var(--space-5) - 28px)); height: 260px; padding: 0 var(--space-10);
}
.segment { box-shadow: var(--gloss-edge); transition: background-color .25s ease, box-shadow .25s ease; }
.segment--1 { background: var(--gloss-sheen), rgba(255, 255, 255, .03); }
.segment--2 { background: var(--gloss-sheen), rgba(255, 255, 255, .06); }
.segment--3 { background: var(--gloss-sheen), rgba(255, 255, 255, .09); }
.segment:hover, .segment.is-current { box-shadow: var(--gloss-edge), inset 0 0 0 1px rgba(171, 235, 240, .25), var(--gloss-glow); }   /* .is-current: the auto carousel lights the card it has scrolled to */
/* Bigger than the H4 it was (24 at 1440), but capped where the longest name
   still sits on one line. Measured per band against the card's inner width:
   'Contact Centers' needs 195px at 24 and 260px at 32, and the card gives 181
   at 1024 rising to 246 at 1440 — so 32 wrapped it and 29 does not. A wrapped
   name against two unwrapped ones was the UX cost (Akshay, 2026-10-01). */
.segment__name { font-size: clamp(22px, 1.5px + 1.9vw, 29px); line-height: var(--lh-heading); font-weight: var(--fw-regular); color: var(--teal-600); }
.segment__tag  { font-size: var(--t-h5); line-height: var(--lh-heading); color: var(--c-accent-pink); }
/* Two lines reserved from 1024, so the three tags sit level. 'The call is the
   contract.' wraps around 1300 where the other two do not, and with the cards
   centred that lifted the first card's whole block by half a line. Pre-dates
   the name change; fixed with it (2026-10-01).                            */
@media (min-width: 1024px) { .segment__tag { min-height: calc(var(--lh-heading) * 2em); } }

/* ── 7 · Certifications ───────────────────────────────────────────────
   A quiet text row, deliberately. This is reassurance, not positioning, and it
   must not out-weigh the sector cards directly above it — hence no tiles, no
   borders and no artwork. The gradient does the separating: it starts on
   --primary-800, where section 6 ends, and finishes on the CTA's black, so the
   strip meets both neighbours at their exact edges and reads as a transition
   rather than a sixth slab. Nothing here is highlighted: the cyan/pink rule
   marks capability and payoff, and a certification is neither.              */
.certs { background: linear-gradient(180deg, var(--primary-800) 0%, var(--c-neutral-darkbg) 100%); }
.certs__inner { display: flex; flex-direction: column; gap: var(--space-5); padding-block: var(--space-15); }
/* the site's eyebrow, as .diagram__eyebrow / .products__eyebrow */
/* An h2, but set at H4 rather than the section-heading H2 the other sections use.
   It has to be in the outline, and it has to not shout: this is the one section on
   the page that is reassurance rather than argument, and sizing it like "Built for
   businesses where the call is THE business" would put it in competition with the
   sector cards immediately above.                                               */
.certs__title { font-size: var(--t-h4); line-height: var(--lh-heading); color: var(--c-text); }
.certs__sub { font-size: var(--t-p1); color: var(--c-text-muted); }   /* full width: it sets up the three cards below, so it should span them rather than stop short of the first */
.certs__note { font-size: var(--t-p3); line-height: var(--lh-body); color: var(--c-text-muted); opacity: .75; }
.certs__row { list-style: none; display: flex; flex-direction: column; gap: var(--space-5); }
/* The site's card surface, the same one section 2's stats sit on: sheen from
   above, lit top edge, dark drop. Hairline rules were the earlier structure and
   they read as a table; a table is right for a register of facts but wrong for
   something meant to land as reassurance. A card makes each one an object.   */
.cert {
    display: flex; flex-direction: column; gap: var(--space-2);
    padding: var(--space-8) var(--space-6);
    border-radius: var(--radius-card);
    background: var(--gloss-sheen), linear-gradient(180deg, var(--primary-500) 0%, var(--primary-800) 100%);
    box-shadow: var(--gloss-edge), 0 16px 40px rgba(19, 0, 26, .45);
}

/* The numeral carries the section. Earlier versions set all three lines at body
   weight with a line icon above, and it read as a footnote next to everything
   around it — section 2 makes its point with a figure, section 4 with a lit node,
   and this had no dominant element at all. The code is what a reader recognises
   and what they search for, so it is the thing that gets the size; the family and
   the year ride its baseline the way a currency symbol rides a price.          */
.cert__standard { font-size: var(--t-p3); color: var(--c-text-muted); line-height: 1.1; white-space: nowrap; }
.cert__code { font-size: var(--t-h2); line-height: 1; letter-spacing: var(--ls-display); color: var(--c-text); }
.cert__name { font-size: var(--t-p2); color: var(--c-text); }   /* no max-width: the column already constrains it, and a cap made the longest name wrap to two lines while the others did not, leaving the three columns ending at different heights */
.cert__no   { font-family: var(--font-secondary); font-size: var(--t-p3); letter-spacing: var(--ls-cta); color: var(--c-text-muted); }


/* ── 8 · Why Voice ────────────────────────────────────────────────── */
.avios__hl { color: var(--c-accent-highlight); }

.why { background: linear-gradient(180deg, var(--primary-800) 0%, var(--primary-900) 100%); }
.why__inner { display: flex; flex-direction: column; gap: var(--space-10); padding-block: var(--space-8); }
.why__img {
    min-height: 260px;
    border-radius: var(--radius-card);
    box-shadow: var(--gloss-edge), 0 20px 60px rgba(0, 0, 0, .4);
    background:
        var(--gloss-sheen),
        linear-gradient(-35deg, rgba(255, 255, 255, 0) 50%, rgba(103, 66, 107, 1) 100%),
        linear-gradient(144deg, rgba(255, 255, 255, 0) 53%, rgba(171, 235, 240, 1) 100%),
        url('../../images/home/why-pradhi.webp') center / 54% auto no-repeat,
        rgba(254, 249, 244, .1);
    /* The frame (2026-09-18): the TV cut-out is placed at ~54% of the card's
       width rather than stretched to fill it. The source is 413px wide, so
       at that size it is never scaled up and stops looking soft; the card
       itself stretches to the text beside it.                              */
}
.why__text { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-6); }
.why__text p { font-size: var(--t-p1); color: var(--c-text-muted); max-width: var(--measure); }   /* P1 18px (was the 16px body default), 2026-10-01 — About and Careers reuse this block, so size changes for Home go on .why__body below */
.why__text p.why__body { font-size: var(--t-h5); }   /* Home section 8 only: 20px, 2026-10-05. Qualified because .why__text p above is more specific than a bare class. */
.why__hl { color: var(--c-accent-pink); }   /* pink, not cyan: the footer tagline repeats this phrase in pink directly below */

/* ── 10 · FAQ ──────────────────────────────────────────────────────────
   GROUND. §9 (.why) ends on primary-900 and the footer is pure black, so
   this band is sandwiched between two dark surfaces. It steps a shade
   LIGHTER at its top (primary-800) and darkens back to primary-900 — the
   site's own rule, "each ground starts a step lighter than the previous
   ends" — which gives the section an edge against §9 and hands off cleanly
   to the footer's black and its cyan hairline.

   THE ROWS ARE NOT A NEW MATERIAL. They are the site's raised surface,
   unchanged: sheen + a translucent white lift over the ground, with the
   1px gloss edge. Hover and open add the same cyan inner line and bloom
   that .segment:hover uses, so an open answer and a hovered card are lit
   the same way everywhere on the page.                                 */
.faq { background: linear-gradient(180deg, var(--primary-800) 0%, var(--primary-900) 100%); }
.faq__inner { padding-block: var(--space-15); }
/* STACKED, THE PROMPT FOLLOWS THE LIST. The heading still leads, but
   "Not answered here?" only makes sense once the questions have been read,
   and on a phone the reader meets it before a single one of them.
   `display: contents` dissolves the aside so its children become flex items
   of the section and can be ordered individually; the wrapper comes back as
   a real box at 1024, where it is the sticky column. Safe on a plain div —
   the display:contents accessibility bug was for elements with an implicit
   role, which this has none of.                                         */
.faq__inner { display: flex; flex-direction: column; }
.faq__aside { display: contents; }
.faq__title { order: 1; margin-bottom: var(--space-6); }
.faq__list  { order: 2; }
.faq__ask   { order: 3; margin-top: var(--space-10); margin-bottom: var(--space-5); font-size: var(--t-h5); line-height: var(--lh-body); color: var(--c-text-muted); }
.faq__cta   { order: 4; }
/* THE CARD IS SIZED FROM THE MEASURE, not the container. Capping the answer
   at --measure inside a full-width card left ~430px of every card empty at
   1440 and read as a bug: the card is what the eye takes for the text's
   boundary, so the two have to agree. Removing the cap instead would give a
   1245px line at 18px, about 145 characters, which is twice a readable
   measure. So the LIST carries the measure plus the card's own side padding
   and the answer fills it; change --measure or --space-6 and they stay in
   step.
   NOTE `ch` resolves against the LIST's font-size, which is the inherited
   16px, not the answer's 18px — so the card is 772px and the answer gets
   about 64ch rather than 68. That is a better measure than 68, so it is left
   alone; it is recorded because the number does not match a naive reading of
   the expression.
   Below 1024 the container is already narrower, so this never binds.     */
.faq__list  { list-style: none; display: flex; flex-direction: column; gap: var(--space-3); max-width: calc(var(--measure) + var(--space-6) * 2); }

.faq__item {
    border-radius: var(--radius-card);
    background: var(--gloss-sheen), rgba(255, 255, 255, .04);
    box-shadow: var(--gloss-edge);
    transition: box-shadow .25s ease, background-color .25s ease;
}
.faq__item:hover,
.faq__item[open] { background: var(--gloss-sheen), rgba(255, 255, 255, .07); box-shadow: var(--gloss-edge), inset 0 0 0 1px rgba(171, 235, 240, .25), var(--gloss-glow); }

.faq__q {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-5);
    padding: var(--space-5) var(--space-6);
    cursor: pointer;
    font-size: var(--t-h5); font-weight: var(--fw-semibold); line-height: var(--lh-heading);
    color: var(--c-text);
    transition: color .2s ease;
}
/* Both are needed to drop the disclosure triangle: the pseudo-element for
   WebKit, `list-style: none` on the summary itself for everyone else.    */
.faq__q { list-style: none; }
.faq__q::-webkit-details-marker { display: none; }
.faq__item:hover .faq__q { color: var(--c-accent-highlight); }
.faq__q:focus-visible { outline: 2px solid var(--c-accent-cyan); outline-offset: 2px; border-radius: var(--radius-card); }

.faq__chev { flex: 0 0 auto; transition: transform .3s cubic-bezier(.25, .46, .45, .94); }
.faq__item[open] .faq__chev { transform: rotate(180deg); }

.faq__a   { padding: 0 var(--space-6) var(--space-5); }
.faq__a p { font-size: var(--t-p1); line-height: var(--lh-body); color: var(--c-text-muted); }

/* The smooth open and close. ::details-content is the anonymous box holding
   everything after the summary; `interpolate-size` is what makes a height of
   `auto` animatable at all, and it is set on .faq rather than :root so it
   cannot change how an `auto` transitions anywhere else on the site.
   `content-visibility ... allow-discrete` keeps the answer from painting
   outside the clip while the row is collapsing.
   Everything is inside @supports: a browser without it (Firefox, today) gets
   an instant snap and an accordion that still works perfectly.          */
@supports (interpolate-size: allow-keywords) {
    .faq { interpolate-size: allow-keywords; }
    .faq__item::details-content {
        block-size: 0;
        overflow: hidden;
        transition: block-size .35s cubic-bezier(.25, .46, .45, .94),
                    content-visibility .35s allow-discrete;
    }
    .faq__item[open]::details-content { block-size: auto; }
}

/* ── 3b · AVI dial (TRIAL) ────────────────────────────────────────────
   Figma 1261:1204 / 1278:1316 for the composition; the mark itself was
   redrawn to spec because the supplied export could not be placed reliably —
   its ring sat off-centre in a 1421x2187 box, so every position became a
   magic percentage and the thing rendered as an ellipse.

   THE TWO FILES ARE BOTH 1000x1000 WITH THE MARK ON 500,500, and the sweep's
   apex is its own box centre. So they overlay exactly, at any size, with no
   offset arithmetic: same box, same place, done. The dial is square, so it
   cannot be stretched either.

   HEIGHT. The section sets none. .aviring__inner is a two-cell grid with
   align-items: center, so the band is the taller cell plus padding and the
   two columns are centred on each other. --dial drives everything.        */
.aviring {
    position: relative; overflow: hidden; isolation: isolate;
    /* ── THE FOUR KNOBS. Override any of them per breakpoint. ──
       --dial       the circle's diameter. Also sets the band height.
       --sweep      the sweep's size, as a multiple of --dial. Bigger = the
                    wash reaches further AND the arch moves further out.
       --sweep-rot  where the sweep points. 90deg = right, 180deg = down.
       --viz-h      how much height the layout reserves for the circle; the
                    circle itself overruns it into the padding. Floor is
                    --dial, or the circle gets cropped.
       The arch's distance from the circle's edge, in px, is
           r0/1000 * --dial * --sweep  -  --dial/2
       where r0 is in the SVG path (includes/avi-sweep.svg) and is currently
       96. At --sweep 5.6 that lands the arch ~20px outside the circle at any
       --dial, because both sides of the sum scale together.              */
    --dial: 400px;
    --sweep: 5.6;
    --sweep-rot: 180deg;
    /* Gloss, the site's own system: the sheen over the ground and a 1px light
       along the top edge. The ground also steps 500 -> 600 so the band hands
       off to the diagram's primary-700 below, while starting a shade lighter
       than the avios band above, which ends on primary-800.              */
    background: var(--gloss-sheen), linear-gradient(180deg, var(--primary-500) 0%, var(--primary-600) 100%);
    box-shadow: var(--gloss-edge);
}
.aviring__inner { position: relative; z-index: 1; display: grid; gap: var(--space-8); align-items: center; padding-block: var(--space-10) var(--space-15); }

/* The box is SHORTER than the circle on purpose. The circle is centred on it
   and allowed to bleed into the section's padding, so the band's height is no
   longer forced to be the full diameter plus padding. --viz-h is how much of
   the dial the layout has to reserve; the floor is the diameter itself, since
   the circle still has to fit inside the band.                            */
.aviring__viz { position: relative; width: var(--dial); height: var(--viz-h, var(--dial)); margin-inline: auto; display: grid; place-items: center; }

.aviring__dial-svg {
    /* Sized from --dial, NOT from the box — inset:0 would squash it the moment
       the box stopped being square, and that is how the ellipse came back. */
    position: absolute; left: 50%; top: 50%;
    width: var(--dial); height: var(--dial); max-width: none;
    translate: -50% -50%;
    animation: avi-spin 42s linear infinite;
}
@keyframes avi-spin { to { rotate: 360deg; } }
@media (prefers-reduced-motion: reduce) { .aviring__dial-svg { animation: none; } }

/* The sweep is drawn pointing UP. It is scaled about the shared centre, so
   the apex stays pinned to the dial whatever the size, and only rotated:
   a quarter turn to point right beside the copy, a half turn to fan down
   behind it when stacked. A wash at .40 — at full strength white text on it
   measures 3.83:1, at .40 it is 9.15:1.                                   */
.aviring__sweep-svg {
    position: absolute; left: 50%; top: 50%;
    width: calc(var(--dial) * var(--sweep)); height: calc(var(--dial) * var(--sweep));
    max-width: none; translate: -50% -50%;
    rotate: var(--sweep-rot);
    opacity: .40; pointer-events: none; z-index: 0;
}

.aviring__dial { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: var(--space-2); text-align: center; padding-inline: 10%; }
.aviring__name { font-size: var(--t-h2); line-height: var(--lh-heading); color: var(--c-text); }
.aviring__sub  { font-size: var(--t-p2); line-height: var(--lh-body); color: rgba(255, 255, 255, .85); max-width: 13ch; }

.aviring__panel { position: relative; z-index: 1; }
.aviring__lead  { margin-bottom: var(--space-5); font-size: var(--t-h5); line-height: var(--lh-body); color: var(--c-text); }
.aviring__lines { display: flex; flex-direction: column; list-style: none; }
.aviring__lines li { padding-block: var(--space-4); font-size: var(--t-h4); line-height: var(--lh-heading); color: var(--c-text); border-bottom: .5px solid rgba(235, 230, 236, .45); }
.aviring__lines li:last-child { border-bottom: 0; }

/* ═══ Tablet — 768 ════════════════════════════════════════════════════ */
@media (min-width: 768px) {
    /* AVI dial: two columns from 768, not 1024. Stacked at this width the
       sweep's opening — a fixed 110 degrees — reads as a hard V below the
       dial, because the viewport is wide enough to show both of its straight
       sides before they leave the frame. Turning it to point right puts those
       sides off-screen and behind the copy, which is what it is for.
       --dial is 400 here (Akshay, 2026-10-10): it clears the half-width
       column at 768 with room to spare, and from about 900 the panel's
       wrapping is what sets the band height rather than the dial.        */
    .aviring { --dial: 400px; --sweep-rot: 90deg; --viz-h: calc(var(--dial) - 32px); }
    .aviring__inner { grid-template-columns: 1fr 1fr; gap: var(--space-8); padding-block: var(--space-10); }
    /* The sub-line stays at the mobile size here rather than going to P1 —
       the hollow is still narrow enough at this dial for 18px to run into the
       arcs. It goes up at 1024, where the dial is 520.                    */

    .avios__row, .why__inner { flex-direction: row; align-items: stretch; }
    .avios__img, .why__img   { flex: 0 0 48%; min-height: 0; }
    .avios__panel, .why__text { flex: 1; }
    .diagram__row { --pipe-gap: var(--space-8); }
    .pipeline { flex-direction: row; align-items: stretch; }
    .pipeline__mark { display: none; }                 /* no room in the box at this width */
    .pipeline-marks { display: flex; gap: var(--pipe-gap); margin-top: var(--space-3); }
    .pipeline-marks li { flex: 1 1 0; min-width: 0; text-align: center; }
    .pipeline-marks li:empty { display: block; }   /* the empty cells are the alignment */
    .pipeline__node { flex: 1 1 0; min-width: 0; }
    .pipeline__node + .pipeline__node::after {
        left: auto; bottom: auto; right: 100%; top: 50%; width: var(--pipe-gap); height: 2px;
        margin: -1px 0 0 0;
        background: linear-gradient(90deg, var(--teal-600), var(--cream-50));
    }
    .pipeline__arrow {
        left: auto; bottom: auto; right: 100%; top: 50%; margin: -5px 2px 0 0;
        border: 0; border-top: 2px solid var(--cream-50); border-right: 2px solid var(--cream-50);
    }
    .products__arc { flex-direction: row; flex-wrap: wrap; justify-content: space-between; }
    .product { flex: 1 1 40%; }
}

/* ═══ Desktop — 1024: the 1440 frame ══════════════════════════════════ */
@media (min-width: 1024px) {
    /* Three across only from 1024, not 768. Measured: at 768 a column is 201px and
       "ISO/IEC 20000-1:2018" needs 207px even with the numeral at its 30px floor,
       so the code broke mid-token as "20000-" / "1:2018". Stacked is right there.
       Hairline rules between the columns, the same device the diagram uses to
       bound its rows: a register of records, not three badges.                 */
    .certs__row { flex-direction: row; gap: var(--space-5); }
    /* Centred only in columns, left-aligned when stacked. Rendered both ways before
       choosing: left-aligned, each record pressed against its own rule with a long
       empty tail to the right, and the short middle column looked like a mistake.
       Centred, each cell reads as one object. Stacked there are no cells, so left
       is right there — a centred column of ragged text has nothing to centre on. */
    .cert { flex: 1 1 0; min-width: 0; align-items: center; text-align: center; }

    .hero { min-height: 775px; align-items: center; padding-block: 0; }
    .hero__inner { padding-bottom: var(--space-5); }
    /* Tied to the viewport, not a fixed 880: on a laptop whose window is shorter
       than that the lower lane fell below the fold, which is the one thing a
       hero cannot do. clamp keeps a floor so a very short window still leaves
       the composition room (2026-10-01).                                     */
    .hero h1 { max-width: 816px; }

    .carousel__track { overflow: visible; margin-inline: 0; padding-inline: 0; scroll-snap-type: none; gap: var(--space-16); }
    .carousel__dots { display: none; }

    .stats__inner { padding-block: var(--space-15); }

    /* The two-column row only FITS at 1440. The frame's numbers — a 468px title,
       a 64px row gap, three 220px tiles with 64px between them — add up to
       exactly the 1320px container at 1440 and to more than the container at
       every width below it. Because the tiles were a fixed 220px, the title
       absorbed the whole shortfall: 338px at 1300, 200px at 1150, 83px at 1024,
       where the heading wrapped to nine lines and the third tile was clipped.
       Fixed 2026-10-09.

       Gaps and tile width now interpolate to the frame's values AT 1440 and
       shrink below it, so the title is never asked to carry the deficit alone.
       Each clamp is solved for the two endpoints, 1024 and 1440:

         row gap    32 → 64     tile gap   24 → 64     tile  170 → 220

       The title keeps the remainder: 345px at 1024, 383px at 1150, 468px at
       1440 — identical to today at 1440, where nothing is scaled. Its
       `min-width` is a floor the arithmetic never reaches; it exists so a copy
       change can never put us back where we were.

       SCOPED TO .stats ON PURPOSE. `.carousel` and `.carousel__track` are
       shared — home.css loads on 15 pages — so an unscoped gap would move the
       swipe rows on the four product pages, why-voice and careers.         */
    .stats__row { display: flex; align-items: center; justify-content: space-between; gap: clamp(32px, -46.77px + 7.69vw, 64px); }
    .stats__title { flex: 0 1 468px; min-width: 300px; margin-bottom: 0; }
    .stats .carousel { flex: 0 0 auto; }
    .stats .carousel__track { gap: clamp(24px, -74.46px + 9.62vw, 64px); }
    .stats .stat { width: clamp(170px, 46.92px + 12.02vw, 220px); }

    /* Full width and a step up in size once the row is side by side: the note
       is the band's closing line, and under two columns a measure-capped
       paragraph reads as a footnote to the LEFT column rather than to both.   */
    .stats__note { max-width: none; margin-top: var(--space-10); font-size: var(--t-h5); }

    /* The frame's split: the dial holds the left ~40%, the list the right.
       The dial labels are pushed to the hollow's centre, which sits at 22%
       of the band — that is where the SVG places it at this width.        */
    /* Two columns; the band's height is the taller cell. */
    .aviring { --dial: 520px; }
    /* Band height = --viz-h + 2x padding = (dial - 40) + 80 = dial + 40, so a
       520 dial gives a 560 band instead of 640. The circle overruns the box by
       20px top and bottom and sits inside the padding. */
    .aviring { --viz-h: calc(var(--dial) - 40px); --sweep-rot: 90deg; }
    .aviring__inner    { grid-template-columns: 1fr 1fr; gap: var(--space-10); padding-block: var(--space-10); }

    .aviring__sub      { font-size: var(--t-p1); max-width: 14ch; }

    .avios__img { flex-basis: 619px; }
    .avios__panel { flex-basis: 666px; padding: var(--space-10) 0 var(--space-10) var(--space-8); }   /* the frame: 40px 0 40px 32px — no right padding, the hairlines run to the edge */

    .diagram__inner { padding-block: var(--space-15); }
    .diagram__row { --pipe-gap: var(--space-16); }
    /* The frame (2026-09-18): two 1px verticals at the container's edges
       spanning both rows, the rows inset 77px from them, and the dashed
       divider running line to line. Rows stay fit-content so the nodes
       keep their equal widths.                                            */
    .diagram__rows { position: relative; padding-inline: 77px; }
    .diagram__rows::before, .diagram__rows::after { content: ''; position: absolute; top: 0; bottom: 0; width: 1px; background: rgba(255, 255, 255, .8); }
    .diagram__rows::before { left: 0; }
    .diagram__rows::after  { right: 0; }

    /* The bracket closes around the comparison. CSS-only transform, which is
       what V2-MOTION already lists for this section, and it says one thing:
       the two rows are one exhibit. Gated on .js so the hairlines are simply
       there when the reveal system is not running, and keyed off the first
       row's own .is-visible so it fires with the diagram rather than on a
       trigger of its own. Reduced motion collapses it to the end state.   */
    @supports selector(:has(*)) {
        .js .diagram__rows::before,
        .js .diagram__rows::after { transform: scaleY(0); transform-origin: top; }
        .js .diagram__rows:has(.diagram__row.is-visible)::before,
        .js .diagram__rows:has(.diagram__row.is-visible)::after { animation: diagram-bracket .9s cubic-bezier(.25, .46, .45, .94) 1 forwards; }
    }
    .diagram__divider { width: calc(100% + 154px); max-width: none; margin-inline: -77px; }   /* line to line: the rows are inset 77px each side (max-width: base.css caps svg at 100%) */
    .diagram__row { width: fit-content; max-width: 100%; }
    /* All four nodes the same width, sized to the longest label. */
    .pipeline__node { width: 236px; flex: 0 1 auto; min-width: 0; padding: var(--space-6) var(--space-4); }
    /* An explicit width (not just a flex-basis): a fit-content row measures
       its items by definite width, but by TEXT width when only a basis is
       set — which collapsed the nodes unequally. With width 236 each node
       may still shrink equally below 4x236 + 3 gaps, and never grows, so
       the row hugs 1136px on wide screens and the divider ends at the
       last node.                                                          */

    /* The three products sit on the arc's dots. Every coordinate is in cqw
       (1% of the container's width), taken from the 1320px Figma container,
       so arc, connector lines and text scale together as one drawing from
       1024 to 1440. The lines are background layers of the same box.       */
    .products__inner { container-type: inline-size; padding-bottom: var(--space-15); }
    .products__arc {
        position: relative; display: block; margin-top: 0;
        /* Was max(42.4cqw, 32.6cqw + 210px) — the bottom product plus its own
           fixed-height content. The detail row now sets the floor instead: it
           starts at 42cqw and its content is in px, so the second term is the
           one that binds at every width below 1440.                        */
        height: calc(max(40cqw, 20.2cqw + 260px) + 230px);   /* Callback's top plus its own stack */
        /* Connector lines, one per product, as background layers in cqw
           (from the SVG's circle centres — see the orbit below). Each is
           a variable so :has(hover) can brighten just one.               */
        --line: linear-gradient(180deg, rgba(255, 255, 255, .55) 0%, rgba(19, 0, 26, 0) 73%);
        --line-lit: linear-gradient(180deg, #ABEBF0 0%, rgba(171, 235, 240, 0) 85%);
        --line-l: var(--line); --line-b: var(--line); --line-r: var(--line);
        background:
            var(--line-l) calc(33.41cqw - .5px) 10.82cqw / 1px 16.5cqw no-repeat,
            var(--line-b) calc(50.38cqw - .5px) 19.16cqw / 1px max(20.84cqw, 1.04cqw + 260px) no-repeat,   /* reaches Callback's new top: its top minus the dot at 19.16cqw */
            var(--line-r) calc(67.20cqw - .5px) 10.90cqw / 1px 16.1cqw no-repeat;
    }
    /* The orbit SVG sits exactly where the background arc used to: its
       viewBox is the 507x170 arc plus a 60/40 margin so the echo arcs and
       the signal's glow are not clipped — hence the offset and larger box. */
    .orbit {
        display: block; position: absolute; pointer-events: none;
        left: calc(31.4cqw - 60 / 507 * 38.4cqw); top: calc(6.7cqw - 40 / 507 * 38.4cqw);
        width: calc(38.4cqw * 627 / 507);
    }
    .orbit svg { display: block; width: 100%; height: auto; overflow: visible; }
    .orbit__core { fill: #fff; }
    /* Dot pulses are SMIL <animate>s inside the SVG, on the same 12s clock
       as the signal, keyed to each dot's arc-length fraction (0.076 / 0.489
       / 0.896 of the path, signal at constant speed there and back). CSS
       keyframes ran on a different clock and drifted.                      */

    /* Hover: the product lights up, and so do its dot and its line. */
    .product__name, .orbit__core { transition: color .2s ease, fill .2s ease; }
    .product:hover .product__name { color: #fff; }
    .products__arc:has(.product--left:hover)   { --line-l: var(--line-lit); }
    .products__arc:has(.product--bottom:hover) { --line-b: var(--line-lit); }
    .products__arc:has(.product--right:hover)  { --line-r: var(--line-lit); }
    .products__arc:has(.product--left:hover)   .orbit__dot--left   .orbit__core,
    .products__arc:has(.product--bottom:hover) .orbit__dot--bottom .orbit__core,
    .products__arc:has(.product--right:hover)  .orbit__dot--right  .orbit__core { fill: #ABEBF0; filter: drop-shadow(0 0 8px #ABEBF0); }
    /* SMIL is not covered by the reduced-motion rule in base.css: hide the
       signal here; home.js pauses the SVG's timeline so the dots freeze.  */
    @media (prefers-reduced-motion: reduce) { .orbit__signal { display: none; } }

    .product { position: absolute; width: 17.6cqw; padding: 0; background: none; border: 0; box-shadow: none; }
    /* Reflection of the orbit on a polished floor. */
    .products__arc::after {
        content: ''; position: absolute; left: 25%; right: 25%; top: 24cqw; height: 6cqw; pointer-events: none;
        background: radial-gradient(ellipse 50% 100% at 50% 0, rgba(232, 182, 240, .14) 0%, rgba(232, 182, 240, 0) 70%);
    }
    .product::before { display: none; }   /* the arc's dots take over */
    /* EACH PRODUCT KEEPS ITS OWN STACK: name, tag, description, button, under
       its own dot. A first attempt lifted the descriptions onto one shared row
       below the orbit; it read as three descriptions belonging to Callback,
       because the row landed directly under Callback's tag. Reverted
       2026-10-09.

       The side columns go from 17.6cqw to 30cqw. Their outer edges do NOT
       move — left stays at 14.8cqw and right still ends at 86.5cqw — so both
       names sit exactly where the frame put them; only the box grows inward,
       where there was nothing. At 17.6cqw (165px at 1024) the description ran
       to six lines of nineteen characters.

       The widened boxes now overlap Callback horizontally, which is fine
       because Callback moves BELOW them. Its top is max(proportional, fixed)
       for the usual reason — the stack above it is mostly px, so the clearance
       it needs grows as the viewport narrows:

         side block ends at   37.3cqw @1440 … 45.7cqw @1024
         Callback top lands   39.9cqw @1440 … 48.0cqw @1024          */
    .product--left   { left: 14.8cqw; top: 20.2cqw; width: 30cqw; }
    .product--right  { left: 56.5cqw; top: 20.3cqw; width: 30cqw; align-items: flex-end; text-align: right; }
    .product--bottom { left: 27cqw; top: max(40cqw, 20.2cqw + 260px); width: 46cqw; align-items: center; text-align: center; }
    /* The button is a child of .product__detail, not of .product, so the
       variant's own align-items never reaches it — the description inherits
       text-align and looks right while the button stays hard left. The detail
       stretches to the full column so "centre" means the column's centre and
       not the width of the longest line.                                  */
    .product__detail { align-self: stretch; }
    .product--right  .product__detail { align-items: flex-end; }
    .product--bottom .product__detail { align-items: center; }


    /* The frame's row is 370 + three 325s + a 32 gap = 1377, which only fits at 1440;
       every width under it pushed the third card off the viewport. Same layout, fluid:
       the title keeps its 370 cap but may shrink, the cards share what is left, and the
       row runs to the viewport's right edge as the frame draws it (2026-09-23). */
    /* padding-right: 0 and padding-block: 0 are what let the tiles bleed to
       the top, bottom and right edges. They stay on the INNER; the row takes
       over the flex. The lede then has to put its own right gutter back,
       because the inner no longer has one.                                */
    .segments__inner { padding-block: 0; padding-right: 0; }
    .segments__row   { display: flex; align-items: center; gap: var(--space-8); }
    /* PADDING, not margin. The inner has padding-block: 0, so a bottom margin
       on the last child collapses straight out of the band and the copy ends
       up ~19px off the section's edge instead of 60.                     */
    .segments__lede  { margin-top: 0; padding-block: var(--space-15) 0; padding-right: var(--page-gutter); }
    .segments__title { margin-bottom: 0; flex: 0 1 28%; }
    .segments .carousel { flex: 1 1 auto; min-width: 0; }   /* the carousel wrapper is the flex item, not the track */
    .segments .carousel__track { gap: 0; }
    .segments .carousel__track > li { flex: 1 1 0; min-width: 0; }
    /* the frame's 545 held two short lines in a lot of empty card; 360 keeps the
       column presence without the cavern (2026-09-23) */
    /* the frame's 40 at 1440, easing to 20 by 1024. The old curve (2.8vw) held
       29px of padding at 1024 and left only 181px of card for the name, which
       wrapped 'Contact Centers' however small the type got; a steeper curve
       keeps the frame's figure where it was drawn and finds the room where
       there is none (2026-10-01).                                          */
    .segment { width: auto; height: 360px; padding-inline: clamp(20px, 4.8vw - 29px, 40px); }


    /* Two columns from 1024. The accordion track is the measure-derived width
       as its MAXIMUM, and the aside takes what is left: 484px at 1440, within
       a few px of the 468 .stats__title uses, so the page already has that
       column width.
       BOTH TRACKS ARE minmax(), and that is not decoration. A fixed 772px
       second track plus the aside's min-content overflowed the 935px
       container at 1024 and the accordion ran off the right edge. minmax(0,
       …) lets the list shrink once the aside has its 260px floor, so the
       split is 260/611 at 1024 and 484/772 at 1440 with nothing clipped.
       STICKY, not static: through a seven-item list the heading and the
       prompt travel with the reader instead of scrolling away at the top.
       The offset uses --nav-height, the same token the scroll-padding does;
       `align-self: start` is required or the item stretches to the row and
       sticky has nothing to move within.                                 */
    .faq__inner { display: grid; grid-template-columns: minmax(260px, 1fr) minmax(0, calc(var(--measure) + var(--space-6) * 2)); gap: var(--space-16); align-items: start; }
    .faq__aside { display: block; position: sticky; top: calc(var(--nav-height) + var(--space-8)); align-self: start; }
    .faq__title { margin-bottom: var(--space-5); }
    .faq__ask   { margin-top: 0; }

    .why__inner { padding-block: var(--space-16) var(--space-15); gap: var(--space-10); }
    .why__text { padding-block: var(--space-8); }
}

@keyframes diagram-bracket { from { transform: scaleY(0); } to { transform: scaleY(1); } }
