/* ==========================================================================
   Dynamic Mockups — the product stage ("10,000+ templates, ready to render")
   --------------------------------------------------------------------------
   Ported from the design source. Scroll-driven: 600px of scroll buys one step,
   four steps make a 2400px run, and that 2400px is real document height (the
   run's padding) with the stage fixed on top of it — so the scrollbar never
   lies and the page never fights the wheel. The expand and the release are
   ramps measured in scroll, not milliseconds; only the gradient slide and the
   step index are scroll-linked.

   ANCESTRY CONSTRAINT: nothing above .dm-stage may set `transform`, `filter`,
   `will-change` or `contain` — each of those would make the stage's fixed
   positioning resolve against that ancestor instead of the viewport. Overflow
   is safe (a fixed box's containing block is the viewport, which sits outside
   the clipper), which is why `main.dm { overflow-x: clip }` does not break it —
   verified over CDP, see DM-REBRAND.md. The section is a sibling of the hero,
   which does clip, rather than a child of it.

   Below 1101px and under reduced motion the pin is off entirely: same markup,
   no scroll dependency — a plain tabbed card that advances on click and on a 5s
   timer, with the run carrying no padding.
   ========================================================================== */

.dm-stage-seq { position: relative; }

/* The stage peeks above the fold now, so it has to arrive with everything else
   instead of being there from the first frame.

   It deliberately does NOT use the page's [data-dm-reveal] machinery. That
   observer fires on a 16% visibility RATIO, and this section is ~3000px tall
   because it reserves the pin's scroll run — a 130px peek is 4% of it, so the
   observer would not fire until 480px had scrolled past, which is long after
   the moment we are animating. The peek is above the fold at load, so there is
   nothing to observe: a delayed animation does the whole job.

   Opacity only, never transform. This is the pinned section, and a transform
   anywhere in the pinned card's ancestry makes its position:fixed resolve
   against that ancestor rather than the viewport — the exact breakage the
   handoff README warns about. Opacity creates a stacking context but not a
   containing block, so fading is safe. `backwards` holds the from-state
   through the delay and then lets go entirely, leaving no animation state
   sitting on an ancestor of the pin once it has run. The delay puts it just
   behind the hero's own items, which run 0-210ms. */
@keyframes dm-stage-in { from { opacity: 0; } }

.dm-motion :where(.dm) .dm-stage-seq {
  /* delay comes from hero.js: the orbit's arrival end, so this lands after the
     last circle has settled rather than racing it. 350ms is the no-JS fallback. */
  animation: dm-stage-in var(--dm-dur-in) var(--dm-ease-press)
             var(--dm-hero-settled, 350ms) backwards;
}
.dm-stage-seq .dm-container { padding-block: 0 8px; }

/* the run is the scroll budget; JS writes the px so the no-pin path costs no height */
.dm-stage-run { padding-bottom: 0; }

/* reserves the collapsed card's measured height once the stage lifts out of flow */
.dm-stage-slot { height: 0; }

/* The host is a fixed full-viewport layer that only forwards pointer events to
   the stage. z-index stays `auto` while collapsed so the fixed header (z 100)
   still wins; it jumps above the header — and above the backdrop — only once
   the panel is open. */
.dm-stage-host { pointer-events: none; }
.dm-stage-host.is-lifted { position: fixed; inset: 0; }
.dm-stage-host.is-over { z-index: 111; }

/* The ground that hides the rest of the page while the panel owns the viewport.
   Opacity is written per frame from the expand progress, so it fades exactly in
   step with the panel rather than on its own clock. Above our header at z 100. */
.dm-stage-backdrop {
  position: fixed;
  inset: 0;
  z-index: 110;
  pointer-events: none;
  background: var(--dm-surface-page);
  opacity: 0;
}

.dm-stage {
  position: relative;
  max-width: 1248px;
  margin-inline: auto;
  pointer-events: auto;
  overflow: hidden;
  isolation: isolate;
  border-radius: var(--dm-stage-r, 20px);
  padding: 32px;
  background: var(--dm-purple-50);
  box-shadow: 0 18px 48px rgba(33, 34, 29, 0.10);
}

/* pinned: geometry comes from JS, one custom property per edge */
.dm-stage.is-lifted {
  position: fixed;
  margin: 0;
  max-width: none;
  top: var(--dm-sy, 0px);
  left: var(--dm-sx, 0px);
  width: var(--dm-sw, auto);
  height: var(--dm-sh, auto);
}

