/* ============================================================================
   reduce.css — the reduced-motion contract, and the fallbacks for every OTHER
   reason an enhancement declines to run.

   THIS FILE LOADS LAST ON EVERY PAGE, AND THAT IS THE WHOLE POINT.
   A media query adds no specificity, so equal-specificity ties are decided
   purely by source order. A prefers-reduced-motion block sitting in an early
   stylesheet silently loses to any later component rule, and the page animates
   for exactly the person who asked it not to.

   The second half of this file is the part that is easy to forget: JS declines
   to run an enhancement for far more reasons than reduced motion — no pointer,
   a narrow viewport, a failed script, a caught exception — and in none of those
   does the media query match. Anything that reserves space therefore keys its
   collapse on a CLASS as well.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

  /* -- global: keep opacity, drop every position change ------------------ */
  :is(.hero__title, .hero__sub, .ask, .hero__go, .hero__foot, .topline) {
    transform: none !important;
    transition-duration: 1ms !important;
    opacity: 1 !important;
  }
  .hero__plate { transform: none !important; }
  .act-cta:hover, .tiles a:hover { transform: none; padding-left: 0; }
  .ghost-cta:hover::after { translate: 0 0; }

  /* -- the portrait: still, eyes open, no loop, NO TRAVEL -----------------
     ROUND 11. He is the constant on this page now and he moves between four
     measured corners as you read. The corner CHOICE is measurement and stays;
     the TRAVEL is motion and goes. companion.js places him exactly once here
     and holds that placement for the whole page, so a reduced-motion visitor
     still gets the character in every act, still gets his lines, and never gets
     an element sliding or teleporting around the viewport.

     The transition is killed rather than the transform: overriding the parked
     transform with an !important of our own would throw away the measurement,
     which is the one part of this feature that reduced motion has no quarrel
     with. He must not be hidden either — he is the signature move and
     fingerprint dimensions 3 and 6, and hiding him deletes both. */
  .dock { transition: none !important; }
  .dock--hero { transform: translate(-7vw, 19vh) scale(1) !important; }
  .dock--park, .dock--close { opacity: 1 !important; visibility: visible !important; }
  .dock--busy.dock--park { opacity: 1 !important; }
  .dock--busy.dock--park .head { transform: none !important; }

  /* -- the preloader never runs; make sure nothing it would have set sticks.
     EXCEPT under ?pre, which exists so the sequence can be reviewed at all.
     Without this exception the forced path set `pre-on`, this rule hid the
     curtain anyway, and home.css held .ask/.hero__go/.hero__foot/.topline at
     opacity 0 until pre-done — a blank hero for up to 4.4s and nothing to
     watch. A reviewer opting in is not a reduced-motion visitor. */
  html:not(.pre-force) .pre { display: none !important; }

  /* -- pinned stacks must not overlap ------------------------------------
     A stack of absolutely-positioned crossfading statements is correct while
     the engine is driving their opacity from scroll. If it is not — and a
     tween being inert is NOT the same as the geometry being right — they all
     land on top of each other in one box. Relay them out in flow. Verified as
     distinct, non-overlapping rects, not just as "opacity is 1". */
  /* ROUND 11 — the floor act needs almost nothing here any more. Its four
     statements are a real list in normal flow at full opacity in BOTH motion
     modes, so there is no absolutely-positioned stack to relay out. The only
     scroll-driven thing left is the ember marker, and under reduce every marker
     is simply lit: the list reads as a list, which is what it is. */
  .floor .sc-stage { position: static; height: auto; overflow: visible; }
  .floor { padding-block: clamp(4rem, 9vh, 6.5rem); }
  .floor__states i { opacity: 1 !important; }

  /* ROUND 10 — the story act. Its scene is a five-stage animation and there is
     no honest still of it, so the drawing goes and the five beats relay out as
     an ordinary numbered list. Nothing is lost: the whole process is in the
     beat copy, and the window being born was always the decoration. */
  .story .sc-stage { position: static; height: auto; overflow: visible; }
  .story { padding-block: clamp(4rem, 9vh, 6.5rem); }
  .story__scene { display: none; }
  .story__beats { position: static; min-height: 0; display: grid; gap: 0; }
  .story__beats li {
    position: static; opacity: 1 !important;
    padding: 0.95rem 0; border-top: 1px solid var(--rule);
  }
  .story__beats li:last-child { border-bottom: 1px solid var(--rule); }
  .story__beats p { font-size: clamp(1.05rem, 0.95rem + 0.7vw, 1.5rem); max-width: 46ch; }

  /* Pinned acts stop being pinned: their content is a normal document block,
     because a sticky stage with an inert cue set is a screen that never
     changes for the length of two viewports. */
  .hero .sc-stage, .close .sc-stage, .proof .sc-stage {
    position: static; height: auto; overflow: visible;
  }
  .hero .sc-stage { min-height: 88svh; }
  /* Not keyed on .sc-ready: the heights are now reserved in CSS from first
     paint, so the release has to be unconditional or a reduced-motion visitor
     gets several viewport-heights of reserved empty space before the engine
     even decides not to mount a pin. */
  /* ROUND 11 — `.story` (#process) joins this list. It gained a reserved height
     in home.css to stop the engine charging a layout shift at mount, and every
     reservation added there must be released here or a reduced-motion visitor
     is handed several viewport-heights of empty ground where a pin would have
     been. That is the exact failure this line already exists to prevent; the
     act that was missing from it was simply the act that had no reservation. */
  :is(.hero, .close, .proof, .floor, .story) { height: auto !important; }
  .close .sc-stage, .proof .sc-stage { padding-block: clamp(4rem, 9vh, 6.5rem); }
  [data-sc-cue] { opacity: 1 !important; transform: none !important; }
  /* padding-inline was zeroed here on the assumption the static parent supplies
     the gutter. It does not — the strip is full-bleed, so the text landed hard
     against the screen edge at x=0 (20px and 72px inset in normal motion).
     Hand the page gutter back explicitly. */
  .hero__foot {
    position: static;
    padding-inline: var(--sc-gutter);
    margin-top: 1.5rem;
  }

  /* The rail's transform IS its navigation, so it is handed back as a real
     scroll region by the engine. Give it room to breathe here. */
  .rail { padding-block: 0; }
  .plate, .rail__lead, .rail__note { padding-block: 1.5rem; }
}

