:root { --cx-band-h: 222px; }   /* the band's height AND the reveal runway (site/callix/mobile.css halves it) */

/* ==========================================================================
   Callix effects — the "gradient art" recipe and the script-text masks, at
   Figma geometry. Colours: Figma's ellipse fills could not be read, so each
   ellipse carries the colour the render shows at its centre — approximate, and
   worth re-reading off the file if it is ever touched — and the blend/veil chain is
   collapsed into those output colours. Structure is exact so real fills drop in.
   Blur is applied to the ellipse layer (filter) rather than as a full-bleed
   backdrop-filter veil — same picture, far cheaper on scroll.
   ========================================================================== */
.cx-art { position: absolute; inset: 0; overflow: hidden; pointer-events: none; contain: layout paint style; }
/* the recipe, literally: a frame with the base fill → the exported gradient groups (or ellipses) at
   mix-blend-overlay → the rgba(0,0,0,.57) overlay veil → the script text masked with the exported shape.
   Figma blurs via `backdrop-filter` on the veil; here the blur sits on the wrapper that holds base + groups
   (same picture on a flat base, rasterised once instead of per frame). The base lives INSIDE that wrapper so
   the groups' overlay blend still sees it (a filter isolates its subtree). */
.cx-art__frame { position: absolute; overflow: hidden; }
.cx-art__blur  { position: absolute; inset: 0; transform: translateZ(0); }
.cx-art__base  { position: absolute; inset: 0; }
.cx-art__g     { position: absolute; mix-blend-mode: overlay; }
.cx-art__g > img { display: block; }
.cx-art__veil  { position: absolute; background: rgba(0, 0, 0, 0.57); mix-blend-mode: overlay; }
.cx-art__e     { position: absolute; border-radius: 50%; }
.cx-art__mask  { position: absolute; overflow: hidden; mix-blend-mode: soft-light; }
.cx-art__script {                                      /* Geist Mono 14 / 38.182 / tracking 4 / white (16.364 / 10.9091 scaled) */
  position: absolute; margin: 0;
  font-family: var(--cx-mono); font-size: 14px; line-height: 38.182px; letter-spacing: 4px; color: #fff;
  white-space: pre-wrap; overflow-wrap: anywhere;
  text-box-trim: trim-both; text-box-edge: cap alphabetic;
}

/* hosts */
.cx-how__slide { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); }
.cx-how__slide .cx-art { z-index: 0; }

/* --------------------------------------------------------------------------
   Four slides sharing one baked gradient read as one slide shown four times,
   so each carries its own hue off the same render. The recipe is untouched —
   same art, same veil, same script mask in the same place — the render is just
   rotated round the wheel and mirrored on alternate slides. It lands on the
   palette the ellipses are drawn from: #00C4FF cyan, #2FFF00 green, #B300FF
   purple and the #ec7c45 the CTA is built on.

   The crop stays at 0 0 on purpose. render-slide.webp is 1920 wide against a
   1278 frame, so there is 642px of slack to pan through, but its far right is
   washed out and panning into it puts a flat vertical band down one edge.

   The variant rides on the slide's class rather than :nth-child because how.js
   clones the first and last slide for the loop; cloneNode carries the class, so
   a clone matches the slide it stands in for and the wrap shows no colour jump.
   -------------------------------------------------------------------------- */
.cx-how__slide.art-2 .cx-art__render { filter: hue-rotate(-45deg); transform: scaleX(-1); }
.cx-how__slide.art-3 .cx-art__render { filter: hue-rotate(90deg); }
.cx-how__slide.art-4 .cx-art__render { filter: hue-rotate(150deg); transform: scaleX(-1); }
/* art-1 is the render as drawn — no rule. The mask is a sibling of the render, so the
   mirror never reaches the script and it stays readable on every slide. */
