/* ==========================================================================
   CELES GLOBAL — Clinics page
   --------------------------------------------------------------------------
   Loads AFTER styles.css and only adds what the B2B page needs. Tokens, nav,
   footer, buttons, forms, .section, .section__head and .container are all
   inherited — nothing below re-declares a colour, a radius, a shadow or a
   spacing step that already has a token.

   ONE SURFACE FOR THE WHOLE PAGE. The homepage alternates --paper / --cream
   scene by scene and marks the seam with a hairline; this page does not. Its
   scenes are pinned screens the reader is snapped into the middle of, and two
   near-identical surfaces alternating behind a sequence of pins reads as a
   flicker at the hand-off rather than as structure. Everything is --cream
   (#faf9f6), with no exception — the one block that used to break the rule
   was the in-development strip, and it has been removed (§7).

   NO PRODUCT UI IN HERE. There is no dashboard mockup, no drawn Clinic OS
   screen and no placeholder frame in this stylesheet, and there should not be
   one: real screenshots are coming and everything drawn to stand in for them
   is something a reader has to un-learn. The classes that used to do it —
   .console, .ckpi, .crow, .metric, .expanel and the .prev__* previews —
   were removed rather than restyled.

   CONTENTS
     1  Page frame
     2  Hero — the marquee as a full-viewport backdrop
    2b  .marquee--bleed — the full-bleed modifier on the shared component
     3  The pinned scene shell — statement → components (§4 and §10 use it)
     4  .fgrid / .fcard — the one feature card, used at three-up and six-up
    4b  .split / .mediaframe — the photography and the footage
    4c  .amb — the ambient floating tag layer behind the statements
     5  .mq / .mqcard — the dual capability marquee, two tracks
     6  .flow — the pinned horizontal sync stream, four nodes on one rail
     7  (removed) .devband — the in-development strip
     8  Onboarding cards
     9  Closing scene — booker + full-bleed footer
    10  Responsive
   ========================================================================== */

/* ---------------------------------------------------------- 1. Page frame */
.page-clinics {
  background: var(--cream);
  /* The hero is a fixed-height box holding an absolutely positioned plane
     that overruns it on every side. Without this a fractional overhang on the
     rhombus becomes a horizontal page scrollbar. */
  overflow-x: hidden;
}

/* "for clinics" lockup next to the wordmark */
.logo__sub {
  align-self: center;
  margin-left: 2px;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--lav-100);
  color: var(--lav-700);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: .62rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ==========================================================================
   2. HERO — the 3D marquee as the landing view
   --------------------------------------------------------------------------
   THREE LAYERS IN ONE STACKING CONTEXT, and the order is the whole idea:

     .chero__bg      the drifting plane, edge to edge          z 0
     .chero__scrim   a cream wash plus a centred bloom          z 1
     .chero__inner   the copy                                   z 2

   `isolation: isolate` rather than a z-index on the section, so however the
   layers inside are stacked the hero can never climb over the fixed nav.
   ========================================================================== */
.chero {
  position: relative;
  isolation: isolate;
  width: 100%;
  /* svh, not vh: on mobile Safari a 100vh hero is taller than the visible
     viewport for as long as the URL bar is showing, which pushes the buttons
     off the bottom of the first screen — the one place they must not be. */
  height: 100svh;
  min-height: 620px;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: var(--cream);
}

.chero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* ------------------------------------------------------------ the scrim ---
   A FLAT WASH PLUS A BLOOM, NOT ONE OR THE OTHER. The wash is the readability
   floor — it takes the whole plane back far enough that no tile ever competes
   with the headline wherever the drift happens to put it. The radial on top
   thickens only the ellipse the words actually occupy, so the corners keep
   most of their contrast and the grid still reads as a grid.

   Never a dark overlay: the page is cream and the type is near-black, so the
   contrast has to be bought by lightening the backdrop, not by dimming it. */
.chero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(66% 58% at 50% 48%,
      rgba(250, 249, 246, .82) 0%,
      rgba(250, 249, 246, .58) 46%,
      rgba(250, 249, 246, .16) 72%,
      rgba(250, 249, 246, 0) 88%),
    /* The flat floor, #FAF9F6 at 65%. Raised from .58 with the drift speed
       below: faster tiles carry more contrast past the headline per second,
       and the wash is what keeps the letters legible wherever they land. */
    rgba(250, 249, 246, .65);
}

/* ---------------------------------------------------------- the content ---
   Centred over the plane. The nav is fixed and transparent over the hero, so
   the block is pushed down by its height to stay optically centred in the
   space the reader actually sees. */
.chero__inner {
  position: relative;
  z-index: 2;
  width: min(940px, 92vw);
  padding-top: var(--nav-h);
  text-align: center;
  display: grid;
  justify-items: center;
}

.chero__badge {
  display: inline-block;
  margin-bottom: clamp(20px, 2.4vw, 30px);
  padding: 8px 16px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .72);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-xs);
  backdrop-filter: blur(6px);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: .7rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--lav-700);
}

.chero__title {
  margin: 0;
  max-width: 17ch;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(2.3rem, 5.4vw, 4.3rem);
  line-height: 1.03;
  letter-spacing: -.045em;
  color: var(--ink);
  text-wrap: balance;
}

.chero__lead {
  margin-top: clamp(20px, 2.4vw, 30px);
  max-width: 56ch;
  font-size: clamp(1rem, 1.2vw, 1.14rem);
  line-height: 1.6;
  color: var(--ink-2);
}

.chero__actions {
  margin-top: clamp(28px, 3.4vw, 42px);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}

.chero__meta {
  margin-top: clamp(26px, 3vw, 38px);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 30px;
}
.chero__meta li {
  position: relative;
  padding-left: 22px;
  font-size: .88rem;
  color: var(--ink-soft);
}
.chero__meta li::before {
  content: "";
  position: absolute;
  left: 0; top: .58em;
  width: 12px; height: 2px;
  border-radius: 2px;
  background: var(--lav);
}

/* The one thing that tells the reader the hero is not the whole page. Pinned
   scenes follow, so the first scroll gesture matters and nothing else on the
   screen points down. */
.chero__cue {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: clamp(20px, 3.4vh, 40px);
  transform: translateX(-50%);
  width: 26px;
  height: 26px;
  color: var(--ink-mute);
  pointer-events: none;
  animation: cheroCue 2.6s var(--ease-soft) infinite;
}
.chero__cue svg { width: 100%; height: 100%; }
@keyframes cheroCue {
  0%, 100% { transform: translate(-50%, 0);    opacity: .45; }
  50%      { transform: translate(-50%, 8px);  opacity: .9; }
}

/* ==========================================================================
   2b. .marquee--bleed — the full-bleed modifier
   --------------------------------------------------------------------------
   The component (styles.css §6b) is page-agnostic and stays that way: it
   sizes itself off its own frame, drifts on its own keyframes, and everything
   below only redeclares the handful of values that are wrong when the frame
   is the whole viewport instead of a 1240 x 640 card.

   FOUR CHANGES, and each is forced by the frame, not a preference.

   1. THE CARD GOES. A rounded plate with a ring and a drop shadow is a widget
      on a page; edge to edge it would be a rounded rectangle inset in cream
      with a visible seam down all four sides. Flat cream ground, no radius,
      no shadow, no ring.

   2. THE DECK GETS BIGGER AND NARROWER-CELLED — 3600 x 3000 in 9 columns
      against the component's 2560 x 2160 in 6. Both numbers are forced by
      coverage. A tipped plane projects to a RHOMBUS, and covering a 16:9
      viewport's corners with one needs a much larger deck than covering a
      short band. Scaling the existing deck up instead (--mq-s ~1.0) covers the
      same area but with tiles nearly 400px across — under four of them span a
      1440px screen, which is the "three big plates" look §6b was widened to
      escape. Growing the deck AND the column count keeps the cell at ~370px,
      so the hero shows five or six across and still reaches the corners at
      --mq-s .76.

      The tile count per column follows from the drift below, not from taste:
      align-self centres each column in the 3000px row, so 12 tiles (~3577px
      of content) overrun the deck by ~288px at each end. The travel has to
      stay under that or a column's first or last tile edge swings into view.

   3. IT DRIFTS ABOUT FIVE TIMES FASTER. The component's 27–41s over 62–78px
      is ~2.7px/s — deliberately slow, because there it was a backdrop to one
      static headline in a band you scroll past. As the landing view it has to
      be moving the moment the page opens, so the periods come down to 12–19s
      and the travel goes up to ~230px: 12–19px/s, which reads as a live
      surface rather than as something that might be a still image.

      Still `alternate`, still mismatched prime-ish periods, so the nine
      columns never resolve into one synchronised sweep.

   4. THE EDGE FADE IS DEEPER AND IN THIS PAGE'S CREAM. #faf9f6, matching the
      page rather than the component's #faf7f2, and pulled further in — the
      corners of the rhombus are exactly where a full-viewport plane is
      thinnest, and a deeper feather is what lets the geometry stop short of
      the corner without anyone seeing where.
   ========================================================================== */