/* ============================================================================
   QC FAIL round 1 — three reduce-only, homepage-only blockers.
   All three have one root cause: with the pins released the hero collapses to
   y=0, and three things that were correct while the engine drove them are
   wrong the moment it does not.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

  /* -- B1: the rail was still a horizontal track ---------------------------
     Releasing its padding left a flex ROW of ~22rem plates: scrollWidth 1631
     against a 360 viewport. `body{overflow-x:clip}` establishes no scroll
     container, so Chrome widened the initial containing block to 1440px and
     every position:fixed element sized to THAT — putting the Menu button and
     the language toggle off-screen at x≈1292. A reduced-motion phone visitor
     had no reachable navigation at all.
     With no transform to pan it, a row cannot be navigation. Stack it. */
  .proof .sc-stage { overflow: visible; }
  .rail {
    flex-direction: column;
    align-items: stretch;
    height: auto;
    gap: clamp(1.25rem, 4vh, 2rem);
  }
  .rail__lead, .rail__note, .plate {
    flex: 0 0 auto;
    width: 100%;
    max-width: 44rem;
  }

  /* -- B2: the h1 was painted over by its own navigation -------------------
     `.topline` is correctly `is-on` from scroll 0 here (a reduced-motion
     visitor must be able to reach the nav), but the collapsed hero puts the
     h1 at y=0 underneath its 153px opaque ground-fade — measured 1.09:1 across
     61 of 65 scanlines while getComputedStyle reported the correct token and
     Lighthouse accessibility scored 100. Occlusion is invisible to both.
     Clear the fixed chrome instead of moving it. */
  .hero .sc-stage { padding-block-start: clamp(7rem, 21vh, 11rem); }

  /* -- B3: the portrait never rendered -------------------------------------
     INTAKE §4 requires a static portrait with the eyes centred. At <=430px the
     dock carries .dock--min at rest, which both the mobile block and this file
     hide — so the signature move and fingerprint dimensions 3 and 6 were simply
     absent for this visitor.
     The fix is the one already proven on the no-JS path below: the dock is a
     body-level element, so `absolute` anchors it to the top of the document —
     it sits in the hero and scrolls away instead of following the reader down
     the page, which is what the corner watcher was suppressed to avoid. */
  .dock {
    position: fixed;
    opacity: 1 !important;
    visibility: visible !important;
  }
  /* Only the hero state is pinned by hand. ROUND 11: `--close` came out of this
     pair — it is a measured corner now like every other parked position, and
     forcing it back into the hero band would have put him on top of the closing
     ask at exactly the moment the page resolves. */
  .dock--hero { transform: translate(-6vw, 11vh) scale(1) !important; }
}