.cx-how__slide > .cx-how__panel, .cx-how__slide > .cx-how__copy, .cx-how__slide > .cx-how__bullets { z-index: 1; }
.cx-how__counter { letter-spacing: -0.77px; }                       /* 894:11699 — Inter 24/40, −0.77 */
.cx-how__bar, .cx-how__arrow { border-color: var(--cx-line-strong); } /* 894:11461 — rgba(38,38,38,.11) */
.cx-dark { position: relative; overflow: hidden; }
.cx-dark > .container { position: relative; z-index: 1; }
.cx-dark__cell { background: var(--cx-dark); }          /* Figma cells are filled: the script shows only outside the grid */
.cx-cta { background: #ec7c45; }                        /* 891:9398 base fill (the stand-in gradient is gone) */
.cx-cta > .cx-cta__head, .cx-cta > .cx-cta__stage { position: relative; z-index: 1; }
.cx-band {                                              /* 891:10611 — 1920 × 222, ground #4582ec */
  position: fixed; left: 0; bottom: 0; width: 100%;     /* pass 27 — see the reveal block below */
  height: calc(var(--cx-band-h) * 2.802); z-index: -1;  /* 622/222 — the render's full height; see the overscroll note */
  overflow: hidden; background: #4582ec;
}

/* --------------------------------------------------------------------------
   Bottom reveal (request 2026-09-16). The band is pinned to the bottom edge of
   the viewport for the whole page and painted BEHIND the page (z-index -1 in the
   root stacking context — nothing in its ancestor chain makes one), so it is
   invisible until the page runs out. `body` carries the band's height as padding
   instead, which is the runway: over the last 222px of scroll the content
   wrapper's bottom edge rises past the viewport bottom and uncovers the band.
   The band never moves — the page slides off it.

   Why the wrapper is the cover and not the sections: the sections are `.container`
   boxes, so they leave a gutter on both sides that a negative-z layer shows
   straight through. The wrapper is full-bleed and spans every section, and it
   stops exactly where the runway starts.

   No scroll listener, no JS, no transform — it is layering, so there is nothing
   to undo for prefers-reduced-motion and nothing to pay for at scroll time. The
   band is out of flow, so `body`'s padding keeps the document the same height.
   -------------------------------------------------------------------------- */
/* the cover the band is revealed from behind: the page wrapper, <main>. */
/* The hero's sticker hangs 18px past the 1360 container by design, and it is not the only piece of
   art placed to overhang. `clip` (not `hidden`) keeps them from ever widening the document without
   creating a scroll container — so position: sticky elsewhere on the page still works. */
main.cx-main { background: var(--cx-page); overflow-x: clip; }     /* the cover (same ground it already showed) */
body { padding-bottom: var(--cx-band-h); }              /* the runway the content travels to uncover it */

/* Overscroll (request 2026-09-16). Rubber-banding past the end lifts the document but NOT the pinned sheet —
   that separation IS the effect — so a gap opens between the content's bottom edge and the sheet's top edge.
   It is filled with the artwork the design already has: the band's gradient frame is 786 tall at top -495.24,
   so 495px of real gradient sits above the 222 strip and the old bake simply cropped it. The render now
   carries 400 of those px and the box is the render's full height, keyed to
   --cx-band-h so the ratio holds at both breakpoints: 222 → 622 and 120 → 336.2. `object-fit: cover` then
   resolves to the same scale and the same crop as before (the height ratio drives it), which is why the
   visible strip is unchanged while the gap fills with gradient instead of flat colour.

/* the seam: the footer's edge casts onto the band as it lifts, so the two read as separate planes rather
   than two stacked sections. The rule was written long before it could work — the old page structure put
   the footer inside a clipped wrapper, which ate the shadow — and the shell's flatter structure is what
   finally lets it paint (decision 2026-09-17). It fades out about 72px into the band. */
.cx-footer { box-shadow: 0 18px 40px rgba(38, 38, 38, 0.45); }

/* hero sticker — 878:4987 · 284.842 × 195.033 at (1404, 255) → visual-relative (372, 118). Figma's exported
   vector (ellipse outline + text on path, 261.5 × 118.38 in the group's local frame), placed at native size
   and rotated back to the node's orientation. Nothing rebuilt. */
.cx-hero__sticker { position: absolute; left: 372px; top: 118px; width: 284.842px; height: 195.033px; pointer-events: none; z-index: 2; }
.cx-hero__sticker-svg { position: absolute; left: 50%; top: 50%; width: 261.501px; height: 118.382px; max-width: none; transform: translate(-50%, -50%) rotate(-18.7deg); }

@media (prefers-reduced-motion: no-preference) {
  .cx-hero__sticker { animation: cx-sticker-float 9s ease-in-out infinite; }
  @keyframes cx-sticker-float { 50% { transform: translateY(-6px) rotate(0.6deg); } }
}
@media (max-width: 1359px) { .cx-art__script, .cx-hero__sticker { display: none; } }
/* …except in the band, which is a full-bleed strip with nothing set over it, so the script reads there
   the way it does on a wide screen. The mask is placed against a 1920 stage (left: calc(50% - 960px + x),
   top: 278.4) and the band's art is only 336 tall on a phone, so the script fell below the crop — it is
   anchored to the art's own box here instead. The other three gradients are NOT switched on: on a wide
   screen the script fills the margins either side of the centred artwork, and a 390 viewport has no
   margins, so it lands on the copy instead. See the note in NOTES for the evidence. */
/* …and then placed per section below 1360. The mask is the Callix waveform, so the script reads
   through the mark; its placement is an inline style against a 1920-wide stage, which is why every
   `left`/`top` here needs !important to land. Each gradient gets the offset that puts a dense part of
   the waveform inside its own crop, because those crops are all different shapes on a phone.

   Two of them carry copy, so the script is dimmed there: at full strength it ran through the closing
   CTA's subtitle and the dark section's second title line and made both hard to read. 0.3 keeps it as
   texture. The band and the carousel have nothing over them and stay at full strength. */
@media (max-width: 1359px) {
  .cx-band .cx-art__script,
  .cx-how .cx-art__script,
  .cx-cta .cx-art__script,
  .cx-dark .cx-art__script { display: block; }

  .cx-band .cx-art__mask { left: 0 !important; top: 0 !important; }
  .cx-how  .cx-art__mask { left: 0 !important; top: -300px !important; }   /* a 200px banner */
  .cx-cta  .cx-art__mask { left: 0 !important; top: -420px !important; opacity: 0.3; }
  .cx-dark .cx-art__mask { left: 0 !important; top: -300px !important; opacity: 0.3; }
}
.cx-art__render { position: absolute; display: block; max-width: none; }   /* baked base + groups + veil */
.cx-art__render.cover { left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; }   /* full-bleed hosts and the slide: scale/crop with the viewport */
