/* =========================================================================
   ChoreoSync site — STAGE (2026-08-26)
   Loaded after cs-pop.css. This is the energy pass: poster typography, the
   VIDEO / AUDIO / SYNC / POST system, dancers that break their boxes, and
   the small rhythm marks (ticks, bars, playheads) that run under the page.

   Rules this file keeps:
   · purple stays the only brand color — the secondary color on the page comes
     from what the dancers are WEARING, not from a palette
   · no gradient blobs, no glass stacks, no neon for its own sake
   · every animation explains something (a playhead moves, a marker locks);
     decoration that moves is removed, not tuned
   · all of it collapses under prefers-reduced-motion
   ========================================================================= */

/* ---------- Chillax, self-hosted -------------------------------------
   One variable file, 55 KB, served from this repo — no third-party font
   host in the critical path and nothing to break if Fontshare moves. */
@font-face {
    font-family: 'Chillax';
    src: url('../fonts/Chillax-Variable.woff2') format('woff2-variations');
    font-weight: 200 700;
    font-style: normal;
    font-display: swap;
}

:root {
    --cs-display-font: 'Chillax', 'Manrope', 'Helvetica Neue', Arial, sans-serif;

    /* ── THE APP-STORE LANGUAGE (Carlos, 2026-08-26) ──────────────────────
       Lifted from the store screenshots he set as the reference: AUTO SYNC /
       YOUR DANCE, PERFECTLY SYNCED, EDIT IT YOUR WAY, MIX YOUR AUDIO,
       EXPORT & SHARE. Four things carry those frames, and all four are now
       tokens rather than one-off values:

         1. the second headline line runs a MAGENTA→VIOLET gradient and glows
         2. the phone sits in a lit DEVICE FRAME with a floor reflection
         3. LIGHT RAYS fan out from behind it
         4. CYAN is the second accent — "2. AUDIO." is cyan in the reference,
            and the rays run purple through magenta into blue

       ⚠ Cyan is a SECOND accent, not a second brand colour. It appears on the
       audio step and inside the rays. It never becomes a button, a link or a
       heading on its own — #6c43ce is still the brand. */
    --cs-magenta: #E93BD4;
    --cs-magenta-rgb: 233, 59, 212;
    --cs-cyan: #38D2F0;
    --cs-cyan-rgb: 56, 210, 240;
    /* The headline gradient, defined once so five headlines cannot drift. */
    --cs-accent-grad: linear-gradient(96deg, #F45CE0 0%, #C34BEA 46%, #8B5CF6 100%);

    /* ── PRODUCT SHOT SIZING (Carlos, 2026-08-26) ────────────────────────
       THE RULE: background effects may bleed, PRODUCT UI MAY NOT. Confetti,
       glows and glints are free to run past a container or off the viewport.
       A screenshot is product evidence — it stays readable, intentional and
       contained, and no important control is ever cropped off an edge.

       Two families, because one number cannot serve both:

       · UI CROPS are landscape (880x275, 880x437) and already tightly framed
         by whoever took them. They cap by WIDTH. Anything past ~620px is
         upscaling a crop past its own pixels, which looks soft AND huge.

       · DEVICE CAPTURES are portrait 660x1434 — a 2.17:1 tower. Capping those
         by width is what produced the billboards: 500px wide is 1086px TALL.
         They cap by HEIGHT, and the width falls out of the aspect ratio.

       Every product image also gets object-fit:contain. These are screenshots,
       not photography — cover would crop controls off an edge, which is the
       one thing this whole block exists to prevent. */
    /* ONE BAND RHYTHM. Every major section pads by this, so the page has a
       beat instead of six values that are all nearly the same (3.25 / 3.5 / 4 /
       4.25rem, which reads as sloppy rather than varied). A section that wants
       to be louder gets a bigger PRODUCT, not a bigger gap. */
    --cs-band: clamp(3rem, 5.5vw, 4.5rem);
    /* ⚠ A framed device drops a floor reflection ~46px below its own box (see
       .cs-device::after). Anything sitting under one has to clear that or the
       glow lands on the text. */
    --cs-device-floor: 3rem;

    --cs-shot-ui-max: 620px;      /* landscape UI crops, by width  */
    --cs-shot-dev-max: 560px;     /* portrait device captures, by height */
}

/* Applied to every product image on the site. Per-component rules below only
   ever change the CAP — never the containment. */
/* ⚠ One deliberate exclusion: .cs-device > img. A framed capture is sized by
   HEIGHT against the bezel, and max-width:100% would fight that. Nothing is
   cropped — the device shrink-wraps the image.
   (.cs-rail-art is gone: the rail carries no pictures at all now.) */
.cs-track img {
    object-fit: contain;
    max-width: 100%;
}

/* ---------- Poster typography ----------------------------------------
   Headlines are set to be READ ACROSS A ROOM: big, tight, left-aligned by
   default. OLMO centers everything; centering is now the exception. */
h1, h2, h3,
.cs-display, .cs-rhythm, .cs-poster {
    font-family: var(--cs-display-font);
    font-weight: 600;
    letter-spacing: -.03em;
}

.cs-poster {
    font-size: clamp(2.4rem, 6vw, 5.2rem);
    line-height: .94;
    color: #fff;
    margin: 0 0 1.25rem;
    text-transform: uppercase;
}
/* The second line of every poster headline. Flat #8B5CF6 before; the store
   frames run a magenta→violet gradient with a bloom behind it, and that one
   change is most of why they read as a launch and the site read as a template.

   ⚠ background-clip:text needs a TRANSPARENT fill to show through, so the
   colour is set and then cleared. The @supports guard keeps a solid magenta
   for anything that cannot clip — never leave the fallback as transparent or
   the word disappears. */
.cs-poster .accent,
.cs-statement .accent {
    color: var(--cs-magenta);
    position: relative;
}
@supports (-webkit-background-clip: text) or (background-clip: text) {
    .cs-poster .accent,
    .cs-statement .accent {
        background: var(--cs-accent-grad);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        -webkit-text-fill-color: transparent;
    }
}
/* The bloom under the word. A blurred copy of the same gradient, drawn behind
   the glyphs — text-shadow cannot bloom a clipped-gradient fill. */
.cs-poster .accent::before,
.cs-statement .accent::before {
    content: "";
    position: absolute;
    inset: 12% -6% 18% -6%;
    background: var(--cs-accent-grad);
    filter: blur(26px);
    opacity: .42;
    z-index: -1;
    pointer-events: none;
}
.cs-poster .quiet  { color: var(--cs-muted); }

.cs-display { font-family: var(--cs-display-font); font-weight: 600; line-height: .95; }

/* Left is the default. `title-01` (OLMO's centered section head) is kept for
   the deeper, copy-heavy sections; the loud ones use .cs-head. */
.cs-head { max-width: 22ch; margin-bottom: 2.5rem; }
.cs-head p { max-width: 46ch; font-size: 1.08rem; }
.cs-head-wide { max-width: 34ch; }

/* A short statement that owns a full band on its own. */
.cs-statement {
    font-family: var(--cs-display-font);
    font-weight: 600;
    font-size: clamp(2rem, 5.4vw, 4.2rem);
    line-height: .98;
    letter-spacing: -.035em;
    text-transform: uppercase;
    color: #fff;
}

/* ---------- THE HERO LEAD --------------------------------------------
   ⚠ THE WHOLE .cs-vasp FAMILY IS GONE (2026-08-27): .cs-vasp, .cs-vasp-word,
   .is-sync and its beat, .is-audio, .cs-vasp-break, .cs-vasp-note,
   .cs-vasp-row. That was the flow set as a smaller second line under the
   headline. Carlos moved the flow INTO the headline ("REDO HERO TEXT. SYNC.
   EDIT. POST."), so the hero has one typographic statement instead of two
   competing ones, and the words are now plain .cs-poster with .accent on the
   sync step. Nothing else referenced any of it — checked before deleting.

   ⚠ .cs-hero-lead IS BACK, and the tombstone that used to sit here saying it
   was removed on 08-26 is withdrawn. What Carlos removed then was the COPY —
   "Post cleaner dance videos, faster.", a second sales claim under a headline
   that was already one. The headline is three bare verbs now and this is the
   line that says what they add up to: "All the tools you need. One simple app."

   Sized between the poster and .cs-hero-note deliberately: big enough to be
   read as part of the statement, small enough that it never competes with the
   three words above it. */
.cs-hero-lead {
    font-size: clamp(1.05rem, 1.5vw, 1.35rem);
    line-height: 1.45;
    color: var(--cs-ink);
    max-width: 30ch;
    margin: 0 0 1.9rem;
}

.cs-hero-note {
    font-family: 'Inter', sans-serif;
    font-size: .95rem;
    color: var(--cs-muted);
    margin: 1.1rem 0 0;
}

/* ⚠ .cs-hero-note IS UNUSED AS OF 2026-08-27. Carlos pulled the line it styled
   ("Free plan available · Reels, TikTok & Shorts ready" — "leftover SaaS copy").
   The RULE is kept, not deleted: it is three declarations, the slot under the
   hero buttons is one somebody asks for again, and cs_cta_note() still exists
   for the closing band. Delete it if it is still unused a release from now. */

/* ---------- Rhythm marks ---------------------------------------------
   The motif: ticks, bars and a marker. Drawn in CSS so they cost nothing
   and can sit under any block. */
.cs-ticks {
    display: block;
    height: 22px;
    background-image: repeating-linear-gradient(90deg,
        rgba(255, 255, 255, .30) 0 1px,
        transparent 1px 14px);
    -webkit-mask-image: linear-gradient(90deg, #000 0%, #000 62%, transparent 100%);
    mask-image: linear-gradient(90deg, #000 0%, #000 62%, transparent 100%);
}
.cs-ticks-beat {
    background-image:
        repeating-linear-gradient(90deg, rgba(139, 92, 246, .85) 0 2px, transparent 2px 56px),
        repeating-linear-gradient(90deg, rgba(255, 255, 255, .22) 0 1px, transparent 1px 14px);
}

/* An audio bar rail — thin, quiet, and it belongs to the section it edges. */
.cs-bars { display: flex; align-items: flex-end; gap: 3px; height: 34px; }
.cs-bars i {
    display: block; width: 3px; border-radius: 2px;
    background: linear-gradient(180deg, #8B5CF6, rgba(139, 92, 246, .35));
}

/* ---------- HERO ------------------------------------------------------
   Two columns that do different jobs: type on the left, a stage on the
   right. The stage is allowed to overflow its column; the SECTION clips,
   so nothing ever reaches the viewport edge and no page scrolls sideways. */
.cs-hero {
    position: relative;
    overflow: hidden;
    padding-bottom: 40px;
}

/* ⚠ THE TOP PAD IS ON A COMPOUND SELECTOR, DELIBERATELY. responsive.css — a
   template file we do not edit — carries `#hero-16 { padding-top: 80px }` in
   two media queries (<=767px and 768-991px). An ID beats a class, so a plain
   `.cs-hero { padding-top }` is silently overridden at those widths no matter
   how late our stylesheet loads. `#hero-16.cs-hero` outranks it everywhere, so
   the three values below are the only ones in play.

   ⚠ THREE CORRECTIONS, IN ORDER, so the number never reads as arbitrary
   (all 2026-08-26): it started at 118px; "move this up" took it to 84px;
   "bring the hero section down about 100px" took it to 184px; "put the hero
   section up 50px" leaves it at 134px (118px below 992px). The floor is
   ~76px — under that the eyebrow starts running into the nav on a 1280-wide
   window. */
#hero-16.cs-hero { padding-top: 134px; }
.cs-hero-txt { position: relative; z-index: 3; }

/* 45 copy / 55 product. The markup is col-lg-6 + col-lg-6; overriding here
   keeps the Bootstrap grid intact (and the mobile stacking with it) while
   giving the stage the extra tenth the product deserves. */
@media (min-width: 992px) {
    .cs-hero > .container > .row > .order-first { flex: 0 0 45%; max-width: 45%; }
    .cs-hero > .container > .row > .order-last  { flex: 0 0 55%; max-width: 55%; }
}
.cs-hero .cs-poster { margin-bottom: .5rem; }
.cs-hero .btns-group { margin-top: .5rem; }
.cs-hero .advantages { margin-top: 1.6rem; }

.cs-hero-stage {
    position: relative;
    /* Holds the framed phone and its floor reflection. Sized from the phone:
       .cs-flow-f's height + the bezel + the reflection + air. Too small and
       .cs-hero { overflow:hidden } takes the top off the device.
       ⚠ It used to reserve a band at the bottom for the waveform track; that
       came out on 2026-08-26 and this tightened with it. */
    /* ⚠ GROWS WITH THE PHONE, ALWAYS. Carlos, 2026-08-27: "the phone could be 5 to
       10% larger. It is the proof." That is .cs-flow-f's height, and this has to move
       by the same factor — .cs-hero has overflow:hidden, so a device taller than its
       stage gets its top sliced off. Both numbers are +8%: 770 -> 830, 84vh -> 90vh. */
    min-height: min(90vh, 830px);
    /* The STAGE leans right and out of its column so the glow and the confetti
       can bleed. The screen inside it is centred and nowhere near the edge, so
       nothing of the product goes with them. */
    margin-right: clamp(-7rem, -4vw, 0rem);
}

/* ⚠ .cs-hero-light is RETIRED. It was a second circular wash sitting under
   cs_glow('screen') — two competing circles in one section, which the glint
   brief rules out. The glow is the hero's one light source now; it is the same
   four-part object every product moment uses. */
.cs-hero-light { display: none; }

/* ⚠ .cs-hero-dancer is GONE (2026-08-26) along with the cut-out itself. The
   screen below took its role as the subject and its size with it. */

/* THE SUBJECT. No phone frame: this is the screen itself, with the corner
   radius the device already has.

   ⚠ SIZED BY HEIGHT, not width. At width:62%/max-width:340px this rendered
   340x739 inside a 620px stage and the top and bottom of the capture were
   being clipped by .cs-hero { overflow:hidden } — the editor's toolbar off
   one end, the filmstrip off the other. The hero screenshot is allowed to be
   the largest on the site, but all of it has to be on the page.

   Sits slightly ABOVE centre so the track along the bottom of the stage has
   its own room and never crosses the capture. */
/* ⚠ THE HERO NO LONGER CROPS (2026-08-26). It used to render the capture
   larger than the stage and clip the bottom off it — defensible for a bare
   screenshot, wrong the moment the phone got a frame: a bezel sliced in half
   is not a window into the product, it is a broken device. Every reference
   frame Carlos set shows the WHOLE phone standing in its own light, and the
   floor reflection under .cs-device now does the job the crop was doing —
   seating the product on the page instead of floating it.

   So: no overflow, no measured crop, and no exception left to the site-wide
   rule that product UI is never cut. */
.cs-hero-screen {
    position: absolute;
    left: 50%;
    top: 47%;
    transform: translate(-50%, -50%);
    width: auto;
    margin: 0;
    z-index: 2;
    text-align: center;
}
/* ⚠ THE SAME z-index AGAIN, ON THE COMPOUND SELECTOR, AND IT IS NOT A DUPLICATE.
   cs-pop.css raises this element out of the confetti field with
   `.cs-hero-stage > .cs-hero-screen { z-index: 1 }`, which at 0-2-0 outranks the
   0-1-0 rule above — so the device was really sitting at 1, the SAME layer as
   .cs-hero-ring, and only stayed on top because the figure happens to come after
   the ring in the markup. Reorder the stage and the glass lands on the product.
   This restates 2 at matching specificity, later in the cascade, so the device
   is above the light because it is declared to be. */
.cs-hero-stage > .cs-hero-screen { z-index: 2; }

/* ⚠ A capture inside a .cs-device draws NO chrome of its own — the bezel IS
   the chrome. The radius/outline/shadow that used to sit here stacked a second
   border inside the first and read as a phone inside a phone. */
/* ---------- The flow, playing in the hero phone ------------------------
   Four frames stacked in the device, cross-fading in order. No JS: one
   keyframe, four literal delays.

   ⚠ The WRAPPER carries the size, not the images. The frames are absolutely
   positioned so they occupy the same box; if the box collapsed to zero the
   phone would render as an empty bezel, so the height and the aspect ratio
   both live here. All four sources are 660x1431, so one ratio is correct for
   every frame — check that before adding another. */
/* ⚠ THE FIRST FRAME SIZES THE BOX; the other three lie on top of it. The
   earlier version gave .cs-flow an aspect-ratio and made every frame
   object-fit:cover — and cover CROPS the moment the box's real ratio differs
   from the image's by even a rounding error, which is what took the top off
   the phone. Now the box has no ratio of its own: it is exactly as tall and
   wide as frame one, and the rest are contained, so nothing can be cut. */
.cs-hero-screen .cs-flow {
    position: relative;
    display: block;
    border-radius: 1.55rem;
    overflow: hidden;
    line-height: 0;
}
.cs-flow-f {
    display: block;
    width: auto;
    max-width: none;
    /* +8% (Carlos, 2026-08-27: "let it dominate a little more"). ⚠ .cs-hero-stage's
       min-height is measured off THIS — move one, move both. */
    height: min(78vh, 715px);
    border-radius: inherit;
}
/* Frames 2-4 stack on frame one and are CONTAINED, never cropped. */
.cs-flow-f:not(:first-child) {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    opacity: 0;
}
/* THE RESTING STATE, and it must always be a VISIBLE frame. This is what shows
   with reduced motion, before the animation's first tick, and if the keyframes
   fail for any reason at all. */
.cs-flow-f:first-child { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
    /* ⚠ THIS ANIMATION ONCE LEFT THE PHONE EMPTY. Two bugs, both worth naming
       because either alone is enough to blank it:

       1. `animation: … both` plus a per-frame delay. `backwards` fill makes a
          frame adopt its 0% keyframe DURING the delay — so with 0% at
          opacity:0 every frame that had not started yet was forced
          transparent, and the rule `.cs-flow-f:first-child { opacity: 0 }`
          that used to sit in here removed the one frame that was left. Fill
          mode is now NONE: before its delay a frame simply uses the opacity
          from the rules above, which is 1 for frame one and 0 for the rest.
          That is exactly the resting state we want.

       2. `animation-delay: calc(var(--i) * 4s)`. If that calc fails to
          resolve, the delay silently falls back to 0s, all four frames run in
          lockstep, and the phone is blank for the 12 seconds of every 16 when
          they are all at opacity 0 together. The delays are plain literals on
          nth-child now — nothing to resolve, nothing to fail.

       ⚠ And the windows OVERLAP on purpose: a frame holds to 25% and fades out
       over 25–28%, while the next fades in from its own 0–3%. Fade one out at
       exactly the moment the next starts and you get a black flash between
       every step. */
    /* ⚠ THE OUTGOING FRAME HOLDS UNTIL THE INCOMING ONE IS FULLY IN. Frames
       are stacked in DOM order, so frame k+1 paints ON TOP of frame k: if k
       fades out while k+1 fades in, both are semi-transparent over black at
       the crossover and the phone visibly dims every four seconds. Holding k
       opaque to 28% — past the point k+1 reaches full at its own 3% — means
       the fade-out happens underneath an opaque frame and is never seen.

       ⚠ And frame one carries a NEGATIVE delay so it is already at full
       opacity on the first painted frame. At 0s it would otherwise be at the
       0% keyframe, which is transparent: a black phone for half a second on
       every page load. */
    /* FOUR frames, so each holds a QUARTER of the round: in by 3.75%, hold to
       28.75%, out by 32.5%. The outgoing frame stays opaque past the point the
       next has fully faded in, so its fade-out happens beneath an opaque frame
       and is never seen.

       ⚠ THESE ARE 1/N WINDOWS AND N IS NOW 4 (screen 0 came out 2026-08-27).
       Every percentage is the old five-frame one x 1.25. Leave them at the
       five-frame values with four frames and the phone goes blank for a fifth
       of every round. */
    @keyframes cs-flow-cycle {
        0%     { opacity: 0; }
        3.75%  { opacity: 1; }
        28.75% { opacity: 1; }
        32.5%  { opacity: 0; }
        100%   { opacity: 0; }
    }
    /* 9s round, 2.25s a frame. ⚠ DELAYS ARE DERIVED, NOT TYPED: each is
       (frame index x round/4) minus the 3.75% fade-in, so frame ONE is already
       opaque on the first painted pixel and every handover still overlaps.

       ⚠ THE STEP IS UNCHANGED AT 2.25s — 11.25/5 and 9/4 are the same number,
       which is why these four delays are the same as the first four were. That
       is a coincidence of this particular edit, NOT a rule: recompute them from
       round/N the next time the count moves, do not assume they carry over. */
    .cs-flow-f { animation: cs-flow-cycle 9s ease-in-out infinite; }
    .cs-flow-f:nth-child(1) { animation-delay: -0.34s; }
    .cs-flow-f:nth-child(2) { animation-delay:  1.91s; }
    .cs-flow-f:nth-child(3) { animation-delay:  4.16s; }
    .cs-flow-f:nth-child(4) { animation-delay:  6.41s; }
}

/* ⚠ .cs-hero-track / .cs-hero-wave / .cs-playhead are GONE (2026-08-26,
   Carlos: "on the hero i feel we can get rid of that moving slider"). They
   drew a waveform strip with a sweeping playhead across the bottom of the
   stage. The phone is the hero's one product object now.

   cs_wave() in inc-viz.php and .cs-ticks in cs-pop.css are left alone: the
   hero was their only caller on this page, but both are general helpers and
   other sections may want them. */

@media (prefers-reduced-motion: no-preference) {

    /* 1.02 drift, twelve seconds — a breath, not a bounce. */
    @keyframes cs-drift { 0%,100% { transform: scale(1); } 50% { transform: scale(1.022); } }
    .cs-drift { animation: cs-drift 12s ease-in-out infinite; }

    /* ⚠ THIS RULE ONCE KILLED THE HERO FLOW. It used to read
       `.cs-hero-screen.cs-drift img { animation: cs-drift-in … }`, from when
       the hero held ONE image. The flow frames are also `img` inside
       `.cs-hero-screen.cs-drift`, so it matched all four — and at specificity
       0-2-1 it beat `.cs-flow-f:nth-child(n)` at 0-2-0 and REPLACED the
       crossfade with a scale that never touches opacity. Frame one sat there
       and the phone looked dead.

       So the breath is on the DEVICE now, which is the right object anyway:
       bezel and screen move together instead of the screen swelling inside a
       fixed frame. Nothing in the hero targets a bare `img` any more — do not
       reintroduce a selector that does.

       ⚠ The figure itself still animates NOTHING: it is X-centred with a
       transform, and the generic .cs-drift would overwrite translateX(-50%)
       and throw it half its own width to the right. */
    @keyframes cs-drift-in {
        0%, 100% { transform: scale(1); }
        50%      { transform: scale(1.018); }
    }
    .cs-hero-screen.cs-drift { animation: none; }
}

/* ---------- Reveals ----------------------------------------------------
   Staggered, short, and only on the first paint of a block. WOW.js already
   ships with the template and handles the rest of the page; the hero does
   its own so it is not waiting on a script to look right. */
@media (prefers-reduced-motion: no-preference) {
    @keyframes cs-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
    .cs-reveal { animation: cs-rise .7s cubic-bezier(.2,.7,.3,1) both; }
    .cs-reveal-1 { animation-delay: .06s; }
    .cs-reveal-2 { animation-delay: .14s; }
    .cs-reveal-3 { animation-delay: .22s; }
}

/* ---------- Hero, smaller screens -------------------------------------
   RECOMPOSED, not shrunk. The stage keeps a real height and the capture is
   capped against the VIEWPORT height, so the whole screen is on the page on a
   phone exactly as it is on a desktop. The stage stops leaning out of its
   column here — with one column there is nothing to lean past, and a negative
   margin at this width is just horizontal overflow. */
@media (max-width: 991px) {
    /* The phone leads here, so this only has to clear the nav — the extra
       air was sized for a headline that no longer comes first. */
    #hero-16.cs-hero { padding-top: 92px; padding-bottom: 40px; }

    /* ⚠ THE PHONE GOES FIRST ON A PHONE (Carlos, 2026-08-26). The markup is
       copy-then-stage because that is the desktop reading order, and Bootstrap's
       .order-first / .order-last hold at every width — so stacked, the visitor
       got a screenful of type before seeing the product.

       ⚠ !important is REQUIRED, not laziness: Bootstrap writes its order
       utilities as `order:-1!important` / `order:6!important`, so a plain
       declaration here is silently ignored no matter how specific the selector.
       This is the one place on the site that needs it. */
    .cs-hero > .container > .row > .order-first { order: 2 !important; }
    .cs-hero > .container > .row > .order-last  { order: 1 !important; }

    /* Was margin-TOP, from when the stage sat below the copy. */
    /* ⚠ THE PHONE GOES BACK INTO FLOW HERE, and that is the whole fix.

       On desktop .cs-hero-screen is position:absolute, centred on a stage whose
       height comes from min-height — the device contributes NO height of its
       own. Stacked, that min-height is a floor with no copy beside it to
       justify it, so it renders as a screenful of dead black; but simply
       zeroing it collapsed the stage to nothing and the absolutely-positioned
       phone spilled over the headline underneath it.

       position:static gives the stage its height back FROM the device, so
       there is no void and no overlap. min-height:0 is only safe because of
       this rule — the two belong together, do not separate them. */
    .cs-hero-stage { min-height: 0; margin-right: 0; margin-bottom: 2rem; }
    /* ⚠ RELATIVE, NOT STATIC — z-index does nothing on a static box.

       This said `position: static` until 2026-08-31, and that is what faded the
       phone on every window under 992px: .cs-glow (z-index 0) and .cs-hero-ring
       (z-index 1) are both ABSOLUTE, and a positioned element with z-index >= 0
       paints over in-flow content no matter what z-index that content asks for.
       So the ring's glass — a translucent fill plus backdrop-filter: blur(3rem)
       — was being drawn ON TOP of the device: the whole capture read hazed,
       washed and purple, which is exactly the "light over product art" the rest
       of this file forbids.

       position:relative keeps every reason static was chosen — the box stays in
       flow, so the stage still takes its height from the device and there is
       neither a void nor an overlap — and it makes z-index work again.

       ⚠ left/top MUST BE RESET WITH IT. The desktop rule sets left:50%/top:47%
       for the absolute centring; on a relative box those are offsets, and
       leaving them in place shoves the phone half a stage to the right.

       ⚠ THE SELECTOR IS COMPOUND ON PURPOSE. cs-pop.css raises this element out
       of the confetti with `.cs-hero-stage > .cs-hero-screen { z-index: 1 }` at
       0-2-0; a bare `.cs-hero-screen` here is 0-1-0 and loses, media query or
       not — media queries add no specificity. Match it, and win on load order. */
    .cs-hero-stage > .cs-hero-screen {
        position: relative;
        left: auto;
        top: auto;
        z-index: 2;
        transform: none;
        display: block;
        margin: 0 auto;
    }

    .cs-flow-f { height: min(66vh, 560px); }
}
@media (max-width: 575px) {
    .cs-hero-stage { min-height: 0; }   /* see the 991px rule: the phone is in flow here */
    /* ⚠ width/bottom here were left over from the dancer composition, where
       the screen was a 38%-wide inset pinned to the bottom of the stage. It
       is the subject now and it is centred; sizing it by height is the only
       thing that keeps its controls on screen. */
    .cs-flow-f { height: min(60vh, 475px); }
    .cs-hero-lead { margin: 0 0 1.9rem; }
    /* One moving thing at most on a phone — and that one thing is the FLOW,
       which is the product story rather than decoration.
       ⚠ The device's breath is stood down at the BOTTOM of this file, not
       here: same specificity as the rule that turns it on, so it only wins if
       it comes later. See the end of the stylesheet. */
    .cs-drift { animation: none; }
}

/* ---------- Stages (how it works) -------------------------------------
   A row is a word and a picture, and the two swap sides down the page. No
   card, no border, no equal-height grid — the rhythm comes from the swap
   and from how far the picture is allowed to run. */
.cs-rail { position: relative; padding: var(--cs-band) 0; overflow: hidden; }
/* ⚠ NO max-width. "THREE STEPS. ONE APP." is set on ONE line (Carlos,
   2026-08-26, of the five-step wording it replaced) and 34rem forced it to
   wrap back into two. The size below is
   capped so the line fits a 1140 container without needing a nowrap, which
   would overflow instead of shrinking on a narrow window. */
/* ⚠ CENTRED (Carlos, 2026-08-27: "titles and text should be centered"). The eyebrow
   and the headline sat left while the three stations under them were centred, so the
   band read as two different layouts stacked. The SYNC band below it was already
   centred (.cs-sync-head) — this makes the rail agree with it.

   ⚠ THE EYEBROW NEEDS ITS OWN LINE. .cs-eyebrow is display:flex (glint, then words),
   and text-align does not move a flex container's children — exactly the same reason
   .cs-sync-head carries a justify-content rule. Set both or the dot stays left. */
.cs-rail-head { margin-bottom: 2.5rem; text-align: center; }
.cs-rail-head .cs-eyebrow   { justify-content: center; }
.cs-rail-head .cs-statement { font-size: clamp(1.75rem, 4.2vw, 3.3rem); }

/* THE RAIL. Three stations on one track — numbers and words, no pictures.

   Grid rather than flex so all columns are exactly equal and the track meets
   every ring dead centre; with flex, the longest sentence would drag its
   station off the line.

   ⚠ THE COLUMN COUNT IS A LITERAL AND IT TRACKS cs_flow()'s LENGTH. It is not
   derived from the item count on purpose: `repeat(var(--n), …)` computes to
   `none` — one full-width column, the whole rail in a stack — if the custom
   property ever fails to resolve, and it would fail silently. Three here, three
   in cs_flow(), and the walkthrough timing below assumes the same three. */
.cs-rail-track {
    /* Ring centre, measured from the top of a stop: its top margin + half the
       ring. The track and every node read this one number. */
    --cs-rail-y: 52px;
    position: relative;
    list-style: none;
    margin: 0; padding: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.25rem;
    /* Three equal columns across a full 1140 container leave each word marooned
       in 370px of its own. Held in, so the track reads as a rail with ends
       instead of a line running the width of the page. */
    max-width: 58rem;
    margin-inline: auto;
}
.cs-rail-track::before {
    content: "";
    position: absolute;
    left: 0; right: 0;
    top: calc(var(--cs-rail-y) - 1px);
    height: 2px;
    background: linear-gradient(90deg,
        rgba(var(--cs-primary-rgb), 0) 0%,
        rgba(var(--cs-primary-rgb), .55) 12%,
        rgba(var(--cs-primary-rgb), .55) 88%,
        rgba(var(--cs-primary-rgb), 0) 100%);
    pointer-events: none;
}

.cs-rail-stop {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

/* The station. Opaque, to punch a hole in the track behind it — matched to
   this band's own tone (canvas plus its purple lift), not the bare canvas. */
.cs-rail-node {
    position: relative;
    margin: 14px 0 1.5rem;
    width: 76px; height: 76px;
    display: grid; place-items: center;
    border-radius: 50%;
    background: #0b0912;
    z-index: 2;
}
/* ---------- The station ring ------------------------------------------
   ⚠ NOT .cs-glint-ring's flat bordered circle (Carlos, 2026-08-26: "the circle
   is not the right style"). This is the app's SUCCESS GLYPH — .cs-sheet-glyph
   in cdn/css/choreosync.css, the ring the editor draws around the green check.
   What makes it that ring and not a border:

     · the visible edge is a CONIC GRADIENT, not a border. border:0, and a
       ::before runs a purple→lavender→purple conic through a mask-composite
       cut-out, so the edge is brighter on two arcs and darker between them.
       A flat border is uniform and reads as a form field.
     · GLASS INSIDE — an off-centre radial highlight at 42% 38%, plus a
       backdrop blur, so the disc has a lit surface rather than a fill.
     · the ring PALETTE is four tokens, so a tone re-points four colours
       instead of restating the gradient.

   Values are the app's own; do not re-eyeball them off a screenshot. */
.cs-rail-node .cs-glint-ring {
    --cs-ring-1: #6e43cc;
    --cs-ring-2: #8155dc;
    --cs-ring-3: #aa82ef;
    --cs-ring-4: #d7c2ff;
    position: relative;
    isolation: isolate;
    display: grid;
    place-items: center;
    width: 100%; height: 100%;
    border: 0;
    border-radius: 50%;
    font-family: var(--cs-display-font);
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1;
    color: #fff;
    background: radial-gradient(circle at 42% 38%,
        rgba(255, 255, 255, .13) 0%,
        rgba(130, 105, 180, .09) 38%,
        rgba(41, 32, 65, .22) 70%,
        rgba(18, 15, 30, .28) 100%);
    box-shadow:
        0 0 8px rgba(166, 120, 255, .28),
        0 0 20px rgba(108, 67, 206, .18),
        inset 0 0 18px rgba(170, 135, 240, .08),
        inset 0 1px 0 rgba(255, 255, 255, .13);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
}
/* The luminous edge. mask-composite cuts the middle out of the conic, leaving
   a 2px lit rim — this is the whole trick, and it is why border:0 above. */
.cs-rail-node .cs-glint-ring::before {
    content: "";
    position: absolute;
    inset: -1px;
    border-radius: 50%;
    padding: 2px;
    background: conic-gradient(from 205deg,
        var(--cs-ring-1) 0deg,
        var(--cs-ring-2) 55deg,
        var(--cs-ring-3) 105deg,
        var(--cs-ring-4) 155deg,
        var(--cs-ring-3) 210deg,
        var(--cs-ring-2) 260deg,
        var(--cs-ring-1) 315deg,
        var(--cs-ring-1) 360deg);
    -webkit-mask:
        linear-gradient(#fff 0 0) content-box,
        linear-gradient(#fff 0 0);
    -webkit-mask-composite: xor;
    mask:
        linear-gradient(#fff 0 0) content-box,
        linear-gradient(#fff 0 0);
    mask-composite: exclude;
    filter:
        drop-shadow(0 0 2px rgba(221, 204, 255, .85))
        drop-shadow(0 0 6px rgba(163, 112, 245, .48))
        drop-shadow(0 0 12px rgba(108, 63, 227, .27));
    pointer-events: none;
}

/* ---------- The circle glints -----------------------------------------
   ⚠ THIS IS THE APP'S OWN GLINT, ported — not a lookalike. Source of truth is
   .cs-ring-glint-a / -b in cdn/css/choreosync.css, the pair that rides every
   success ring in the editor. Three things make it read as ours; drop any one
   and it stops looking like the app:

     1. PLACEMENT BY ROTATION, not by top/left. Each glint is anchored dead
        centre and pushed out to the orbit:
          translate(-50%,-50%) rotate(θ) translateY(-orbit)
        so animating θ alone carries it round the ring.
     2. A SHARP WHITE CORE inside a blurred blob. The blob is filter:blur();
        the ::after core has filter:none. Without the core it is a smudge.
     3. A AND B ARE NOT TWINS. A is larger and brighter at upper-left (-45deg),
        B is smaller, quieter and opposite (135deg). Equal glints read as a
        loading spinner.

   ⚠ orbit = HALF THE RING, and it sits beside the ring size for that reason —
   the two cannot be allowed to drift. The app says the same of its own. */
.cs-rail-node {
    /* ⚠ HALF THE RING. It sits next to the ring size on purpose — the app's
       own comment says the same of --cs-ring-orbit, and the two cannot drift. */
    --cs-ring-orbit: 38px;
    --cs-glint-pale-rgb: 218, 195, 255;
    --cs-glint-deep-rgb: 145, 95, 245;
}
.cs-ring-glint {
    position: absolute;
    top: 50%; left: 50%;
    border-radius: 50%;
    background: rgba(var(--cs-glint-pale-rgb), .95);
    filter: blur(3.2px);
    pointer-events: none;
    z-index: 3;
}
/* The sharp white core — 22% of its own glint, exactly as the app sizes it, so
   both cores match across the two sizes. NO FILTER. */
.cs-ring-glint::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 22%; height: 22%;
    transform: translate(-50%, -50%);
    background: #fff;
    border-radius: 50%;
    filter: none;
    z-index: 2;
}
.cs-ring-glint-a {
    width: 13px; height: 13px;
    box-shadow:
        0 0 7px rgba(255, 255, 255, .36),
        0 0 14px rgba(var(--cs-glint-pale-rgb), .40),
        0 0 24px rgba(var(--cs-glint-deep-rgb), .28),
        0 0 36px rgba(var(--cs-glint-deep-rgb), .18);
    /* The SETTLED pose. It is also what shows before a step's turn and under
       reduced motion, so it must be the resting position, not a start pose. */
    transform: translate(-50%, -50%) rotate(-45deg) translateY(calc(-1 * var(--cs-ring-orbit)));
}
.cs-ring-glint-b {
    width: 8.5px; height: 8.5px;
    opacity: .7;
    box-shadow:
        0 0 5px rgba(255, 255, 255, .30),
        0 0 10px rgba(var(--cs-glint-pale-rgb), .32),
        0 0 18px rgba(var(--cs-glint-deep-rgb), .22);
    transform: translate(-50%, -50%) rotate(135deg) translateY(calc(-1 * var(--cs-ring-orbit)));
}

.cs-rail-word {
    font-family: var(--cs-display-font);
    font-size: clamp(1.15rem, 2vw, 1.5rem);
    font-weight: 600;
    letter-spacing: -.02em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .45);
    margin: 0 0 .3rem;
}
.cs-rail-line { font-size: .92rem; color: var(--cs-muted); margin: 0; }

/* ⚠ margin-inline:auto IS REQUIRED, not decoration. text-align centres the LINES
   inside the box; the box itself is 70ch wide and stays hard against the left edge
   without it, so the copy reads centred-but-offset — which looks like a mistake
   rather than a choice. */
.cs-fineprint {
    color: var(--cs-muted);
    font-size: .92rem;
    max-width: 70ch;
    text-align: center;
    margin-inline: auto;
}

/* ---------- The walkthrough -------------------------------------------
   The three stations light in turn, 1 to 3, and repeat — the rail plays the
   flow rather than listing it. 6s round, 2s a step.

   ⚠ THE ROUND IS 2s x THE NUMBER OF STATIONS, AND THE KEYFRAME PERCENTAGES ARE
   1/N WINDOWS. That is the whole contract: a station's lit window has to be its
   share of the round, or the chase leaves a hole. Going 5 -> 3 (2026-08-27) is
   why every percentage below moved by 5/3 and the round went 10s -> 6s; the
   absolute timing of a step is unchanged, there are just fewer of them. Each step's glints spin
   twice round its ring and settle as it lights: the same 720deg settle the
   app's success rings do, with the app's own easing.

   ⚠ IT RAN BACKWARDS ONCE, and the cause is worth keeping. The delays were one
   shared rule for the ring, the word AND the glints:
       …:nth-child(2) .cs-glint-ring,
       …:nth-child(2) .cs-rail-word,
       …:nth-child(2) .cs-rail-node::before { animation-delay: -1.4s, 2s; }
   The glints carried TWO animations and needed two delays. The ring and the
   word carry ONE, so they silently took the FIRST value — the -1.4s meant for
   the spin. Every later step got a bigger negative delay, so step 5 lit first
   and the whole sequence ran in reverse. Selectors that need different delay
   LISTS must be different rules. Never share one.

   ⚠ Delays are LITERALS, never calc(var(--i) * 2s): if the calc fails to
   resolve it silently becomes 0s, every step lights at once, and the sequence
   is gone with no error anywhere. That blanked the hero flow once already. */
@media (prefers-reduced-motion: no-preference) {
    @keyframes cs-step-ring {
        0%, 3.33% { border-color: rgba(var(--cs-primary-rgb), .85);
                    box-shadow: 0 0 18px rgba(var(--cs-primary-rgb), .45),
                                inset 0 0 18px rgba(var(--cs-primary-rgb), .25);
                    color: rgba(255, 255, 255, .70); }
        11.67%, 30% { border-color: #fff;
                    box-shadow: 0 0 10px rgba(var(--cs-primary-rgb), .9),
                                0 0 34px 6px rgba(var(--cs-primary-rgb), .55),
                                inset 0 0 24px rgba(var(--cs-primary-rgb), .5);
                    color: #fff; }
        40%, 100% { border-color: rgba(var(--cs-primary-rgb), .85);
                    box-shadow: 0 0 18px rgba(var(--cs-primary-rgb), .45),
                                inset 0 0 18px rgba(var(--cs-primary-rgb), .25);
                    color: rgba(255, 255, 255, .70); }
    }
    @keyframes cs-step-word {
        0%, 3.33%   { color: rgba(255, 255, 255, .45); }
        11.67%, 30% { color: #fff; }
        40%, 100%   { color: rgba(255, 255, 255, .45); }
    }
    /* 720deg spent in the first slice of the step's window; the rest is the
       crawl into place. Straight from the app's cs-glint-orbit-a / -b. */
    @keyframes cs-rail-glint-a {
        0%        { transform: translate(-50%, -50%) rotate(-765deg) translateY(calc(-1 * var(--cs-ring-orbit))); }
        36.67%, 100% { transform: translate(-50%, -50%) rotate(-45deg)  translateY(calc(-1 * var(--cs-ring-orbit))); }
    }
    @keyframes cs-rail-glint-b {
        0%        { transform: translate(-50%, -50%) rotate(-585deg) translateY(calc(-1 * var(--cs-ring-orbit))); }
        36.67%, 100% { transform: translate(-50%, -50%) rotate(135deg)  translateY(calc(-1 * var(--cs-ring-orbit))); }
    }

    .cs-rail-stop .cs-glint-ring   { animation: cs-step-ring 6s ease-in-out infinite; }
    .cs-rail-stop .cs-rail-word    { animation: cs-step-word 6s ease-in-out infinite; }
    .cs-rail-stop .cs-ring-glint-a { animation: cs-rail-glint-a 6s cubic-bezier(.05,.85,.12,1) infinite; }
    .cs-rail-stop .cs-ring-glint-b { animation: cs-rail-glint-b 6s cubic-bezier(.05,.85,.12,1) infinite; }

    /* ONE animation each, so ONE delay each, and every element in a step
       shares the same value. Ascending, so it plays 1 → 3. */
    .cs-rail-stop:nth-child(1) .cs-glint-ring,
    .cs-rail-stop:nth-child(1) .cs-rail-word,
    .cs-rail-stop:nth-child(1) .cs-ring-glint { animation-delay: 0s; }
    .cs-rail-stop:nth-child(2) .cs-glint-ring,
    .cs-rail-stop:nth-child(2) .cs-rail-word,
    .cs-rail-stop:nth-child(2) .cs-ring-glint { animation-delay: 2s; }
    .cs-rail-stop:nth-child(3) .cs-glint-ring,
    .cs-rail-stop:nth-child(3) .cs-rail-word,
    .cs-rail-stop:nth-child(3) .cs-ring-glint { animation-delay: 4s; }

    /* ---------- The success ring, post-checkout --------------------------
       The SAME orbit the rail runs, played ONCE. /welcome/ mounts a lone
       .cs-rail-node (no .cs-rail-stop around it, which is where the rail's
       animation is scoped), so the glints would otherwise sit frozen in their
       settled pose and the ring would never light.

       ⚠ ONCE, NOT INFINITE — and this is the whole difference between the two
       uses. On the rail the loop is ambient: three stations taking turns,
       forever. Here it is an EVENT — somebody just paid, the lights spin twice
       round and settle, and then the page is still. A ring that re-fires every
       few seconds behind a receipt turns a moment into a fidget.

       `both` holds the settled end state so nothing snaps back when it stops.
       Reused keyframes, no new ones: cs-rail-glint-a/-b spend 720deg in the
       first 36.67% and crawl into place over the rest, so the spin lands in
       ~0.6s and the ring is at rest well before the eye leaves the headline.

       ⚠ 1.68s, NOT 2.8s — AND THAT IS THE SAME SPEED IT ALWAYS WAS. These
       keyframes are the RAIL's, and their percentages are a 1/N window of the
       rail's round. When the rail went 5 stations -> 3 (2026-08-27) every
       percentage grew by 5/3, so this duration had to shrink by 3/5 to keep the
       ring doing the same thing in the same wall-clock time: 2.8 x 3/5 = 1.68.
       Spin still ends at 0.616s, the ring still lights 0.196s-0.504s. If the
       rail's length ever changes again, rescale this the same way — or give
       /welcome/ its own keyframes and stop sharing. */
    .cs-pricing .cs-rail-node .cs-glint-ring   { animation: cs-step-ring 1.68s ease-in-out 1 both; }
    .cs-pricing .cs-rail-node .cs-ring-glint-a { animation: cs-rail-glint-a 1.68s cubic-bezier(.05, .85, .12, 1) 1 both; }
    .cs-pricing .cs-rail-node .cs-ring-glint-b { animation: cs-rail-glint-b 1.68s cubic-bezier(.05, .85, .12, 1) 1 both; }
}

/* Three across is the point, and three narrow columns survive far longer than
   five did — the 900px break that dropped five to three is GONE, because three
   IS the base now. The track goes when it stops joining every station; the
   numbers never go. */
@media (max-width: 900px) {
    .cs-rail-track { gap: 2rem 1rem; }
}
@media (max-width: 520px) {
    .cs-rail { padding: 2.5rem 0; }
    /* Three columns on a 375px phone is ~105px a station, and "Effects, colors,
       and more." breaks into five lines of nothing in that. Two columns. */
    .cs-rail-track { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.75rem 1rem; }
    /* ⚠ AN ODD COUNT IN TWO COLUMNS leaves the last one alone in a half row,
       hard against the left edge, reading as a mistake. Span it and centre it.
       This was :nth-child(5) when the flow had five steps — it is the LAST stop
       that needs it, so it moves whenever cs_flow() changes length. */
    .cs-rail-stop:nth-child(3) { grid-column: 1 / -1; }
    /* Two columns cannot be joined by one horizontal line. */
    .cs-rail-track::before { display: none; }
    .cs-rail-head { margin-bottom: 1.75rem; }
    .cs-rail-node { margin-bottom: 1rem; }
}

/* ---------- SYNC ------------------------------------------------------
   A band that gives the two tracks room. Head on the left, tracks below and
   wider than the head so the eye lands on the UI, not the copy. */
/* MOMENT 2 of 3. The loudest band on the page: a centred statement over the
   two tracks at their NATIVE 880px, which is the largest any UI crop is shown
   here. Centred deliberately — every other section on this page was headline
   left / picture right, and the repetition was the complaint. */
.cs-sync { position: relative; padding: var(--cs-band) 0; overflow: hidden; }
.cs-sync::before {
    content: "";
    position: absolute; inset: 0;
    background: radial-gradient(60% 60% at 78% 30%, rgba(var(--cs-primary-rgb), .20) 0%, rgba(var(--cs-primary-rgb), 0) 70%);
    pointer-events: none;
}
.cs-sync-head {
    position: relative;
    max-width: 46rem;
    margin: 0 auto 2.5rem;
    text-align: center;
}
.cs-sync-head .cs-eyebrow { justify-content: center; }
.cs-sync-head p { max-width: 46ch; color: var(--cs-body); margin: 1rem auto 1.75rem; }

.cs-tracks { position: relative; display: grid; gap: 1.75rem; }
.cs-track { margin: 0; }
.cs-track figcaption {
    /* matches the image cap above so the numbering tracks the art */
    font-family: 'Inter', sans-serif;
    font-size: .74rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
    color: var(--cs-muted);
    margin-bottom: .5rem;
    /* The caption is centred over its own capped image, not over the whole
       container, so the numbering tracks the art at every width. */
    width: min(100%, 880px);
    margin-inline: auto;
    display: flex; align-items: center; gap: .55rem;
}
/* ⚠ CENTRED AND CAPPED, and the centring is load-bearing: .cs-tracks-head is
   pinned at left:50% of .cs-tracks because the marker sits at ~49.9% of each
   capture. Centre the images and 50% of the container is still 50% of the
   image, so the annotation keeps pointing at the marker. Left-align them or
   give the two different widths and the annotation starts pointing at nothing.
   These are 880px crops — 100% of a 1140px container was upscaling them. */
.cs-track img {
    display: block;
    /* 880px is these captures' NATIVE width — the largest they go without
       upscaling, and the most product this page shows anywhere. */
    width: min(100%, 880px);
    height: auto;
    margin-inline: auto;
    border-radius: .8rem;
    border: 1px solid rgba(255, 255, 255, .10);
    box-shadow: 0 22px 50px rgba(0, 0, 0, .55);
}

/* The annotation head — outside the strips, spanning both. */
.cs-tracks-head {
    /* 50%: MEASURED from the two captures — the marker in wizard-mark.webp is at
       49.9% of its width and the one in wizard-finetune.webp at 49.8%. If either
       capture is replaced, re-measure and move this, or the annotation will point
       at nothing and the animation will be a lie. */
    position: absolute; top: 1.4rem; bottom: 0; left: 50%;
    width: 2px;
    background: linear-gradient(180deg, rgba(255,255,255,0), #fff 10%, #fff 90%, rgba(255,255,255,0));
    box-shadow: 0 0 18px rgba(var(--cs-primary-rgb), .9);
    z-index: 2;
}
.cs-tracks-head i {
    position: absolute; left: 50%; top: 50%;
    width: 13px; height: 13px; margin: -6.5px 0 0 -6.5px;
    border-radius: 50%; background: #fff;
    box-shadow: 0 0 0 5px rgba(var(--cs-primary-rgb), .32), 0 0 22px 6px rgba(var(--cs-primary-rgb), .8);
}
.cs-tracks-lock {
    position: absolute; right: 0; top: -2.6rem;
    display: inline-flex; align-items: center; gap: .55rem;
    font-family: 'Inter', sans-serif; font-size: .8rem; font-weight: 700;
    letter-spacing: .14em; text-transform: uppercase; color: #b79dff;
}
.cs-tracks-lock em { font-style: normal; }

.cs-sync-foot {
    margin-top: 2.5rem;
    font-family: var(--cs-display-font);
    font-weight: 600;
    font-size: clamp(1.4rem, 3.4vw, 2.4rem);
    letter-spacing: -.03em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .5);
    text-align: center;
}

@media (prefers-reduced-motion: no-preference) {
    /* The two tracks are read by ONE head at ONE x — that is the explanation. */
    @keyframes cs-lock {
        0%   { left: 6%;  opacity: .5; }
        55%  { left: 50%; opacity: 1; }
        100% { left: 50%; opacity: 1; }
    }
    .cs-tracks-head { animation: cs-lock 6s cubic-bezier(.3,.8,.3,1) infinite; }
    @keyframes cs-lock-in { 0%,60% { opacity: 0; transform: translateY(4px); } 75%,100% { opacity: 1; transform: none; } }
    .cs-tracks-lock { animation: cs-lock-in 6s ease-out infinite; }
}

@media (max-width: 767px) {
    .cs-sync { padding: 3.5rem 0; }
    .cs-tracks-lock { position: static; display: flex; justify-content: flex-end; margin-bottom: .75rem; }
    .cs-tracks-head { top: 3.2rem; }
    /* The closing line is a NOTE on a phone, not a second headline: the display
       slab wrapped to three uppercase lines and read as an orphaned heading. */
    .cs-sync-foot {
        margin-top: 1.5rem;
        font-size: 1.05rem;
        letter-spacing: 0;
        text-transform: none;
        font-family: 'Inter', sans-serif;
        font-weight: 400;
    }
    .cs-proc-note { margin-bottom: 1.5rem; }
}

/* ---------- The room (Studio Sound / mixer) ---------------------------- */
/* MOMENT 3 of 3.
   ⚠ The TWO stacked radial gradients that used to paint this band are gone —
   "use one large circular glow behind the mixer, not multiple generic
   gradients." The glow is now cs_glow('room') in the markup, which is the same
   four-part object every other product moment uses. This band keeps only a
   flat tone; the light comes from the glow. */
.cs-room {
    position: relative;
    overflow: hidden;
    padding: var(--cs-band) 0;
}
/* Product LEFT, copy RIGHT — the mirror of the editor band above it, so two
   adjacent sections do not read as the same slide twice. */
/* ⚠ 5fr / 6fr, not 7fr / 4fr. That split was set when this column held an
   880px-wide landscape crop. It holds a ~250px portrait phone now, so 7fr left
   the device swimming in empty space while the copy was squeezed into 4fr. */
.cs-room-grid {
    position: relative; z-index: 2;
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    align-items: center;
    gap: clamp(2rem, 5vw, 3.5rem);
}
.cs-room-txt { order: 2; padding-left: 0; }
.cs-room-ui  { order: 1; }
.cs-room-txt p { color: #d6cfee; max-width: 40ch; margin-bottom: 0; }

/* A framed full screen, sized by height like every other device shot. */
.cs-room-ui { margin: 0; text-align: center; }
.cs-room-ui .cs-device { position: relative; z-index: 1; }
.cs-room-ui .cs-device > img {
    display: block;
    width: auto;
    max-width: none;
    height: min(64vh, 540px);
}
.cs-room-ui figcaption {
    /* Clears the device's floor reflection — .9rem put the caption inside it. */
    margin-top: var(--cs-device-floor);
    font-size: .92rem;
    color: var(--cs-muted);
}


@media (max-width: 991px) {
    .cs-room-grid { grid-template-columns: 1fr; }
    /* Copy leads on a phone; the mirror only makes sense with two columns. */
    .cs-room-txt { order: 1; }
    .cs-room-ui  { order: 2; }
}
@media (max-width: 575px) {
    .cs-room { padding: 3.5rem 0; }
}

/* ---------- The editor ------------------------------------------------
   Type left, one screen right. The screen is CONTAINED and centred in its
   column. It used to be width:118% with margin-left:6% — deliberately past
   the column and off the viewport — which rendered a 660x1434 capture at
   roughly 614x1334 and let .cs-editor { overflow:hidden } take the edges off
   it. The glow still bleeds; the screenshot does not. */
.cs-editor { position: relative; overflow: hidden; padding: var(--cs-band) 0; }
.cs-editor-lead {
    display: grid;
    grid-template-columns: minmax(0, 6fr) minmax(0, 6fr);
    align-items: center;
    gap: clamp(1.5rem, 4vw, 3.5rem);
    margin-bottom: 2.25rem;
}
/* ⚠ CUTS THE POSTER FACE DOWN, exactly as .cs-pricing-title does and for the same
   reason. Bare .cs-poster is the hero's clamp(2.4rem, 6vw, 5.2rem), sized for three
   words alone on a line. This headline is "Sync is just the start." in a half-width
   column, and at hero scale it stacks into a wall of type. Same family, inner-page
   size — do not "restore" it to the hero's. */
.cs-editor-title { font-size: clamp(1.9rem, 4.4vw, 3.4rem) !important; }

/* ⚠ THE SECOND LINE IS BIGGER THAN THE FIRST, and that is the point of the
   headline. Carlos, 2026-08-27: "A full editor should be bigger."

   "Sync is just the start." is the setup and "A FULL EDITOR" is the payoff, so
   they are not the same size — the accent line steps up and carries the weight.

   ⚠ `em`, NOT a second clamp. It multiplies whatever .cs-editor-title resolved to,
   so the ratio between the two lines holds at every viewport width. A second clamp
   would cross over the first somewhere in the middle of the range and the payoff
   would briefly be the smaller line.

   ⚠ NO display CHANGE. .cs-poster .accent stays inline: its ::before bloom is
   absolutely positioned against it and is inset in PERCENTAGES of that box, so the
   bloom scales with the type on its own. Making it inline-block to "contain" the
   pseudo-element re-boxes it and the bloom shifts off the word.

   ⚠ line-height comes from .cs-poster (.94) and resolves against each line's OWN
   font-size, so the bigger line takes its own extra leading and the two do not
   collide. Nothing to set here. */
.cs-editor-title .accent { font-size: 1.34em; }

.cs-editor-txt p { max-width: 40ch; color: var(--cs-body); margin: 1rem 0 1.75rem; }

.cs-editor-shot { position: relative; margin: 0; text-align: center; }
/* ⚠ The ::before radial-gradient that used to live here is GONE — cs_glow('screen')
   in the markup does this job now, with the rim and the two glints that make it
   read as our light rather than a gradient circle. Do not put it back: two blooms
   on one screenshot is exactly the busy background the system forbids. The glow
   is allowed to bleed past the figure the way the gradient did. */
.cs-editor-shot .cs-glow { inset: -12% -18%; }
/* THE RING AROUND THE EDITOR PHONE — placement only; the object itself is the
   app's, ported into cs-ring.css. Same rule shape as the hero's, different
   numbers, because choreosync.css:8546 is explicit that every ring declares its
   own diameter and sets --cs-ring-orbit to exactly half of it.

   ⚠ SIZED OFF THIS PHONE, WHICH IS SHORTER THAN THE HERO'S: min(66vh, 560px)
   against the hero's min(78vh, 715px). The diameter has to clear the screenshot's
   HEIGHT or the top and bottom arcs pass behind it and the glints spend most of
   the orbit hidden. Re-derive it whenever the shot is resized. */
.cs-editor-shot .cs-hero-ring.cs-sheet-icon.cs-sheet-glyph {
    position: absolute;
    z-index: 0;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    width:  min(70vh, 595px);
    height: min(70vh, 595px);
    --cs-ring-orbit: calc(min(70vh, 595px) / 2);
    --cs-ring-k: 0.85;
    flex: none;
    opacity: 0.75;
    pointer-events: none;
}

.cs-editor-shot .cs-device { position: relative; z-index: 1; }
.cs-editor-shot .cs-device > img {
    display: block; width: auto; max-width: none;
    height: min(66vh, 560px);
}

/* ⚠ .cs-tabs / .cs-top4 / .cs-more / .cs-tab-name / .cs-tab-line are GONE
   (2026-08-26) with the collapsed disclosure they styled. The control list is
   open on the page now — see .cs-caps below. */
/* ⚠ .cs-editor-caps is GONE (2026-08-26). It was the four-label summary —
   TRIM / REFRAME / COLOR / EXPORT — which said the same four things the full
   .cs-caps list below now says, one screenful lower. One list, not two. */

/* ---------- Every editing control -------------------------------------
   The full transcription from inc-editor.php.

   ⚠ THIS WAS BUILT TOO QUIET AND HAD TO BE TURNED UP. The first pass was
   "small type, muted, no cards" on purpose, so it would stay an appendix —
   and Carlos's verdict was "too small, too dark, too boring". A spec list
   that nobody reads is not restraint, it is a dead patch of page. It is a lit
   panel per tab now, at readable size.

   ⚠ It still must not outrank the product. The panels are washes, not solid
   cards; the screen above is still the loudest thing in the section. If this
   ever starts pulling the eye off the phone, lower the wash alpha — do not
   shrink the type back down.

   The FOUR ACCENTS come from the store frames: purple, cyan, magenta and a
   bright violet, assigned by column via :nth-child so the markup stays plain
   divs. Video/Audio/Cover/Export, in that order — reorder $cs_editor_tabs and
   the colours follow the position, not the tab. */
.cs-caps {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1.25rem;
    margin-top: 2rem;
}
.cs-caps-col {
    --cap: var(--cs-primary-rgb);
    position: relative;
    padding: 1.5rem 1.35rem 1.6rem;
    border-radius: 1.1rem;
    border: 1px solid rgba(var(--cap), .30);
    background:
        radial-gradient(120% 90% at 50% 0%, rgba(var(--cap), .20) 0%, rgba(var(--cap), 0) 62%),
        linear-gradient(180deg, rgba(255, 255, 255, .055) 0%, rgba(255, 255, 255, .015) 100%);
    box-shadow: 0 18px 44px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .09);
}
/* The lit edge along the top — the same rim light the device frame carries. */
.cs-caps-col::before {
    content: "";
    position: absolute; left: 1.35rem; right: 1.35rem; top: -1px;
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(90deg, rgba(var(--cap), 0) 0%, rgb(var(--cap)) 30%, rgb(var(--cap)) 70%, rgba(var(--cap), 0) 100%);
    box-shadow: 0 0 14px rgba(var(--cap), .9);
}
/* ---------- HOVER: LIFT, ZOOM, GLOW ----------------------------------
   Carlos, 2026-08-27: "add some hover effects to the feature cards, zoom glow".

   Three things move together and they are all keyed to --cap, so each card
   answers in ITS OWN colour rather than every card flaring the same violet:
   the border and the top rim light come up, the card's own radial wash
   strengthens, and a coloured bloom joins the black drop shadow.

   ⚠ SCALE ON THE CARD, NOT A WIDTH/PADDING CHANGE. A transform does not touch
   layout, so a card growing 2% cannot nudge the three beside it or reflow the
   grid — with four columns of unequal height, anything that reflows on hover
   makes the whole row twitch.

   ⚠ 1.02, NOT MORE. These are tall cards, up to ~500px: at 1.05 the top edge
   travels 12px and the text visibly resamples. 2% reads as a lift.

   ⚠ TRANSITION ON THE BASE RULE, NOT ON :hover — or it snaps back instantly on
   the way out and only animates inward.

   ⚠ AND IT IS :hover ONLY, GATED ON A REAL POINTER. On a touch screen :hover
   sticks after a tap and the card stays lit until you touch something else,
   which reads as a selected state on something that is not selectable. */
.cs-caps-col {
    transition:
        transform .28s cubic-bezier(.2, .7, .3, 1),
        border-color .28s ease,
        box-shadow .28s ease,
        background .28s ease;
}
@media (hover: hover) and (pointer: fine) {
    .cs-caps-col:hover {
        transform: translateY(-4px) scale(1.02);
        border-color: rgba(var(--cap), .62);
        background:
            radial-gradient(120% 90% at 50% 0%, rgba(var(--cap), .34) 0%, rgba(var(--cap), 0) 66%),
            linear-gradient(180deg, rgba(255, 255, 255, .085) 0%, rgba(255, 255, 255, .02) 100%);
        box-shadow:
            0 26px 60px rgba(0, 0, 0, .55),
            0 0 26px rgba(var(--cap), .30),
            0 0 70px 12px rgba(var(--cap), .16),
            inset 0 1px 0 rgba(255, 255, 255, .16);
    }
    /* The rim light runs the full width and burns brighter — it is inset from the
       corners at rest, so widening it is itself part of the "on" state. */
    .cs-caps-col:hover::before {
        left: .75rem; right: .75rem;
        box-shadow: 0 0 22px rgba(var(--cap), 1);
    }
    .cs-caps-col::before {
        transition: left .28s ease, right .28s ease, box-shadow .28s ease;
    }
    /* The dot beside the name is the section's motif; it lights with the card. */
    .cs-caps-col:hover .cs-caps-name::before {
        box-shadow:
            0 0 0 3px rgba(var(--cap), .42),
            0 0 16px 3px rgba(var(--cap), 1),
            0 0 36px 12px rgba(var(--cap), .5);
    }
    .cs-caps-name::before { transition: box-shadow .28s ease; }
}
/* ⚠ Reduced motion keeps the LIGHT and drops the MOVEMENT. The colour change is
   the information ("this card is under the cursor"); the travel is decoration. */
@media (prefers-reduced-motion: reduce) {
    .cs-caps-col { transition: border-color .28s ease, box-shadow .28s ease, background .28s ease; }
    .cs-caps-col:hover { transform: none; }
}

.cs-caps-col:nth-child(1) { --cap: var(--cs-primary-rgb); }
.cs-caps-col:nth-child(2) { --cap: var(--cs-cyan-rgb); }
.cs-caps-col:nth-child(3) { --cap: var(--cs-magenta-rgb); }
.cs-caps-col:nth-child(4) { --cap: 139, 92, 246; }

.cs-caps-name {
    display: flex; align-items: center; gap: .5rem;
    font-family: var(--cs-display-font);
    font-size: 1.15rem; font-weight: 600; letter-spacing: .01em; text-transform: uppercase;
    color: #fff;
    margin: 0 0 .35rem;
}
/* The motif, once per column, lit in that column's accent. */
.cs-caps-name::before {
    content: "";
    width: 9px; height: 9px;
    border-radius: 50%;
    background: #fff;
    flex: 0 0 auto;
    box-shadow:
        0 0 0 3px rgba(var(--cap), .30),
        0 0 12px 2px rgba(var(--cap), .85),
        0 0 26px 8px rgba(var(--cap), .35);
}
.cs-caps-sum {
    font-size: .95rem;
    color: rgba(255, 255, 255, .74);
    margin: 0 0 1rem;
    line-height: 1.4;
}
.cs-caps ul { list-style: none; margin: 0; padding: 0; }
.cs-caps li {
    position: relative;
    padding: .42rem 0 .42rem 1.05rem;
    font-size: .95rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .80);
    border-top: 1px solid rgba(255, 255, 255, .07);
}
.cs-caps li:first-child { border-top: none; }
/* The glint at list-marker scale, in the column's accent. */
.cs-caps li::before {
    content: "";
    position: absolute; left: 0; top: 1.02em;
    width: 5px; height: 5px;
    border-radius: 50%;
    background: rgb(var(--cap));
    box-shadow: 0 0 9px rgba(var(--cap), .95);
}

@media (max-width: 1100px) { .cs-caps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
    .cs-caps { grid-template-columns: 1fr; gap: 1rem; margin-top: 1.75rem; }
    .cs-caps-col { padding: 1.25rem 1.15rem 1.35rem; }
    .cs-caps-name { font-size: 1.05rem; }
}

@media (max-width: 991px) {
    .cs-editor-lead { grid-template-columns: 1fr; }
    /* ⚠ HEIGHT, not width. width:74% of a one-column phone layout put this
       660x1434 capture back over 600px tall — the same billboard, just
       narrower. A portrait shot is capped against the viewport it is being
       read on, at every breakpoint. */
    .cs-editor-shot .cs-device > img { height: min(52vh, 430px); }
    .cs-editor-shot .cs-hero-ring.cs-sheet-icon.cs-sheet-glyph {
        width:  min(56vh, 460px);
        height: min(56vh, 460px);
        --cs-ring-orbit: calc(min(56vh, 460px) / 2);
        --cs-ring-k: 0.7;
    }
}
@media (max-width: 575px) {
    .cs-editor { padding: 3.5rem 0; }
    .cs-editor-shot .cs-device > img { height: min(46vh, 380px); }
    .cs-editor-shot .cs-hero-ring.cs-sheet-icon.cs-sheet-glyph {
        width:  min(50vh, 405px);
        height: min(50vh, 405px);
        --cs-ring-orbit: calc(min(50vh, 405px) / 2);
        --cs-ring-k: 0.55;
        opacity: 0.6;
    }
}

/* ⚠ .cs-why* is GONE (2026-08-26) with the "Stop syncing by hand" band.
   inc-features.php is still on disk and unreferenced; if it is ever reinstated
   this CSS comes back from git with it. */

/* ⚠ .cs-crew* is GONE (2026-08-26) with the "Built for people who move" band
   — the last section on the page carrying people. inc-creators.php is still on
   disk and unreferenced; if it is ever reinstated this CSS comes back from git
   with it. Do not re-add people to fill space. */

/* ---------- Closing band ---------- */

/* ---------- Progressive disclosure (the full control list) -------------
   <details> so it works without JavaScript and is keyboard-operable and
   announced for free. The content stays in the HTML either way, so nothing
   is hidden from crawlers. */

/* ---------- Focus -----------------------------------------------------
   The template shipped `outline: none` on several interactive elements,
   which leaves a keyboard user with no idea where they are. One visible
   ring, everywhere, and only for keyboard focus so a mouse click stays
   clean. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid #b79dff !important;
    outline-offset: 3px;
    border-radius: 4px;
}
.wsmenu > .wsmenu-list > li > a:focus-visible { outline-offset: -4px; }

/* Skip link — the page opens with a nav of 12 links before any content. */
.cs-skip {
    position: absolute; left: -9999px; top: 0;
    z-index: 10010;
    padding: .8rem 1.2rem;
    background: #100d1b; color: #fff;
    border: 1px solid var(--cs-line-2); border-radius: 0 0 .6rem 0;
    font-size: .9rem;
}
.cs-skip:focus { left: 0; }

/* ---------- SECTION DEPTH ---------------------------------------------
   ⚠ Carlos, 2026-08-26: "do not use pure near-black with the same intensity
   across every section." The canvas is #050409 with its blooms on <html>, and
   every band sat on exactly that, so the page read as one unbroken void with
   content floating in it.

   Three tones, alternating, all within a few percent of the canvas — this is
   depth, not stripes. If any of these reads as a visible band edge it is too
   strong; the seams are gradients precisely so no hard line ever appears.

     · LIFT  — a touch lighter and warmer, for the two product moments
     · SINK  — a touch deeper, for the quiet beats between them
   The hero and the close keep the bare canvas so the page opens and ends on
   the same ground. */
.cs-rail,
.cs-editor {
    background:
        linear-gradient(180deg, rgba(124, 92, 210, 0) 0%, rgba(124, 92, 210, .045) 18%,
                                rgba(124, 92, 210, .045) 82%, rgba(124, 92, 210, 0) 100%);
}
.cs-sync,
.cs-room {
    background:
        radial-gradient(80% 120% at 50% 40%, rgba(var(--cs-primary-rgb), .13) 0%, rgba(var(--cs-primary-rgb), 0) 70%),
        linear-gradient(180deg, rgba(8, 5, 18, 0) 0%, rgba(8, 5, 18, .55) 22%,
                                rgba(8, 5, 18, .55) 78%, rgba(8, 5, 18, 0) 100%);
}
/* The quiet beats go the other way — slightly deeper than the canvas, so the
   moments either side of them read as lit by comparison. */
#faqs {
    background: linear-gradient(180deg, rgba(2, 1, 6, 0) 0%, rgba(2, 1, 6, .5) 25%,
                                        rgba(2, 1, 6, .5) 75%, rgba(2, 1, 6, 0) 100%);
}

/* ---------- FAQ, deliberately secondary --------------------------------
   Structurally fine, so nothing here changes how it works — it just stops
   competing with the product storytelling for weight. */
#faqs { padding: 3.25rem 0 3rem !important; }
#faqs .section-title { margin-bottom: 2.25rem !important; }
#faqs .section-title h2,
#faqs .section-title h3 { font-size: clamp(1.5rem, 3vw, 2.1rem); }
#faqs .accordion-thumb h5 { font-size: 1rem; }
#faqs .accordion-panel p { font-size: .92rem; }

/* ---------- Footer -----------------------------------------------------
   The close flows straight into it: no gap, no second void. */
.footer { padding-top: 2.75rem !important; padding-bottom: 1.25rem !important; }
.footer .row + hr,
.footer hr { margin: 1.5rem 0 1rem; }
.bottom-footer { padding-bottom: .5rem; }


/* =========================================================================
   THE DEVICE FRAME AND THE RAYS (2026-08-26)

   From the App Store frames Carlos set as the reference. Two objects, both
   BACKGROUND — they frame the product and never cover it:

     .cs-device  a lit bezel around a screenshot, with a floor reflection
     .cs-rays    the light fan behind it

   ⚠ Hierarchy is unchanged: app UI first, then headline, then glow/glints,
   then confetti, then copy. These sit at glow level. If a rim or a ray ever
   reads louder than the screen inside it, turn it down — do not move it.
   ========================================================================= */

/* ---------- Device frame ----------------------------------------------
   A bezel drawn in CSS around the capture, not a PNG mock-up: it scales to
   any screenshot, costs no bytes, and cannot go out of date when Apple
   changes a corner radius.

   The rim is the point. In the reference every phone carries a bright purple
   edge-light and sits in a pool of its own glow, which is what stops the
   screenshot reading as a flat rectangle pasted onto black. */
.cs-device {
    position: relative;
    display: inline-block;
    padding: 8px;
    border-radius: 2.15rem;
    background: linear-gradient(168deg, #2a2340 0%, #14101f 42%, #221b36 100%);
    box-shadow:
        /* the lit rim */
        0 0 0 1px rgba(var(--cs-primary-rgb), .55),
        0 0 14px 1px rgba(var(--cs-primary-rgb), .55),
        0 0 46px 6px rgba(var(--cs-primary-rgb), .34),
        0 0 96px 22px rgba(var(--cs-magenta-rgb), .16),
        /* the body */
        0 34px 74px rgba(0, 0, 0, .72),
        inset 0 1px 0 rgba(255, 255, 255, .16);
}
.cs-device > img {
    display: block;
    border-radius: 1.55rem;
    /* the screen's own inner edge, so the bezel reads as depth not a border */
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .85);
}

/* The floor: a pool of light under the phone. Every reference frame has one
   and it is what seats the device on a surface instead of floating it. */
.cs-device::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: -30px;
    width: 150%;
    height: 60px;
    transform: translateX(-50%);
    background: radial-gradient(50% 50% at 50% 50%,
        rgba(var(--cs-primary-rgb), .50) 0%,
        rgba(var(--cs-magenta-rgb), .16) 42%,
        rgba(var(--cs-primary-rgb), 0) 72%);
    filter: blur(10px);
    pointer-events: none;
    z-index: -1;
}

/* ---------- Light rays -------------------------------------------------
   The fan behind the phone. repeating-conic-gradient draws it in ONE
   declaration — no SVG, no 24 elements — and the mask fades it out toward the
   centre so the rays appear to come from behind the device rather than
   through it.

   ⚠ Rays are the loudest thing this site has ever drawn, so they are used on
   a few loud moments only — sync and mixer on the home page, and /pricing/ — and
   never behind body copy. ⚠ It said "the THREE moments (hero, sync, mixer)" until
   2026-08-27; the hero is no longer one of them. */
.cs-rays {
    position: absolute;
    left: 50%; top: 50%;
    width: 176%;
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    pointer-events: none;
    z-index: 0;
    background: repeating-conic-gradient(from 0deg at 50% 50%,
        rgba(var(--cs-primary-rgb), .30) 0deg 1.1deg,
        rgba(var(--cs-primary-rgb), 0)   1.1deg 5.4deg,
        rgba(var(--cs-magenta-rgb), .22) 5.4deg 6.2deg,
        rgba(var(--cs-primary-rgb), 0)   6.2deg 11.5deg,
        rgba(var(--cs-cyan-rgb), .16)    11.5deg 12.2deg,
        rgba(var(--cs-primary-rgb), 0)   12.2deg 18deg);
    /* hollow in the middle, faded at the rim */
    -webkit-mask: radial-gradient(closest-side,
        transparent 0%, transparent 26%, #000 46%, #000 62%, transparent 92%);
    mask: radial-gradient(closest-side,
        transparent 0%, transparent 26%, #000 46%, #000 62%, transparent 92%);
    opacity: 1;
}

/* ⚠ `.cs-hero-stage > .cs-rays` IS GONE (2026-08-27). It widened the fan's hollow
   centre so the burst stopped fighting the phone's edge — a fix for a problem that
   no longer exists, because Carlos then pulled the fan off the hero entirely
   ("REMOVE cs-rays from the hero!!!!"). The ring is the hero's light now.

   ⚠ IF THE FAN EVER GOES BACK ON THE HERO, IT NEEDS THIS RULE AGAIN. The shared
   mask's hole (26% of the closest side) is smaller than the device, so the
   brightest part of the fan lands directly behind the bezel. The values that
   worked were `transparent 0% 38%, #000 60%, #000 72%, transparent 94%` at .82
   opacity — and scoped, because the sync band, Studio Sound and /pricing/ have no
   phone in the middle to protect and only lose background by being hollowed out. */

/* ⚠ THE HAND-ROLLED HERO RING IS GONE (2026-08-27). It lived here: a conic rim,
   two glint lights, and a pair of continuous-orbit keyframes down in the Motion
   block — all of it written against the RAIL's simplified copy of the app's ring
   rather than against the ring itself.

   Carlos: "remove the radial glint from hero and add the STANDARD CIRCLE WITH
   GLINT!!!!!! EXACTLY THE SAME AS THIS ONE", pointing at the app's own home hero.
   It is a verbatim port of the real primitive now, in its own file so it can be
   re-copied whenever the app's ring changes: assets/css/cs-ring.css.

   ⚠ DO NOT RE-CREATE A SECOND ONE HERE. Two circles that are nearly the same is
   the exact fault being corrected. */

/* ---------- Motion -----------------------------------------------------
   The fan turns, very slowly. One full revolution takes three minutes, which
   is under a degree a second — present in peripheral vision, never something
   you catch looping. Nothing else here moves. */
@media (prefers-reduced-motion: no-preference) {
    @keyframes cs-rays-turn { to { transform: translate(-50%, -50%) rotate(360deg); } }
    .cs-rays { animation: cs-rays-turn 180s linear infinite; }
}

@media (max-width: 767px) {
    /* A phone has a fraction of the canvas: the fan tightens and dims so it
       stays background, and the bezel thins so the screen keeps its size. */
    .cs-rays { width: 170%; opacity: .55; }
    .cs-device { padding: 6px; border-radius: 1.85rem; }
    .cs-device > img { border-radius: 1.4rem; }
    .cs-device::after { bottom: -22px; height: 44px; }
}

/* ---------- The process strip -----------------------------------------
   Four framed phones in a row: the auto-sync flow, start to confirmation.
   ⚠ It is a SEQUENCE, so it must stay one row for as long as a row fits —
   two-up reads as four unrelated screens, and the whole point is that these
   are consecutive states of one run. It only breaks to 2x2 when four phones
   would be too narrow to read. */
.cs-proc {
    position: relative;
    z-index: 1;
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 2.5rem 1.5rem;
    align-items: start;
}
.cs-proc-step { text-align: center; }
/* The bezel is heavy at four-across, so the frame thins here — the rim light
   still reads, the screens keep their width. */
.cs-proc .cs-device { padding: 5px; border-radius: 1.5rem; }
.cs-proc .cs-device > img {
    width: 100%;
    max-width: 190px;
    height: auto;
    border-radius: 1.15rem;
}
.cs-proc .cs-device::after { bottom: -16px; height: 34px; }

.cs-proc-cap {
    margin: 1.5rem 0 0;
    font-family: 'Inter', sans-serif;
    font-size: .82rem;
    letter-spacing: .02em;
    color: rgba(255, 255, 255, .70);
}
.cs-proc-cap b {
    display: block;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .16em;
    color: #b79dff;
    margin-bottom: .15rem;
    font-variant-numeric: tabular-nums;
}

/* The manual path, introduced under the strip. */
.cs-proc-note {
    position: relative;
    z-index: 1;
    max-width: 44ch;
    margin: 0 auto 2rem;
    text-align: center;
    font-size: .95rem;
    color: var(--cs-muted);
}

@media (max-width: 860px) {
    .cs-proc { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2rem 1.25rem; }
    .cs-proc .cs-device > img { max-width: 168px; }
}
@media (max-width: 420px) {
    /* Still 2x2, never one-up: a four-step sequence stacked into a single
       column is four screenfuls of scrolling to read one flow. */
    .cs-proc { gap: 1.5rem 1rem; }
    .cs-proc .cs-device { padding: 4px; border-radius: 1.15rem; }
    .cs-proc .cs-device > img { max-width: 130px; border-radius: .85rem; }
    .cs-proc-cap { margin-top: 1.1rem; font-size: .76rem; }
}

/* ⚠ .cs-track-third and .cs-step-n are GONE (2026-08-26) with the three-step
   wizard sequence they labelled. The sequence on this band is .cs-proc now,
   and the two tracks below it are the mechanism rather than steps 1 and 2. */

/* ---------- More life on the hero device -------------------------------
   Carlos, 2026-08-26: "this needs more life. its boring and small."

   The size came from .cs-flow-f. The LIFE is here: the rim light breathes and
   the floor pool breathes with it, a couple of seconds out of phase, so the
   phone looks lit rather than pasted on. Slow — a six-second round — and it is
   the only thing on the stage that moves apart from the frames advancing. */
@media (prefers-reduced-motion: no-preference) {
    @keyframes cs-device-breathe {
        0%, 100% {
            box-shadow:
                0 0 0 1px rgba(var(--cs-primary-rgb), .55),
                0 0 14px 1px rgba(var(--cs-primary-rgb), .55),
                0 0 46px 6px rgba(var(--cs-primary-rgb), .34),
                0 0 96px 22px rgba(var(--cs-magenta-rgb), .16),
                0 34px 74px rgba(0, 0, 0, .72),
                inset 0 1px 0 rgba(255, 255, 255, .16);
        }
        50% {
            box-shadow:
                0 0 0 1px rgba(var(--cs-primary-rgb), .80),
                0 0 20px 2px rgba(var(--cs-primary-rgb), .78),
                0 0 66px 10px rgba(var(--cs-primary-rgb), .48),
                0 0 130px 30px rgba(var(--cs-magenta-rgb), .26),
                0 34px 74px rgba(0, 0, 0, .72),
                inset 0 1px 0 rgba(255, 255, 255, .22);
        }
    }
    /* Two animations, different properties: the rim light breathes (box-shadow)
       and the whole device breathes with it (transform). */
    .cs-hero-stage .cs-device {
        animation: cs-device-breathe 6s ease-in-out infinite,
                   cs-drift-in 12s ease-in-out infinite;
        transform-origin: 50% 45%;
    }

    @keyframes cs-floor-breathe {
        0%, 100% { opacity: .85; transform: translateX(-50%) scaleX(1); }
        50%      { opacity: 1;   transform: translateX(-50%) scaleX(1.08); }
    }
    .cs-hero-stage .cs-device::after { animation: cs-floor-breathe 6s ease-in-out -2s infinite; }
}

/* ---------- Small screens, last word -----------------------------------
   ⚠ MUST STAY AT THE END. `.cs-hero-stage .cs-device` is the same specificity
   in both places, so whichever comes last wins — put this back up beside the
   other 575px rules and the device keeps breathing on phones. */
@media (max-width: 575px) {
    .cs-hero-stage .cs-device,
    .cs-hero-stage .cs-device::after { animation: none; }
}

/* ---------- Anchor targets ---------------------------------------------
   The header sits over the page, so a jump to #how / #editor / #get-app lands
   with the section's first line UNDER the nav. scroll-margin-top pushes the
   landing point down by the header's height — it costs no layout, unlike the
   old trick of an offset absolutely-positioned anchor span.

   .cs-anchor is a zero-size marker for a section that answers to a second
   name; see inc-cta.php, which is both #cta and #get-app. */
#hero-16, #how, #sync, #editor, #studio-sound, #faqs, #cta,
.cs-anchor { scroll-margin-top: 110px; }
.cs-anchor { display: block; width: 0; height: 0; }

/* ---------- Mobile, the last word --------------------------------------
   ⚠ MUST STAY AT THE END, like the 575px device rule above it: these share
   specificity with the desktop rules they correct, so they only win by coming
   later. Everything here is a MOBILE VOID — space that was sized for a
   two-column layout and became dead black once the page stacked.

   Carlos, 2026-08-26: "site doesnt look too good on mobile." */
@media (max-width: 767px) {
    /* The footer's own top pad plus the close section's bottom pad stacked
       into roughly a screen and a half of nothing before the logo. */
    .footer { padding-top: 1.75rem !important; }
    #footer-4 img.footer-logo { max-height: 64px; }

    /* A framed phone is the last thing in these bands; its floor reflection
       already reads as the ending, so the section does not need to pad past
       it as well. */
    .cs-close { padding-bottom: 2rem; }
    .cs-editor { padding-bottom: 2.25rem; }

    /* The editor's four panels are the section on a phone — they do not need
       to be held away from the screen above them by a desktop-sized gap. */
    .cs-editor-lead { margin-bottom: 1.5rem; }

    /* ---------- Centred on a phone ------------------------------------
       Every section's type is left-aligned on desktop, where it sits beside a
       product column and the ragged left edge is the alignment. Stacked, the
       product is ABOVE the copy and centred, so left-aligned type under a
       centred phone reads as broken rather than as a choice.

       ⚠ The eyebrow and the flow line are FLEX, not blocks: text-align does
       nothing to them and they need justify-content. Same for the button row
       and the store badges. Miss those four and the headline centres while
       everything around it stays left. */
    .cs-hero-txt,
    .cs-rail-head,
    .cs-editor-txt,
    .cs-room-txt,
    .cs-close-txt   { text-align: center; }

    .cs-hero-txt .cs-eyebrow,
    .cs-rail-head .cs-eyebrow,
    .cs-editor-txt .cs-eyebrow,
    .cs-room-txt .cs-eyebrow,
    .cs-hero .btns-group,
    .cs-editor-txt .btns-group,
    .cs-close-badges { justify-content: center; }

    /* A centred block of type has to be a centred BOX too, or a max-width
       leaves it hugging the left edge with centred lines inside it. */
    .cs-hero-lead,
    .cs-hero-note,
    .cs-editor-txt p,
    .cs-room-txt p,
    .cs-close-txt p.p-lg,
    .cs-close-foot   { margin-inline: auto; }

    /* Bootstrap's .btns-group is not flex by default, so the rule above has
       nothing to act on until it is. */
    .cs-hero .btns-group,
    .cs-editor-txt .btns-group {
        display: flex;
        flex-wrap: wrap;
        gap: .75rem;
    }
    .cs-hero .btns-group .btn { margin-right: 0; }

    /* ---------- One button width in the close ---------------------------
       The ask stacks into three controls on a phone — the CTA pill, then the
       iPhone and Android badges — and they came out three different widths
       because the pill sizes to its label while .cs-store-badge is
       `flex: 0 1 230px`. Three different widths in a vertical stack reads as
       three unrelated things rather than one set of choices.

       ⚠ One shared width, set in ONE place. If a fourth control is ever added
       here it inherits it; do not give it its own. */
    .cs-close-txt {
        --cs-cta-w: min(100%, 300px);
    }
    .cs-close-txt .btn,
    .cs-close-badges .cs-store-badge {
        flex: 0 0 auto;
        width: var(--cs-cta-w);
        margin-inline: auto;
    }
    /* Stacked, so the badges' own wrap has nothing to do. */
    .cs-close-badges { flex-direction: column; align-items: center; gap: .6rem; }
    /* The badge's label block is left-aligned inside a 230px chip; centred in
       a 300px one it would drift away from its icon. */
    .cs-close-badges .cs-store-badge { justify-content: flex-start; padding-left: 1.25rem; }
}

/* =========================================================================
   PRICING — the same three layers as the home page
   Carlos, 2026-08-26: "this page needs energy and love!"

   ⚠ NAMED FOR THE PAGE IT WAS BUILT FOR, BUT IT IS THE SHARED INNER-PAGE BAND.
   Four surfaces wear `.cs-pricing` now: /pricing/, /signin/, /welcome/ and
   /account/ — the money path, end to end. Do NOT rename it to something generic
   on that basis; the class is in four templates and the rename buys nothing.

   ⚠ IT IS STILL NOT FOR EVERY PAGE. Contact and legal do not get it, and adding
   them would flatten the difference the home page relies on. The test is whether
   the page is part of deciding, buying or owning the plan.

   ⚠ RAYS ARE NARROWER STILL — /pricing/ only, of these four. Carlos pulled the
   fan off /signin/ ("remove the spinning lights") and it never went on /welcome/
   or /account/: it turns continuously and must not sit behind body copy. The
   band and the confetti are what travel.
   ========================================================================= */
.cs-pricing {
    position: relative;
    overflow: hidden;
    /* Depth, matching the home page's lit bands — the flat canvas was most of
       why this page felt like a different site. */
    background:
        radial-gradient(80% 90% at 62% 46%, rgba(var(--cs-primary-rgb), .15) 0%, rgba(var(--cs-primary-rgb), 0) 68%),
        linear-gradient(180deg, rgba(8, 5, 18, 0) 0%, rgba(8, 5, 18, .5) 20%,
                                rgba(8, 5, 18, .5) 80%, rgba(8, 5, 18, 0) 100%);
}
/* The fan sits behind the PRO card, not the middle of the section. */
.cs-pricing > .cs-rays { left: 62%; top: 52%; width: 120%; opacity: .7; }

.cs-pricing .container { position: relative; z-index: 1; }

/* The headline gets the gradient every other poster headline on the site
   carries. .cs-poster brings the display face and the accent treatment with
   it; the size stays the inner-page size rather than the hero's. */
.cs-pricing-title {
    font-size: clamp(1.9rem, 4.2vw, 3.2rem) !important;
    margin-bottom: 1rem;
}

/* The two plans sit above the light. */
.cs-pricing .cs-form-card { position: relative; z-index: 1; }

/* ⚠ AND THEY ARE A PANEL, NOT A PANE OF GLASS (2026-08-31, Carlos: "cleanup").
   .cs-form-card's dark-mode fill is var(--cs-panel) = rgba(255,255,255,.035),
   which is a tint, not a background — so the ray fan and the confetti field
   behind this section were running straight THROUGH the cards and across the
   bullet lists. On /pricing/ that is worst of all, because the fan is aimed at
   the PRO card by design.

   The band's own light still reads around and under the cards; it just stops
   crossing the copy. !important is needed to beat cs-dark.css, which sets the
   fill on a list of card shapes with !important of its own. */
.cs-pricing .cs-form-card {
    background-color: rgba(16, 13, 27, .82) !important;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
}

/* PRO is the focal point, so it wears the lit rim the device frame and the
   editor panels use — not just a brighter border. */
.cs-pricing .cs-plan-featured {
    border-color: rgba(var(--cs-primary-rgb), .55);
    box-shadow:
        0 0 0 1px rgba(var(--cs-primary-rgb), .35),
        0 0 34px 4px rgba(var(--cs-primary-rgb), .22),
        0 0 90px 20px rgba(var(--cs-magenta-rgb), .12),
        0 28px 64px rgba(0, 0, 0, .55);
}
.cs-pricing .cs-plan-featured .cs-glow { inset: -14% -10%; z-index: 0; }
.cs-pricing .cs-plan-featured > *:not(.cs-glow) { position: relative; z-index: 1; }

/* ⚠ THE GLOW INSIDE A CARD HAS TO BE BIGGER THAN THE CARD, NOT A BALL IN IT.
   cs_glow('cta') is sized --gw:66% of its box, and a box the shape of a plan
   card makes that a circle two-thirds of the card's WIDTH sitting in the middle
   of it — with .cs-glow-edge drawing a 1px rim right round it. On a phone, where
   the card is narrow and tall, that read as a grey sphere pasted on the Pro card
   rather than as light behind it (Carlos, 2026-08-31: "cleanup").

   So: the bloom is widened past the card's own edges, and the rim comes off.
   Everywhere else on the site .cs-glow-edge gives the light a form because the
   whole circle is visible; here the circle is deliberately larger than the frame
   that clips it, and an arc cutting across a card is not a form, it is a smudge. */
.cs-pricing .cs-plan-featured .cs-glow-cta { --gw: 140%; }
.cs-pricing .cs-plan-featured .cs-glow-edge { display: none; }

/* The price is the one number on the page — give it the accent gradient. */
.cs-pricing .cs-plan-featured h2 { color: var(--cs-magenta); }
@supports (-webkit-background-clip: text) or (background-clip: text) {
    .cs-pricing .cs-plan-featured h2 {
        background: var(--cs-accent-grad);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        -webkit-text-fill-color: transparent;
    }
}

/* The tick marks pick up the brand light, like the caps list. */
.cs-pricing .cs-plan-list li::before {
    color: #a98cff;
    text-shadow: 0 0 10px rgba(var(--cs-primary-rgb), .8);
}

@media (max-width: 767px) {
    .cs-pricing > .cs-rays { width: 170%; opacity: .45; }
    .cs-pricing .cs-form-card { padding: 2rem 1.5rem; }

    /* ⚠ STACKED, THE TWO CARDS NEED A GUTTER AND BOOTSTRAP HAS NONE.
       Its columns carry a horizontal gutter only, so below 768px the Free
       card's bottom edge sat flush against Pro's top edge and the pair read as
       one long panel with a seam through it (Carlos, 2026-08-31).

       On the COLUMN, not the card: the cards are .h-100, so a margin on the
       card is inside the height the column hands it and closes the gap again. */
    .cs-pricing .cs-plans > [class*="col-"] + [class*="col-"] { margin-top: 1.75rem; }
}