.marquee--bleed {
  --mq-s: .76;
  width: 100%;
  height: 100%;
  margin: 0;
  border-radius: 0;
  background: var(--cream);
  box-shadow: none;
}

.marquee--bleed .marquee__deck {
  width: 3600px;
  height: 3000px;
  grid-template-columns: repeat(9, 1fr);
}

/* NOTICEABLY FASTER, ON REQUEST. Periods come down by ~40% (13-19s becomes
   8-11.5s) against the same ~230px of travel, so the plane runs at roughly
   20-29px/s where it used to run at 12-19. The point of the increase is that
   the hero has to read as a live surface within the first second of the page
   being open, before the headline has finished assembling over it.

   THE PRIME-ISH MISMATCH IS PRESERVED, and it is the only thing about these
   numbers that is load-bearing. Nine columns on periods with no common
   factor never resolve into one synchronised sweep; put two neighbours on the
   same period and the plane visibly pulses. Still `alternate`, so no column
   ever jumps back to its start. */
.marquee--bleed .marquee__col:nth-child(1) { --mq-t: 8s;    --mq-y:  228px; }
.marquee--bleed .marquee__col:nth-child(2) { --mq-t: 10.5s; --mq-y: -212px; }
.marquee--bleed .marquee__col:nth-child(3) { --mq-t: 8.6s;  --mq-y:  236px; }
.marquee--bleed .marquee__col:nth-child(4) { --mq-t: 11.5s; --mq-y: -224px; }
.marquee--bleed .marquee__col:nth-child(5) { --mq-t: 7.4s;  --mq-y:  218px; }
.marquee--bleed .marquee__col:nth-child(6) { --mq-t: 9.8s;  --mq-y: -232px; }
.marquee--bleed .marquee__col:nth-child(7) { --mq-t: 9.2s;  --mq-y:  222px; }
.marquee--bleed .marquee__col:nth-child(8) { --mq-t: 11s;   --mq-y: -238px; }
.marquee--bleed .marquee__col:nth-child(9) { --mq-t: 8.2s;  --mq-y:  214px; }

/* The glow and aurora tiles breathe on their own clocks (styles.css §6b-iii)
   and those were tuned against a slow plane too. Speeding the drift without
   speeding these leaves the tiles looking like they are lagging the surface
   they sit on. */
.marquee--bleed .mtile--glow-1 { --mq-bt: 5s;   --mq-bd: -1.4s; }
.marquee--bleed .mtile--glow-2 { --mq-bt: 6.5s; --mq-bd: -3.2s; }
.marquee--bleed .mtile--aurora-1 { --mq-at: 11s; --mq-ad: -2s; }
.marquee--bleed .mtile--aurora-2 { --mq-at: 14s; --mq-ad: -6s; }
.marquee--bleed .mtile--aurora-3 { --mq-at: 12.5s; --mq-ad: -4s; }

.marquee--bleed::after {
  border-radius: 0;
  background:
    linear-gradient(to bottom,
      rgba(250, 249, 246, 1), rgba(250, 249, 246, 0) 20%,
      rgba(250, 249, 246, 0) 80%, rgba(250, 249, 246, 1)),
    linear-gradient(to right,
      rgba(250, 249, 246, 1), rgba(250, 249, 246, 0) 15%,
      rgba(250, 249, 246, 0) 85%, rgba(250, 249, 246, 1));
}

/* ==========================================================================
   3. THE PINNED SCENE SHELL
   --------------------------------------------------------------------------
   Two sections use it — "what's in Clinic OS" (§4's six cards) and the
   onboarding cards (§8) — and both play the same three beats under one pin: a
   centred statement holds and assembles, it leaves, and the components flow
   up into the space it left.

   TWO LAYOUTS, ONE MARKUP.

     WITHOUT .is-seq (no GSAP, reduced motion, or a small window) the stage is
     a plain flow column: statement, gap, components. Everything is visible,
     nothing is stacked, and nothing needs a timeline to be readable. This is
     also the state the markup ships in, so a failed script leaves a working
     page rather than an empty screen.

     WITH .is-seq — added by clinics.js §2 only where a timeline exists to take
     it away again — the two children share ONE grid cell. That is what makes
     "centred" mean centred in the pinned screen: a statement centred inside a
     700px cell that hangs below the fold is not centred to the reader.

   ZERO OVERLAP IS A SEQUENCING RULE, NOT A Z-INDEX ONE. The two do share a
   cell, so they could in principle be on screen together — the timeline is
   what guarantees they are not. Beat 2 takes the statement to visibility:
   hidden before beat 3 starts, to the frame. Stacking order buys nothing
   while the thing on top is still transparent.
   ========================================================================== */
.scene2 {
  position: relative;
  background: var(--cream);
}

.scene2__stage {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: center;
  gap: clamp(44px, 6vw, 76px);
  padding:
    calc(var(--nav-h) + clamp(48px, 6vw, 86px)) 0
    clamp(64px, 8vw, 104px);
}

/* --- pinned ------------------------------------------------------------- */
.scene2__stage.is-seq {
  gap: 0;
  padding: var(--nav-h) 0 0;
}
.scene2__stage.is-seq > .scene2__statement,
.scene2__stage.is-seq > .scene2__body {
  grid-area: 1 / 1;
  align-self: center;
}
/* Only the BODY is stretched to the cell. The statement keeps its own reading
   measure and its `margin: 0 auto` centres it — widening it to the cell would
   run a centred sentence out to both page edges. */
.scene2__stage.is-seq > .scene2__body { width: 100%; }

/* --- the statement ------------------------------------------------------ */
.scene2__statement {
  width: min(940px, 92vw);
  margin: 0 auto;
  text-align: center;
}
.scene2__title {
  margin: 0;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(2.1rem, 4.8vw, 3.7rem);
  line-height: 1.05;
  letter-spacing: -.042em;
  color: var(--ink);
  text-wrap: balance;
}

/* --- the scroll-driven colour fill --------------------------------------
   THE SAME MOVE AS THE HOMEPAGE STATEMENT, and deliberately the same values:
   styles.css §12 sweeps `.statement__text .w` from --ink-mute to --ink under
   a scrubbed pin, and this is that sentence's counterpart on the clinics
   page. clinics.js §2c splits [data-seq-fill] into one span per word and
   tweens their colour word by word as the reader scrolls.

   THE RESTING COLOUR IS AN OPAQUE-ON-CREAM MATCH FOR rgba(26,26,26,.25), NOT
   THAT VALUE ITSELF. A transparent ink over a cream section lets #faf9f6
   through and the unread words go warm-grey, while the read ones are neutral
   #1A1A1A — the sweep then reads as a hue change as much as a darkening.
   Compositing .25 black over the cream gives #c6c5c2, which is the same
   lightness with none of the colour cast. GSAP also interpolates opaque
   colours in one pass; crossing an alpha boundary makes it tween rgba, which
   on some builds snaps to full opacity on the first frame.

   will-change: color is what keeps the sweep off the main thread's paint
   path — twenty words each repainting on their own frame is the one way this
   effect gets expensive. */
.scene2__title .w {
  color: #c6c5c2;
  will-change: color;
}
.scene2__title .w.is-read { color: #1A1A1A; }

/* No GSAP, or reduced motion: the sentence is simply the finished colour.
   A statement nobody can scroll-fill must not be left sitting at 25%. */
@media (prefers-reduced-motion: reduce) {
  .scene2__title .w { color: #1A1A1A; }
}

/* ==========================================================================
   4. .fgrid / .fcard — the feature card
   --------------------------------------------------------------------------
   ONE CARD COMPONENT, ONE PLACEMENT NOW. "What's in Clinic OS" runs six of
   them as the payload of its pinned scene. The pillars scene ran three of the
   same object above it and was deleted on request, so the three-up modifier
   went with it (§ below) — but the component stays parameterised, because the
   thing that made it worth writing this way is that a second arrangement
   costs a number rather than a second card.

   `--fcols` rather than two grid classes, so the count is a number at the
   call site instead of a second component.

   .fgrid--tight IS THE SAME CARD WITH LESS AIR IN IT, not a second component:
   it moves two numbers and nothing else. Six cards at the full padding with a
   paragraph each is a wall of text where a comparison should be, and the fix
   is one line of copy per card — but one line inside 36px of padding leaves a
   plate that is mostly empty. The plate, the radius, the lavender icon well
   and the hover lift are untouched.
   ========================================================================== */
.fgrid {
  --fcols: 3;
  display: grid;
  grid-template-columns: repeat(var(--fcols), minmax(0, 1fr));
  gap: clamp(20px, 2.4vw, 32px);
  list-style: none;
  margin: 0;
  padding: 0;
}

.fcard {
  display: grid;
  align-content: start;
  gap: 14px;
  padding: clamp(24px, 2.8vw, 36px);
  border-radius: var(--r);
  background: var(--white);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-xs);
  transition: transform .45s var(--ease), box-shadow .45s var(--ease);
}
.fcard:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }

.fcard__icon {
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  margin-bottom: 4px;
  border-radius: var(--r-xs);
  background: var(--lav-50);
  color: var(--lav-600);
}
.fcard__icon svg { width: 23px; height: 23px; }