@media (prefers-reduced-motion: reduce) and (max-width: 767px),
       (prefers-reduced-motion: reduce) and (max-height: 560px) {
  .dock--park, .dock--close { opacity: 1 !important; visibility: visible !important; }

  /* QC round 2: making the portrait visible here exposed a collision that was
     already present in normal motion at <=375px — the face paints over the h1,
     and at 320 the brand name reads "ATL STUD". The band is defined once in
     home.css and consumed here so both motion modes resolve the same geometry;
     the earlier fixed padding is deliberately overridden by it.
     ROUND 11: only the hero state takes the band. The parked corner is measured
     against the live text at this width like everywhere else. */
  .dock--hero {
    transform: translate(-4vw, var(--dock-band-y)) scale(1) !important;
  }
  .hero .sc-stage {
    padding-block-start: calc(var(--dock-band-y) + var(--dock-band-w) + 1.5rem);
  }
}

/* ============================================================================
   Class-keyed fallbacks — these fire whether or not reduced motion is on.
   ========================================================================== */

/* No JavaScript at all: the portrait composes correctly on its own (irises at
   rest inside the eye mask read as the untouched portrait), but nothing would
   ever move the dock out of the hero, so it must not be able to sit over the
   rest of the page. Without .sc-ready the engine never mounted. */
html:not(.sc-ready) .dock { position: absolute; }
html:not(.sc-ready) [data-sc-cue] { opacity: 1; }
html:not(.sc-ready) [data-sc-in],
html:not(.sc-ready) [data-sc-stagger] > * { opacity: 1; transform: none; }
html:not(.sc-ready) [data-sc-reveal] { clip-path: none; }
html:not(.sc-ready) .topline { opacity: 1; visibility: visible; }
/* Before the engine mounts — and forever, for a visitor with no JavaScript —
   the hero stage is an ordinary div. Give it the same box the engine's
   .sc-stage will have, so the composition is bottom-anchored from the first
   paint instead of resolving there a moment later. The pinned SECTION heights
   are deliberately NOT released here: they are reserved in home.css to keep
   CLS at zero, and :not(.sc-ready) is true for every visitor's first paint,
   so releasing them here would hand back the 0.325 shift it cost to remove.
   A no-JS visitor therefore reads a correct page with extra air in the four
   sections that would have pinned — the graceful failure, not a broken one. */
html:not(.sc-ready) .hero [data-sc-stage] {
  min-height: 100svh; display: grid; align-items: end;
}


/* ============================================================================
   ROUND 5 — the contract for the components added when the page stopped being
   a document. Two obligations each, and they are different obligations:

     under reduced motion  — the thing must be FINISHED, not animating;
     before the engine     — the thing must be VISIBLE, not waiting for a class
                             that a no-JS visitor will never receive.

   Anything drawn with `transform: scaleX(0)` owes both. Miss the second and a
   visitor without JavaScript gets a blank browser frame and five invisible
   rules where the process steps should be.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  /* The drawn browser arrives already drawn. */
  .mock :is(.mock__hero, .mock__ln, .mock__grid span, .mock__btn, .mock__url) {
    transform: none !important;
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }
  /* Each step's rule is simply there. */
  .process__list li::after { transform: scaleX(1) !important; transition: none !important; }
  /* A live dot that pulses forever is exactly the kind of thing this media
     query exists for. It stays ember — the status is information — it just
     stops moving. */
  .dot--live { animation: none !important; box-shadow: none !important; }
  /* Touch feedback without travel. */
  .chip:hover, .chip:active { transform: none !important; }
  .plate:hover .plate__shot img { transform: none !important; }
  .ladder__list li::before { transition: none !important; }
  .tiles a:hover .tile__ico { transform: none !important; }
  /* The bubble appears without travelling. */
  .say { transition: opacity 120ms linear !important; translate: 0 0 !important; }
  /* The maker redraws instantly rather than cross-fading. */
  .phone :is(.phone__screen, .phone__bar, .phone__url, .phone__eyebrow, .phone__h,
             .phone__p, .phone__cta, .phone__grid span) { transition: none !important; }
  .sw:hover { transform: none !important; }
}

/* Before the engine mounts, and forever without JavaScript: nothing below may
   depend on a class the engine adds. `.sc-in` never arrives for these people. */
html:not(.sc-ready) .mock :is(.mock__hero, .mock__ln, .mock__grid span, .mock__btn, .mock__url) {
  transform: scaleX(1);
}
html:not(.sc-ready) .process__list li::after { transform: scaleX(1); }


