/* ==========================================================================
   Motion — scroll-driven, in CSS.

   Everything scrubbed here uses native scroll-driven animations
   (`animation-timeline: view()` / `scroll()`), so the page ships premium
   scroll motion with zero bytes of JavaScript.

   That is not a purity exercise. GSAP + ScrollTrigger + Lenis is ~180KB
   parsed and executed before anything moves, on a site whose primary visitor
   is on a phone, on Florida mobile data, and in pain. And Lenis specifically
   hijacks native scrolling, which is one of the four aesthetics the client
   explicitly rejected ("agency portfolio: hijacked scroll").

   Everything below is wrapped in @supports and in a reduced-motion guard.
   Where the feature is missing, layout.css's IntersectionObserver reveal is
   still in place and the page simply fades in instead of scrubbing.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {

    /* --- Section entrance -------------------------------------------------
     * Scrubbed against the element's own position in the viewport, so it
     * tracks the scroll rather than firing once at a threshold.
     *
     * CRITICAL: this animates transform ONLY. Opacity stays with the JS reveal
     * in reveal.js, which has a 2.5s failsafe behind it.
     *
     * The earlier version drove opacity from here and disabled the JS path
     * whenever scroll-driven animation was supported — which removed the only
     * safety net. A view() range that does not resolve then rests on the `from`
     * keyframe forever, and the section is simply invisible with all of its
     * markup correctly in the DOM. That shipped: the three payment cards and
     * the financing steps rendered as empty navy on a page that linted clean
     * and returned 200.
     *
     * Now the worst case is an element sitting 2rem low, which no visitor will
     * ever notice. Content cannot be hidden by a timeline that misbehaves.    */
    [data-reveal] {
      animation: da-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 26%;
    }

    @keyframes da-rise {
      from { transform: translateY(2rem); }
      to   { transform: none; }
    }

    /* --- The rake ----------------------------------------------------------
     * One light travelling across a whole cut plane, so a plane reads as a
     * single surface lit by one lamp rather than as N tiles that happen to be
     * adjacent. This is the one piece of motion that is about the material.
     *
     * Deliberately navy-only. On the reading surfaces a moving highlight is
     * noise, and light grounds get the measured motion instead (rules drawing,
     * markers igniting). That split is what stops the site feeling like one
     * effect applied twelve times.
     *
     * mix-blend-mode: screen can only ever lighten, so it is incapable of
     * dropping any text below its verified contrast ratio.                  */
    @media (min-width: 64rem) {
      .section--deep .plane::after,
      .section--navy .plane::after {
        content: "";
        position: absolute;
        inset: -30% -60%;
        z-index: 2;
        pointer-events: none;
        background: linear-gradient(105deg,
          transparent 44%,
          oklch(0.905 0.098 96 / 0.10) 50%,
          transparent 56%);
        mix-blend-mode: screen;
        animation: da-rake linear both;
        animation-timeline: view();
        animation-range: entry 20% exit 10%;
      }
      @keyframes da-rake {
        from { transform: translateX(-55%); }
        to   { transform: translateX(55%); }
      }
    }

    /* --- Faceted images: slow counter-drift --------------------------------
     * Deliberately NOT on every photograph on the site.
     *
     * This used to target `.facet-frame img` plus every card image, which was
     * the uniform reflex: one effect applied to eleven pictures at once, each
     * paying for a composited layer. It is now the three photographs that are
     * the subject of their band — the two doctors and an inner-page masthead —
     * where the drift makes the gold edge-light travel across the cut as you
     * scroll, which is the whole "leaf catching light" idea doing something.
     * A card thumbnail gains nothing from it.
     *
     * The hero portrait is no longer in this list because it no longer exists:
     * the hero's subject is now the practice's own footage, which is already
     * moving and needs no help.                                             */
    /* The masthead figure is gone — its photograph is the band's ground now, and a
       full-bleed background does not drift. Only the two doctors keep the counter-drift. */
    .doctor__media .facet > img {
      animation: da-drift linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
      will-change: transform;
    }

    @keyframes da-drift {
      from { transform: translateY(-2.2%) scale(1.06); }
      to   { transform: translateY(2.2%) scale(1.06); }
    }

    /* Hover zoom and scroll drift both want transform on the same element.
       Hovering hands control back to the interaction. */
    .doctor:hover .doctor__media .facet > img { animation: none; }

    /* --- The gold leaf rule draws itself -----------------------------------
     * Both forms: the real <hr> and the ::after that gives every light section's heading
     * the same mark. If only one of them drew, the motif would read as two different
     * things depending on which section you were looking at. */
    .section--light .section-head > h2::after,
    .t-light .section-head > h2::after {
      animation: da-draw linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 30%;
      transform-origin: left center;
    }
    .leaf-rule {
      animation: da-draw linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 30%;
      transform-origin: left center;
    }

    @keyframes da-draw {
      from { clip-path: inset(0 100% 0 0); }
      to   { clip-path: inset(0 0 0 0); }
    }

    /* --- First visit: the progress line fills as you read the steps --------
     * `timeline-scope` lets the line, which sits in the parent, be driven by
     * the scroll position of the step list. The line is the section's whole
     * idea: four steps, and you can see how far through them you are.      */
    .first-visit { timeline-scope: --steps; }
    .steps { view-timeline: --steps block; }

    /* The bright fill only. The dim track underneath stays full width so the
       sequence is complete and legible whatever the scroll position. */
    .steps::after {
      animation: da-progress linear both;
      animation-timeline: --steps;
      animation-range: entry 55% cover 55%;
    }

    @keyframes da-progress {
      from { transform: scaleX(0); }
      to   { transform: scaleX(1); }
    }

    /* Each step's marker lights up as it arrives. */
    .steps__marker {
      animation: da-ignite linear both;
      animation-timeline: view();
      animation-range: entry 20% cover 35%;
    }

    /*
     * The `from` state must be LEGIBLE, not invisible.
     *
     * This previously started at gold 15%/30% over transparent, which on the
     * reading surface is effectively white — and a view() range that never
     * resolves rests on `from` forever. That is the same failure mode that
     * shipped three sections as blank rectangles earlier in this build: the
     * numerals carrying the sequence simply were not there. Now the step reads
     * at full contrast throughout and the animation only adds the halo.
     */
    @keyframes da-ignite {
      from {
        border-color: var(--edge);
        box-shadow: 0 0 0 0 transparent;
      }
      to {
        border-color: var(--edge-lit);
        box-shadow: 0 0 0 6px var(--ring-halo);
      }
    }

    /* --- Hero parallax ------------------------------------------------------
     * Scroll-linked, so the copy lifts away from the footage as the section
     * leaves. Only the hero gets this; parallax on every section is the tell.
     *
     * The counter-sink that used to run on `.hero__portrait` is gone with the
     * portrait. Nothing replaces it: the background is a video, and sliding a
     * moving image against moving copy is two motions arguing.               */
    .hero__copy {
      animation: da-hero-lift linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }

    /*
     * The floor is 0.6, not 0.25.
     *
     * `.hero__copy` contains the H1 and the booking button. Fading it to a
     * quarter meant that anyone reading slowly — which on this site is anyone
     * anxious enough to be reading carefully — watched the one sentence the
     * page exists to say dissolve while they were still on it. The parallax is
     * worth keeping; punishing a slow reader for it is not.
     */
    @keyframes da-hero-lift {
      from { transform: none; opacity: 1; }
      to   { transform: translateY(-3rem); opacity: 0.6; }
    }
    @keyframes da-hero-sink {
      from { transform: none; }
      to   { transform: translateY(2rem); }
    }

    /* --- Masthead: the same parallax the home hero gets ---------------------- */
    .masthead__copy {
      animation: da-hero-lift linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }
    .masthead__bg {
      animation: da-hero-sink linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }

    /* ======================================================================
       ADDED MOTION — four moves, each tied to a material rather than to the
       fact that a section exists.

       The constraint everything below obeys: a `view()` range that never
       resolves rests on its `from` keyframe FOREVER. This build has shipped
       three sections as blank navy rectangles that way. So no `from` state
       here hides anything: the entrance floors are transforms and a partial
       opacity, never `opacity: 0`, and every one of these is decoration over
       content that is already fully visible and readable without it.
       ====================================================================== */

    /* --- 1. A photograph settles into its cut ------------------------------
     * Entry range only, and 3.5% of scale. Not `da-drift`, which was removed
     * from every image on the site for good reason: that was a continuous
     * counter-move across the whole cover range, on eleven pictures, each
     * paying for a composited layer the entire time it was on screen. This
     * runs while the image arrives and then stops, and it only applies where
     * a photograph sits inside a cut — the settle is the picture finding its
     * frame, which is the one thing the facet is about.                     */
    .facet > img,
    .belief__img,
    .doctor-profile__img {
      animation: da-settle linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 90%;
    }
    @keyframes da-settle {
      from { transform: scale(1.035); }
      to   { transform: none; }
    }
    /* Hover hands control back to the interaction, same rule the doctors' drift follows. */
    .face--link:hover .facet > img { animation: none; }

    /* --- 2. The cut-line kindling: TRIED AND REMOVED ----------------------
     * `.plane` animated its background-color from `--edge-dim` to `--edge` as it entered —
     * gold arriving as light rather than as a border colour, which is the brand's whole
     * idea and looked right.
     *
     * It shipped for about a minute. Two costs, and either alone is enough:
     *
     *   `--edge-dim` is 1.82:1 and is declared decorative-only precisely because it cannot
     *   carry a division. Animating the plane's edge through it means every cut plane spends
     *   its arrival with a boundary below the 3:1 floor SC 1.4.11 sets. A contrast ratio
     *   that is legal only once you stop scrolling is not legal.
     *
     *   And it broke chip-edge, which samples each assembly's computed `background-color`
     *   once and compares the painted pixels to it — 69 segments reported as missing, because
     *   the colour was read out of view and the pixels were read in view. The guard could be
     *   taught to re-read, but an effect whose only defect report is "the guard now has to
     *   work around me" is an effect that has stopped being free.
     * --------------------------------------------------------------------- */

    /* --- 3. The payer marks arrive in sequence -----------------------------
     * A stagger within ONE list, which is the legitimate case — nine marks at
     * 45ms is ~400ms end to end. Transform only: an opacity floor of zero on
     * a logo rail is how a section ships invisible.                          */
    .payers__rail .payer {
      animation: da-lift linear both;
      animation-timeline: view();
      animation-range: entry 10% entry 85%;
      animation-delay: calc(var(--i, 0) * 45ms);
    }
    @keyframes da-lift {
      from { transform: translateY(0.5rem); }
      to   { transform: none; }
    }

    /* --- 4. The credential plane rises out of the footage -------------------
     * The bridge straddles the hero's bottom edge, and this is the one place
     * on the site where an object crosses between two worlds. Six pixels, over
     * its own entry — enough to read as the plane lifting off the film, not
     * enough to notice as movement.                                          */
    @media (min-width: 64rem) {
      .trust__bridge {
        animation: da-bridge linear both;
        animation-timeline: view();
        animation-range: entry 0% entry 70%;
      }
      @keyframes da-bridge {
        from { transform: translateY(6px); }
        to   { transform: none; }
      }
    }

    /* --- Section grounds cross-fade at their seam --------------------------
     * A hairline of gold brightens exactly where two art-directed worlds
     * meet, so the switch between navy and reading surface reads as
     * deliberate rather than as an abrupt background change.               */
    .section--light + .section:not(.section--light)::before,
    .section:not(.section--light) + .section--light::before {
      content: "";
      position: absolute;
      inset-block-start: 0;
      inset-inline: 0;
      height: 1px;
      background: linear-gradient(90deg,
        transparent, color-mix(in oklch, var(--gold) 55%, transparent) 50%, transparent);
      animation: da-seam linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 100%;
    }

    @keyframes da-seam {
      from { opacity: 0; transform: scaleX(0.3); }
      to   { opacity: 1; transform: scaleX(1); }
    }
  }
}

/* --- Sticky section headings: REMOVED -------------------------------------
 * `.treatments .section-head` and `.doctors .section-head` were sticky, so a
 * long section would keep its heading in view while you read the body.
 *
 * It cannot work in this layout and it was never going to. The heading is a
 * full-width block whose next sibling is the content grid, and it has no
 * background — so pinning it means the cards scroll underneath it and the two
 * sets of text print on top of each other. On the doctors band that rendered
 * "Two dentists. One family." straight through Dr. Romero's name and bio.
 *
 * A sticky heading is only safe when it sits in its OWN column of a two-column
 * layout, where nothing can pass beneath it. If that composition is ever wanted
 * here, it has to be built as such — an opaque background would only paper over
 * the same collision.
 * -------------------------------------------------------------------------- */