.fcard__h {
  font-size: clamp(1.1rem, 1.6vw, 1.28rem);
  letter-spacing: -.03em;
  line-height: 1.2;
}
.fcard__p {
  font-size: .95rem;
  line-height: 1.62;
  color: var(--ink-soft);
}

/* --- the three-up modifier ----------------------------------------------
   FLEX, NOT GRID, AND THAT IS THE FIX. As `.fgrid--3` these three cards were
   three columns of a grid stretched across the full 1240px container, so each
   card was ~390px wide and the row filled the container edge to edge. Against
   a centred statement of a much narrower measure, a row that wide reads as
   leaning — the eye takes the text block as the page's centre line and the
   cards visibly overhang it on both sides, and any difference in card content
   made one side look heavier than the other.

   Capping the ROW at 1140 and each CARD at 360 fixes it at the source: the
   three cards are identical widths by construction, the row is symmetrical
   about its own centre, and `margin: 0 auto` puts that centre on the page's.
   `align-items: stretch` keeps them equal height whatever the copy does.

   Wrapping is what makes it responsive without a second breakpoint — below
   ~1130px the third card drops to its own line and stays centred, and the
   §10 media queries never need to know about this modifier at all. */
/* --- (removed) .fgrid--trio — the three pillar cards --------------------
   The centred three-up row belonged to the pillars scene, which was deleted
   on request: unified patient record / automated insight digests /
   appointments that arrive briefed each restated a module the six-up grid
   below names properly. .fgrid--3 is the only arrangement left.
   ------------------------------------------------------------------------ */

/* --- the six-up modifier ------------------------------------------------ */
.fgrid--tight .fcard { gap: 11px; padding: clamp(20px, 2.1vw, 27px); }
.fgrid--tight .fcard__p { font-size: .92rem; line-height: 1.58; }

/* ==========================================================================
   4b. .split / .mediaframe / .band — the photography and the footage
   --------------------------------------------------------------------------
   THE ASPECT RATIO IS DECLARED FROM THE FILE, AND THE FILE IS NEVER CROPPED
   TO THE BOX. This is the whole of the media policy and every rule below is
   just a consequence of it.

   The source clip is 2160x3840 — 4K PORTRAIT, 9:16. The obvious thing to do
   with a video on a marketing page is to give it a wide box and `object-fit:
   cover`, and on this file that throws away two thirds of the frame and cuts
   the treatment out of a shot of a treatment. So: the frame carries the
   source's own `aspect-ratio`, the media inside is `object-fit: contain` at
   width/height 100%, and the frame is capped narrow enough that a 9:16 block
   never tries to be a hero band. Contain rather than cover is belt and braces
   — with the ratios matched the two are identical, and if a file is ever
   swapped for one of a different shape it letterboxes instead of cropping.

   The photos are pre-cropped to the exact ratio of the frame they go in
   (clinic-consult.jpg 1000x1250 = 4:5, clinic-treatment.jpg 1760x990 = 16:9),
   so the cover on those resolves to a 1:1 fit and no crop happens at all.

   .split IS THE PAGE'S RHYTHM INSTRUMENT. Text one side, one piece of media
   the other, and `--split-flip` swaps which is which so two of them in a page
   do not read as the same block twice. It is what sits between the card grids
   and the lists, and it is the reason no two grids touch.
   ========================================================================== */
.split__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(36px, 5vw, 80px);
  align-items: center;
}
/* The phone-shaped frame is capped at 380px, so an even split leaves it
   stranded in the middle of half a very wide page. The narrow column is
   whichever one the frame is actually in — which the flip swaps. */
.split__grid--narrow { grid-template-columns: minmax(0, 420px) minmax(0, 1fr); }
.split--flip .split__grid--narrow { grid-template-columns: minmax(0, 1fr) minmax(0, 420px); }

/* Source order stays text-after-media for screen readers and for the small
   screen stack; the flip is a visual reordering only. */
.split--flip .split__media { order: 2; }
.split--flip .split__copy  { order: 1; }

.split__copy { display: grid; justify-items: start; gap: 0; }
.split__title {
  margin: 0;
  max-width: 18ch;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(1.9rem, 3.4vw, 2.9rem);
  line-height: 1.08;
  letter-spacing: -.04em;
  color: var(--ink);
  text-wrap: balance;
}
.split__lead {
  margin-top: clamp(18px, 2vw, 26px);
  max-width: 46ch;
  font-size: 1.04rem;
  line-height: 1.66;
  color: var(--ink-soft);
}
.split__copy .btn { margin-top: clamp(24px, 2.6vw, 34px); }

/* --- the frames --------------------------------------------------------- */
.mediaframe {
  position: relative;
  margin: 0;
  width: 100%;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--paper);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-sm);
}
.mediaframe > img,
.mediaframe > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* 4:5, matching clinic-consult.jpg exactly. */
.mediaframe--portrait {
  aspect-ratio: 4 / 5;
  max-width: 520px;
}
.mediaframe--portrait > img { object-fit: cover; }

/* 9:16, matching the clip exactly. The cap is what keeps a portrait video
   from ever being laid out as a wide block. */
.mediaframe--phone {
  aspect-ratio: 9 / 16;
  max-width: 380px;
  border-radius: var(--r-lg);
  background: #0f0d14;
}

/* ==========================================================================
   4c. .amb — the ambient floating tag layer
   --------------------------------------------------------------------------
   THE SAME IDEA AS THE HOMEPAGE STATEMENT'S .chip FIELD, REBUILT RATHER THAN
   REUSED, and the reason is scope: .chip lives in styles.css, which both
   pages load, and it carries a cursor-repel behaviour and a five-tone palette
   this page has no use for. Editing it to suit the clinics page would change
   the homepage. So this is a page-local component with the same job.

   WHAT IT BORROWS: absolutely positioned tags on a z-0 layer under the type,
   a coloured dot before the label, depth expressed as scale + opacity so the
   field reads as three planes rather than one flat sheet.

   WHAT IT CHANGES: the pill is glass rather than solid white (the statement
   scenes are plain cream, and a solid pill on cream is a sticker), the radius
   is a full round instead of 12px, and the tags come in three lengths — a
   badge, a feature pill and a status card. One length repeated eight times is
   wallpaper; three lengths at three depths is a background.

   IT IS DECORATION AND IT IS MARKED AS SUCH. The whole layer is aria-hidden
   and pointer-events: none. Every string in it restates something the page
   says properly somewhere else, and none of it is reachable by keyboard —
   there is nothing here to reach.

   THE STAGE MUST CLIP IT. .scene2 is already `position: relative`; the layer
   is `inset: 0` inside the stage with its own overflow: hidden, so a tag
   drifting at the edge is cut at the section boundary rather than spilling
   into the one above.

   ABSOLUTE, SO IT IS NOT A GRID ITEM. The pinned stage is a one-cell grid
   (§3) and both its real children are placed in that cell. An absolutely
   positioned child is taken out of grid flow entirely, so this layer cannot
   claim a row or shift the statement it sits behind. */