/* ============================================================================
   ROUND 11 — the contract for what was added this round.

   Two obligations, and they are different obligations:
     under reduced motion  — the thing must be FINISHED, not animating;
     before the engine     — the thing must be VISIBLE, not waiting for a class
                             a no-JS visitor will never receive.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  /* The hero title arrives already up. It is transform-only in both modes —
     `opacity` on this element would disqualify the largest text on the page as
     an LCP candidate, which costs real Lighthouse points for no visual gain. */
  .hero__title .ln__i { transform: none !important; transition: none !important; animation: none !important; }
  /* The trade photograph swaps instantly rather than cross-fading. */
  .phone__shot { transition: none !important; }
  .phone__shot.is-swapping { opacity: 1 !important; }
}

/* Before the engine mounts, and forever without JavaScript. The masked lines
   are the trap here: their resting position is INSIDE a clipping box, so if the
   rise never runs the brand name is simply not on the page. */
html:not(.sc-ready) .hero__title .ln__i { transform: none; }
html:not(.sc-ready) .floor__states i { opacity: 1; }


/* ============================================================================
   ROUND 12 — the contract for the imagery added this round.

   Every new plate is a plain <img> inside a <figure>. None of them animates,
   none of them is positioned by the engine, and none of them carries a
   scroll-driven transform — so there is no tween to neutralise here. What DOES
   have to hold is the second obligation: the reveal wrappers.

   `.offer__plate` and every `.ind__plate` sit inside elements the engine
   reveals (`data-sc-in`). If the engine never mounts — no JavaScript, a caught
   exception, a browser that declined — those elements must still be visible,
   which `html:not(.sc-ready)` below guarantees for the one new element that
   carries the hook itself. The industry plates deliberately do NOT carry
   `data-sc-in` of their own: they inherit their parent block's reveal, so
   there is nothing for them to wait on.

   The engine's own `.sc-in` base rule already covers the mounted case; this is
   only the belt for the paths where it never arrives.
   ========================================================================== */
html:not(.sc-ready) .offer__plate { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  /* Nothing moves. Stated rather than assumed, so a later change that adds a
     transition to a plate cannot silently escape the quiet path. */
  .offer__plate,
  .offer__plate img,
  .plate-fig img,
  .ind__plate img,
  .band-fig img,
  .oops__ground img { transition: none !important; animation: none !important; }
}


/* ============================================================================
   ROUND 14 - the contract for .kin and .pf-crop.
     under reduced motion  - FINISHED, not animating;
     before the engine     - VISIBLE, not waiting for a class no-JS never gets.

   Both devices put every moving rule inside
   `@media (prefers-reduced-motion: no-preference)` in page.css (and, for the
   parallax, in atl-scroll.css), so under reduce the heading is a set heading
   and the plate a still photograph BY CONSTRUCTION rather than by an override
   a later edit could out-specify. What is left here is the belt.
   ========================================================================== */

/* No JavaScript at all: .sc-in never arrives, so the lines must never be
   parked inside their own clip boxes and the heading must not wait on a
   reveal. page.css keys the transform on html.sc-ready; this is the other half
   of the same guarantee, for the wrapper's opacity. */
html:not(.sc-ready) .kin[data-sc-in] { opacity: 1; transform: none; }
html:not(.sc-ready) .kin .ln__i { transform: none; }
html:not(.sc-ready) .pf-crop > img { transform: none; }

@media (prefers-reduced-motion: reduce) {
  /* The heading is simply set; the masks stay (they cost nothing and the
     optical line spacing depends on them) and nothing travels. */
  .kin .ln__i { transform: none !important; transition: none !important; animation: none !important; }
  /* atl-scroll.css already zeroes [data-sc-parallax]; this states the resting
     position so a later rate change cannot leave a visible edge here. */
  .pf-crop > img { transform: none !important; transition: none !important; }
}

/* ROUND 15 — the price table. Nothing here is load-bearing for comprehension:
   the tick is already in the right place, it simply stops drawing itself, and
   the spotlight stops easing. The count-up is skipped in pages.js, not here,
   because a number mid-count is a WRONG number, not a fast one. */
@media (prefers-reduced-motion: reduce) {
  .tick { transition: none !important; stroke-dashoffset: 0 !important; }
  .tiers__b,
  .matrix--price [data-tier],
  .matrix--price thead th.tier,
  .matrix--price[data-hot] :is(tbody, tfoot) td { transition: none !important; }
}