/* released: parks at the bottom of the run and scrolls away like any other card */
.dm-stage.is-parked {
  position: absolute;
  margin: 0;
  max-width: none;
  top: var(--dm-sy, 0px);
  left: var(--dm-sx, 0px);
  width: var(--dm-sw, auto);
  height: var(--dm-sh, auto);
}

/* ---------- the gradient ----------
   A 4-zone painted strip that translates by exactly one zone per step. Its size
   comes from --dm-gw/--dm-gh (px, written on resize from the OPEN panel size),
   never from the stage itself — so blur(64px) rasterises once and the expand
   never re-renders it. Sizing it in % of the stage was the single biggest cost
   in the design's first version: the blur layer is ~5.5k px wide, and
   re-blurring it every frame of the grow is what made the transition spike.
   .dm-stage-paint bleeds past the track on every side so the blur never fades a
   visible edge, and every stop is a token value off the DM ramps. */
.dm-stage-grad {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
}

.dm-stage-track {
  position: absolute;
  left: 0;
  top: 50%;
  width: var(--dm-gw, 400%);
  height: var(--dm-gh, 100%);
  margin-top: calc(var(--dm-gh, 100%) * -0.5);
  will-change: transform;
}

.dm-stage-paint {
  position: absolute;
  inset: -18% -4%;
  filter: blur(64px);
  background-color: var(--dm-purple-100);
  background-image:
    /* 01 · templates — cyan */
    radial-gradient(44% 78% at 3% 20%,  #8CFFFB 0%, rgba(140, 255, 251, 0) 68%),
    radial-gradient(38% 72% at 12% 86%, #45C6A8 0%, rgba(69, 198, 168, 0)  70%),
    radial-gradient(34% 68% at 19% 28%, #79C2F7 0%, rgba(121, 194, 247, 0) 72%),
    radial-gradient(30% 60% at 8%  56%, #2AD3DC 0%, rgba(42, 211, 220, 0)  74%),
    /* 02 · your own PSDs — purple + blue */
    radial-gradient(40% 76% at 31% 12%, #C5B1F7 0%, rgba(197, 177, 247, 0) 70%),
    radial-gradient(36% 70% at 41% 82%, #56ADEA 0%, rgba(86, 173, 234, 0)  72%),
    radial-gradient(32% 64% at 36% 46%, #A386EC 0%, rgba(163, 134, 236, 0) 74%),
    /* 03 · batch + AI — pink */
    radial-gradient(42% 80% at 57% 22%, #FAA5C3 0%, rgba(250, 165, 195, 0) 68%),
    radial-gradient(36% 72% at 66% 84%, #FE5E96 0%, rgba(254, 94, 150, 0)  70%),
    radial-gradient(30% 62% at 61% 50%, #C5B1F7 0%, rgba(197, 177, 247, 0) 74%),
    /* 04 · the API — lime + orange, closing back toward cyan so the run reads as a loop */
    radial-gradient(40% 78% at 81% 16%, #E0FB7F 0%, rgba(224, 251, 127, 0) 70%),
    radial-gradient(36% 70% at 90% 80%, #F4CE7C 0%, rgba(244, 206, 124, 0) 72%),
    radial-gradient(32% 66% at 86% 44%, #A5D544 0%, rgba(165, 213, 68, 0)  74%),
    radial-gradient(28% 58% at 98% 62%, #8CFFFB 0%, rgba(140, 255, 251, 0) 76%);
}

/* the same 180px grain tile the final CTA panel uses, so both gradient moments
   share one surface treatment */
.dm-stage-grain {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.32;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g' x='0' y='0' width='180' height='180' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='2 2' numOctaves='3' seed='861' stitchTiles='stitch' result='noise'/%3E%3CfeColorMatrix in='noise' type='luminanceToAlpha' result='a'/%3E%3CfeComponentTransfer in='a' result='m1'%3E%3CfeFuncA type='discrete' tableValues='1 0'/%3E%3C/feComponentTransfer%3E%3CfeFlood flood-color='black' flood-opacity='0.25' result='f1'/%3E%3CfeComposite operator='in' in='f1' in2='m1' result='c1'/%3E%3CfeComponentTransfer in='a' result='m2'%3E%3CfeFuncA type='discrete' tableValues='0 1'/%3E%3C/feComponentTransfer%3E%3CfeFlood flood-color='white' flood-opacity='0.25' result='f2'/%3E%3CfeComposite operator='in' in='f2' in2='m2' result='c2'/%3E%3CfeMerge%3E%3CfeMergeNode in='c1'/%3E%3CfeMergeNode in='c2'/%3E%3C/feMerge%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)'/%3E%3C/svg%3E") repeat;
  background-size: 180px 180px;
}

/* skip control — only reachable once the panel is open; jumps past the whole run */
.dm-stage-close {
  position: absolute;
  z-index: 4;
  top: 28px;
  right: 28px;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  border: 1px solid rgba(33, 34, 29, 0.16);
  border-radius: 50%;
  background: rgba(244, 245, 241, 0.42);
  color: var(--dm-ink);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  pointer-events: none;
  transition: background-color .12s ease;
}

.dm-stage-close:hover { background: rgba(244, 245, 241, 0.75); }
.dm-stage-close svg { width: 16px; height: 16px; display: block; }

/* --dm-inner-w is the COLLAPSED content width, held constant through the
   expand. The card therefore never changes width and its copy never re-wraps
   while the panel grows — only the gradient field around it does. */
.dm-stage-inner {
  position: relative;
  z-index: 2;
  width: var(--dm-inner-w, 100%);
  max-width: 100%;
  margin-inline: auto;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

/* ---------- the tab row ----------
   Four squircle chips standing on the card's top edge, each an always-on chrome
   plate, a connector, and a face whose accent sweeps in.

   The connector is a droplet, not a bracket: a bead forms at the chip's
   underside, a slim stem draws down behind it, and the bead lands in the card's
   surface. All four shapes (lip · stem · drop · surface) sit in one
   #dm-stage-goo group — blur, then a hard alpha threshold — so every join is a
   concave fillet and the whole thing reads as one liquid body pulling out of the
   chip and merging into the card, rather than a tab pasted on top of it. Two
   transitions with a 70ms offset do the timing: the stem draws first, the drop
   follows on a slight overshoot and settles. */
.dm-stage-tabs {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

.dm-stage-tab {
  --dm-chrome: var(--dm-surface-page);
  position: relative;
  width: 56px;
  height: 56px;
  flex: none;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  outline: 0;
  -webkit-tap-highlight-color: transparent;
}

.dm-stage-tab:focus-visible {
  outline: 2px solid var(--dm-cyan-700);
  outline-offset: 3px;
  border-radius: 20px;
}

.dm-stage-tab .dm-chrome {
  position: absolute;
  z-index: 0;
  inset: 0;
  border-radius: 18px;
  background: var(--dm-chrome);
}

/* The drip spans the 10px gap: its top 8px sit inside the chip, its bottom 7px
   inside the card, so both ends are anchored in matching paper and only the neck
   shows. overflow:visible lets the goo's spread paint outside the 36x26 box. */
.dm-stage-tab .dm-drip {
  position: absolute;
  z-index: 1;
  left: 50%;
  top: 48px;
  width: 36px;
  height: 26px;
  transform: translateX(-50%);
  overflow: visible;
  pointer-events: none;
  color: var(--dm-chrome);
  opacity: 0;
  transition: opacity 150ms linear 130ms;
}

.dm-stage-tab .dm-drip > g { filter: url(#dm-stage-goo); }

.dm-stage-tab .dm-drip .dm-stem,
.dm-stage-tab .dm-drip .dm-drop { transform-box: fill-box; }

.dm-stage-tab .dm-drip .dm-stem {
  transform-origin: 50% 0;
  transform: scaleY(.04);
  transition: transform 240ms cubic-bezier(.4, 0, .7, 1);
}

.dm-stage-tab .dm-drip .dm-drop {
  transform-origin: 50% 50%;
  transform: translateY(-13px) scale(.2);
  transition: transform 260ms cubic-bezier(.4, 0, .7, 1);
}

.dm-stage-tab[aria-selected="true"] .dm-drip {
  opacity: 1;
  transition: opacity 90ms linear;
}

.dm-stage-tab[aria-selected="true"] .dm-drip .dm-stem {
  transform: scaleY(1);
  transition: transform 320ms cubic-bezier(.34, .9, .4, 1);
}

.dm-stage-tab[aria-selected="true"] .dm-drip .dm-drop {
  transform: translateY(0) scale(1);
  transition: transform 470ms cubic-bezier(.5, 1.55, .6, 1) 70ms;
}

.dm-stage-tab .dm-face {
  position: absolute;
  z-index: 2;
  inset: 4px;
  border-radius: 15px;
  overflow: hidden;
  background: var(--dm-neutral-100);
}

/* Base state only — once the selection moves, the script writes the transform
   inline so the fill can sweep in the direction of travel (see setIndex). */
.dm-stage-tab .dm-face i {
  position: absolute;
  inset: 0;
  display: block;
  background: var(--dm-accent);
  transform: translateX(106%);
  transition: transform 520ms cubic-bezier(.5, 0, .5, 1);
}

.dm-stage-tab[aria-selected="true"] .dm-face i { transform: translateX(0); }

.dm-stage-tab .dm-ico {
  position: relative;
  z-index: 3;
  width: 24px;
  height: 24px;
  display: block;
  color: var(--dm-ink);
}

.dm-stage-tab:hover:not([aria-selected="true"]) .dm-face { background: var(--dm-neutral-200); }

/* ---------- the card ---------- */
.dm-stage-card {
  position: relative;
  z-index: 2;
  width: 100%;
  margin-top: 10px;
  display: grid;
  background: var(--dm-surface-page);
  border-radius: var(--dm-radius-card-inner);
  padding: 40px;
  box-shadow: 0 2px 4px rgba(33, 34, 29, 0.04), 0 14px 30px rgba(33, 34, 29, 0.07);
}

.dm-stage-panel {
  grid-area: 1 / 1;
  display: grid;
  grid-template-columns: 330px minmax(0, 1fr);
  gap: 48px;
  align-content: center;
  align-items: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 460ms cubic-bezier(.5, 0, .5, 1);
}

.dm-stage-panel.is-on {
  opacity: 1;
  pointer-events: auto;
}

.dm-stage-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}

.dm-stage-copy h3 {
  font-family: var(--dm-font-body);
  font-weight: 500;
  font-size: 32px;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--dm-ink);
}

.dm-stage-copy p {
  font-size: 15px;
  line-height: 1.55;
  color: var(--dm-text-secondary);
}

.dm-stage-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  font-size: 14px;
  font-weight: 700;
  color: var(--dm-ink);
  border-bottom: 1px solid var(--dm-border-hairline);
  padding-bottom: 2px;
}

.dm-stage-link:hover {
  color: var(--dm-pink-700);
  border-bottom-color: var(--dm-pink-300);
}

.dm-stage-link svg { width: 14px; height: 14px; display: block; }

.dm-stage-media {
  position: relative;
  aspect-ratio: 484 / 300;
  overflow: hidden;
  border-radius: var(--dm-radius-card);
  background: var(--dm-surface-card);
}

/* The four visuals are renders the page already ships — stage.js copies their
   src across at boot rather than shipping a second copy of each file. */
.dm-stage-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dm-stage-media--terminal { background: #191A15; }

.dm-stage-media--terminal .dm-term-body {
  position: absolute;
  inset: 0;
  padding: 20px 22px;
  overflow: hidden;
  font-family: var(--dm-font-mono);
  color: var(--dm-neutral-400);
  white-space: pre-wrap;
}

/* progress: one hairline per step under the card, filling across its width */
.dm-stage-ticks {
  display: flex;
  gap: 6px;
  margin-top: 18px;
}

/* These sit on the gradient rather than on the card, so the fill is white and
   the track is a soft dark — that pair reads on the light zones (lime, gold)
   and the dark ones (teal) alike, which a single ink fill did not. Fully
   rounded, and the track clips the fill so its leading edge inherits the
   rounding without the scaleX distorting a radius of its own. */
.dm-stage-ticks span {
  position: relative;
  width: 34px;
  height: 4px;
  border-radius: 999px;
  background: rgba(20, 20, 18, 0.22);
  overflow: hidden;
}

.dm-stage-ticks span::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: #fff;
  transform: scaleX(var(--dm-f, 0));
  transform-origin: 0 50%;
}

/* No pin below 1101px (the script disarms at the same breakpoint) — the panel is
   just a tabbed card, so it only needs to stop being two columns. */
@media (max-width: 1100px) {
  .dm-stage { padding: 20px; }
  .dm-stage-card { padding: 28px; }
  .dm-stage-panel { grid-template-columns: 1fr; gap: 28px; }
  .dm-stage-copy h3 { font-size: 27px; }
  .dm-stage-media { aspect-ratio: 16 / 10; }
  .dm-stage-close { display: none; }
}

@media (max-width: 720px) {
  .dm-stage { padding: 14px; border-radius: 16px; }
  .dm-stage-card { padding: 22px; }
  .dm-stage-copy h3 { font-size: 24px; }
  .dm-stage-tabs { gap: 7px; }
  .dm-stage-tab { width: 48px; height: 48px; }
  .dm-stage-tab .dm-chrome { border-radius: 15px; }
  .dm-stage-tab .dm-drip { top: 40px; }
  .dm-stage-tab .dm-face { border-radius: 12px; }
  .dm-stage-tab .dm-ico { width: 22px; height: 22px; }
  .dm-stage-ticks span { width: 28px; }
}