.amb {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* --- the cursor glow ----------------------------------------------------
   A violet radial that follows the pointer across the section, sitting
   BETWEEN the tags and the type: the tags pick up a wash of colour as it
   passes over them, and the headline never does.

   TWO CUSTOM PROPERTIES AND NOTHING ELSE. clinics.js §5b writes --mouse-x and
   --mouse-y as percentages of the section box; the gradient reads them. No
   layout property is touched, so moving the mouse over a pinned scene cannot
   invalidate a ScrollTrigger measurement — which is the whole reason this is
   a background-position trick and not a translated element.

   THE 50%/50% FALLBACKS MATTER. Before the first pointermove — and forever on
   a touch device, where clinics.js never starts the tracker — the glow rests
   dead centre, which is a composition rather than a missing effect.

   opacity: 0 UNTIL THE POINTER IS INSIDE. A radial that is always on is a
   blob painted on the section; one that fades up as the cursor enters and
   away as it leaves reads as a response to the reader. */
.amb__glow {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity .6s var(--ease-soft);
  background: radial-gradient(600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
    rgba(216, 212, 248, .25), transparent 80%);
}
.scene2__stage.is-lit .amb__glow { opacity: 1; }

/* --- the white wash ------------------------------------------------------
   ONE STAGE, TWO SURFACES. The statement and the six-card grid share a single
   pinned stage, so without this the ambient tag field is still painted behind
   the cards — and the grid is meant to be clean white with no floating badges
   in it at all. This pane fades up as the statement leaves (clinics.js §2,
   tied to beat 2 so it can never disagree with it), covering the tags and
   taking the surface to #FFFFFF in the same move.

   IT SITS ABOVE THE TAGS AND THE GLOW, BELOW THE TYPE. z-index 2 against the
   tags' 0 and the glow's 1, with the statement and body lifted to 3 — so it
   hides the decoration and nothing else. Being opaque white is also what
   makes it a background change rather than a veil: at partial opacity it
   would grey the tags on the way out instead of replacing them. */
.scene2__wash {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: #FFFFFF;
  opacity: 0;
  pointer-events: none;
}

/* The two real children of the stage sit above every decorative layer. Stated
   here rather than in §3 because those layers are the only reason either of
   them needs a z-index. */
.scene2__statement,
.scene2__body { position: relative; z-index: 3; }

/* --- slot vs pill, and this split is a bug fix, not tidiness -------------
   GSAP'S CSSPlugin TAKES OWNERSHIP OF THE INDIVIDUAL TRANSFORM PROPERTIES.
   The moment it animates anything transform-ish on an element — here just
   `y` for the float — it writes `translate: none; rotate: none; scale: none`
   inline on that element to stop the standalone properties fighting the
   matrix it is building. Measured: with the pill as the tween target, its
   computed `scale` was `none` both at rest and on hover, so the entire depth
   ladder AND the hover scale silently did nothing.

   So the two effects get two elements, which is the rule this codebase
   already follows for the app cards on the homepage:

     .amb__slot   position + y          — owned by GSAP (clinics.js §5)
     .amb__tag    scale, rotate, hover  — owned by CSS, never touched by GSAP

   --s / --rot are declared on the slot and inherit down to the pill, so the
   placement block below stays a single list of positions and depths. */
.amb__slot {
  --s: 1;                 /* depth scale   */
  --o: 1;                 /* depth opacity */
  --rot: 0deg;            /* resting tilt  */
  position: absolute;
  /* The pill opts back into pointer events; the layer around it is none. */
  pointer-events: auto;
}

/* EVERY TAG IS FULLY OPAQUE, AND THE DEPTH IS IN THE SCALE ALONE. --o used to
   run 0.58 → 0.96 down the depth ladder, which made the outer ring read as
   washed-out and half-loaded rather than distant — on cream, a 58% white pill
   is barely a pill. The far cards are now the same crisp glass as the near
   ones and only their SIZE says they are further away, which is how the
   consumer statement's chips do it too. --o survives as a variable because
   the placement block still declares it, but nothing consumes it. */
.amb__tag {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 17px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, .90);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, .8);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .04);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: clamp(12px, .9vw, 14px);
  letter-spacing: -.005em;
  color: #1A1A1A;
  white-space: nowrap;
  opacity: 1;
  /* Safe on THIS element: nothing animates it from JS, so the standalone
     transform properties survive. See the note above the slot. */
  scale: var(--s);
  rotate: var(--rot);
  cursor: default;
  transition:
    scale .5s var(--ease),
    background-color .5s var(--ease),
    box-shadow .5s var(--ease),
    border-color .5s var(--ease);
}

/* --- hover: it comes FORWARD, it does not go away ------------------------
   NOTHING FADES HERE, AND THAT IS THE POINT. The instinct with a decorative
   layer is to drop it out of the way when the reader engages with it, which
   is exactly backwards: these cards are the thing being pointed at, so the
   pointer should bring them closer. Scale up, brighten the glass, lift the
   shadow — the same three moves the consumer statement pills make.

   THE SCALE MULTIPLIES THE DEPTH RATHER THAN REPLACING IT. A flat
   `scale: 1.1` would snap a far-plane card (--s: .78) up past its near-plane
   neighbours, so the ladder inverts the moment you touch it. Multiplying
   keeps every card in its own plane while still moving it 10% toward you.

   OPACITY GOES TO 1 for the same reason: a far card at .54 that brightens
   its background but keeps its depth opacity reads as smudged, not lit. */
.amb__tag:hover {
  scale: calc(var(--s) * 1.1);
  background: #fff;
  border-color: rgba(255, 255, 255, .95);
  box-shadow: 0 12px 32px rgba(216, 212, 248, .45),
              0 4px 12px rgba(27, 24, 34, .06);
}

/* ONE VIOLET, NOT THREE PASTELS. The dots were #D8D4F8 / #A5B4FC / #C4B5FD —
   three tints so pale that against a white pill they read as smudges rather
   than as indicators. A single saturated violet is legible at 8px, which is
   the only thing a dot this size has to be. */
.amb__dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  flex: none;
  background: #6E56CF;
}

/* --- placement: an even ring, not a scatter and not two edge stacks ------
   TEN POSITIONS ON THREE BANDS PLUS AN OUTER PAIR. The rule that shapes all
   of them is that the CENTRE OF THE SCENE IS SPOKEN FOR — the statement
   holds it, and then three cards land in it. So the ring runs around that
   claim: a top band of three, a shoulder pair just above the cards, an outer
   pair level with them but outside their 1140px row, and a bottom band of
   three. Evenly spread across the width at every band, which is what stops
   the field reading as two piles at the edges.

   DEPTH RUNS FRONT-TO-BACK BY BAND, not at random: the top and bottom rings
   are the near plane, the shoulder pair sits behind them, and the outer pair
   is furthest away. That is what gives the layer three readable planes
   instead of one sheet of pills at slightly different sizes. */
.amb--tl  { top: 11%;    left: 9%;                              --s: 1;   --o: .96; --rot: -3deg; }
.amb--tc  { top: 7%;     left: 50%; margin-left: -70px;         --s: .94; --o: .9;  --rot:  2deg; }
.amb--tr  { top: 11%;    right: 9%;                             --s: 1;   --o: .96; --rot:  3deg; }

.amb--mlc { top: 27%;    left: 20%;                             --s: .88; --o: .8;  --rot:  4deg; }
.amb--mrc { top: 27%;    right: 20%;                            --s: .88; --o: .8;  --rot: -4deg; }

.amb--ocl { top: 49%;    left: 2%;                              --s: .78; --o: .58; --rot: -6deg; }
.amb--ocr { top: 49%;    right: 2%;                             --s: .78; --o: .58; --rot:  6deg; }

.amb--bl  { bottom: 12%; left: 11%;                             --s: .98; --o: .94; --rot:  3deg; }
.amb--bc  { bottom: 7%;  left: 50%; margin-left: -66px;         --s: .92; --o: .88; --rot: -2deg; }
.amb--br  { bottom: 12%; right: 11%;                            --s: .98; --o: .94; --rot: -3deg; }

/* The outer pair sits level with the three feature cards, outside their
   1140px row. Below ~1500px that gutter is narrower than the card is wide,
   so they would sit on top of the content they are meant to frame. */
@media (max-width: 1499px) {
  .amb--ocl, .amb--ocr { display: none; }
}
/* Two bands is still a ring; four cards crowding a phone is not. */
@media (max-width: 980px) {
  .amb--mlc, .amb--mrc, .amb--tc, .amb--bc { display: none; }
}

/* --- (removed) .band — the wide photo under the sync steps --------------
   The treatment photograph that sat under the four sync steps is gone, and
   the .band rules with it. It was doing real damage to the section it was
   supposed to close: at 1000x667 it was the single largest object on the
   page, and the four-step stream — which is the actual argument of §8 and is
   now a pinned, scroll-drawn sequence — was reading as a caption above a
   picture rather than as the point.

   The asset is still in assets/img/ if it is ever wanted elsewhere. Note
   that both source photographs are 4K PORTRAIT close-ups, so any future wide
   block built from them runs into the framing problem written up in §4b.
   ------------------------------------------------------------------------ */

/* --- section rhythm -----------------------------------------------------
   §6 and §7 are one argument in two blocks — the media break states what the
   automation does and the list is the evidence — so the seam between them is
   closed up rather than given a full section's worth of air, which would read
   as the list belonging to nothing. The intro split gets the opposite: it is
   followed by a pinned scene that opens with a centred statement, and that
   needs to arrive on an empty screen. */
.section--media { padding-bottom: clamp(40px, 4.5vw, 68px); }
.section--intro { padding-bottom: clamp(104px, 11vw, 180px); }

/* --- the automation section is a plain block ------------------------------
   It keeps none of the shared .section box. The stage below owns the padding
   and the rhythm; the section is just the thing it sits in. */
.section--list {
  display: block;
  min-height: 0;
  padding-block: 0;
}

/* --- the automation stage ------------------------------------------------
   THE PIN IS BACK, AND THE COMPACT CARD IS WHAT MADE IT POSSIBLE AGAIN. It
   was removed when the card carried a 220px photograph and ran 493px tall:
   two bands of those came to ~1190px against the ~920px a 1000px window
   leaves below the nav, and a pin cannot compress what it locks — it would
   have put the bottom track permanently out of reach. At 320x339 the pair is
   ~758px, which fits, so clinics.js §7b locks the stage for 150% and the
   tracks glide under the lock.

   THE STAGE IS A FULL SCREEN, which it did not need to be while unpinned. A
   pin holds whatever box it is given, so the box has to be the thing the
   reader should be looking at: 100svh centred, with the nav cleared at the
   top. Padding rather than margin, because `box-sizing: border-box` is global
   and min-height counts it — the stage is exactly one screen either way.

   ITS GATE IS TALLER THAN THE OTHER PINS' (900px against 700px, clinics.js
   §7b), and the number comes from the box below rather than from taste:
   758px of bands + 78px of nav + up to 48px of bottom padding = 884px, so a
   window under 900 cannot hold this stage and pinning it there would lock the
   bottom track out of reach. Below the gate the section is ordinary flow and
   the tracks simply run. */
.autos__stage {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  min-height: 100svh;
  padding-top: var(--nav-h);
  padding-bottom: clamp(24px, 3vw, 48px);
}

/* ==========================================================================
   5. .mq / .mqcard — the dual capability marquee
   --------------------------------------------------------------------------
   Two tracks, opposite directions, running forever. The section above states
   what these ten things are; this is them, moving, because "it runs on its
   own" is a claim about continuity and a static list is the one shape that
   cannot express it.

   THE LOOP IS ONE TRANSFORM AND A DUPLICATED RUN. clinics.js clones the five
   cards once, so the track holds ten and is exactly twice the width of its
   content run. Translating it by half its own width therefore lands on a
   frame pixel-identical to the start — that is why the keyframe is -50% and
   not a px value, and why the clone must be exact.

   ANIMATION, NOT SCROLL POSITION. These tracks are not tied to the reader's
   gesture: they run whether or not the page is being scrolled, which is the
   entire point. It is also why the whole thing costs one composited
   transform per track and no JavaScript after the clone.

   --mq-dur IS PER TRACK so the two are not in lockstep. Equal speeds in
   opposite directions is symmetrical enough to look mechanical; a few
   seconds' difference makes the pair read as two independent processes.

   THE MASK IS WHAT MAKES IT A TRACK RATHER THAN A ROW THAT OVERFLOWS. Cards
   fade out at both edges instead of being chopped by the viewport, so there
   is no hard vertical line where the content is clipped.

   THE BAND HUGS ITS CARDS AND THE PADDING IS SHADOW ROOM. `.mq` has to stay
   `overflow: hidden` for the mask and the clip, so anything a card paints
   outside its own box has to fit in room the band already has. The card no
   longer scales — it lifts 4px — but its hover shadow is `0 16px 32px`,
   which reaches ~32px below the box. 28px of padding covers that and the
   lift with a little over. Mixing `overflow-x: hidden` with
   `overflow-y: visible` is not an option: CSS promotes the visible axis to
   `auto`, and the section grows a scrollbar.

   ALIGN-ITEMS: STRETCH. Nothing about a card changes height — the sentence is
   always shown — so every card in a row matching the tallest is exactly what
   is wanted, and it is what keeps the row a row.
   ========================================================================== */
.mq {
  position: relative;
  width: 100%;
  overflow: hidden;
  padding-block: 28px;
  -webkit-mask-image: linear-gradient(to right,
    transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(to right,
    transparent, #000 6%, #000 94%, transparent);
}

/* 24px BETWEEN THE CARDS, WHICH IS NOT 24px BETWEEN THE BANDS. Each band
   carries 28px of invisible shadow room top and bottom, so stacking them with
   a 24px margin would put 80px of daylight between the two rows. The margin
   is the 24px the design asks for minus the 56px of padding already between
   them — the bands overlap in their own empty gutters and the cards land
   exactly 24px apart. */
.mq + .mq { margin-top: -32px; }

.mq__track {
  --mq-dur: 56s;
  display: flex;
  align-items: stretch;
  gap: 24px;
  width: max-content;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* No .is-ready — no clone — no animation. A five-card track animated as if it
   were ten jumps visibly back to its start every lap, which is worse than a
   static row. clinics.js adds the class only after the duplicate is in.

   THE DURATION IS TUNED TO PIXELS PER SECOND, NOT TO A FEELING. Speed reads
   relative to the size of the thing moving, and the card has been three sizes
   now — so the number is derived rather than picked. A 320px card with a 24px
   gap makes a five-card run 1696px, which the loop crosses in --mq-dur, so
   56s is ~61px/s and the 62s of the lower track ~55px/s. That is the band
   these have read well in throughout; the durations changed with every
   resize precisely to keep it. */
.mq__track.is-ready { animation: mqRun var(--mq-dur) linear infinite; }
.mq__track[data-mq-dir="right"].is-ready { animation-direction: reverse; }
.mq__track[data-mq-dir="left"]  { --mq-dur: 62s; }

@keyframes mqRun {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* --- only the cards are hit-targets, and the overlap is why ---------------
   The bands overlap by 32px (see the margin above), so the lower band's box —
   and its track, which fills it — sits over the upper band's bottom gutter.
   Being the later sibling it hit-tests first, so a pointer aimed at the
   bottom edge of an upper card lands on the lower band's empty track and the
   card never lights. So the bands and tracks stop being hit-targets and the
   cards opt back in, the same layer/element split the ambient tags use in
   §4c. */
.mq,
.mq__track { pointer-events: none; }
.mqcard { pointer-events: auto; }

/* Hovering a card stops its whole track outright, so the card being read
   cannot slide out from under the reader.

   :has() RATHER THAN .mq:hover, WHICH THE RULE ABOVE JUST MADE IMPOSSIBLE —
   a pointer-events: none ancestor never matches :hover even while a
   descendant with pointer-events: auto does. :has() asks the question the
   other way round. :focus-within needs no such help: focus propagates to
   ancestors regardless of hit-testing. */
.mq__track.is-ready:has(.mqcard:hover),
.mq__track.is-ready:has(.mqcard:focus-visible),
.mq:focus-within .mq__track.is-ready { animation-play-state: paused; }

/* The band holding the lifted card comes forward. `mask-image` makes each .mq
   a STACKING CONTEXT, so a card's own z-index is trapped inside its band and
   the two bands order against each other by DOM position alone — without
   this, a lifted card in the top track is painted over by the bottom one. */
.mq:has(.mqcard:hover),
.mq:has(.mqcard:focus-visible),
.mq:focus-within { z-index: 5; }

/* --- the card ------------------------------------------------------------
   COMPACT, AND EVERYTHING IN IT IS VISIBLE AT ALL TIMES. It carries a
   category, a photograph, a title and one short sentence, and hides none of
   it. That is what let the section stop being pinned (§4b): a card that has
   to be hovered to be read needs the page to hold still, and a card that does
   not, does not.

   320 WIDE, DOWN FROM 420. At the larger size two rows of them ran ~1190px
   and the section was two full screens of card; at 320 with a 160px image the
   card lands ~330px and the pair reads as a component rather than a wall.

   THE WIDTH IS FIXED, NOT A min/max RANGE, AND THAT IS LOAD-BEARING. The loop
   translates the track by exactly -50% of its own width, which only lands on
   an identical frame while that width is CONSTANT. A card whose width can
   respond to its own content resizes the track mid-animation and the loop
   develops a visible slip every lap. (min-width is set alongside `width` only
   because the spec names it; on a `flex: none` item it is the same number
   twice and neither can win over the other.)

   THE PADDING AND THE COLUMN ARE ON .mqcard__in, NOT HERE, because the glare
   and foil layers below are `inset: 0` and have to reach the card's true
   edges — 20px of padding on the card itself would inset them and leave an
   unlit border all the way round.

   --r-x / --r-y / --m-x / --m-y / --glare ARE WRITTEN BY clinics.js §7b. They
   default to a flat, unlit card, so a card that JS never reaches is simply a
   still card rather than a broken one. */
.mqcard {
  --r-x: 0deg;              /* tilt about the vertical axis */
  --r-y: 0deg;              /* tilt about the horizontal axis */
  --m-x: 50%;               /* glare centre */
  --m-y: 50%;
  --bg-x: 50%;              /* foil offset */
  --bg-y: 50%;
  --glare: 0;               /* 0 at rest, 1 with a pointer inside */
  --lift: 0px;
  --sc: 1;

  position: relative;
  z-index: 0;
  flex: none;
  width: 320px;
  min-width: 320px;
  border-radius: 18px;
  background: rgba(255, 255, 255, .90);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid rgba(255, 255, 255, .7);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .04);
  overflow: hidden;         /* clips the glare + foil to the 18px radius */
  cursor: default;
  transform:
    perspective(1100px)
    rotateY(var(--r-x)) rotateX(var(--r-y))
    translateY(var(--lift)) scale(var(--sc));
  transition:
    transform .3s ease,
    box-shadow .3s ease,
    border-color .3s ease;
  will-change: transform;
}

/* --- hover: it lifts ------------------------------------------------------
   THE LIFT AND THE SCALE ARE CUSTOM PROPERTIES RATHER THAN A SECOND
   `transform` DECLARATION, because the pointer tilt already owns transform
   and a competing declaration would replace it outright — the card would snap
   flat at the exact moment it was hovered. Feeding all three through one
   transform is what lets them compose.

   1.03, NOT THE 1.08 THE BIG CARD USED. At 320px a card is close enough to
   its neighbours that a larger scale reads as the row jostling; 1.03 plus the
   4px lift and the deeper shadow is the whole response. */
.mqcard:hover,
.mqcard:focus-visible,
.mqcard:focus-within {
  --lift: -4px;
  --sc: 1.03;
  z-index: 20;
  box-shadow: 0 16px 32px rgba(0, 0, 0, .08);
  border-color: rgba(255, 255, 255, .9);
  outline: none;
}

/* --- the glare, and the foil under it ------------------------------------
   The move borrowed from the reference card: a bright sheen that tracks the
   pointer across the surface, over a faint iridescent sweep that shifts as
   the card tilts. Both fade in from nothing, so a card nobody is pointing at
   is plain glass.

   TONED TO THIS PAGE RATHER THAN COPIED. The reference is a dark card under a
   full-spectrum rainbow foil; on a cream page full of white glass that reads
   as a novelty sticker. The foil here is the site's own lavender and
   periwinkle range at low opacity under `soft-light`, which on a light surface tints
   rather than blows out — `color-dodge`, which the dark original uses, drives
   anything already near-white straight to pure white.

   BOTH ARE pointer-events: none, so the layer that paints the effect can
   never be the thing the pointer lands on. */
.mqcard__foil,
.mqcard__glare {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--glare);
  transition: opacity .4s var(--ease-soft);
}

.mqcard__foil {
  z-index: 2;
  --step: 6%;
  mix-blend-mode: soft-light;
  background:
    repeating-linear-gradient(102deg,
      rgba(216, 212, 248, .95) calc(var(--step) * 1),
      rgba(165, 180, 252, .95) calc(var(--step) * 2),
      rgba(196, 181, 253, .95) calc(var(--step) * 3),
      rgba(226, 216, 248, .95) calc(var(--step) * 4),
      rgba(216, 212, 248, .95) calc(var(--step) * 5))
      var(--bg-x) var(--bg-y) / 260% 260% no-repeat,
    radial-gradient(farthest-corner circle at var(--m-x) var(--m-y),
      rgba(255, 255, 255, .55) 10%,
      rgba(255, 255, 255, .15) 45%,
      rgba(255, 255, 255, 0) 90%)
      var(--bg-x) var(--bg-y) / 300% 300% no-repeat;
  background-blend-mode: overlay;
}

.mqcard__glare {
  z-index: 3;
  background: radial-gradient(farthest-corner circle at var(--m-x) var(--m-y),
    rgba(255, 255, 255, .9) 6%,
    rgba(255, 255, 255, .45) 20%,
    rgba(255, 255, 255, 0) 62%);
}

/* The content sits above both effect layers. The 12px gap replaces every
   per-child margin — one number owns the vertical rhythm of the card, so
   changing the density is one edit rather than four. */
.mqcard__in {
  position: relative;
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  padding: 20px;
}

/* --- the category pill, the first thing in the card ---------------------- */
.mqcard__tag {
  align-self: flex-start;
  margin: 0;
  padding: 3px 8px;
  border-radius: 100px;
  background: #F0EDFF;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #6E56CF;
}

/* --- the photograph -------------------------------------------------------
   THE SOURCES ARE 2:3 PORTRAIT (840x1260 after the resize) IN A 320x160 BAND,
   so `cover` keeps a horizontal slice and discards the rest.

   object-position IS BACK TO `center`, WHICH IT WAS NOT AT 220px. The taller
   band needed a 38% bias to keep the subject out of the bottom of the frame;
   at 160px the slice is narrow enough that biasing it upward starts cutting
   foreheads instead. Centre is the right compromise at this height.

   The figure carries a lavender wash of its own, so a slot whose file has not
   been dropped in yet is a soft branded panel rather than a broken-image
   glyph. clinics.js §7c adds .is-missing when a source 404s. */
.mqcard__shot {
  position: relative;
  margin: 0;
  width: 100%;
  height: 160px;
  flex: none;
  border-radius: 12px;
  overflow: hidden;
  background:
    linear-gradient(142deg, var(--lav-100), var(--lav-50) 60%, #fff);
}
.mqcard__shot img {
  display: block;
  width: 100%;
  height: 160px;
  object-fit: cover;
  object-position: center;
}
.mqcard__shot.is-missing img { display: none; }

.mqcard__h {
  margin: 0;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -.024em;
  line-height: 1.25;
  color: #1A1A1A;
}

.mqcard__p {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: #555555;
}

/* Reduced motion: the tracks hold still. Nothing else needs unwinding — the
   cards were never hiding anything behind an animation. */
@media (prefers-reduced-motion: reduce) {
  .mq__track.is-ready { animation: none; }
  .mqcard { transition: none; }
}

/* ==========================================================================
   6. .flow — how the sync works
   --------------------------------------------------------------------------
   Four steps under one rule, in the same language as the onboarding cards but
   without their card: this is a sequence to read across, and four plates in a
   row would compete with the six above it for the same attention.

   THE RULE IS DRAWN PER STEP AND SUPPRESSED ON THE LAST ONE. Drawing one
   continuous line across the list is fewer rules and looks the same for three
   quarters of its length — but it then runs on past the fourth marker to the
   edge of the grid with nothing after it, which reads as a fifth step that
   failed to render. A connector has to end at the thing it connects to.

   Each segment therefore runs from the right edge of its own marker to the
   left edge of the next one: 100% of the column, plus the gap, less the
   marker and its clearance. --flow-gap exists so that number and the grid's
   own gap can only ever be the same value.
   ========================================================================== */
.flow {
  --flow-gap: clamp(24px, 3vw, 40px);
  --node: 36px;
  /* THE STEP COUNT, AND THE ONLY PLACE IT IS WRITTEN. The grid reads it and
     so does the rail's right-hand inset, which has to end at the centre of
     the LAST node — a number that changes with the column count. Adding the
     fifth step was this variable and one <li>; hard-coding 4 in two places is
     what made the rail overshoot the last marker the first time. */
  --cols: 5;
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: var(--flow-gap);
  list-style: none;
  margin: 0;
  /* No bottom padding. It reserved room for the return arc, which has been
     removed — left behind it would be ~60px of empty section under the step
     descriptions with nothing in it. */
  padding: 0;
}

/* --- the rail ------------------------------------------------------------
   ONE ELEMENT SPANNING ALL FOUR NODES, drawn behind them. It starts at the
   centre of node 1 and ends at the centre of node 4, which is why the insets
   are half a node either side rather than 0: a rail that runs to the edges of
   the grid points at nothing at both ends, and the reader reads that as two
   steps that failed to render.

   The fill is a separate element inside the track so the unfilled remainder
   stays visible ahead of the head — the reader can see how much of the
   sequence is still to come, which is most of what makes a progress line
   worth drawing at all. */
/* THE INSETS ARE HALF A NODE, NOT HALF A COLUMN, and getting this wrong is
   what made the rail look truncated. The node is a 36px circle at the START
   of its column, not centred in it — so node 1's centre is 18px from the
   grid's left edge, and the last node's centre is 18px into the last column,
   which is (column width - 18px) from the right edge. Insetting by half a
   column instead started the rail ~110px right of node 1 and ran it ~110px
   past the last one: a line that begins after the first marker and overshoots
   the last.

   Column width is (100% - (cols-1) gaps) / cols, so the right inset below is
   that minus half a node. Every term is a variable — --flow-gap, --node and
   --cols — so the grid's gap, the marker size and the number of steps cannot
   drift out of step with the rail that connects them. That is what let the
   fifth step be added without touching this rule at all. */
.flow__rail {
  position: absolute;
  top: calc(var(--node) / 2 - 1px);
  left: calc(var(--node) / 2);
  right: calc(
    (100% - var(--flow-gap) * (var(--cols) - 1)) / var(--cols)
    - var(--node) / 2);
  height: 2px;
  border-radius: 2px;
  background: var(--line);
  z-index: 0;
}
.flow__rail-fill {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(to right, var(--lav-600), var(--lav));
  /* scaleX from a left origin: one transform, GPU-composited, and it cannot
     reflow the four columns it runs under however far it is driven. */
  transform: scaleX(0);
  transform-origin: 0 50%;
}

/* --- (removed) .flow__loop — the dashed return arc -----------------------
   A dashed curve ran from the centre of the last node back to the centre of
   the first, with an arrowhead into node 1, and was removed on request along
   with the padding-bottom on .flow that reserved room for it. The straight
   rule through the five nodes is the only connector this section carries.
   ------------------------------------------------------------------------ */

.flowstep {
  position: relative;
  z-index: 1;
  display: grid;
  align-content: start;
  gap: 12px;
}

/* --- the nodes -----------------------------------------------------------
   Every node starts unlit and neutral. There is no `:first-child` exception
   any more: which node is active is a fact about scroll position now, and
   baking one into the cascade meant step 01 was lit before the sequence had
   started and stayed lit after it had passed. */
.flowstep__no {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--node); height: var(--node);
  margin-bottom: 6px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--line-2);
  font-family: var(--font-head);
  font-weight: 700;
  font-size: .86rem;
  color: var(--ink-mute);
  transition:
    background-color .45s var(--ease-soft),
    border-color .45s var(--ease-soft),
    color .45s var(--ease-soft),
    box-shadow .45s var(--ease-soft);
}

/* Lit: the soft lavender glow, set as a class by clinics.js §6 off the same
   timeline that draws the rail, so the rail head and the lit node can never
   disagree about where the sequence has got to. */
.flowstep.is-on .flowstep__no {
  background: var(--lav);
  border-color: var(--lav-600);
  color: #2c2540;
  box-shadow: 0 0 0 6px rgba(216, 212, 248, .45),
              0 6px 18px rgba(122, 97, 189, .22);
}
.flowstep__h,
.flowstep__p { transition: color .45s var(--ease-soft); }
.flowstep.is-on .flowstep__h { color: var(--ink); }
.flowstep__h {
  font-size: 1.06rem;
  letter-spacing: -.028em;
  line-height: 1.25;
}
.flowstep__p {
  font-size: .93rem;
  line-height: 1.62;
  color: var(--ink-soft);
}

/* ==========================================================================
   7. (removed) .devband — the in-development strip
   --------------------------------------------------------------------------
   The muted "in development" strip and everything that styled it — .devband,
   .dev, .dev__label, .dev__body, .dev__title, .dev__note — were deleted on
   request along with the section itself. Clinic OS is presented as one
   shipped platform, so there is no roadmap block and no release-stage label
   anywhere on the page. The --paper surface change went with it: the page is
   now --cream end to end, with no exception.
   ========================================================================== */

/* ==========================================================================
   8. ONBOARDING CARDS
   --------------------------------------------------------------------------
   THREE ROWS FOR THREE CHILDREN, and the copy takes the slack. That is what
   keeps the three cards reading as a set: they are equal height because they
   are grid siblings, and a `1fr` on the paragraph puts every pixel of the
   difference inside the copy block rather than leaving one card's heading
   floating higher than its neighbours'.

   Nothing here is taken out of flow. The index and the badge are the two ends
   of a flex row of their own, so a long badge pushes nothing and wraps
   nowhere — the old version positioned the step number absolutely over the
   card and let the heading run under it, which collided at every width where
   the heading wrapped.
   ========================================================================== */
.convo__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(32px, 3.4vw, 48px);
  list-style: none;
  margin: 0;
  padding: 0;
}

.convo__card {
  display: grid;
  grid-template-rows: auto auto 1fr;
  gap: 14px;
  padding: clamp(26px, 2.8vw, 36px);
  border-radius: var(--r);
  background: var(--white);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-xs);
  transition:
    transform .5s var(--ease),
    box-shadow .5s var(--ease),
    border-color .5s var(--ease-soft);
}
.convo__card.is-on {
  border-color: var(--lav-300);
  box-shadow: var(--shadow-md);
  transform: translateY(-6px);
}

.convo__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.convo__no {
  font-family: var(--font-head);
  font-weight: 800;
  font-size: .82rem;
  letter-spacing: .16em;
  color: var(--ink-mute);
  transition: color .4s var(--ease-soft);
}
.convo__card.is-on .convo__no { color: var(--lav-700); }

.convo__badge {
  padding: 6px 13px;
  border-radius: 999px;
  background: var(--cream);
  border: 1px solid var(--line-2);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: .68rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
  transition:
    background .4s var(--ease-soft),
    border-color .4s var(--ease-soft),
    color .4s var(--ease-soft);
}
.convo__card.is-on .convo__badge {
  background: var(--lav);
  border-color: var(--lav-600);
  color: #2c2540;
}

.convo__h {
  font-size: clamp(1.18rem, 1.8vw, 1.4rem);
  letter-spacing: -.03em;
}
.convo__p {
  font-size: .95rem;
  line-height: 1.62;
  color: var(--ink-soft);
}

/* ==========================================================================
   9. THE CLOSING SCENE — scheduler + footer, one container
   --------------------------------------------------------------------------
   ONE BLOCK, NOT TWO. The scheduler and the footer are one `min-height:
   100svh` container with no border and no change of surface between them, so
   the end of the page reads as a single closing screen rather than a section
   followed by a page footer.

   IT IS NOT A FLEX COLUMN, AND THAT IS NOT A STYLE CHOICE — IT IS FORCED.
   This started as `display: flex; flex-direction: column; justify-content:
   space-between`, which is the obvious way to write "one screen, scheduler at
   the top, footer at the bottom". It silently broke the scene. ScrollTrigger
   REFUSES TO ADD PIN SPACING WHEN THE PINNED ELEMENT'S PARENT IS FLEX — from
   the vendored source, where `se` is pinSpacing and `ae` is the pin:

       se = !(!se && ae.parentNode && ae.parentNode.style &&
              "flex" === mb(ae.parentNode).display) && bt

   so an unset pinSpacing inside a flex parent resolves to `false`. The pin
   below then reserved no scroll length at all: its trigger still ran past the
   end of the document, so the sequence could only ever reach a quarter of its
   progress and the statement never left the screen. No error, no warning —
   just a scene that does not play.

   Nothing is lost. `space-between` had nothing to distribute anyway: the pin
   below is itself `min-height: 100svh`, so the free space it would have been
   sharing out is always zero, on every path. Ordinary block flow puts the
   footer exactly where the flex version did, and the pin works.
   ========================================================================== */
.closing {
  position: relative;
  background: var(--cream);
  min-height: 100svh;
}

/* The pinned part: statement, then the scheduler in its place. Same two-in-
   one-cell arrangement as §3 and for the same reason — see the note there. */
.closing__pin {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: center;
  gap: clamp(40px, 5vw, 64px);
  padding: calc(var(--nav-h) + clamp(40px, 5vw, 72px)) 0 clamp(48px, 6vw, 76px);
}
.closing__pin.is-seq {
  gap: 0;
  padding: var(--nav-h) 0 0;
}
.closing__pin.is-seq > .closing__statement,
.closing__pin.is-seq > .closing__panel {
  grid-area: 1 / 1;
  align-self: center;
}
.closing__pin.is-seq > .closing__panel { width: 100%; }

.closing__statement {
  width: min(880px, 92vw);
  margin: 0 auto;
  text-align: center;
}
.closing__title {
  margin: 0;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: clamp(2.1rem, 4.8vw, 3.7rem);
  line-height: 1.05;
  letter-spacing: -.042em;
  color: var(--ink);
  text-wrap: balance;
}
.closing__lead {
  margin-top: 20px;
  font-size: 1.06rem;
  color: var(--ink-soft);
}

/* ------------------------------------------------------------ the booker -- */
.booker {
  max-width: 1060px;
  margin: 0 auto;
  padding: clamp(22px, 2.8vw, 34px);
  border-radius: var(--r-lg);
  background: var(--white);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-md);
}

/* --- step indicator ----------------------------------------------------- */
.booker__steps {
  display: flex;
  gap: 10px;
  margin: 0 0 clamp(20px, 2.4vw, 28px);
  padding: 0;
  list-style: none;
}
.booker__steps li {
  flex: 1;
  padding-top: 12px;
  border-top: 2px solid var(--line);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-mute);
  transition: color .35s var(--ease-soft), border-color .35s var(--ease-soft);
}
.booker__steps li.is-on {
  border-top-color: var(--lav);
  color: var(--lav-700);
}

.booker__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(26px, 4vw, 48px);
  align-items: start;
}

/* --- calendar ----------------------------------------------------------- */
.cal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.cal__month {
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -.02em;
}
.cal__nav {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--ink);
  transition: background .25s, color .25s, opacity .25s;
}
.cal__nav svg { width: 16px; height: 16px; }
.cal__nav:hover:not(:disabled) { background: var(--ink); color: var(--white); }
.cal__nav:disabled { opacity: .3; cursor: not-allowed; }

.cal__dow,
.cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
}
.cal__dow {
  margin-bottom: 8px;
  font-family: var(--font-head);
  font-weight: 600;
  font-size: .68rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-mute);
  text-align: center;
}

.day {
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border-radius: var(--r-xs);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: .92rem;
  color: var(--ink);
  background: var(--cream);
  border: 1.5px solid transparent;
  transition:
    background .25s var(--ease-soft),
    color .25s var(--ease-soft),
    border-color .25s var(--ease-soft),
    transform .3s var(--ease);
}
.day:hover:not(:disabled) { background: var(--lav-100); transform: translateY(-2px); }
.day:disabled {
  color: var(--ink-mute);
  background: transparent;
  cursor: not-allowed;
  opacity: .45;
}
.day--empty { background: none; pointer-events: none; }
.day--today { border-color: var(--lav-300); }
.day[aria-pressed="true"] {
  background: var(--lav);
  border-color: var(--lav-600);
  color: #241f33;
}

.cal__note {
  margin-top: 16px;
  font-size: .8rem;
  color: var(--ink-soft);
}

/* --- slots -------------------------------------------------------------- */
.booker__side { min-height: 240px; }

.slots__empty {
  padding: 34px 26px;
  border-radius: var(--r-sm);
  border: 1px dashed var(--line);
  background: var(--cream);
  text-align: center;
  font-size: .93rem;
  color: var(--ink-soft);
}
.slots__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 8px;
}
.slot {
  padding: 12px 8px;
  border-radius: var(--r-xs);
  border: 1.5px solid var(--line);
  background: var(--white);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: .9rem;
  transition:
    background .25s var(--ease-soft),
    border-color .25s var(--ease-soft),
    color .25s var(--ease-soft),
    transform .3s var(--ease);
}
.slot:hover { border-color: var(--lav); transform: translateY(-2px); }
.slot[aria-pressed="true"] {
  background: var(--lav);
  border-color: var(--lav-600);
  color: #241f33;
}

/* --- details form ------------------------------------------------------- */
.booker__form { margin-top: 20px; }
.booker__chosen {
  margin-bottom: 16px;
  padding: 13px 16px;
  border-radius: var(--r-xs);
  background: var(--lav-50);
  border: 1px solid var(--line-2);
  font-family: var(--font-head);
  font-weight: 600;
  font-size: .9rem;
  color: var(--lav-700);
}
.opt {
  font-weight: 500;
  font-size: .74rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

/* --- confirmation ------------------------------------------------------- */
.booked {
  text-align: center;
  padding: clamp(26px, 4vw, 40px) 20px;
  border-radius: var(--r-sm);
  background: var(--lav-50);
  border: 1px solid var(--line-2);
}
.booked h3 { font-size: 1.4rem; }
.booked__when {
  margin-top: 12px;
  font-family: var(--font-head);
  font-weight: 700;
  font-size: 1.02rem;
  color: var(--lav-700);
}
.booked__note {
  margin-top: 14px;
  font-size: .92rem;
  color: var(--ink-soft);
}
.booked .btn { margin-top: 26px; }

/* ------------------------------------------------- the full-bleed footer --
   THE SHRUNKEN-FOOTER FIX. The footer's rows are `.container`, which is
   capped at --maxw (1240px). On the homepage that cap is lifted to 1560px by
   `.closing .container` so the footer lines up with the CTA card above it;
   this page has no such card, so the cap stayed at 1240 and the footer sat
   visibly narrower than the scheduler and the scenes above it — a 1240px
   block of links marooned in the middle of a 1900px screen.

   `max-width: none` on the rows, not a new number: there is nothing above it
   here to line up WITH, so the honest width is the full page inside its own
   gutter. --pad still supplies that gutter, so the columns keep their margin
   from the window edge at every width.
   ------------------------------------------------------------------------- */
.footer--full {
  width: 100%;
  padding-top: clamp(28px, 4vw, 56px);
  border-top: 1px solid var(--line);
}
.footer--full .container { max-width: none; }
/* SIX COLUMNS SINCE THE LEGAL COLUMN WAS ADDED: Location, Contact, Follow,
   Platform, Celes, Legal. This footer is the full-bleed one (.footer--full
   drops the container's max-width above), so at 1440 the sixth column costs
   each of the others about 30px rather than the 45px it would cost inside a
   1240px box — which is why the homepage footer stops at five and this one
   can carry the same six links in a column of their own.

   The gap ceiling comes down from 44 to 36 for the same reason: six columns
   at the old maximum left "Data Deletion Request" wrapping to three lines on
   a 1440 screen. */
/* THE FIRST COLUMN IS WIDER THAN THE OTHER FIVE, and that is the address's
   doing rather than a preference. At an even 1fr, "Celes Global Private
   Limited" and "Hyderabad, Telangana, India" both broke across two lines, and
   an address wrapped mid-phrase reads as a layout that ran out of room. 1.3fr
   gives it about 40px more and holds each line whole; the five link columns
   lose 8px each, which none of them needed. */
.footer--full .footer__cols {
  grid-template-columns: 1.3fr repeat(5, minmax(0, 1fr));
  gap: clamp(22px, 2.4vw, 36px);
}

/* ========================================================= 10. Responsive */

/* Six cards three-up need more room than three do, so the feature grid drops
   to two columns before the layout does anything else. */
@media (max-width: 1180px) {
  .fgrid--3 { --fcols: 2; }

  /* The phone frame has a fixed 380px cap, so below this the fixed column and
     the fluid one stop being a sensible ratio and it goes back to an even
     split with the frame centred in its half. */
  .split__grid--narrow { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* The pinned sequences stand down at 980px (clinics.js §2 gates on the same
   number). Below it the stages are plain flow columns, so they must not be
   forced to a screen each — that is what leaves a phone scrolling through
   half-empty views. */
@media (max-width: 980px) {
  .scene2__stage,
  .closing__pin { min-height: 0; }

  .scene2__stage,
  .closing__pin {
    padding:
      calc(var(--nav-h) + clamp(52px, 9vw, 84px)) 0
      clamp(64px, 10vw, 96px);
  }
  .closing { min-height: 0; }

  /* Media over text, both full width, media first — the photograph is the
     faster read and it belongs at the top of the block on a narrow screen.
     The flip modifier's order is reset with it, or the copy would jump above
     the media in one of the two blocks and not the other. */
  /* .split--flip .split__grid--narrow above is a two-class selector, and a
     media query adds no specificity — so the single-column rule has to match
     it selector for selector or the flipped block keeps its 420px column on a
     390px screen, which collapses the other one to nothing. */
  .split__grid,
  .split__grid--narrow,
  .split--flip .split__grid--narrow { grid-template-columns: minmax(0, 1fr); gap: clamp(28px, 5vw, 44px); }
  .split--flip .split__media,
  .split--flip .split__copy { order: 0; }

  /* CENTRED WITH AUTO MARGINS, NEVER `justify-self: center`. The frames are
     width:100% under a max-width cap, and justify-self takes a grid item off
     `stretch` and on to fit-content — which for the video is a box with no
     intrinsic size until the file loads, so the frame resolves to a couple of
     pixels and the observer that would have loaded it never sees it come into
     view. Auto margins centre the item without touching how it is sized. */
  .split__media { margin-inline: auto; }
  .split__title { max-width: 22ch; }


  /* The rail runs along ONE row. Two-up it would join step 1 to step 2 and
     then stop dead, with steps 3, 4 and 5 on rows it never reaches — so it
     comes off entirely rather than being made to wrap. The nodes still light
     in sequence; only the line between them goes. */
  .flow {
    --cols: 2;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: clamp(28px, 4vw, 44px);
  }
  .flow__rail { display: none; }

  .convo__grid { grid-template-columns: 1fr; }


  .booker__body { grid-template-columns: 1fr; }
  .booker__side { min-height: 0; }

  .footer--full .footer__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* The plane is fitted by scaling as a whole, never by re-laying it out. One
     number does it — and it comes DOWN as the viewport narrows, because the
     rhombus only has to reach a smaller frame and a smaller scale puts more
     tiles across it. */
  .marquee--bleed { --mq-s: .58; }
}

@media (max-width: 760px) {
  .chero { min-height: 560px; }
  .chero__title { font-size: clamp(2rem, 8vw, 2.7rem); max-width: none; }
  .chero__actions { width: 100%; flex-direction: column; }
  .chero__actions .btn { width: 100%; text-align: center; }
  .chero__cue { display: none; }

  .fgrid--3 { --fcols: 1; }
  .flow { --cols: 1; grid-template-columns: minmax(0, 1fr); }

  .marquee--bleed { --mq-s: .46; }
}

@media (max-width: 560px) {
  .booker { padding: 18px; }
  .booker__steps li { font-size: .68rem; letter-spacing: .06em; }
  .cal__dow { font-size: .6rem; }
  .day { font-size: .84rem; border-radius: 8px; }

  .footer--full .footer__cols { grid-template-columns: 1fr; gap: 26px; }

  .marquee--bleed { --mq-s: .42; }
}

/* ------------------------------------------------------ Reduced motion ---
   styles.css already stands the marquee's drift down wholesale (§17). The
   only thing left that is this page's own is the hero's scroll cue, which is
   a loop with no resting state worth keeping. */
@media (prefers-reduced-motion: reduce) {
  .chero__cue { animation: none; transform: translateX(-50%); opacity: .55; }
}

/* ==========================================================================
   11. MOBILE ISOLATION — below 768px only
   --------------------------------------------------------------------------
   The same containment rule as styles.css §20, and for the same reason: every
   declaration is inside the query, so no desktop layout can be reached by it.
   §10's breakpoints (1180 / 980 / 760 / 560) still do the ordinary responsive
   work; this block is only the phone-specific viewport-unit correction and the
   two places this page still has more than one column at that width.
   ========================================================================== */
@media (max-width: 768px) {
  /* Dynamic viewport: the hero fills what is actually visible and re-fills as
     the address bar retracts, instead of being sized for a bar state that is
     not the current one. See §20 in styles.css for why not vh and not svh. */
  .chero { height: 100dvh; min-height: 520px; }

  /* Both remaining two-column blocks. The marquee needs no rule: its tracks
     are `width: max-content` and scroll horizontally by design, so a narrow
     window simply shows fewer cards at a time. */
  .split__grid,
  .split__grid--narrow,
  .split--flip .split__grid--narrow,
  .fgrid--3 { grid-template-columns: minmax(0, 1fr); }
  .fgrid--3 { --fcols: 1; }

  /* The pinned stages are already stood down below 980px (clinics.js §2), so
     these are plain flow columns here — they must not be forced to a screen
     each or a phone scrolls through half-empty views. */
  .scene2__stage,
  .closing__pin { min-height: 0; }
}
