/* =======================================================================
   PRIVATE PRESENTATION — /presentation/:slug
   -----------------------------------------------------------------------
   Standalone by design, and the only page on the platform that is. Every
   storefront page loads bootstrap + all.min + animate + swiper + style.css
   before it can paint; a deck opened live in a meeting cannot afford that,
   so this file carries its own reset and its own @font-face and nothing
   else is requested.

   Standalone is NOT a second design system. Every value in the token block
   below is copied from, or derived from, the platform's own tokens in
   assets/css/style.css — the same warm off-white paper (#fafaf9 / #fdfdfc /
   #f2f1ee), the same near-black ink (#1a1a1a) that is deliberately never
   pure black, the same muted/faint text stops, the same 10px + 100px radius
   pair, the same shadow ramp, the same Montserrat. Where this file departs, it
   departs in ONE direction only: scale. Bigger type, more air, longer
   easing. A deck is the same brand spoken at a different volume, and the
   things that carry brand identity — hue, weight, radius, letterspacing,
   the pill button, the hairline rule — are untouched.

   Mobile and desktop are authored as separate layouts, not one scaled. The
   clearest examples, in section order: the hero (§7, one column with the
   slideshow between the copy and the buttons vs. two columns with it
   beside them), the how-it-works wizard (§10, accordion vs. two-column
   rail), the showcase page rail (§14c, snap-scrolling strip vs. centred
   pills), the campaigns bento (§17b, whose spans are declared per
   breakpoint rather than auto-placed) and the setup wizard (§19a, whose
   live estimate is a sticky side rail on desktop and a compact bar above
   the question on a phone).
   ======================================================================= */

/* Same six faces, same paths, same font-display as style.css declares —
   a deck rendered in a fallback face while Montserrat loads would not be the
   same brand for the first 200ms. */
@font-face { font-family: "Montserrat"; font-style: normal; font-weight: 400; font-display: swap; src: url("../fonts/montserrat/montserrat-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "Montserrat"; font-style: normal; font-weight: 500; font-display: swap; src: url("../fonts/montserrat/montserrat-latin-500-normal.woff2") format("woff2"); }
@font-face { font-family: "Montserrat"; font-style: normal; font-weight: 600; font-display: swap; src: url("../fonts/montserrat/montserrat-latin-600-normal.woff2") format("woff2"); }
@font-face { font-family: "Montserrat"; font-style: normal; font-weight: 700; font-display: swap; src: url("../fonts/montserrat/montserrat-latin-700-normal.woff2") format("woff2"); }
@font-face { font-family: "Montserrat"; font-style: normal; font-weight: 800; font-display: swap; src: url("../fonts/montserrat/montserrat-latin-800-normal.woff2") format("woff2"); }
@font-face { font-family: "Montserrat"; font-style: italic; font-weight: 400; font-display: swap; src: url("../fonts/montserrat/montserrat-latin-400-italic.woff2") format("woff2"); }

/* -----------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------- */
:root {
  /* ---- Palette: identical values to style.css's monochrome system ---- */
  --p-bg: #fafaf9;
  --p-surface: #fdfdfc;
  --p-surface-2: #f2f1ee;
  --p-ink: #1a1a1a;
  --p-ink-hover: #2e2e2c;
  --p-muted: #55534f;
  --p-faint: #9c9993;
  --p-border: rgba(26, 26, 26, 0.1);
  --p-border-soft: rgba(26, 26, 26, 0.06);
  --p-danger: #dc2626;
  --p-success: #059669;

  --p-font: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* ---- Radius + shadow: the platform's exact scale ---- */
  --p-radius: 10px;
  --p-radius-lg: 18px;
  --p-radius-pill: 100px;
  --p-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04), 0 1px 3px rgba(15, 23, 42, 0.06);
  --p-shadow-md: 0 4px 16px rgba(15, 23, 42, 0.08);
  --p-shadow-lg: 0 24px 64px rgba(15, 23, 42, 0.12), 0 4px 16px rgba(15, 23, 42, 0.06);

  /* ---- Motion. --p-ease is style.css's own --nav-ease, the curve the
     mobile drawer and its accordion already move on, so a step opening
     here feels like the same product as a nav panel opening there.
     --p-ease-soft is its longer sibling, used only for scroll reveals
     where the distance travelled is larger. ---- */
  --p-ease: cubic-bezier(0.25, 1, 0.5, 1);
  --p-ease-soft: cubic-bezier(0.16, 1, 0.3, 1);
  --p-dur-fast: 160ms;
  --p-dur: 240ms;
  --p-dur-slow: 620ms;

  /* ---- Spacing. One rhythm, expressed fluidly so 320px and 1920px each
     get a proportionate amount of air rather than the same pixel gap. ---- */
  --p-gap-xs: 8px;
  --p-gap-sm: 14px;
  --p-gap: clamp(20px, 2.4vw, 32px);
  --p-gap-lg: clamp(32px, 4vw, 56px);
  --p-section-y: clamp(84px, 11vw, 168px);
  --p-shell-x: clamp(20px, 5vw, 56px);
  --p-max: 1240px;

  /* ---- Type scale ---- */
  --p-hero: clamp(46px, 9.2vw, 132px);
  --p-h2: clamp(29px, 4.4vw, 60px);
  --p-h3: clamp(18px, 1.5vw, 21px);
  --p-lead: clamp(17px, 1.55vw, 21px);
  --p-body: clamp(15.5px, 1.15vw, 17px);
  --p-small: 13px;
}

/* -----------------------------------------------------------------------
   2. RESET + SHELL
   No bootstrap on this page, so the handful of resets it would have
   provided are declared here explicitly rather than assumed.
   -------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  /* Anchored rail jumps land below the progress bar, not under it. */
  scroll-padding-top: 80px;
  -webkit-text-size-adjust: 100%;
}

body,
h1, h2, h3, h4, p, ul, ol, li, blockquote, figure, form {
  margin: 0;
  padding: 0;
}

ul, ol { list-style: none; }

img, svg { max-width: 100%; display: block; }

button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

input, textarea {
  font: inherit;
  color: inherit;
}

.pres-body {
  font-family: var(--p-font);
  font-size: var(--p-body);
  line-height: 1.65;
  font-weight: 400;
  color: var(--p-muted);
  background: var(--p-bg);
  /* Not on the storefront's `text-transform: capitalize` body rule on
     purpose: this deck is written in sentences, and Title Casing an
     investor-facing paragraph is the fastest way to look unconsidered. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* The rail is positioned against the viewport; nothing here may ever
     produce a horizontal scrollbar at any width. */
  overflow-x: hidden;
}

/* Focus ring matches style.css's sitewide :focus-visible treatment exactly
   (2px solid ink, 2px offset) — keyboard users get the same affordance here
   as on every other page. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--p-ink);
  outline-offset: 2px;
  border-radius: 4px;
}

.u-hidden { display: none !important; }

.pres-container {
  width: 100%;
  max-width: var(--p-max);
  margin: 0 auto;
  /* env() carries the notch/home-indicator inset on iOS in landscape, where
     the safe area is horizontal rather than vertical. */
  padding-left: max(var(--p-shell-x), env(safe-area-inset-left));
  padding-right: max(var(--p-shell-x), env(safe-area-inset-right));
}
.pres-container--narrow { max-width: 860px; }

.pres-section {
  position: relative;
  padding-top: var(--p-section-y);
  padding-bottom: var(--p-section-y);
}

/* Alternating paper tone is the only sectioning device used — no borders
   between sections, no colored bands. It is the same --color-surface-2 the
   storefront uses for its own tinted strips. */
.pres-section--tint { background: var(--p-surface-2); }

/* Hairline above each tinted band, so the tone change reads as intentional
   at the exact pixel rather than as a rendering artefact on a low-contrast
   display. */
.pres-section--tint::before,
.pres-section--tint::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--p-border-soft);
}
.pres-section--tint::before { top: 0; }
.pres-section--tint::after { bottom: 0; }

/* -----------------------------------------------------------------------
   3. SCROLL REVEAL
   Only opacity and a translation animate. Both are composited, neither
   triggers layout or paint, and that is what keeps the deck at 60fps
   while scrolling on a phone. The hidden state is scoped to html.pres-js
   (set by presentation-boot.js) so the page is fully readable without
   scripting.

   The offset is carried on `translate`, the independent transform
   property, rather than inside `transform` itself. That is what lets a
   card be revealed and magnetically hovered at the same time: the two
   effects become separate properties that compose natively, instead of
   two rules overwriting one `transform` and the more specific one
   silently winning.
   -------------------------------------------------------------------- */
.pres-js [data-reveal] {
  opacity: 0;
  translate: 0 26px;
  transition:
    opacity var(--p-dur-slow) var(--p-ease-soft),
    translate var(--p-dur-slow) var(--p-ease-soft);
}
.pres-js [data-reveal].is-revealed {
  opacity: 1;
  translate: 0 0;
}

/* Stagger inside a group. Expressed as nth-child delays rather than a
   per-element inline --i custom property, because the platform's CSP
   forbids inline style attributes outright (see index.js). Nine steps
   covers the largest group on the page; anything beyond it lands on the
   last delay, which is the right behaviour anyway — a 12th card arriving
   700ms after the first would read as broken, not as choreography. */
[data-reveal-group] > [data-reveal]:nth-child(1) { transition-delay: 0ms; }
[data-reveal-group] > [data-reveal]:nth-child(2) { transition-delay: 55ms; }
[data-reveal-group] > [data-reveal]:nth-child(3) { transition-delay: 110ms; }
[data-reveal-group] > [data-reveal]:nth-child(4) { transition-delay: 165ms; }
[data-reveal-group] > [data-reveal]:nth-child(5) { transition-delay: 200ms; }
[data-reveal-group] > [data-reveal]:nth-child(6) { transition-delay: 235ms; }
[data-reveal-group] > [data-reveal]:nth-child(7) { transition-delay: 270ms; }
[data-reveal-group] > [data-reveal]:nth-child(8) { transition-delay: 300ms; }
[data-reveal-group] > [data-reveal]:nth-child(n + 9) { transition-delay: 330ms; }

/* -----------------------------------------------------------------------
   4. TYPOGRAPHY PRIMITIVES
   -------------------------------------------------------------------- */
.pres-eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--p-faint);
  margin-bottom: var(--p-gap-sm);
}

.pres-h2 {
  font-size: var(--p-h2);
  font-weight: 700;
  line-height: 1.08;
  /* Negative tracking is what stops Montserrat looking loose at display sizes;
     it scales with the type, which is why it's set here and not per-block. */
  letter-spacing: -0.03em;
  color: var(--p-ink);
  text-wrap: balance;
}

.pres-lead {
  font-size: var(--p-lead);
  line-height: 1.6;
  color: var(--p-muted);
  max-width: 62ch;
  margin-top: var(--p-gap);
}

.pres-head { margin-bottom: var(--p-gap-lg); }
.pres-head--center {
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}
.pres-head--center .pres-lead {
  margin-left: auto;
  margin-right: auto;
}

/* -----------------------------------------------------------------------
   5. BUTTONS
   The platform's pill button, restated. Same 100px radius, same ink fill,
   same white label, same secondary treatment (transparent with a hairline).
   The storefront's .btn-one builds its hover from two rotating pseudo-
   element discs; that effect depends on markup this page doesn't have, so
   the hover here is the same *intent* (the surface darkens and lifts)
   expressed with transform + shadow, which is also the cheaper one to run.
   -------------------------------------------------------------------- */
.pres-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 52px tall: comfortably past the 44px minimum touch target, and the same
     height as the checkout's primary action. */
  min-height: 52px;
  padding: 14px 32px;
  border-radius: var(--p-radius-pill);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-align: center;
  text-decoration: none;
  transition:
    background-color var(--p-dur) var(--p-ease),
    color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease),
    transform var(--p-dur) var(--p-ease);
}

.pres-btn--primary {
  background: var(--p-ink);
  /* Literal white, not a token: a label on a filled surface always wants
     white regardless of page theme — the same rule style.css states for its
     own accent buttons. */
  color: #ffffff;
  box-shadow: var(--p-shadow-sm);
}
.pres-btn--primary:hover {
  background: var(--p-ink-hover);
  transform: translateY(-2px);
  box-shadow: var(--p-shadow-md);
}
.pres-btn--primary:active { transform: translateY(0); }

.pres-btn--secondary {
  background: transparent;
  color: var(--p-ink);
  border: 1px solid var(--p-border);
}
.pres-btn--secondary:hover {
  background: var(--p-surface);
  border-color: rgba(26, 26, 26, 0.28);
  transform: translateY(-2px);
  box-shadow: var(--p-shadow-sm);
}
.pres-btn--secondary:active { transform: translateY(0); }

.pres-btn[disabled] {
  opacity: 0.55;
  pointer-events: none;
}

/* Selected CTA. The form below the buttons reflects whichever one is
   active, so the active state has to be unmistakable, not a tint. */
.pres-btn--secondary.is-active {
  background: var(--p-ink);
  color: #ffffff;
  border-color: var(--p-ink);
}

/* -----------------------------------------------------------------------
   6. PROGRESS BAR + SECTION RAIL
   -------------------------------------------------------------------- */
.pres-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 40;
  background: var(--p-border-soft);
  /* The bar is the one always-visible piece of chrome; it must sit above
     the notch inset rather than under it on a landscape iPhone. */
  padding-top: env(safe-area-inset-top);
}
.pres-progress__bar {
  height: 100%;
  background: var(--p-ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  /* scaleX, never width — this updates on every scroll frame, and a width
     animation would relayout the document each time. */
  will-change: transform;
}

.pres-rail {
  position: fixed;
  top: 50%;
  right: clamp(20px, 2.2vw, 40px);
  transform: translateY(-50%);
  z-index: 35;
  display: none;
  flex-direction: column;
  gap: 4px;
}

.pres-rail__item {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  padding: 7px 0;
  color: var(--p-faint);
  /* These are anchors (so they work without JS and can be opened/copied),
     which means the UA underline has to be turned off explicitly. */
  text-decoration: none;
  transition: color var(--p-dur) var(--p-ease);
}
.pres-rail__label {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(6px);
  transition:
    opacity var(--p-dur) var(--p-ease),
    transform var(--p-dur) var(--p-ease);
}
.pres-rail__dot {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.4;
  transition:
    opacity var(--p-dur) var(--p-ease),
    transform var(--p-dur) var(--p-ease);
}
.pres-rail__item:hover,
.pres-rail__item:focus-visible,
.pres-rail__item.is-active { color: var(--p-ink); }

/* Labels appear on hover/focus ONLY — never for the merely-active item.
   The rail lives in the page gutter, which at 1440px is about 100px wide;
   a label that stayed expanded for the current section would sit on top of
   the right-hand column of every grid on the page. The enlarged dot carries
   "you are here" on its own, and the name is one hover away. */
.pres-rail__item:hover .pres-rail__label,
.pres-rail__item:focus-visible .pres-rail__label {
  opacity: 1;
  transform: translateX(0);
}
.pres-rail__item.is-active .pres-rail__dot {
  opacity: 1;
  transform: scale(1.35);
}

/* A hovered label needs a surface under it, since it reaches into the
   gutter and can pass over content on a narrower desktop. */
.pres-rail__label {
  padding: 3px 9px;
  border-radius: var(--p-radius-pill);
  background: var(--p-surface);
  box-shadow: var(--p-shadow-sm);
}

/* The rail needs a gutter to sit in. Below this the deck is full-bleed
   enough that a fixed rail would overlap content, and the progress bar
   carries orientation on its own. */
@media (min-width: 1200px) {
  .pres-rail { display: flex; }
}

/* -----------------------------------------------------------------------
   7. HERO
   -------------------------------------------------------------------- */
.pres-hero {
  position: relative;
  min-height: 100vh;
  /* dvh follows collapsing mobile browser chrome; the vh above is the
     fallback for browsers without it. Both are needed. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Tighter than the one-column hero's padding was. The hero now carries a
     headline, the slogan, a subtitle, a 5:4 slideshow and two buttons, and
     on a 667px phone the old 16vh top inset pushed the primary action below
     the fold — which is the one thing a hero must never do. */
  padding: clamp(84px, 11vh, 200px) 0 clamp(64px, 9vh, 140px);
  background: var(--p-bg);
}

/* Mobile first, and authored as its own layout rather than as the desktop
   grid with one column: single column, slideshow between the words and the
   buttons, and the whole thing sized so the headline, the slogan, a real
   picture of the product and the primary action all fit one phone screen. */
.pres-hero__inner {
  width: 100%;
  max-width: var(--p-max);
  margin: 0 auto;
  padding-left: max(var(--p-shell-x), env(safe-area-inset-left));
  padding-right: max(var(--p-shell-x), env(safe-area-inset-right));
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "copy"
    "show"
    "actions";
  gap: clamp(26px, 5vw, 40px);
  align-content: center;
}

.pres-hero__copy { grid-area: copy; min-width: 0; }
.pres-hero__actions { grid-area: actions; }
.pres-hs { grid-area: show; min-width: 0; }

@media (min-width: 1024px) {
  .pres-hero__inner {
    /* The words get slightly less than half. The slideshow is the thing a
       viewer looks at first and it needs the width to stay legible at the
       scale the mock UI is drawn. */
    grid-template-columns: minmax(0, 0.88fr) minmax(0, 1fr);
    grid-template-areas:
      "copy    show"
      "actions show";
    grid-template-rows: auto auto;
    column-gap: clamp(40px, 5vw, 84px);
    row-gap: clamp(28px, 3vw, 40px);
    align-items: center;
  }
  /* Both rows of the left column are vertically centred against the stage
     as a pair, which is why the slideshow spans them rather than sitting in
     a row of its own. */
  .pres-hs { grid-row: 1 / span 2; align-self: center; }
}

/* The hero's own entrance. A deliberately short, ordered sequence on load —
   eyebrow, title, subtitle, then the supporting lines — rather than four
   things arriving at once. Runs regardless of the IntersectionObserver,
   which never fires for content already in the viewport at first paint. */
.pres-hero__logo,
.pres-hero__eyebrow,
.pres-hero__title,
.pres-hero__subtitle,
.pres-hero__recipient,
.pres-hero__note,
.pres-hero__actions,
.pres-hs {
  animation: pres-rise var(--p-dur-slow) var(--p-ease-soft) both;
}
/* Height-driven and first in the hero's rise sequence, so the mark lands
   before the eyebrow rather than sitting there while the rest animates in. */
.pres-hero__logo {
  height: clamp(30px, 4.4vw, 44px);
  width: auto;
  display: block;
  margin-bottom: clamp(18px, 2.4vw, 28px);
}
.pres-hero__logo { animation-delay: 20ms; }
.pres-hero__eyebrow { animation-delay: 60ms; }
.pres-hero__title { animation-delay: 130ms; }
/* The slogan's own two-line sequence runs at 300–520ms (see below), so the
   subtitle and everything after it are pushed back to land afterwards —
   the sentence should finish before the supporting copy starts arriving. */
.pres-hero__subtitle { animation-delay: 620ms; }
.pres-hero__recipient { animation-delay: 700ms; }
.pres-hero__note { animation-delay: 740ms; }
.pres-hero__actions { animation-delay: 800ms; }
/* The stage comes in early and alongside the headline rather than last:
   it is half the hero, and a hero that assembles its words and then pops a
   large panel in beside them a second later reads as a layout shift even
   though the box was reserved the whole time. */
.pres-hs { animation-delay: 260ms; }

@keyframes pres-rise {
  from { opacity: 0; transform: translate3d(0, 30px, 0); }
  to { opacity: 1; transform: translate3d(0, 0, 0); }
}

.pres-hero__title {
  /* Smaller than the full --p-hero the one-column hero used: the headline
     now shares its row with the slideshow, and at 132px it either wrapped
     to three lines or forced the stage down to a postage stamp. This is
     the same ratio of headline-to-measure, re-derived for half the width. */
  font-size: clamp(40px, 7vw, 82px);
  font-weight: 700;
  /* Sub-1 leading is only safe because this string is one or two words at
     display size; it is what makes the wordmark read as a mark. */
  line-height: 0.94;
  letter-spacing: -0.045em;
  color: var(--p-ink);
  margin-bottom: clamp(16px, 2vw, 22px);
}

.pres-hero__subtitle {
  font-size: clamp(17px, 1.5vw, 21px);
  line-height: 1.5;
  letter-spacing: -0.01em;
  font-weight: 500;
  color: var(--p-muted);
  max-width: 42ch;
  margin-top: clamp(18px, 2.2vw, 26px);
}

/* -----------------------------------------------------------------------
   7b. THE SLOGAN
   The company's one line, promoted out of the philosophy section near the
   bottom of the deck to directly under the wordmark.

   The brief asked for memorable and elegant without excessive movement, so
   there is exactly one gesture: each line wipes up from behind a clipping
   edge, 220ms apart, and then nothing moves again. No loop, no shimmer, no
   character-by-character stagger — the sentence is the point, and anything
   still animating while it is being read competes with it.

   Weight, not colour, carries the emphasis: the palette is monochrome by
   brand rule, so the second line steps up to 700 and full ink while the
   first sits at 600 and muted. That is the same device the storefront's
   own pull-quotes use.
   -------------------------------------------------------------------- */
.pres-slogan {
  margin-top: clamp(14px, 1.8vw, 20px);
  font-size: clamp(21px, 2.5vw, 34px);
  line-height: 1.2;
  letter-spacing: -0.028em;
  text-wrap: balance;
}

.pres-slogan__line {
  display: block;
  /* The clip is on a wrapper-free element via overflow on the block itself;
     a translated child inside an overflow:hidden line box is what produces
     the wipe rather than a fade. */
  overflow: hidden;
  font-weight: 600;
  color: var(--p-muted);
}

.pres-slogan__line--accent {
  font-weight: 700;
  color: var(--p-ink);
}

/* The moving part. ::after would not inherit the text, so the wipe is done
   by animating the line itself inside its own clip using a mask-free
   translate — cheap, compositor-only, and it degrades to a plain fade if
   the animation never runs. */
.pres-slogan__line {
  animation: pres-slogan-wipe 720ms var(--p-ease-soft) both;
}
.pres-slogan__line:first-child { animation-delay: 300ms; }
.pres-slogan__line--accent { animation-delay: 520ms; }

@keyframes pres-slogan-wipe {
  from { opacity: 0; transform: translate3d(0, 0.9em, 0); }
  to { opacity: 1; transform: translate3d(0, 0, 0); }
}

/* -----------------------------------------------------------------------
   7c. HERO ACTIONS
   -------------------------------------------------------------------- */
.pres-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--p-gap-sm);
}

/* Full width and stacked on the narrowest phones: two pills side by side
   below ~380px leaves each of them too narrow for its own label. */
@media (max-width: 400px) {
  .pres-hero__actions { flex-direction: column; }
  .pres-hero__actions .pres-btn { width: 100%; }
}

/* The View Product button. Secondary treatment plus a travelling arrow, so
   it reads as "goes somewhere" rather than "does something here" — it is
   the only control in the hero that leaves the deck. */
.pres-btn--product { gap: 10px; }
.pres-btn__arrow {
  display: inline-flex;
  width: 18px;
  height: 18px;
  transition: transform var(--p-dur) var(--p-ease);
}
.pres-btn__arrow svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pres-btn--product:hover .pres-btn__arrow { transform: translateX(3px); }

.pres-hero__recipient {
  margin-top: clamp(28px, 3.4vw, 44px);
  font-size: var(--p-small);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--p-faint);
}
.pres-hero__recipient strong {
  font-weight: 700;
  color: var(--p-ink);
}

.pres-hero__note {
  margin-top: var(--p-gap-sm);
  font-size: var(--p-lead);
  line-height: 1.6;
  color: var(--p-muted);
  max-width: 56ch;
}

/* .pres-hero__intro is gone with the paragraph it styled — the hero's
   closing block of body copy was dropped when the slideshow took that
   half of the section. Its content survives, unchanged, as the Who We Are
   lead directly below the fold. */

.pres-hero__cue {
  position: absolute;
  left: max(var(--p-shell-x), env(safe-area-inset-left));
  bottom: max(clamp(28px, 5vh, 52px), env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 12px;
  animation: pres-rise var(--p-dur-slow) var(--p-ease-soft) 560ms both;
}
.pres-hero__cue-line {
  display: block;
  width: 44px;
  height: 1px;
  background: var(--p-ink);
  transform-origin: 0 50%;
  animation: pres-cue 2.4s var(--p-ease) infinite;
}
.pres-hero__cue-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--p-faint);
}
@keyframes pres-cue {
  0%, 100% { transform: scaleX(0.35); opacity: 0.35; }
  50% { transform: scaleX(1); opacity: 1; }
}

/* -----------------------------------------------------------------------
   7d. HERO SLIDESHOW
   Ten drawn panels cross-fading inside one fixed box.

   The no-layout-shift rule is structural, not careful authoring: the stage
   declares an aspect-ratio, so its height is known before any content is
   measured, and every slide is absolutely positioned inside it. Slides
   therefore cannot influence each other's size or the page's, at any
   viewport, at any point in the cycle — including the first frame, before
   the script has run.

   The 60fps rule is likewise structural: the only properties that ever
   animate here are opacity and transform, both compositor-only. Nothing in
   this block animates width, height, top or left.
   -------------------------------------------------------------------- */
.pres-hs {
  position: relative;
  width: 100%;
  min-width: 0;
}

.pres-hs__stage {
  position: relative;
  width: 100%;
  /* Taller on a phone, where the stage is full-bleed and the drawing needs
     vertical room; wider on desktop, where it sits beside the copy. */
  aspect-ratio: 5 / 4;
  border-radius: var(--p-radius-lg);
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
  box-shadow: var(--p-shadow-lg);
  overflow: hidden;
  /* Establishes the sizing context every mock dimension below is derived
     from, so the drawings scale with the stage rather than with the
     viewport — the same panel is used at 320px and at 2560px. */
  container-type: inline-size;
  /* Promote once, here, rather than per-slide: ten promoted layers is ten
     textures held in GPU memory for one that is visible. */
  will-change: transform;
}

@media (min-width: 1024px) {
  .pres-hs__stage { aspect-ratio: 16 / 11; }
}

.pres-hs__slide {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: clamp(14px, 2.6cqw, 30px);
  gap: clamp(10px, 1.8cqw, 18px);
  opacity: 0;
  /* Leaving slides scale down a hair and outgoing ones stay put: the pair
     reads as one panel replacing another rather than two panels dissolving
     into each other. */
  transform: scale(0.985);
  transition:
    opacity 620ms var(--p-ease-soft),
    transform 620ms var(--p-ease-soft);
  /* An inactive slide is not just invisible, it is unreachable: without
     this its buttons and text stay in the tab order and in the
     accessibility tree, and a keyboard user tabs into nine hidden panels. */
  visibility: hidden;
  pointer-events: none;
}

.pres-hs__slide.is-active {
  opacity: 1;
  transform: scale(1);
  visibility: visible;
  pointer-events: auto;
}

.pres-hs__caption {
  flex: none;
  font-size: clamp(12.5px, 1.1vw, 14px);
  line-height: 1.45;
  color: var(--p-muted);
  /* Reserved so the drawing above never resizes between a one-line and a
     two-line caption. */
  min-height: 3.2em;
}
.pres-hs__caption strong {
  display: block;
  font-weight: 700;
  color: var(--p-ink);
  letter-spacing: -0.01em;
}

/* ---- Controls ---- */
.pres-hs__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: clamp(14px, 1.6vw, 20px);
}

.pres-hs__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px: the same minimum touch target the rest of the deck holds to. */
  width: 44px;
  height: 44px;
  border-radius: var(--p-radius-pill);
  border: 1px solid var(--p-border);
  background: transparent;
  color: var(--p-ink);
  transition:
    background-color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    transform var(--p-dur) var(--p-ease);
}
.pres-hs__arrow svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pres-hs__arrow:hover {
  background: var(--p-surface);
  border-color: rgba(26, 26, 26, 0.28);
}
.pres-hs__arrow:active { transform: scale(0.94); }

/* Arrows are a desktop affordance. On touch the gesture is the swipe, and
   two 44px circles either side of the dots eat width the dots need. */
@media (max-width: 767px) {
  .pres-hs__arrow { display: none; }
}

.pres-hs__dots {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 7px;
}

/* Each dot is a real button with the slide's name on it. The visible mark
   is small, but the hit area around it is not — the padding is the target. */
.pres-hs__dot {
  position: relative;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  background: transparent;
  border-radius: var(--p-radius-pill);
}
/* The mark is centred at a fixed size rather than inset from the button,
   so the hit area can grow on touch (below) without the dot growing with
   it. Ten 44px-square targets would be 440px of row on a 390px phone. */
.pres-hs__dot::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  border-radius: var(--p-radius-pill);
  background: rgba(26, 26, 26, 0.18);
  transition:
    background-color var(--p-dur) var(--p-ease),
    transform var(--p-dur) var(--p-ease);
}

/* Touch: full-height targets, narrow enough that ten of them still fit
   across the narrowest phone we support. 28x44 is the compromise — the
   dimension a thumb actually misses on is the vertical one, and the
   swipe is the primary gesture here regardless.

   Keyed on the pointer as well as the width: a tablet is 768px+ and gets
   the desktop arrows, but it is still a finger on the dots. */
@media (max-width: 767px), (hover: none) {
  .pres-hs__dot { width: 28px; height: 44px; }
  .pres-hs__dots { gap: 0; }
}
.pres-hs__dot:hover::before { background: rgba(26, 26, 26, 0.34); }
.pres-hs__dot.is-active::before {
  background: var(--p-ink);
  /* Grows from the centre instead of changing width, so the row of dots
     never reflows as the active one moves along it. */
  transform: scale(1.75);
}

/* -----------------------------------------------------------------------
   7e. MOCK UI PRIMITIVES (.hsm)
   The vocabulary the ten hero panels are drawn from — a frame, a sidebar,
   cards, lines, thumbnails, a chart, a phone. Ten bespoke drawings would
   have been ten maintenance problems; these are eight shapes recombined.

   Everything is sized in cqw against the stage and coloured from the same
   monochrome ramp as the rest of the deck, so a panel is recognisably the
   platform without pretending to be a screenshot of it.
   -------------------------------------------------------------------- */
.hsm {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-radius: clamp(8px, 1.2cqw, 14px);
  background: var(--p-surface);
  border: 1px solid var(--p-border-soft);
  box-shadow: var(--p-shadow-sm);
  overflow: hidden;
  /* Fallback for browsers without container query units; the @supports
     block below is what actually applies almost everywhere. */
  font-size: 10px;
}
@supports (width: 1cqw) {
  .hsm { font-size: clamp(5.5px, 1.75cqw, 12px); }
}

/* Window chrome */
.hsm__bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 1.2em;
  padding: 0.9em 1.2em;
  border-bottom: 1px solid var(--p-border-soft);
  background: var(--p-surface-2);
}
.hsm__dots { display: inline-flex; gap: 0.45em; }
.hsm__dots i {
  width: 0.62em;
  height: 0.62em;
  border-radius: 50%;
  background: rgba(26, 26, 26, 0.14);
}
.hsm__url {
  flex: 1;
  font-size: 0.92em;
  color: var(--p-faint);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.hsm__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 1.1em;
  padding: 1.3em;
}
.hsm__body--split { flex-direction: row; gap: 1.2em; }

/* App shell: sidebar + main */
.hsm__app { flex: 1; min-height: 0; display: flex; }
.hsm__side {
  flex: none;
  width: 20%;
  display: flex;
  flex-direction: column;
  gap: 0.9em;
  padding: 1.2em 0.9em;
  border-right: 1px solid var(--p-border-soft);
  background: var(--p-surface-2);
}
.hsm__side-mark {
  width: 55%;
  height: 0.85em;
  border-radius: 0.3em;
  background: rgba(26, 26, 26, 0.3);
  margin-bottom: 0.5em;
}
.hsm__side-item {
  height: 0.72em;
  border-radius: 0.3em;
  background: rgba(26, 26, 26, 0.09);
}
.hsm__side-item.is-on { background: rgba(26, 26, 26, 0.26); }

.hsm__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1.1em;
  padding: 1.3em;
}

/* Grids and cards */
.hsm__grid { display: grid; gap: 0.9em; }
.hsm__grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.hsm__grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* A grid that is the last thing in a panel takes the panel's remaining
   height and divides it between its rows, so the drawing fills its frame
   instead of clustering at the top over a band of empty white. Restricted
   to :last-child because the grids that are followed by something else
   (the stat row above a list, say) must keep their content height. */
.hsm__body > .hsm__grid:last-child {
  flex: 1;
  min-height: 0;
  grid-auto-rows: minmax(0, 1fr);
}

.hsm__card {
  display: flex;
  flex-direction: column;
  gap: 0.55em;
  padding: 0.9em;
  border-radius: 0.7em;
  border: 1px solid var(--p-border-soft);
  background: var(--p-bg);
}
.hsm__card--person { align-items: flex-start; }
.hsm__card--meal { flex-direction: row; align-items: center; gap: 0.8em; }

/* Lines: the stand-in for text. Five widths is enough vocabulary for every
   panel and keeps the drawings from looking like the same block repeated. */
.hsm__line {
  height: 0.62em;
  border-radius: 0.31em;
  background: rgba(26, 26, 26, 0.1);
}
.hsm__line--xs { width: 32%; }
.hsm__line--sm { width: 48%; }
.hsm__line--md { width: 66%; }
.hsm__line--lg { width: 84%; }
.hsm__line--strong { background: rgba(26, 26, 26, 0.24); height: 0.8em; }

.hsm__col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.55em; }
.hsm__col--pad { padding: 0.2em 0; }
.hsm__row { display: flex; gap: 0.9em; }
.hsm__row--card {
  align-items: center;
  padding: 0.9em;
  border-radius: 0.7em;
  border: 1px solid var(--p-border-soft);
  background: var(--p-bg);
}
.hsm__row--split { align-items: flex-end; justify-content: space-between; }
.hsm__hero-row { display: flex; align-items: center; gap: 1.1em; }

.hsm__avatar {
  flex: none;
  width: 2.2em;
  height: 2.2em;
  border-radius: 50%;
  background: rgba(26, 26, 26, 0.13);
}
.hsm__avatar--xl { width: 3.8em; height: 3.8em; }

.hsm__pills { display: flex; gap: 0.5em; }
.hsm__pill {
  width: 2.6em;
  height: 0.95em;
  border-radius: 100px;
  background: rgba(26, 26, 26, 0.09);
}

/* Thumbnails. A soft diagonal rather than a flat fill so a grid of them
   does not read as a wall of grey rectangles. */
.hsm__thumb {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 0.5em;
  background: linear-gradient(135deg, rgba(26, 26, 26, 0.1), rgba(26, 26, 26, 0.045));
  overflow: hidden;
}
.hsm__thumb--wide { aspect-ratio: 16 / 9; }
.hsm__thumb--sq { width: 2.8em; aspect-ratio: 1; flex: none; }
.hsm__thumb--feature { background: linear-gradient(135deg, rgba(26, 26, 26, 0.14), rgba(26, 26, 26, 0.06)); }

/* The one piece of real photography in the hero: the selected product's
   own artwork, dropped into the feature card of the products and library
   panels. object-fit keeps any aspect ratio from distorting the card. */
.hsm__shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Stats */
.hsm__stat {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  padding: 0.9em;
  border-radius: 0.7em;
  border: 1px solid var(--p-border-soft);
  background: var(--p-bg);
}
.hsm__stat-num {
  width: 55%;
  height: 1.25em;
  border-radius: 0.3em;
  background: rgba(26, 26, 26, 0.28);
}
.hsm__stat-num--wide { width: 78%; height: 1.8em; }
.hsm__stat--big { flex: 1; }

/* Progress meters */
.hsm__meter {
  display: block;
  width: 100%;
  height: 0.45em;
  border-radius: 100px;
  background: rgba(26, 26, 26, 0.08);
  overflow: hidden;
}
.hsm__meter-fill {
  display: block;
  height: 100%;
  border-radius: 100px;
  background: var(--p-ink);
}
/* Widths as classes, not inline styles — the platform's CSP forbids the
   style attribute (see the header of presentation.js). */
.hsm__meter-fill--34 { width: 34%; }
.hsm__meter-fill--62 { width: 62%; }
.hsm__meter-fill--88 { width: 88%; }

/* Bar chart */
.hsm__chart {
  display: flex;
  align-items: flex-end;
  gap: 0.7em;
  height: 5.5em;
  padding-top: 0.5em;
}
.hsm__chart-col {
  flex: 1;
  border-radius: 0.3em 0.3em 0 0;
  background: rgba(26, 26, 26, 0.12);
}
.hsm__chart-col--1 { height: 42%; }
.hsm__chart-col--2 { height: 58%; }
.hsm__chart-col--3 { height: 36%; }
.hsm__chart-col--4 { height: 72%; }
.hsm__chart-col--5 { height: 88%; }
.hsm__chart-col--6 { height: 64%; }
/* The current period, filled: a chart where every bar is the same grey has
   nothing to look at. */
.hsm__chart-col--7 { height: 100%; background: var(--p-ink); }

/* Lists */
.hsm__list { display: flex; flex-direction: column; gap: 0.6em; }
.hsm__list-row {
  height: 1.5em;
  border-radius: 0.4em;
  background: rgba(26, 26, 26, 0.06);
  border: 1px solid var(--p-border-soft);
}
.hsm__list-row.is-on {
  background: rgba(26, 26, 26, 0.13);
  border-color: rgba(26, 26, 26, 0.16);
}

/* Video player */
.hsm__player {
  position: relative;
  flex: 1.5;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.6em;
  background: linear-gradient(135deg, rgba(26, 26, 26, 0.16), rgba(26, 26, 26, 0.07));
}
.hsm__play {
  width: 3em;
  height: 3em;
  border-radius: 50%;
  background: var(--p-surface);
  box-shadow: var(--p-shadow-md);
  position: relative;
}
.hsm__play::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 54%;
  transform: translate(-50%, -50%);
  border-left: 0.85em solid var(--p-ink);
  border-top: 0.55em solid transparent;
  border-bottom: 0.55em solid transparent;
}
.hsm__scrub {
  position: absolute;
  left: 0.9em;
  right: 0.9em;
  bottom: 0.9em;
  height: 0.4em;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.55);
  overflow: hidden;
}

/* Macro rings */
.hsm__macros { display: flex; gap: 1.2em; justify-content: center; }
.hsm__ring {
  width: 3.4em;
  height: 3.4em;
  border-radius: 50%;
  /* conic-gradient is the cheapest correct donut: one paint, no SVG, no
     extra element per ring. */
  background: conic-gradient(var(--p-ink) var(--hsm-ring, 60%), rgba(26, 26, 26, 0.1) 0);
  position: relative;
}
.hsm__ring::after {
  content: "";
  position: absolute;
  inset: 0.62em;
  border-radius: 50%;
  background: var(--p-surface);
}
.hsm__ring--a { --hsm-ring: 72%; }
.hsm__ring--b { --hsm-ring: 45%; }
.hsm__ring--c { --hsm-ring: 88%; }

/* Phone frame */
.hsm--stage {
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  box-shadow: none;
}
.hsm__phone {
  position: relative;
  width: 11.5em;
  max-width: 42%;
  aspect-ratio: 9 / 18;
  border-radius: 1.6em;
  background: var(--p-surface);
  border: 0.32em solid var(--p-ink);
  box-shadow: var(--p-shadow-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.hsm__notch {
  position: absolute;
  top: 0.35em;
  left: 50%;
  transform: translateX(-50%);
  width: 32%;
  height: 0.5em;
  border-radius: 100px;
  background: var(--p-ink);
}
.hsm__phone-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6em;
  padding: 1.6em 0.9em 0.9em;
}
.hsm__tabbar {
  margin-top: auto;
  display: flex;
  justify-content: space-around;
  padding-top: 0.7em;
  border-top: 1px solid var(--p-border-soft);
}
.hsm__tabbar i {
  width: 0.85em;
  height: 0.85em;
  border-radius: 0.25em;
  background: rgba(26, 26, 26, 0.14);
}
.hsm__tabbar i:first-child { background: var(--p-ink); }

/* The two-device panel: a desktop window with a phone overlapping its
   lower-right corner, which is how the platform is actually used. */
.hsm__combo {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
}
.hsm--flat {
  width: 84%;
  height: 82%;
  flex: none;
}
.hsm__phone--inset {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 8.5em;
  max-width: 34%;
}

/* -----------------------------------------------------------------------
   8. WHO WE ARE — audience list
   Four numbered entries. On desktop they sit as columns divided by
   hairlines, which is the editorial treatment; on mobile the same hairline
   becomes a horizontal rule between stacked rows. Same DOM, same device,
   rotated.
   -------------------------------------------------------------------- */
.pres-audience {
  display: grid;
  gap: 0;
  border-top: 1px solid var(--p-border);
}
.pres-audience__item {
  padding: clamp(26px, 3vw, 36px) 0;
  border-bottom: 1px solid var(--p-border);
}
.pres-audience__index {
  display: block;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--p-faint);
  margin-bottom: 10px;
  font-variant-numeric: tabular-nums;
}
.pres-audience__name {
  font-size: clamp(20px, 1.9vw, 25px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.pres-audience__desc {
  font-size: var(--p-body);
  line-height: 1.6;
  color: var(--p-muted);
  max-width: 42ch;
}

@media (min-width: 860px) {
  .pres-audience {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    border-bottom: 1px solid var(--p-border);
  }
  .pres-audience__item {
    padding: clamp(28px, 2.6vw, 38px) clamp(20px, 2vw, 30px);
    border-bottom: 0;
    border-left: 1px solid var(--p-border);
  }
  .pres-audience__item:first-child { padding-left: 0; border-left: 0; }
  .pres-audience__item:last-child { padding-right: 0; }
}

/* -----------------------------------------------------------------------
   9. CARD GRIDS — services + studio
   -------------------------------------------------------------------- */
.pres-grid {
  display: grid;
  gap: var(--p-gap);
  grid-template-columns: 1fr;
}

@media (min-width: 620px) {
  .pres-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1000px) {
  .pres-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Service card: the platform's own bordered-flat-card idiom (surface fill,
   hairline border, 10px radius) with the deck's larger padding. Hover lifts
   by 3px and swaps the hairline for a shadow — the same gesture the
   storefront's product cards use. */
.pres-card {
  position: relative;
  padding: clamp(24px, 2.4vw, 32px);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  border-radius: var(--p-radius);
  transition:
    transform var(--p-dur) var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease);
}
.pres-card:hover {
  transform: translateY(-3px);
  border-color: transparent;
  box-shadow: var(--p-shadow-md);
}

/* The check mark, drawn rather than iconfont-loaded — Font Awesome is not
   on this page, and one CSS shape is cheaper than a webfont request for the
   eleven identical marks this grid needs. */
.pres-card__mark {
  display: block;
  width: 20px;
  height: 20px;
  margin-bottom: 16px;
  border-radius: 50%;
  background: var(--p-ink);
  position: relative;
}
.pres-card__mark::after {
  content: "";
  position: absolute;
  left: 6.5px;
  top: 5px;
  width: 5px;
  height: 9px;
  border: solid #ffffff;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}

.pres-card__title {
  font-size: var(--p-h3);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.pres-card__desc {
  font-size: var(--p-body);
  line-height: 1.6;
  color: var(--p-muted);
}

/* Studio tile: same family, lighter weight. No mark, a top rule instead of
   a full border — so the two grids read as related but not interchangeable,
   which is the difference between a platform capability and a service. */
.pres-tile {
  padding: clamp(22px, 2.2vw, 30px) 0 0;
  border-top: 2px solid var(--p-ink);
  transition: border-color var(--p-dur) var(--p-ease);
}
.pres-tile:hover { border-color: var(--p-faint); }
.pres-tile__title {
  font-size: var(--p-h3);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.pres-tile__desc {
  font-size: var(--p-body);
  line-height: 1.6;
  color: var(--p-muted);
  max-width: 40ch;
}

/* -----------------------------------------------------------------------
   10. HOW IT WORKS — the step wizard
   Two authored layouts sharing one DOM.

   MOBILE (< 1024px): a vertical accordion. Each head opens its own body
   directly beneath it, with a continuous rail running down the left through
   every number. Heights animate via max-height against a measured value set
   by presentation.js — grid-template-rows: 0fr/1fr would be cleaner but is
   not safe on the Safari versions a client's iPad is likely to be running.

   DESKTOP (>= 1024px): the heads become a fixed numbered rail in column 1
   and every body is stacked into column 2, cross-fading between them. The
   rail no longer moves when the selection changes, which is what makes it
   feel like a presentation rather than a FAQ.
   -------------------------------------------------------------------- */
.pres-wizard {
  position: relative;
  padding-left: 0;
}

/* The rail line itself, and the fill that tracks progress through it. */
.pres-wizard__rail {
  position: absolute;
  left: 21px;
  top: 30px;
  bottom: 30px;
  width: 1px;
  background: var(--p-border);
}
.pres-wizard__rail-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 100%;
  background: var(--p-ink);
  transform: scaleY(0);
  transform-origin: 50% 0;
  transition: transform var(--p-dur-slow) var(--p-ease-soft);
}

.pres-step { position: relative; }

.pres-step__head {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.6vw, 22px);
  width: 100%;
  /* Full-width, 64px-tall target — the whole row is tappable, not just the
     label. */
  min-height: 64px;
  padding: 12px 0;
  text-align: left;
}

.pres-step__num {
  position: relative;
  z-index: 1;
  flex: none;
  width: 42px;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--p-bg);
  border: 1px solid var(--p-border);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--p-faint);
  transition:
    background-color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    color var(--p-dur) var(--p-ease),
    transform var(--p-dur) var(--p-ease);
}
.pres-section--tint .pres-step__num { background: var(--p-surface-2); }

.pres-step.is-active .pres-step__num,
.pres-step.is-complete .pres-step__num {
  background: var(--p-ink);
  border-color: var(--p-ink);
  color: #ffffff;
}
.pres-step.is-active .pres-step__num { transform: scale(1.06); }

.pres-step__label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: clamp(18px, 1.7vw, 23px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--p-faint);
  transition: color var(--p-dur) var(--p-ease);
}
.pres-step.is-active .pres-step__label,
.pres-step.is-complete .pres-step__label { color: var(--p-ink); }
.pres-step__head:hover .pres-step__label { color: var(--p-ink); }

/* Chevron drawn from a rotated border, same as the mobile drawer's
   accordion in style.css. */
.pres-step__chevron {
  flex: none;
  width: 9px;
  height: 9px;
  border: solid var(--p-faint);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--p-dur) var(--p-ease), border-color var(--p-dur) var(--p-ease);
}
.pres-step.is-active .pres-step__chevron {
  border-color: var(--p-ink);
  transform: rotate(-135deg) translate(-2px, -2px);
}

.pres-step__body {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  transition:
    max-height var(--p-dur) var(--p-ease),
    opacity var(--p-dur) var(--p-ease);
}
.pres-step.is-active .pres-step__body { opacity: 1; }

.pres-step__body-inner {
  padding: 0 0 26px calc(42px + clamp(14px, 1.6vw, 22px));
  font-size: var(--p-lead);
  line-height: 1.62;
  color: var(--p-muted);
  max-width: 60ch;
}

@media (min-width: 1024px) {
  .pres-wizard {
    display: grid;
    grid-template-columns: minmax(0, 380px) minmax(0, 1fr);
    /* Six explicit rows, one per step, and they must be EXPLICIT: the rail
       and the body panels below both span `1 / -1`, and -1 resolves against
       the explicit grid only. Left implicit, -1 would collapse to line 1 and
       every panel would silently shrink to a single row. */
    grid-template-rows: repeat(6, auto);
    column-gap: clamp(40px, 5vw, 88px);
    align-items: start;
  }
  /* Absolutely positioned, but still grid-placed: with the wizard as its
     containing block, the placement below is what sizes the rail to column
     1's full height instead of the whole container. */
  .pres-wizard__rail {
    grid-column: 1;
    grid-row: 1 / -1;
  }
  .pres-step {
    grid-column: 1;
    display: contents;
  }
  .pres-step__head { grid-column: 1; }

  /* Every body lands in the same cell of column 2 and they cross-fade.
     `visibility` (not just opacity) so an inactive panel is never a
     keyboard tab stop or a screen-reader stop sitting invisibly on top of
     the active one. */
  .pres-step__body {
    grid-column: 2;
    grid-row: 1 / -1;
    max-height: none;
    visibility: hidden;
    align-self: start;
    /* Held off the top edge so the panel optically aligns with step 1's
       label rather than its container. */
    padding-top: 4px;
    transform: translate3d(0, 12px, 0);
    transition:
      opacity var(--p-dur) var(--p-ease),
      transform var(--p-dur) var(--p-ease),
      visibility 0s linear var(--p-dur);
  }
  .pres-step.is-active .pres-step__body {
    visibility: visible;
    transform: translate3d(0, 0, 0);
    transition-delay: 0s, 0s, 0s;
  }
  .pres-step__body-inner {
    padding: 0;
    font-size: clamp(18px, 1.35vw, 21px);
  }

  /* No chevron on desktop. It is an accordion affordance, and here nothing
     expands beneath the row — a chevron flipping to point up at the active
     step promises a disclosure that isn't there. The filled number and the
     ink label already say which step is selected. */
  .pres-step__chevron { display: none; }
}

/* -----------------------------------------------------------------------
   11. DEPTH LAYERS
   The paper stays paper. What changes is that three sections now sit on a
   soft field instead of a flat fill: two oversized radial washes and, on
   the showcase, a faint rule grid fading out under the headline.

   Two rules keep this from becoming a different design system. First, the
   only colour used is the existing ink at 4-9% — there is no new hue
   anywhere in the file. Second, none of it uses filter: blur(), which is
   the usual way this effect is built and the reason it usually costs a
   phone its frame budget. A radial-gradient falls off on its own, for
   free, on the GPU.
   -------------------------------------------------------------------- */
.pres-section--deep {
  position: relative;
  /* Orbs overhang the section by design; nothing may reach the viewport
     edge and produce a horizontal scrollbar. */
  overflow: hidden;
  isolation: isolate;
}
/* Content above the field. Stated on children rather than by giving the
   aura a negative index, which would push it behind the section's own
   background and make it invisible. */
.pres-section--deep > .pres-container,
.pres-section--deep > .pres-belt { position: relative; z-index: 1; }

.pres-aura {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.pres-aura__orb {
  position: absolute;
  display: block;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(26, 26, 26, 0.085), rgba(26, 26, 26, 0) 78%);
  will-change: transform;
}
.pres-aura__orb--a {
  width: clamp(320px, 52vw, 700px);
  aspect-ratio: 1;
  top: -14%;
  left: -16%;
  animation: pres-drift-a 26s var(--p-ease) infinite alternate;
}
.pres-aura__orb--b {
  width: clamp(260px, 42vw, 560px);
  aspect-ratio: 1;
  bottom: -18%;
  right: -14%;
  animation: pres-drift-b 31s var(--p-ease) infinite alternate;
}
.pres-aura__orb--c {
  width: clamp(340px, 58vw, 760px);
  aspect-ratio: 1;
  top: 4%;
  right: -22%;
  animation: pres-drift-b 29s var(--p-ease) infinite alternate;
}
.pres-aura__orb--d {
  width: clamp(300px, 46vw, 620px);
  aspect-ratio: 1;
  top: 18%;
  left: -18%;
  animation: pres-drift-a 34s var(--p-ease) infinite alternate;
}

@keyframes pres-drift-a {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(5%, 7%, 0); }
}
@keyframes pres-drift-b {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-6%, -5%, 0); }
}

/* Rule grid, masked to a soft ellipse so it never shows an edge. */
.pres-aura__grid {
  position: absolute;
  top: -6%;
  left: -6%;
  right: -6%;
  height: 62%;
  background-image:
    linear-gradient(to right, rgba(26, 26, 26, 0.05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(26, 26, 26, 0.05) 1px, transparent 1px);
  background-size: 58px 58px;
  -webkit-mask-image: radial-gradient(ellipse 66% 58% at 50% 4%, #000 0%, transparent 74%);
  mask-image: radial-gradient(ellipse 66% 58% at 50% 4%, #000 0%, transparent 74%);
}

/* Magnetic hover. The pointer offset arrives from presentation.js as two
   custom properties and composes with a CSS-owned lift, so hover and
   magnetism never fight over the same transform. The 260ms transition is
   what turns a raw pointer delta into something that trails the cursor
   and settles instead of tracking it exactly.

   Scoped to .pres-body only to out-specify `.pres-js [data-reveal]`,
   which every magnetic element on this page also carries: `transition` is
   a shorthand, so the reveal's would otherwise replace this one whole and
   the magnetism would snap rather than trail. */
.pres-body [data-magnetic] {
  transform: translate3d(var(--mx, 0px), calc(var(--my, 0px) + var(--lift, 0px)), 0);
  transition:
    transform 260ms var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease);
}

/* -----------------------------------------------------------------------
   12. BENEFIT BELT — the infinite carousel
   A single transform on one track, advanced by rAF. Everything else is
   arithmetic in JS: the cards are cloned until the strip is at least twice
   the viewport, and the offset wraps against the width of one full set, so
   there is no seam and no snap-back at either end.

   Full-bleed on purpose. The band has to run off both edges for the loop
   to read as endless rather than as a slider with a beginning.
   -------------------------------------------------------------------- */
.pres-belt {
  position: relative;
  margin-top: var(--p-gap-lg);
}

.pres-belt__viewport {
  overflow: hidden;
  /* Room above and below for the hover lift and its shadow, which would
     otherwise be clipped by the same overflow that hides the strip. */
  padding: 10px 0 18px;
  cursor: grab;
  /* pan-y, not none: a vertical swipe that starts on a card still scrolls
     the deck. Only the horizontal axis is claimed. */
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* Soft edges so cards dissolve rather than being cut off. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 44px, #000 calc(100% - 44px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 44px, #000 calc(100% - 44px), transparent 100%);
}
.pres-belt__viewport:focus-visible {
  outline: 2px solid var(--p-ink);
  outline-offset: -4px;
  border-radius: var(--p-radius);
}
.pres-belt.is-dragging .pres-belt__viewport { cursor: grabbing; }

.pres-belt__track {
  display: flex;
  align-items: stretch;
  gap: clamp(12px, 1.2vw, 18px);
  width: max-content;
  will-change: transform;
}

.pres-benefit {
  position: relative;
  flex: 0 0 auto;
  width: clamp(228px, 72vw, 304px);
  padding: clamp(20px, 2vw, 26px);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  border-radius: var(--p-radius-lg);
  box-shadow: var(--p-shadow-sm);
  overflow: hidden;
  transition:
    transform var(--p-dur) var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease);
}
/* The glow. Held on a pseudo-element so the card's own transform stays
   free for the lift, and faded rather than moved so it costs one
   composited opacity change. */
.pres-benefit::before {
  content: "";
  position: absolute;
  inset: -50% -30% auto -30%;
  height: 130%;
  background: radial-gradient(58% 58% at 32% 0%, rgba(26, 26, 26, 0.09), rgba(26, 26, 26, 0) 72%);
  opacity: 0;
  transition: opacity var(--p-dur) var(--p-ease);
  pointer-events: none;
}
.pres-benefit:hover { transform: translateY(-6px); }
.pres-benefit:hover::before { opacity: 1; }

.pres-benefit__icon {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin-bottom: 18px;
  border-radius: var(--p-radius);
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
  color: var(--p-ink);
  transition:
    background-color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    color var(--p-dur) var(--p-ease),
    transform var(--p-dur) var(--p-ease);
}
.pres-benefit__icon svg,
.pres-switch__icon svg,
.pres-cat__icon svg,
.pres-journey__node svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.pres-benefit__icon svg { width: 24px; height: 24px; }
.pres-benefit:hover .pres-benefit__icon {
  background: var(--p-ink);
  border-color: var(--p-ink);
  color: #ffffff;
  transform: translateY(-2px) scale(1.05);
}

.pres-benefit__title {
  position: relative;
  font-size: clamp(16px, 1.15vw, 18px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--p-ink);
  margin-bottom: 7px;
}
.pres-benefit__desc {
  position: relative;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--p-muted);
}

.pres-belt__hint {
  margin-top: 4px;
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--p-faint);
  transition: opacity var(--p-dur-slow) var(--p-ease);
}
/* The hint has done its job the moment someone drags. Leaving it up after
   that is the interface still explaining itself to a visitor who has
   already understood. */
.pres-belt.is-engaged .pres-belt__hint { opacity: 0; }

/* -----------------------------------------------------------------------
   13. PILLARS
   The three reasons, rebuilt.

   The previous version was an index, a heading and one line of copy on a
   glass panel. It was the oldest block on the deck and it showed: next to
   the campaigns bento and the value grid it read as a placeholder.

   What changed, and why each part earns its place: a drawn glyph that
   animates the claim rather than decorating it (four scattered tools
   collapsing to one, a payment firing three steps, a rising chart), and a
   two-cell proof row that turns an adjective into a before/after a viewer
   can argue with. The glass, the radius, the hairline and the hover lift
   are unchanged — this is the same card, given a hierarchy.
   -------------------------------------------------------------------- */
.pres-pillars {
  display: grid;
  gap: var(--p-gap);
  grid-template-columns: 1fr;
  margin-top: var(--p-gap-lg);
}
.pres-pillar {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(26px, 2.6vw, 36px);
  border-radius: var(--p-radius-lg);
  border: 1px solid var(--p-border);
  overflow: hidden;
  /* Glass, and the only place on the deck that uses it: the pillars are
     the one block sitting directly over an aura, so there is something
     behind them worth blurring. Three elements is a cost a phone can pay;
     the twelve catalogue tiles below deliberately do not. */
  background: linear-gradient(180deg, rgba(253, 253, 252, 0.94), rgba(253, 253, 252, 0.62));
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: var(--p-shadow-sm);
}

/* A hairline that draws itself across the top edge on hover. One scaleX on
   a pseudo-element — the cheapest possible way to make a static card feel
   like it responded. */
.pres-pillar::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--p-ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 420ms var(--p-ease-soft);
}
.pres-pillar:hover::before { transform: scaleX(1); }

.pres-pillar:hover {
  --lift: -5px;
  box-shadow: var(--p-shadow-md);
  border-color: rgba(26, 26, 26, 0.2);
}

.pres-pillar__index {
  display: block;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--p-faint);
  font-variant-numeric: tabular-nums;
}

/* ---- The glyph ----
   48x48 drawings in the deck's own stroke/fill language. Each one animates
   on hover and once on reveal; all of it is transform and opacity. */
.pres-pillar__icon {
  display: block;
  width: 48px;
  height: 48px;
  margin: clamp(18px, 2vw, 24px) 0 clamp(16px, 1.8vw, 22px);
  color: var(--p-ink);
}
.pres-pillar__icon svg { width: 100%; height: 100%; overflow: visible; }

.pres-pillar__glyph,
.pres-pillar__core,
.pres-pillar__bar {
  fill: rgba(26, 26, 26, 0.14);
}
.pres-pillar__core,
.pres-pillar__bar.is-filled { fill: var(--p-ink); }

.pres-pillar__track {
  fill: none;
  stroke: rgba(26, 26, 26, 0.16);
  stroke-width: 2;
  stroke-linecap: round;
}
.pres-pillar__pip { fill: rgba(26, 26, 26, 0.16); }
.pres-pillar__bolt { fill: var(--p-ink); }

/* 1 — the four outer squares converge on the centre. transform-box +
   transform-origin are required for percentage origins to mean anything
   inside an SVG; without them Chrome rotates about the viewport corner. */
.pres-pillar__glyph {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  transition: transform 520ms var(--p-ease-soft), opacity 520ms var(--p-ease-soft);
}
.pres-pillar:hover .pres-pillar__glyph--a { transform: translate(5px, 5px) scale(0.82); }
.pres-pillar:hover .pres-pillar__glyph--b { transform: translate(-5px, 5px) scale(0.82); }
.pres-pillar:hover .pres-pillar__glyph--c { transform: translate(5px, -5px) scale(0.82); }
.pres-pillar:hover .pres-pillar__glyph--d { transform: translate(-5px, -5px) scale(0.82); }
.pres-pillar__core {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  transform: scale(0.86);
  transition: transform 520ms var(--p-ease-soft);
}
.pres-pillar:hover .pres-pillar__core { transform: scale(1.08); }

/* 2 — the bolt drops, then the three pips light in sequence. Runs on hover
   and whenever the card has been revealed (.is-revealed is set by the
   scroll-reveal observer in presentation.js — see §3). */
.pres-pillar__bolt {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  transition: transform 420ms var(--p-ease);
}
.pres-pillar:hover .pres-pillar__bolt { transform: translateY(2px) scale(1.06); }

.pres-pillar__pip { transition: fill 260ms var(--p-ease); }
.pres-pillar:hover .pres-pillar__pip,
.pres-pillar.is-revealed .pres-pillar__pip { animation: pres-pip 1.6s var(--p-ease) infinite; }
.pres-pillar__pip--2 { animation-delay: 0.18s !important; }
.pres-pillar__pip--3 { animation-delay: 0.36s !important; }

@keyframes pres-pip {
  0%, 60%, 100% { fill: rgba(26, 26, 26, 0.16); }
  20%, 40% { fill: var(--p-ink); }
}

/* 3 — the columns grow from their baselines on hover. */
.pres-pillar__bar {
  transform-box: fill-box;
  transform-origin: 50% 100%;
  transition: transform 520ms var(--p-ease-soft);
}
.pres-pillar:hover .pres-pillar__bar--1 { transform: scaleY(1.14); }
.pres-pillar:hover .pres-pillar__bar--2 { transform: scaleY(1.1); }
.pres-pillar:hover .pres-pillar__bar--3 { transform: scaleY(1.07); }
.pres-pillar:hover .pres-pillar__bar--4 { transform: scaleY(1.04); }

.pres-pillar__title {
  font-size: clamp(20px, 1.7vw, 24px);
  font-weight: 700;
  letter-spacing: -0.025em;
  color: var(--p-ink);
  margin-bottom: 10px;
}
.pres-pillar__desc {
  font-size: var(--p-body);
  line-height: 1.62;
  color: var(--p-muted);
}

/* ---- Proof row ----
   Pushed to the bottom edge so the three cards' proof rows line up with
   each other even though their descriptions run to different lengths. */
.pres-pillar__proof {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(12px, 1.4vw, 20px);
  margin-top: auto;
  padding-top: clamp(20px, 2.2vw, 26px);
}
.pres-pillar__proof dt {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--p-faint);
  margin-bottom: 5px;
}
.pres-pillar__proof dd {
  font-size: clamp(15px, 1.2vw, 17px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
  color: var(--p-ink);
}
/* A hairline between the two cells, matching the divided-column treatment
   the audience list uses. */
.pres-pillar__proof > div + div {
  padding-left: clamp(12px, 1.4vw, 20px);
  border-left: 1px solid var(--p-border);
}

@media (min-width: 780px) {
  .pres-pillars { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* -----------------------------------------------------------------------
   14. PRODUCT SHOWCASE — switcher, stage, device shells, mockups

   The mockups are drawn at two fixed design sizes (1120x730 for the
   desktop app, 340x736 for the phone) and scaled into their frame with a
   single transform. That is what keeps the internal proportions, the
   optical weight of the type and the spacing rhythm identical on a 390px
   phone and a 27" monitor: there is exactly one layout, sized once.

   The stage's own height is derived in CSS from the width the container
   can actually offer, so neither frame ever overflows and the height it
   settles on is known before paint. Switching devices animates that
   height once, on a click, which is the only layout-affecting transition
   on the page and is excluded from CLS for exactly that reason.
   -------------------------------------------------------------------- */
.pres-switch {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  width: max-content;
  max-width: 100%;
  margin: 0 auto var(--p-gap-lg);
  padding: 5px;
  border-radius: var(--p-radius-pill);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  box-shadow: var(--p-shadow-sm);
}
/* The selected pill is one element that slides, not a background swapped
   between two buttons — the movement is the thing that makes the change
   of device read as a change rather than a repaint. */
.pres-switch__thumb {
  position: absolute;
  top: 5px;
  left: 5px;
  width: 0;
  height: calc(100% - 10px);
  border-radius: var(--p-radius-pill);
  background: var(--p-ink);
  transform: translate3d(var(--thumb-x, 0px), 0, 0);
  transition:
    transform 460ms var(--p-ease-soft),
    width 460ms var(--p-ease-soft);
  pointer-events: none;
}
.pres-switch__btn {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 44px;
  padding: 10px clamp(16px, 2vw, 26px);
  border-radius: var(--p-radius-pill);
  font-size: 14.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--p-muted);
  transition: color var(--p-dur) var(--p-ease);
}
.pres-switch__btn:hover { color: var(--p-ink); }
.pres-switch__btn.is-active,
.pres-switch__btn.is-active:hover { color: #ffffff; }
.pres-switch__icon { display: flex; }
.pres-switch__icon svg { width: 18px; height: 18px; }

.pres-stage {
  /* The width the container can give a frame at this viewport. Mirrors
     .pres-container's own max-width and padding so the two cannot drift. */
  --stage-avail: calc(min(100vw, var(--p-max)) - 2 * var(--p-shell-x));
  --stage-cap-desktop: clamp(300px, 34vw + 120px, 620px);
  /* The phone's floor is deliberately high. Derived from the narrowest
     viewport the deck supports: at 320px the container can offer 280px,
     and 600 x 0.4788 is 287, so the frame is width-limited rather than
     height-limited from 320px up and always fills the space it is given
     instead of sitting as a thumbnail in the middle of it. */
  --stage-cap-phone: clamp(600px, 24vw + 360px, 700px);
  position: relative;
  /* Height is whichever is smaller: the ceiling for this device, or the
     height at which the frame's aspect ratio still fits the width. */
  height: min(var(--stage-cap-desktop), calc(var(--stage-avail) / 1.5328));
  transition: height 520ms var(--p-ease-soft);
}
.pres-stage[data-device="phone"] {
  height: min(var(--stage-cap-phone), calc(var(--stage-avail) / 0.4788));
}
.pres-stage__glow {
  position: absolute;
  left: 50%;
  top: 52%;
  width: 116%;
  height: 116%;
  transform: translate3d(-50%, -50%, 0);
  background: radial-gradient(closest-side, rgba(26, 26, 26, 0.13), rgba(26, 26, 26, 0) 76%);
  pointer-events: none;
}

.pres-pane {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  /* visibility, not display: an inactive pane must be out of the tab order
     and out of the accessibility tree, but must keep its layout so the
     switch has something already laid out to fade to. */
  visibility: hidden;
  transform: translate3d(0, 20px, 0) scale(0.94);
  transition:
    opacity 400ms var(--p-ease-soft),
    transform 520ms var(--p-ease-soft),
    visibility 0s linear 520ms;
  will-change: transform, opacity;
}
.pres-pane.is-active {
  opacity: 1;
  visibility: visible;
  transform: translate3d(0, 0, 0) scale(1);
  /* The incoming pane waits 80ms so the two are never both at full
     opacity, which is what makes a cross-fade look muddy. */
  transition-delay: 80ms, 80ms, 0s;
}

.pres-shell {
  position: relative;
  height: 100%;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  box-shadow: var(--p-shadow-lg);
  overflow: hidden;
}
.pres-shell--desktop {
  aspect-ratio: 1122 / 732;
  border-radius: 14px;
}
.pres-shell--phone {
  aspect-ratio: 362 / 758;
  border-radius: 36px;
  padding: 10px;
  background: var(--p-ink);
  border-color: rgba(26, 26, 26, 0.9);
}
/* The island, sized in percentages of the shell rather than pixels so it
   keeps its place in the status bar as the frame scales. */
.pres-shell__notch {
  position: absolute;
  top: 2.3%;
  left: 50%;
  transform: translateX(-50%);
  width: 21%;
  height: 2.4%;
  border-radius: var(--p-radius-pill);
  background: var(--p-ink);
  z-index: 2;
}
.pres-shell__screen {
  position: relative;
  height: 100%;
  overflow: hidden;
  background: var(--p-bg);
}
.pres-shell--phone .pres-shell__screen { border-radius: 27px; }

/* The scaler. transform-origin at the top-left corner so the design's
   own 0,0 lands on the screen's 0,0 at every scale. */
.pres-fit {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  transform: scale(var(--fit-scale, 0.5));
}
/* presentation.js sets --fit-scale exactly, from the measured screen. The
   values below are the same arithmetic evaluated at five widths, so the
   frame is already close to correct on the first frame and stays usable
   if the script never runs. */
.pres-pane--desktop .pres-fit { --fit-scale: 0.31; }
.pres-pane--phone .pres-fit { --fit-scale: 0.6; }
@media (min-width: 600px) {
  .pres-pane--desktop .pres-fit { --fit-scale: 0.44; }
  .pres-pane--phone .pres-fit { --fit-scale: 0.65; }
}
@media (min-width: 900px) {
  .pres-pane--desktop .pres-fit { --fit-scale: 0.58; }
  .pres-pane--phone .pres-fit { --fit-scale: 0.75; }
}
@media (min-width: 1200px) {
  .pres-pane--desktop .pres-fit { --fit-scale: 0.72; }
  .pres-pane--phone .pres-fit { --fit-scale: 0.85; }
}
@media (min-width: 1400px) {
  .pres-pane--desktop .pres-fit { --fit-scale: 0.83; }
  .pres-pane--phone .pres-fit { --fit-scale: 0.92; }
}

/* Flanking callouts. Hidden until there is real room beside the phone,
   which is the whole point of them: a wide screen showing a 335px frame
   has 400px of empty paper on either side, and this is what goes in it. */
.pres-stage__notes { display: none; }

@media (min-width: 1120px) {
  .pres-stage__notes {
    display: block;
    position: absolute;
    inset: 0;
    pointer-events: none;
  }
  .pres-stage__note {
    position: absolute;
    width: 250px;
    padding: 16px 18px;
    border-radius: var(--p-radius-lg);
    border: 1px solid var(--p-border);
    background: linear-gradient(180deg, rgba(253, 253, 252, 0.95), rgba(253, 253, 252, 0.66));
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    box-shadow: var(--p-shadow-sm);
    opacity: 0;
    /* They arrive from the outside edge, away from the phone, so the eye
       is pulled outward to read them rather than off the device. */
    transform: translate3d(var(--note-from, -14px), 0, 0);
    transition:
      opacity 460ms var(--p-ease-soft),
      transform 560ms var(--p-ease-soft);
  }
  /* A hairline reaching toward the frame, so each note reads as pointing
     at the phone rather than floating near it. */
  .pres-stage__note::after {
    content: "";
    position: absolute;
    top: 26px;
    width: 34px;
    height: 1px;
    background: var(--p-border);
  }
  .pres-stage__note--1,
  .pres-stage__note--2 { left: 2%; }
  .pres-stage__note--3,
  .pres-stage__note--4 { right: 2%; --note-from: 14px; }
  .pres-stage__note--1 { top: 9%; }
  .pres-stage__note--2 { top: 57%; }
  .pres-stage__note--3 { top: 15%; }
  .pres-stage__note--4 { top: 62%; }
  .pres-stage__note--1::after,
  .pres-stage__note--2::after { left: 100%; }
  .pres-stage__note--3::after,
  .pres-stage__note--4::after { right: 100%; }

  /* Staggered, and only once the phone is the thing being looked at. */
  .pres-stage[data-device="phone"] .pres-stage__note {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
  .pres-stage[data-device="phone"] .pres-stage__note--1 { transition-delay: 200ms; }
  .pres-stage[data-device="phone"] .pres-stage__note--3 { transition-delay: 280ms; }
  .pres-stage[data-device="phone"] .pres-stage__note--2 { transition-delay: 360ms; }
  .pres-stage[data-device="phone"] .pres-stage__note--4 { transition-delay: 440ms; }
}

.pres-stage__note-title {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--p-ink);
  margin-bottom: 5px;
}
.pres-stage__note-body { font-size: 12.5px; line-height: 1.5; color: var(--p-muted); }

.pres-stage__caption {
  margin: var(--p-gap) auto 0;
  max-width: 54ch;
  text-align: center;
  font-size: var(--p-body);
  line-height: 1.6;
  color: var(--p-muted);
  transition: opacity 220ms var(--p-ease);
}
.pres-stage__caption.is-swapping { opacity: 0; }

/* ---- Shared mockup base ---- */
.mock {
  font-family: var(--p-font);
  color: var(--p-ink);
  line-height: 1.4;
  letter-spacing: 0;
  text-align: left;
}
.mock--desktop {
  width: 1120px;
  height: 730px;
  display: flex;
  flex-direction: column;
  background: var(--p-bg);
}

/* ---- Desktop: browser chrome ---- */
.mock__chrome {
  flex: 0 0 30px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 14px;
  background: var(--p-surface-2);
  border-bottom: 1px solid var(--p-border);
}
.mock__dots { display: flex; gap: 6px; flex: 0 0 auto; }
.mock__dots i { width: 8px; height: 8px; border-radius: 50%; background: var(--p-border); }
.mock__url {
  flex: 0 1 300px;
  margin: 0 auto;
  height: 17px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--p-radius-pill);
  background: var(--p-surface);
  border: 1px solid var(--p-border-soft);
  font-size: 9.5px;
  font-weight: 500;
  color: var(--p-faint);
}
.mock__chrome-pad { flex: 0 0 38px; }

/* ---- Desktop: shell ---- */
.mock__app { flex: 1 1 auto; display: flex; min-height: 0; }

.mock__side {
  flex: 0 0 236px;
  display: flex;
  flex-direction: column;
  padding: 22px 18px;
  background: var(--p-surface);
  border-right: 1px solid var(--p-border);
}
.mock__brand { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; }
.mock__brand-mark {
  position: relative;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--p-ink);
}
.mock__brand-mark::after {
  content: "";
  position: absolute;
  inset: 9px;
  border-radius: 2px;
  background: #ffffff;
}
.mock__brand-name { font-size: 14px; font-weight: 700; letter-spacing: -0.025em; }
.mock__side-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--p-faint);
  margin-bottom: 9px;
}
.mock__nav { margin-bottom: 22px; }
.mock__nav li {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 33px;
  padding: 0 10px;
  border-radius: 8px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--p-muted);
}
.mock__nav li.is-active { background: var(--p-ink); color: #ffffff; font-weight: 600; }
.mock__nav-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: 0.45; }
.mock__nav li.is-active .mock__nav-dot { opacity: 1; }
.mock__side-card {
  margin-top: auto;
  padding: 15px;
  border-radius: 12px;
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
}
.mock__side-card-title { font-size: 12.5px; font-weight: 700; margin-bottom: 4px; }
.mock__side-card-body { font-size: 11px; line-height: 1.45; color: var(--p-muted); margin-bottom: 12px; }
.mock__side-card-btn {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 14px;
  border-radius: var(--p-radius-pill);
  background: var(--p-ink);
  color: #ffffff;
  font-size: 11px;
  font-weight: 600;
}

.mock__main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 17px;
  padding: 24px 26px;
}
.mock__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
.mock__hello { font-size: 20px; font-weight: 700; letter-spacing: -0.03em; }
.mock__hello-sub { font-size: 12px; color: var(--p-muted); margin-top: 4px; }
.mock__top-right { display: flex; align-items: center; gap: 11px; flex: 0 0 auto; }
.mock__search {
  display: flex;
  align-items: center;
  height: 34px;
  padding: 0 16px;
  border-radius: var(--p-radius-pill);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  font-size: 11.5px;
  color: var(--p-faint);
}
.mock__avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--p-ink);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12.5px;
  font-weight: 700;
}

.mock__stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 11px; }
.mock__stat {
  padding: 13px 15px;
  border-radius: 12px;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
}
.mock__stat-value { display: block; font-size: 24px; font-weight: 700; letter-spacing: -0.035em; line-height: 1; }
.mock__stat-label { display: block; margin-top: 6px; font-size: 10.5px; font-weight: 500; color: var(--p-faint); }

.mock__cols {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 262px;
  gap: 15px;
}
.mock__panel {
  display: flex;
  flex-direction: column;
  padding: 17px;
  border-radius: 14px;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
}
.mock__panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 11px; }
.mock__panel-head--sub { margin-top: 16px; }
.mock__panel-title { font-size: 13px; font-weight: 700; letter-spacing: -0.015em; }
.mock__panel-link { font-size: 11px; font-weight: 600; color: var(--p-faint); }

.mock__products, .mock__files { display: flex; flex-direction: column; gap: 8px; }
.mock__product {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px;
  border-radius: 10px;
  background: var(--p-bg);
  border: 1px solid var(--p-border-soft);
}
.mock__cover { flex: 0 0 46px; height: 46px; border-radius: 8px; }
.mock__cover--a { background: linear-gradient(140deg, #2b2b29, #5f5c56); }
.mock__cover--b { background: linear-gradient(140deg, #6d6a63, #c6c3bc); }
.mock__cover--c { background: linear-gradient(140deg, #3e3c39, #8c8982); }
.mock__product-body { flex: 1 1 auto; min-width: 0; }
.mock__product-title { display: block; font-size: 12.5px; font-weight: 600; letter-spacing: -0.015em; }
.mock__product-meta { display: block; font-size: 10.5px; color: var(--p-faint); margin: 3px 0 8px; }
.mock__product-pct { flex: 0 0 auto; font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Progress fills animate on scaleX rather than width — same picture, no
   layout inside a subtree that is already being scaled. */
/* .mockp__owned-bar rather than another .mockp__bar: that class is already
   the phone's top app bar (flex: 0 0 54px), and the two only coexist today
   because every other progress bar using it sits in a ROW flex parent where
   the 54px lands on width and is overridden. In a column parent — the
   library slide — the same declaration became a 54px-tall black slab. */
.mock__bar, .mockp__bar, .mockp__owned-bar, .mockp__video-scrub {
  display: block;
  height: 4px;
  border-radius: 2px;
  background: var(--p-surface-2);
  overflow: hidden;
}
.mock__bar i, .mockp__bar i, .mockp__owned-bar i, .mockp__video-scrub i {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  background: var(--p-ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 900ms var(--p-ease-soft);
}
.mock__bar--light { background: rgba(255, 255, 255, 0.22); }
.mock__bar--light i { background: #ffffff; }

.mock__file {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px;
  border-radius: 10px;
  background: var(--p-bg);
  border: 1px solid var(--p-border-soft);
}
.mock__file-icon {
  position: relative;
  flex: 0 0 26px;
  height: 32px;
  border-radius: 5px;
  background: var(--p-surface-2);
  border: 1px solid var(--p-border);
}
.mock__file-icon::after {
  content: "";
  position: absolute;
  top: 7px;
  left: 5px;
  right: 5px;
  height: 2px;
  border-radius: 1px;
  background: var(--p-border);
  box-shadow: 0 5px 0 var(--p-border), 0 10px 0 var(--p-border);
}
.mock__file-name { flex: 1 1 auto; min-width: 0; font-size: 11.5px; font-weight: 600; }
.mock__file-meta { font-size: 10.5px; color: var(--p-faint); }
.mock__file-btn {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 12px;
  border-radius: var(--p-radius-pill);
  border: 1px solid var(--p-border);
  font-size: 10.5px;
  font-weight: 600;
}

.mock__rail { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.mock__player { padding: 14px; border-radius: 14px; background: var(--p-ink); color: #ffffff; }
.mock__player-cover {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100px;
  margin-bottom: 12px;
  border-radius: 10px;
  background: linear-gradient(140deg, #3a3835, #726f68);
}
.mock__play, .mockp__hero-play {
  position: relative;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.94);
}
.mock__play { width: 34px; height: 34px; }
.mock__play::after {
  content: "";
  position: absolute;
  left: 13px;
  top: 10px;
  border-style: solid;
  border-width: 7px 0 7px 11px;
  border-color: transparent transparent transparent #1a1a1a;
}
.mock__player-title { font-size: 12.5px; font-weight: 600; letter-spacing: -0.015em; }
.mock__player-meta { font-size: 10.5px; color: rgba(255, 255, 255, 0.6); margin: 4px 0 9px; }

.mock__progress {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  padding: 14px;
  border-radius: 14px;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
}
.mock__rail-title { font-size: 11.5px; font-weight: 700; margin-bottom: 9px; }
.mock__chart { flex: 1 1 auto; min-height: 50px; }
.mock__chart svg { width: 100%; height: 100%; }
.mock__spark {
  fill: none;
  stroke: var(--p-ink);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 300;
  stroke-dashoffset: 300;
}
.mock__spark-area { fill: rgba(26, 26, 26, 0.06); stroke: none; opacity: 0; transition: opacity 700ms var(--p-ease) 600ms; }
.mock__rail-meta { font-size: 10.5px; color: var(--p-faint); margin-top: 8px; }

.mock__cert {
  /* Grid, not flex: the seal spans both rows so the title and the product
     name stack beside it instead of sitting side by side. */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 11px;
  align-items: center;
  padding: 12px 14px;
  border-radius: 14px;
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
}
.mock__cert-seal {
  position: relative;
  grid-row: 1 / span 2;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 2px solid var(--p-ink);
}
.mock__cert-seal::after {
  content: "";
  position: absolute;
  left: 9px;
  top: 6px;
  width: 7px;
  height: 12px;
  border: solid var(--p-ink);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.mock__cert-title { font-size: 12px; font-weight: 700; }
.mock__cert-meta { font-size: 10.5px; color: var(--p-faint); margin-top: 2px; }

/* Gated on .is-live, added when the stage first scrolls into view, so the
   chart is not redrawing itself against the compositor while the visitor
   is four sections above it. */
.pres-stage.is-live .mock__spark { animation: mock-draw 1500ms var(--p-ease-soft) 260ms forwards; }
.pres-stage.is-live .mock__spark-area { opacity: 1; }
@keyframes mock-draw { to { stroke-dashoffset: 0; } }

/* ---- Phone mockup ---- */
.mock--phone {
  width: 340px;
  height: 736px;
  display: flex;
  flex-direction: column;
  background: var(--p-bg);
}
.mockp__status {
  flex: 0 0 34px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 20px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.mockp__status-icons { display: flex; align-items: center; gap: 4px; }
.mockp__status-icons i { width: 11px; height: 8px; border-radius: 2px; background: var(--p-ink); opacity: 0.7; }
.mockp__status-icons i:nth-child(2) { width: 9px; }
.mockp__status-icons i:nth-child(3) { width: 17px; border-radius: 3px; }

.mockp__bar {
  flex: 0 0 54px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 16px;
  border-bottom: 1px solid var(--p-border-soft);
}
.mockp__back {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border: solid var(--p-ink);
  border-width: 0 0 1.8px 1.8px;
  transform: rotate(45deg);
}
.mockp__bar-text { flex: 1 1 auto; min-width: 0; }
.mockp__bar-title { display: block; font-size: 14px; font-weight: 700; letter-spacing: -0.025em; }
.mockp__bar-sub { display: block; font-size: 10.5px; color: var(--p-faint); margin-top: 2px; }
.mockp__avatar {
  flex: 0 0 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--p-ink);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  font-weight: 700;
}

.mockp__deck { flex: 1 1 auto; min-height: 0; overflow: hidden; touch-action: pan-y; }
.mockp__slides {
  display: flex;
  height: 100%;
  width: max-content;
  transform: translate3d(0, 0, 0);
  transition: transform 520ms var(--p-ease-soft);
  will-change: transform;
}
.mockp__slides.is-dragging { transition: none; }
.mockp__slide {
  flex: 0 0 340px;
  width: 340px;
  display: flex;
  flex-direction: column;
  gap: 13px;
  padding: 15px 16px;
  overflow: hidden;
}

.mockp__hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 130px;
  border-radius: 14px;
  background: linear-gradient(140deg, #2f2d2b, #6e6b64);
}
.mockp__hero-play { width: 44px; height: 44px; }
.mockp__hero-play::after {
  content: "";
  position: absolute;
  left: 17px;
  top: 13px;
  border-style: solid;
  border-width: 9px 0 9px 14px;
  border-color: transparent transparent transparent #1a1a1a;
}
.mockp__hero-tag {
  position: absolute;
  left: 12px;
  bottom: 12px;
  padding: 4px 10px;
  border-radius: var(--p-radius-pill);
  background: rgba(255, 255, 255, 0.93);
  font-size: 10px;
  font-weight: 700;
}
.mockp__section { font-size: 12.5px; font-weight: 700; letter-spacing: -0.015em; }
.mockp__list { display: flex; flex-direction: column; gap: 8px; }
.mockp__list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 12px;
  border-radius: 11px;
  background: var(--p-surface);
  border: 1px solid var(--p-border-soft);
}
.mockp__list li.is-live { border-color: var(--p-ink); box-shadow: 0 0 0 1px var(--p-ink); }
.mockp__tick {
  position: relative;
  flex: 0 0 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.6px solid var(--p-border);
}
.mockp__list li.is-done .mockp__tick { background: var(--p-ink); border-color: var(--p-ink); }
.mockp__list li.is-done .mockp__tick::after {
  content: "";
  position: absolute;
  left: 5.5px;
  top: 3.5px;
  width: 4px;
  height: 8px;
  border: solid #ffffff;
  border-width: 0 1.6px 1.6px 0;
  transform: rotate(45deg);
}
.mockp__row-name { flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 600; letter-spacing: -0.015em; }
.mockp__row-meta { flex: 0 0 auto; font-size: 11px; color: var(--p-faint); font-variant-numeric: tabular-nums; }
.mockp__meal { flex: 0 0 18px; height: 18px; border-radius: 6px; background: var(--p-surface-2); border: 1px solid var(--p-border); }
.mockp__chapter { flex: 0 0 20px; font-size: 10.5px; font-weight: 700; color: var(--p-faint); font-variant-numeric: tabular-nums; }
.mockp__attach {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 12px;
  border-radius: 11px;
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
}
.mockp__attach-icon {
  position: relative;
  flex: 0 0 26px;
  height: 32px;
  border-radius: 5px;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
}
.mockp__attach-icon::after {
  content: "";
  position: absolute;
  top: 7px;
  left: 5px;
  right: 5px;
  height: 2px;
  border-radius: 1px;
  background: var(--p-border);
  box-shadow: 0 5px 0 var(--p-border), 0 10px 0 var(--p-border);
}
.mockp__attach-text { flex: 1 1 auto; min-width: 0; }
.mockp__attach-name { display: block; font-size: 12px; font-weight: 600; letter-spacing: -0.015em; }
.mockp__attach-meta { display: block; font-size: 10.5px; color: var(--p-faint); margin-top: 2px; }
.mockp__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  margin-top: auto;
  border-radius: var(--p-radius-pill);
  background: var(--p-ink);
  color: #ffffff;
  font-size: 13px;
  font-weight: 600;
}

.mockp__macro {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px;
  border-radius: 14px;
  background: var(--p-surface);
  border: 1px solid var(--p-border-soft);
}
.mockp__ring { position: relative; flex: 0 0 84px; height: 84px; }
.mockp__ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.mockp__ring-track { fill: none; stroke: var(--p-surface-2); stroke-width: 9; }
.mockp__ring-fill {
  fill: none;
  stroke: var(--p-ink);
  stroke-width: 9;
  stroke-linecap: round;
  stroke-dasharray: 264;
  stroke-dashoffset: 264;
}
.pres-stage.is-live .mockp__ring-fill { animation: mockp-ring 1400ms var(--p-ease-soft) 320ms forwards; }
@keyframes mockp-ring { to { stroke-dashoffset: 66; } }
.mockp__ring-value {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.035em;
}
.mockp__ring-value i { font-style: normal; font-size: 9px; font-weight: 600; letter-spacing: 0.06em; color: var(--p-faint); }
.mockp__macros { flex: 1 1 auto; display: flex; flex-direction: column; gap: 9px; }
.mockp__macros li { display: grid; grid-template-columns: 44px minmax(0, 1fr) 38px; align-items: center; gap: 8px; }
.mockp__macro-name { font-size: 10.5px; font-weight: 600; color: var(--p-muted); }
.mockp__macro-val { font-size: 10.5px; font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }
.mockp__bar, .mockp__owned-bar { height: 5px; border-radius: 3px; }

.mockp__video {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 168px;
  border-radius: 14px;
  background: linear-gradient(140deg, #26241f, #625f58);
}
.mockp__video-scrub {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 30px;
  background: rgba(255, 255, 255, 0.28);
}
.mockp__video-scrub i { background: #ffffff; }
.mockp__video-time {
  position: absolute;
  left: 14px;
  bottom: 12px;
  font-size: 10px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.82);
  font-variant-numeric: tabular-nums;
}

.mockp__dots { flex: 0 0 auto; display: flex; justify-content: center; gap: 6px; padding: 8px 0 4px; }
.mockp__dots i {
  width: 6px;
  height: 6px;
  border-radius: 3px;
  background: var(--p-border);
  transition: width 300ms var(--p-ease), background-color 300ms var(--p-ease);
}
.mockp__dots i.is-on { width: 18px; background: var(--p-ink); }
.mockp__tabs {
  flex: 0 0 64px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  padding-top: 9px;
  border-top: 1px solid var(--p-border-soft);
}
.mockp__tabs span {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  font-size: 9.5px;
  font-weight: 600;
  color: var(--p-faint);
}
.mockp__tabs span.is-active { color: var(--p-ink); }
/* Four drawn glyphs rather than four identical squares: a tab bar where
   every icon is the same shape reads as a placeholder, which is exactly
   what this is not meant to look like. */
.mockp__tab-icon {
  position: relative;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  border: 1.7px solid currentColor;
}
.mockp__tabs span:nth-child(1) .mockp__tab-icon { border-radius: 4px 4px 5px 5px; border-top-width: 5px; }
.mockp__tabs span:nth-child(2) .mockp__tab-icon::after {
  content: "";
  position: absolute;
  inset: 2.5px auto 2.5px 3px;
  width: 1.7px;
  background: currentColor;
}
.mockp__tabs span:nth-child(3) .mockp__tab-icon {
  border: 0;
  border-radius: 0;
  /* Three bars of different heights, drawn as three one-colour gradients
     so the glyph costs nothing but background-position. */
  background:
    linear-gradient(currentColor 0 0) 0 100% / 3px 55% no-repeat,
    linear-gradient(currentColor 0 0) 50% 100% / 3px 100% no-repeat,
    linear-gradient(currentColor 0 0) 100% 100% / 3px 75% no-repeat;
}
.mockp__tabs span:nth-child(4) .mockp__tab-icon { border-radius: 50%; }

/* -----------------------------------------------------------------------
   14b. SHOWCASE — the browsable pages
   The desktop mockup used to be one screen. It is now four, cross-faded
   inside the laptop, with the sidebar nav lighting up in step.

   The pages are absolutely positioned inside a fixed-height box, so the
   laptop shell cannot change size when the page changes — which matters
   more here than anywhere else on the page, because the whole mockup sits
   inside a scale() transform whose factor was measured from that shell.
   -------------------------------------------------------------------- */
.mock__pages {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
}

.mock__page {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 15px;
  opacity: 0;
  /* Comes in from slightly below and leaves upward — the direction a page
     moves when you click a nav item, rather than a straight dissolve. */
  transform: translate3d(0, 10px, 0);
  transition:
    opacity 420ms var(--p-ease-soft),
    transform 420ms var(--p-ease-soft);
  visibility: hidden;
  pointer-events: none;
}
.mock__page.is-active {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  visibility: visible;
  pointer-events: auto;
}

/* Two-column body shared by the workout, nutrition and progress pages:
   the screen itself, and a narrow rail of supporting detail. Same
   proportion as the library page's .mock__cols, so the four pages feel
   like one application rather than four layouts. */
.mock__viewer {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 262px;
  gap: 15px;
}
.mock__viewer-main {
  display: flex;
  flex-direction: column;
  gap: 13px;
  padding: 17px;
  border-radius: 14px;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  min-height: 0;
}
.mock__demos {
  display: flex;
  flex-direction: column;
  padding: 17px;
  border-radius: 14px;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
}

.mock__stats--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* ---- Workout page ---- */
.mock__stagevid {
  position: relative;
  /* Fixed rather than flexible. Left to grow, the player ate the whole
     panel and pushed the set list — the part that shows the product is a
     structured program and not just a video — down out of sight. */
  flex: 0 0 268px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  background: linear-gradient(140deg, #2f2d2a, #6f6c65);
}
.mock__stagevid-tag {
  position: absolute;
  left: 12px;
  top: 12px;
  padding: 5px 10px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.92);
  font-size: 10.5px;
  font-weight: 600;
}
.mock__bar--scrub {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 12px;
}

.mock__sets { display: flex; flex-direction: column; gap: 8px; }
.mock__sets li,
.mock__meals li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--p-bg);
  border: 1px solid var(--p-border-soft);
}
.mock__sets li.is-live {
  border-color: rgba(26, 26, 26, 0.3);
  background: var(--p-surface-2);
}
.mock__set-name { flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 600; letter-spacing: -0.015em; }
.mock__set-meta { font-size: 11px; color: var(--p-muted); font-variant-numeric: tabular-nums; }
.mock__set-note { font-size: 10.5px; color: var(--p-faint); }

/* Looping exercise demonstrations. Each figure is one element on a short,
   eased, infinitely alternating transform — the visual shorthand for a
   movement clip. Three of them run at once, which is affordable only
   because each is a single compositor-layer transform and they pause with
   the rest of the deck under prefers-reduced-motion (§23). */
.mock__demo-list { display: flex; flex-direction: column; gap: 9px; margin-top: 2px; }
.mock__demo { display: flex; align-items: center; gap: 10px; }
/* The rail's closing line is pushed to the bottom edge so the column reads
   as a full panel rather than a short list floating in white space. */
.mock__demos .mock__rail-meta { margin-top: auto; }

.mock__demo-art {
  position: relative;
  flex: 0 0 64px;
  height: 48px;
  border-radius: 8px;
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
  overflow: hidden;
}
.mock__demo-figure {
  position: absolute;
  left: 50%;
  bottom: 8px;
  width: 16px;
  height: 16px;
  margin-left: -8px;
  border-radius: 3px;
  background: var(--p-ink);
}
.mock__demo-figure--squat { animation: mock-demo-squat 1.6s var(--p-ease) infinite; }
.mock__demo-figure--hinge { animation: mock-demo-hinge 1.9s var(--p-ease) infinite; }
.mock__demo-figure--press { animation: mock-demo-press 1.4s var(--p-ease) infinite; }

@keyframes mock-demo-squat {
  0%, 100% { transform: translateY(0) scaleY(1); }
  50% { transform: translateY(6px) scaleY(0.6); }
}
@keyframes mock-demo-hinge {
  0%, 100% { transform: rotate(0deg) translateY(0); }
  50% { transform: rotate(-32deg) translateY(3px); }
}
@keyframes mock-demo-press {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-10px); }
}

.mock__demo-name { font-size: 11px; font-weight: 600; color: var(--p-muted); }

/* ---- Nutrition page ---- */
.mock__meals { display: flex; flex-direction: column; gap: 8px; }
.mock__meal-art {
  flex: 0 0 34px;
  height: 34px;
  border-radius: 8px;
  background: linear-gradient(140deg, #4a4844, #a3a099);
}
.mock__macrolist { display: flex; flex-direction: column; gap: 12px; margin-top: 4px; }
.mock__macrolist li { display: flex; flex-direction: column; gap: 6px; }
.mock__macro-name { font-size: 11px; font-weight: 600; color: var(--p-muted); }

/* ---- Progress page ---- */
/* A column chart. The fills animate on scaleY from the bottom edge, the
   vertical twin of the scaleX the horizontal bars use, so neither one ever
   animates a height and triggers layout inside the scaled subtree. */
.mock__bars,
.mockp__bars {
  flex: 1 1 auto;
  min-height: 90px;
  display: flex;
  align-items: flex-end;
  gap: 7px;
}
.mock__bars i,
.mockp__bars i {
  flex: 1 1 0;
  height: 100%;
  border-radius: 4px 4px 0 0;
  background: linear-gradient(180deg, #3a3835, #6f6c65);
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform 900ms var(--p-ease-soft);
}
.mockp__bars { min-height: 64px; gap: 5px; }

.mock__milestones { display: flex; flex-direction: column; gap: 10px; margin-top: 2px; }
.mock__milestones li {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--p-muted);
}
.mock__tickmark {
  flex: 0 0 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--p-ink);
  position: relative;
}
.mock__tickmark::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 4px;
  width: 6px;
  height: 3px;
  border-left: 1.5px solid #fff;
  border-bottom: 1.5px solid #fff;
  transform: rotate(-45deg);
}

/* ---- Phone: library + progress slides ---- */
.mockp__owned { display: flex; flex-direction: column; gap: 10px; }
.mockp__owned li { display: flex; align-items: center; gap: 11px; }
.mockp__owned-art {
  position: relative;
  flex: 0 0 44px;
  height: 44px;
  border-radius: 9px;
  background: linear-gradient(140deg, #3e3c39, #8c8982);
  overflow: hidden;
}
.mockp__owned-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.mockp__owned-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 7px; }

.mockp__figures { display: flex; gap: 8px; }
.mockp__figures span {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 8px;
  border-radius: 10px;
  background: var(--p-surface);
  border: 1px solid var(--p-border-soft);
  font-size: 9.5px;
  color: var(--p-faint);
  font-weight: 500;
}
.mockp__figures b { font-size: 16px; font-weight: 700; letter-spacing: -0.03em; color: var(--p-ink); }

/* -----------------------------------------------------------------------
   14c. PAGE RAIL + SHOWCASE CTA
   -------------------------------------------------------------------- */
.pres-pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
  margin: var(--p-gap) auto 0;
}
.pres-pager__btn {
  min-height: 40px;
  padding: 9px 18px;
  border-radius: var(--p-radius-pill);
  border: 1px solid transparent;
  background: transparent;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--p-muted);
  transition:
    background-color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    color var(--p-dur) var(--p-ease);
}
.pres-pager__btn:hover { color: var(--p-ink); background: var(--p-surface); }
.pres-pager__btn.is-active {
  background: var(--p-ink);
  border-color: var(--p-ink);
  color: #ffffff;
}

.pres-stage__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--p-gap-sm);
  margin-top: var(--p-gap-lg);
  padding-top: var(--p-gap);
  border-top: 1px solid var(--p-border);
  text-align: center;
}
.pres-stage__cta-label {
  font-size: var(--p-small);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--p-faint);
}
.pres-stage__cta-label strong { font-weight: 700; color: var(--p-ink); }

/* On a phone the rail is a horizontal scroller rather than a wrapped pile
   of pills: four labels wrapping to two rows pushes the stage's caption
   around and reads as a mistake. Snap points keep it feeling deliberate. */
@media (max-width: 640px) {
  .pres-pager {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Bleed to the shell edges so the row is visibly scrollable rather
       than looking like it was cut off. */
    margin-left: calc(var(--p-shell-x) * -1);
    margin-right: calc(var(--p-shell-x) * -1);
    padding: 0 var(--p-shell-x);
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .pres-pager::-webkit-scrollbar { display: none; }
  .pres-pager__btn { flex: 0 0 auto; scroll-snap-align: center; }
  .pres-stage__cta .pres-btn { width: 100%; }
}

/* -----------------------------------------------------------------------
   15. CATALOGUE — what a customer actually receives
   -------------------------------------------------------------------- */
.pres-catalogue {
  display: grid;
  gap: clamp(10px, 1.2vw, 16px);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 680px) {
  .pres-catalogue { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--p-gap); }
}
@media (min-width: 1040px) {
  .pres-catalogue { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.pres-cat {
  position: relative;
  padding: clamp(16px, 1.9vw, 26px);
  border-radius: var(--p-radius-lg);
  border: 1px solid var(--p-border);
  /* Translucent rather than blurred: twelve backdrop-filters is the kind
     of thing that reads as premium in a screenshot and as 40fps on a
     phone. The aura shows through the alpha instead. */
  background: linear-gradient(180deg, rgba(253, 253, 252, 0.95), rgba(253, 253, 252, 0.6));
}
.pres-cat:hover {
  --lift: -5px;
  box-shadow: var(--p-shadow-md);
  border-color: rgba(26, 26, 26, 0.2);
}
.pres-cat__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-bottom: 14px;
  border-radius: var(--p-radius);
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
  color: var(--p-ink);
  transition:
    background-color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    color var(--p-dur) var(--p-ease);
}
.pres-cat__icon svg { width: 21px; height: 21px; }
.pres-cat:hover .pres-cat__icon { background: var(--p-ink); border-color: var(--p-ink); color: #ffffff; }
.pres-cat__name {
  font-size: clamp(14px, 1.15vw, 16px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--p-ink);
}
.pres-cat__meta { margin-top: 5px; font-size: 12.5px; line-height: 1.45; color: var(--p-faint); }

/* -----------------------------------------------------------------------
   16. CUSTOMER JOURNEY
   One ordered list, two authored layouts. Below 960px it is a vertical
   spine with the nodes down the left; above it, the same spine rotates and
   the steps become seven columns. The fill and the lit nodes are driven by
   how far the section has travelled through the viewport rather than by a
   timer, so the sequence plays at the speed the visitor is reading it.
   -------------------------------------------------------------------- */
.pres-journey { position: relative; }

.pres-journey__line {
  position: absolute;
  left: 22px;
  top: 24px;
  bottom: 24px;
  width: 2px;
  border-radius: 1px;
  background: var(--p-border);
  overflow: hidden;
}
.pres-journey__line-fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--p-ink);
  transform: scaleY(var(--journey, 0));
  transform-origin: 50% 0;
  /* Short and linear: this tracks a scroll position, and an easing curve
     on top of a value that is already being sampled every frame reads as
     lag rather than as smoothing. */
  transition: transform 120ms linear;
}

.pres-journey__steps { display: flex; flex-direction: column; gap: clamp(18px, 2.2vw, 28px); }
.pres-journey__step { position: relative; padding-left: 64px; }

.pres-journey__node {
  position: absolute;
  left: 0;
  top: -1px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  color: var(--p-faint);
  box-shadow: var(--p-shadow-sm);
  transition:
    background-color 420ms var(--p-ease),
    border-color 420ms var(--p-ease),
    color 420ms var(--p-ease),
    transform 420ms var(--p-ease-soft);
}
.pres-journey__node svg { width: 21px; height: 21px; }
.pres-journey__step.is-lit .pres-journey__node {
  background: var(--p-ink);
  border-color: var(--p-ink);
  color: #ffffff;
  transform: scale(1.07);
}

.pres-journey__label {
  padding-top: 9px;
  font-size: clamp(16px, 1.4vw, 19px);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.3;
  color: var(--p-ink);
}
.pres-journey__note { margin-top: 4px; font-size: 13.5px; line-height: 1.5; color: var(--p-muted); }

/* The closing beat. The timeline shows the mechanism; this says what the
   mechanism is worth, which is the sentence a reader should leave the
   section holding. */
.pres-journey__outcome {
  display: grid;
  gap: 1px;
  margin-top: clamp(36px, 4.5vw, 64px);
  border-radius: var(--p-radius-lg);
  border: 1px solid var(--p-border);
  background: var(--p-border);
  overflow: hidden;
}
.pres-journey__outcome-item {
  padding: clamp(18px, 2vw, 26px) clamp(18px, 2.2vw, 30px);
  background: var(--p-surface);
}
.pres-journey__outcome dt {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--p-faint);
  margin-bottom: 8px;
}
.pres-journey__outcome dd {
  margin: 0;
  font-size: clamp(18px, 1.7vw, 23px);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.25;
  color: var(--p-ink);
}

@media (min-width: 760px) {
  .pres-journey__outcome { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 960px) {
  /* The spine ends at the first and last node's centre, not at the
     container edge, so the rule never runs out past the sequence. */
  .pres-journey__line {
    left: 7%;
    right: 7%;
    top: 23px;
    bottom: auto;
    width: auto;
    height: 2px;
  }
  .pres-journey__line-fill {
    transform: scaleX(var(--journey, 0));
    transform-origin: 0 50%;
  }
  .pres-journey__steps { flex-direction: row; gap: clamp(8px, 1vw, 18px); }
  .pres-journey__step {
    flex: 1 1 0;
    min-width: 0;
    padding: 64px 0 0;
    text-align: center;
  }
  .pres-journey__node { left: 50%; top: 0; transform: translate3d(-50%, 0, 0); }
  .pres-journey__step.is-lit .pres-journey__node { transform: translate3d(-50%, 0, 0) scale(1.07); }
  /* Two lines' worth, reserved. One label out of seven wraps, and without
     this its note starts a line lower than the other six. */
  .pres-journey__label { padding-top: 0; min-height: 2.6em; }
}

/* -----------------------------------------------------------------------
   17. WHAT IT'S WORTH — the value grid
   Cards rather than the hairline-divided list the Technology section used.
   A list of seven equal rows reads as a specification; six cards with a
   figure at the top of each read as claims, which is what these are.

   Mobile is one column of full cards, desktop is three — and the card
   itself is identical in both, because unlike the comparison block or the
   wizard there is no second way to express "a figure, a heading and a
   paragraph" that suits a narrow screen better.
   -------------------------------------------------------------------- */
.pres-value {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(12px, 1.4vw, 18px);
}

@media (min-width: 700px) {
  .pres-value { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1080px) {
  .pres-value { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.pres-value__item {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 30px);
  border-radius: var(--p-radius-lg);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  /* --mx/--my come from the magnetic-hover module (see §11 and the
     pointer handler in presentation.js); composing them here rather than
     writing transform from JS is what lets the reveal, the hover lift and
     the magnet share one element without overwriting each other. */
  transform: translate3d(var(--mx, 0px), var(--my, 0px), 0);
  transition:
    transform var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease);
}
.pres-value__item:hover {
  border-color: rgba(26, 26, 26, 0.22);
  box-shadow: var(--p-shadow-md);
}

/* The figure. Deliberately words as often as numbers — "Day one" and
   "Zero" are the honest version of these claims, and a made-up percentage
   would be the fastest way to lose an investor's trust. */
.pres-value__figure {
  font-size: clamp(24px, 2.4vw, 32px);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.05;
  color: var(--p-ink);
  padding-bottom: clamp(14px, 1.6vw, 18px);
  margin-bottom: clamp(14px, 1.6vw, 18px);
  border-bottom: 1px solid var(--p-border);
}

.pres-value__name {
  font-size: clamp(16px, 1.3vw, 18px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.35;
  color: var(--p-ink);
  margin-bottom: 10px;
}

.pres-value__desc {
  font-size: var(--p-body);
  line-height: 1.62;
  color: var(--p-muted);
}

/* -----------------------------------------------------------------------
   17b. PROMOTIONAL CAMPAIGNS — the bento
   Six columns on desktop, one on mobile. The two wide cards take three
   columns each on the middle breakpoint and the narrow ones take two, so
   the grid resolves without a single orphan card at any width — which is
   the failure mode every bento has and the reason the spans are declared
   per breakpoint rather than left to auto-placement.
   -------------------------------------------------------------------- */
.pres-camp {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(12px, 1.4vw, 18px);
}

/* Spans are declared per card, not per size class, and they are chosen so
   each breakpoint's rows add up to exactly six. Auto-placement with a
   generic "wide"/"narrow" pair looked right in the abstract and left a
   one-column hole at the end of two rows in practice. */
@media (min-width: 760px) {
  .pres-camp { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  /* 6 | 3+3 | 6 | 6 */
  .pres-camp__card--codes { grid-column: span 6; }
  .pres-camp__card--limits { grid-column: span 3; }
  .pres-camp__card--giveaway { grid-column: span 3; }
  .pres-camp__card--analytics { grid-column: span 6; }
  .pres-camp__card--partners { grid-column: span 6; }
}

@media (min-width: 1080px) {
  /* 4+2 | 2+4 | 6 — the two cards carrying the most content get the wide
     slot on their row, and the row of six closes the section. */
  .pres-camp__card--codes { grid-column: span 4; }
  .pres-camp__card--limits { grid-column: span 2; }
  .pres-camp__card--giveaway { grid-column: span 2; }
  .pres-camp__card--analytics { grid-column: span 4; }
  .pres-camp__card--partners { grid-column: span 6; }
}

.pres-camp__card {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 32px);
  border-radius: var(--p-radius-lg);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  transform: translate3d(var(--mx, 0px), var(--my, 0px), 0);
  transition:
    transform var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease);
}
.pres-camp__card:hover {
  border-color: rgba(26, 26, 26, 0.22);
  box-shadow: var(--p-shadow-md);
}

/* The analytics card inverts. One dark card in the grid is what makes the
   set read as a dashboard rather than as five leaflets, and inverting the
   card that holds the numbers is the same move the platform's own admin
   makes with its primary metric tile. */
.pres-camp__card--dark {
  background: var(--p-ink);
  border-color: var(--p-ink);
}
.pres-camp__card--dark .pres-camp__title { color: #ffffff; }
.pres-camp__card--dark .pres-camp__body { color: rgba(255, 255, 255, 0.66); }
.pres-camp__card--dark:hover {
  border-color: var(--p-ink);
  box-shadow: var(--p-shadow-lg);
}

.pres-camp__title {
  font-size: clamp(17px, 1.45vw, 21px);
  font-weight: 700;
  letter-spacing: -0.022em;
  line-height: 1.3;
  color: var(--p-ink);
  margin-bottom: 10px;
}
.pres-camp__body {
  font-size: var(--p-body);
  line-height: 1.62;
  color: var(--p-muted);
}

/* ---- Code list ---- */
.pres-camp__codes {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: var(--p-gap);
}
.pres-camp__codes li {
  display: grid;
  grid-template-columns: auto auto;
  gap: 4px 12px;
  align-items: center;
  padding: 14px 16px;
  border-radius: var(--p-radius);
  background: var(--p-bg);
  border: 1px solid var(--p-border-soft);
}
.pres-camp__code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--p-ink);
}
.pres-camp__code-kind {
  justify-self: start;
  padding: 3px 10px;
  border-radius: var(--p-radius-pill);
  background: var(--p-ink);
  color: #ffffff;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.pres-camp__code-note {
  grid-column: 1 / -1;
  font-size: 13px;
  line-height: 1.5;
  color: var(--p-muted);
}

/* ---- Limits ---- */
.pres-camp__limits {
  display: flex;
  flex-direction: column;
  margin-top: var(--p-gap);
  border-top: 1px solid var(--p-border);
}
.pres-camp__limits li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--p-border);
}
.pres-camp__limit-key {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--p-faint);
}
.pres-camp__limit-val {
  font-size: var(--p-body);
  font-weight: 600;
  color: var(--p-ink);
  text-align: right;
}

/* ---- Giveaway tag ---- */
.pres-camp__tag {
  align-self: flex-start;
  margin-top: auto;
  padding-top: var(--p-gap);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--p-faint);
}

/* ---- Metrics + chart ---- */
.pres-camp__metrics {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 26px);
  margin-top: var(--p-gap);
}
@media (min-width: 1080px) {
  .pres-camp__metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.pres-camp__metric-val {
  display: block;
  font-size: clamp(22px, 2.1vw, 29px);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  color: #ffffff;
  font-variant-numeric: tabular-nums;
}
.pres-camp__metric-key {
  display: block;
  margin-top: 7px;
  font-size: 12px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.55);
}

/* Grows on scaleY from the baseline, like every other chart on this page,
   so nothing here touches layout. Filled by the campaigns module in
   presentation.js when the card first comes into view. */
.pres-camp__chart {
  display: flex;
  align-items: flex-end;
  gap: 5px;
  height: 62px;
  margin-top: var(--p-gap);
}
.pres-camp__chart i {
  flex: 1 1 0;
  height: 100%;
  border-radius: 3px 3px 0 0;
  background: rgba(255, 255, 255, 0.28);
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform 780ms var(--p-ease-soft);
}
.pres-camp__chart i:last-child { background: #ffffff; }

/* ---- Partner thread ---- */
/* The partner card runs the full six columns, which is far too wide for a
   heading, a paragraph and two chat bubbles stacked in a single column.
   At that width it becomes two: the argument on the left, the thread it
   describes on the right, vertically centred against it. */
@media (min-width: 1080px) {
  .pres-camp__card--partners {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto;
    column-gap: clamp(32px, 4vw, 72px);
    align-content: center;
  }
  .pres-camp__card--partners .pres-camp__title { grid-column: 1; grid-row: 1; }
  .pres-camp__card--partners .pres-camp__body { grid-column: 1; grid-row: 2; }
  .pres-camp__card--partners .pres-camp__thread {
    grid-column: 2;
    grid-row: 1 / span 2;
    margin-top: 0;
    padding-top: 0;
    align-self: center;
  }
}

.pres-camp__thread {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: auto;
  padding-top: var(--p-gap);
}
.pres-camp__msg {
  max-width: 88%;
  padding: 11px 14px;
  border-radius: 14px 14px 14px 4px;
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
  font-size: 13px;
  line-height: 1.5;
  color: var(--p-muted);
}
/* The reply comes from the other side: aligned right, inked, and with the
   opposite corner squared off. */
.pres-camp__msg--in {
  align-self: flex-end;
  border-radius: 14px 14px 4px 14px;
  background: var(--p-ink);
  border-color: var(--p-ink);
  color: #ffffff;
}

/* -----------------------------------------------------------------------
   18. PHILOSOPHY — statement
   -------------------------------------------------------------------- */
.pres-section--statement { text-align: center; }
.pres-section--statement .pres-eyebrow { margin-bottom: var(--p-gap); }

.pres-statement {
  font-size: clamp(30px, 5.2vw, 68px);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.035em;
  color: var(--p-ink);
  text-wrap: balance;
}
.pres-statement__body {
  margin: var(--p-gap-lg) auto 0;
  max-width: 60ch;
  font-size: var(--p-lead);
  line-height: 1.68;
  color: var(--p-muted);
}

/* -----------------------------------------------------------------------
   18b. COMING SOON — the ambient drift
   Two rows, opposite directions, ~70 and ~84 seconds for a full pass. That
   is roughly 20px a second: slow enough that nothing is ever "going past",
   which was the brief — elegant, not a carousel.

   The whole loop is one CSS animation on one transform per row. No rAF, no
   scroll listener, no JS running while it plays; the only thing JS does is
   clone the cards once so the track holds two identical sets, which is what
   makes -50% land on a pixel-identical frame and the seam invisible.
   -------------------------------------------------------------------- */
.pres-soon {
  margin-top: var(--p-gap-lg);
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.4vw, 18px);
  /* Full-bleed out of .pres-container's max-width, then faded at both
     edges so cards dissolve instead of being sliced by the viewport. */
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
}

.pres-soon__row { overflow: hidden; }

.pres-soon__track {
  display: flex;
  gap: clamp(12px, 1.4vw, 18px);
  width: max-content;
  animation: pres-soon-drift 70s linear infinite;
  will-change: transform;
}
.pres-soon__row--reverse .pres-soon__track {
  animation-duration: 84s;
  animation-direction: reverse;
}

@keyframes pres-soon-drift {
  from { transform: translate3d(0, 0, 0); }
  /* Exactly half, because the track holds the set twice. Any other value
     and the loop visibly jumps. */
  to { transform: translate3d(-50%, 0, 0); }
}

/* Paused while the section is off screen — an animation nobody can see is
   still a composited layer being repainted every frame. The class is set
   by the observer in presentation.js. */
.pres-soon.is-paused .pres-soon__track { animation-play-state: paused; }

/* And paused on hover, so a card that catches someone's eye can be read
   rather than chased across the screen. */
.pres-soon__row:hover .pres-soon__track { animation-play-state: paused; }

.pres-soon__card {
  flex: 0 0 auto;
  width: clamp(232px, 74vw, 296px);
  padding: clamp(20px, 2vw, 26px);
  border-radius: var(--p-radius-lg);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  box-shadow: var(--p-shadow-sm);
  transition:
    transform var(--p-dur) var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease);
}
.pres-soon__card:hover {
  transform: translateY(-3px);
  border-color: rgba(26, 26, 26, 0.22);
  box-shadow: var(--p-shadow-md);
}

.pres-soon__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-bottom: 16px;
  border-radius: var(--p-radius);
  background: var(--p-surface-2);
  border: 1px solid var(--p-border-soft);
  color: var(--p-ink);
}
.pres-soon__icon svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.pres-soon__name {
  font-size: clamp(15.5px, 1.2vw, 17px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.pres-soon__desc {
  font-size: 14px;
  line-height: 1.55;
  color: var(--p-muted);
}

/* -----------------------------------------------------------------------
   19. CTA + FORM
   -------------------------------------------------------------------- */
.pres-section--cta {
  background: var(--p-surface-2);
  border-top: 1px solid var(--p-border-soft);
}

/* -----------------------------------------------------------------------
   19a. SETUP WIZARD
   Mobile and desktop are two different layouts, not one reflowed.

   On desktop the estimate is a sticky rail beside the questions: the
   number has to stay on screen while they answer, because watching it
   move is the reason they keep answering. On mobile there is no room for
   a rail, so the estimate becomes a compact bar pinned above the step —
   same job, a third of the height, and it does not push the question
   itself below the fold.
   -------------------------------------------------------------------- */
.pres-setup {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(14px, 1.8vw, 22px);
  max-width: 1040px;
  margin: 0 auto;
}

@media (min-width: 900px) {
  .pres-setup {
    grid-template-columns: 300px minmax(0, 1fr);
    align-items: start;
    gap: clamp(20px, 2.4vw, 32px);
  }
}

/* ---- The live estimate ---- */
.pres-setup__meter {
  padding: clamp(18px, 2vw, 26px);
  border-radius: var(--p-radius-lg);
  background: var(--p-ink);
  color: #ffffff;
}

@media (min-width: 900px) {
  .pres-setup__meter {
    position: sticky;
    /* Clear of the scroll progress bar at the top of the viewport. */
    top: 28px;
  }
}

.pres-setup__meter-label {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

.pres-setup__meter-value {
  display: flex;
  align-items: baseline;
  gap: 2px;
  margin-top: 10px;
  font-size: clamp(46px, 6vw, 64px);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 1;
  /* Tabular figures: without them the number visibly jitters sideways as
     it counts between 9-wide and 1-wide glyphs. */
  font-variant-numeric: tabular-nums;
}
.pres-setup__meter-unit {
  font-size: 0.46em;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: rgba(255, 255, 255, 0.6);
}

.pres-setup__gauge {
  height: 6px;
  margin-top: 18px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.16);
  overflow: hidden;
}
.pres-setup__gauge-fill {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 100px;
  background: #ffffff;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 420ms var(--p-ease-soft);
}

.pres-setup__meter-scale {
  display: flex;
  justify-content: space-between;
  margin-top: 8px;
  font-size: 11px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.42);
}

.pres-setup__meter-note {
  margin-top: 14px;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.62);
}

/* On a phone the rail becomes a two-column bar: the number on the left,
   its gauge and note on the right. */
@media (max-width: 899px) {
  .pres-setup__meter {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: 18px;
    row-gap: 4px;
  }
  .pres-setup__meter-label { grid-column: 1 / -1; }
  .pres-setup__meter-value { grid-row: 2; margin-top: 4px; font-size: 42px; }
  .pres-setup__gauge { grid-column: 2; grid-row: 2; margin-top: 0; }
  .pres-setup__meter-scale { display: none; }
  .pres-setup__meter-note { grid-column: 1 / -1; margin-top: 8px; }
}

/* ---- The step panel ---- */
.pres-setup__main {
  display: flex;
  flex-direction: column;
  padding: clamp(20px, 2.6vw, 34px);
  border-radius: var(--p-radius-lg);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  box-shadow: var(--p-shadow-sm);
}

.pres-setup__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 12px;
}

.pres-setup__dots { display: flex; gap: 6px; }
.pres-setup__dot {
  width: 26px;
  height: 4px;
  border-radius: 100px;
  background: rgba(26, 26, 26, 0.12);
  transition: background-color var(--p-dur) var(--p-ease), opacity var(--p-dur) var(--p-ease);
}
.pres-setup__dot.is-done { background: rgba(26, 26, 26, 0.4); }
.pres-setup__dot.is-active { background: var(--p-ink); }
/* A step the current answers skip past. Still shown — the viewer should
   be able to see that choosing "platform only" removed four questions
   rather than wonder where the progress went. */
.pres-setup__dot.is-skipped { opacity: 0.3; }

.pres-setup__count {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--p-faint);
  font-variant-numeric: tabular-nums;
}

.pres-setup__progress {
  height: 3px;
  border-radius: 100px;
  background: rgba(26, 26, 26, 0.09);
  overflow: hidden;
  margin-bottom: clamp(22px, 2.6vw, 32px);
}
.pres-setup__progress-fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--p-ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 480ms var(--p-ease-soft);
}

/* Steps are stacked absolutely and the container is given the active
   step's measured height by the script, with a transition on it.

   Sharing one grid cell was the first attempt and it sized the panel to
   the TALLEST step — which is the result, with its enquiry form — leaving
   two thirds of the card empty behind every question. Animating the
   container's height is the one place on this page that deliberately
   touches layout; it is one element for 380ms on an explicit interaction,
   which is the same trade the process wizard's accordion already makes.

   The height is a progressive enhancement: with no JS the container is
   auto-height and the first step simply sizes it. */
.pres-setup__steps {
  position: relative;
  flex: 1 1 auto;
  transition: height 380ms var(--p-ease-soft);
}
.pres-setup__step {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
  opacity: 0;
  transform: translate3d(14px, 0, 0);
  transition:
    opacity 340ms var(--p-ease-soft),
    transform 340ms var(--p-ease-soft);
  visibility: hidden;
  pointer-events: none;
}
.pres-setup__step.is-active {
  /* The active step returns to the flow, so the container still has a
     sensible height before the script measures anything. */
  position: relative;
  opacity: 1;
  transform: translate3d(0, 0, 0);
  visibility: visible;
  pointer-events: auto;
}

.pres-setup__q {
  display: block;
  padding: 0;
  font-size: clamp(20px, 2.1vw, 28px);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.22;
  color: var(--p-ink);
  text-wrap: balance;
}
.pres-setup__hint {
  margin-top: 10px;
  font-size: var(--p-body);
  line-height: 1.6;
  color: var(--p-muted);
  max-width: 56ch;
}

/* ---- Options ---- */
.pres-setup__options {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  margin-top: clamp(20px, 2.4vw, 28px);
}
@media (min-width: 640px) {
  .pres-setup__options--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.pres-setup__opt { display: block; cursor: pointer; }

/* The real input is hidden from sight but not from the accessibility tree
   or the keyboard — it is what carries checked state, focus and the
   space/arrow behaviour. The box below is only its appearance. */
.pres-setup__opt input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.pres-setup__opt-box {
  position: relative;
  display: block;
  height: 100%;
  padding: 16px 18px 16px 46px;
  border-radius: var(--p-radius);
  background: var(--p-bg);
  border: 1px solid var(--p-border);
  transition:
    background-color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease),
    box-shadow var(--p-dur) var(--p-ease);
}
.pres-setup__opt-box::before {
  content: "";
  position: absolute;
  left: 16px;
  top: 17px;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  border: 1.5px solid rgba(26, 26, 26, 0.28);
  background: var(--p-surface);
  transition:
    background-color var(--p-dur) var(--p-ease),
    border-color var(--p-dur) var(--p-ease);
}
/* Radios get a circle, checkboxes a rounded square — the shape is the only
   cue that step one is a choice between two and the rest are multi-select. */
.pres-setup__opt input[type="radio"] + .pres-setup__opt-box::before { border-radius: 50%; }

/* The tick. Drawn with two borders rotated 45°, so there is no icon font
   and no second SVG per option. */
.pres-setup__opt-box::after {
  content: "";
  position: absolute;
  left: 21px;
  top: 22px;
  width: 8px;
  height: 4px;
  border-left: 1.7px solid #ffffff;
  border-bottom: 1.7px solid #ffffff;
  transform: rotate(-45deg) scale(0.5);
  opacity: 0;
  transition: opacity var(--p-dur) var(--p-ease), transform var(--p-dur) var(--p-ease);
}

.pres-setup__opt:hover .pres-setup__opt-box {
  border-color: rgba(26, 26, 26, 0.28);
  background: var(--p-surface);
}

.pres-setup__opt input:checked + .pres-setup__opt-box {
  border-color: var(--p-ink);
  background: var(--p-surface);
  box-shadow: inset 0 0 0 1px var(--p-ink);
}
.pres-setup__opt input:checked + .pres-setup__opt-box::before {
  background: var(--p-ink);
  border-color: var(--p-ink);
}
.pres-setup__opt input:checked + .pres-setup__opt-box::after {
  opacity: 1;
  transform: rotate(-45deg) scale(1);
}

/* Focus lands on the box, because the input it belongs to is visually
   hidden and an invisible focus ring is the same as none. */
.pres-setup__opt input:focus-visible + .pres-setup__opt-box {
  outline: 2px solid var(--p-ink);
  outline-offset: 2px;
}

.pres-setup__opt-name {
  display: block;
  font-size: var(--p-body);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--p-ink);
}
.pres-setup__opt-desc {
  display: block;
  margin-top: 5px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--p-muted);
}

/* ---- Result ---- */
.pres-setup__result { text-align: center; }

.pres-setup__result-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--p-faint);
}
.pres-setup__result-range {
  margin-top: 12px;
  font-size: clamp(40px, 6.4vw, 76px);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 1;
  color: var(--p-ink);
  font-variant-numeric: tabular-nums;
}
.pres-setup__result-summary {
  margin-top: 14px;
  font-size: var(--p-lead);
  line-height: 1.55;
  color: var(--p-muted);
  max-width: 48ch;
  margin-left: auto;
  margin-right: auto;
}

/* The disclaimer is not small print. It is the sentence that keeps an
   estimate from reading as an offer, so it gets a real box. */
.pres-setup__disclaimer {
  margin: clamp(22px, 2.6vw, 30px) auto 0;
  max-width: 62ch;
  padding: 16px 20px;
  border-radius: var(--p-radius);
  background: var(--p-surface-2);
  border: 1px solid var(--p-border);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--p-muted);
}

.pres-setup__form {
  margin-top: clamp(26px, 3vw, 36px);
  padding-top: clamp(24px, 2.8vw, 32px);
  border-top: 1px solid var(--p-border);
  text-align: left;
}

/* ---- Navigation ---- */
.pres-setup__nav {
  display: flex;
  gap: 10px;
  margin-top: clamp(24px, 2.8vw, 34px);
  padding-top: clamp(20px, 2.4vw, 26px);
  border-top: 1px solid var(--p-border);
}
.pres-setup__nav .pres-btn { flex: 1 1 0; }

@media (min-width: 640px) {
  .pres-setup__nav { justify-content: flex-end; }
  .pres-setup__nav .pres-btn { flex: 0 0 auto; min-width: 150px; }
}

/* ---- Admin CTA links ---- */
.pres-cta__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(14px, 2vw, 28px);
  margin-top: var(--p-gap-lg);
}
.pres-cta__links:empty { display: none; }
.pres-cta__link {
  font-size: var(--p-body);
  font-weight: 600;
  color: var(--p-muted);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--p-border);
  transition: color var(--p-dur) var(--p-ease), text-decoration-color var(--p-dur) var(--p-ease);
}
.pres-cta__link:hover {
  color: var(--p-ink);
  text-decoration-color: var(--p-ink);
}

/* .pres-cta__actions — the three-button row — was removed with the old
   contact section. Its one surviving idea, the selected-CTA fill, lives on
   as .pres-btn--secondary.is-active in §5. */

.pres-form {
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  border-radius: var(--p-radius-lg);
  padding: clamp(24px, 3vw, 40px);
  box-shadow: var(--p-shadow-md);
}

.pres-form__intent {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--p-faint);
  padding-bottom: var(--p-gap-sm);
  margin-bottom: var(--p-gap);
  border-bottom: 1px solid var(--p-border);
}

.pres-field { margin-bottom: 18px; }
.pres-field__label {
  display: block;
  font-size: var(--p-small);
  font-weight: 600;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.pres-field__opt {
  font-weight: 400;
  color: var(--p-faint);
}

.pres-field__input {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 14px 16px;
  border: 1px solid var(--p-border);
  border-radius: var(--p-radius);
  background: var(--p-bg);
  color: var(--p-ink);
  /* 16px is not a style choice: anything smaller makes iOS Safari zoom the
     viewport on focus, which on this page would break the layout mid-pitch.
     Same reasoning, same value, as the checkout fields in style.css. */
  font-size: 16px;
  line-height: 1.5;
  transition:
    border-color var(--p-dur-fast) var(--p-ease),
    box-shadow var(--p-dur-fast) var(--p-ease),
    background-color var(--p-dur-fast) var(--p-ease);
}
.pres-field__input::placeholder { color: var(--p-faint); }
.pres-field__input:focus {
  outline: none;
  background: var(--p-surface);
  border-color: var(--p-ink);
  box-shadow: 0 0 0 3px rgba(26, 26, 26, 0.08);
}
.pres-field__input--area {
  min-height: 116px;
  resize: vertical;
}
.pres-field.is-invalid .pres-field__input {
  border-color: var(--p-danger);
  box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.09);
}
.pres-field__error {
  margin-top: 7px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--p-danger);
}

.pres-form__submit {
  width: 100%;
  margin-top: var(--p-gap-xs);
}

.pres-form__status {
  margin-bottom: var(--p-gap-sm);
  padding: 13px 16px;
  border-radius: var(--p-radius);
  font-size: 14px;
  line-height: 1.5;
}
.pres-form__status--error {
  background: rgba(220, 38, 38, 0.07);
  border: 1px solid rgba(220, 38, 38, 0.22);
  color: var(--p-danger);
}
.pres-form__status--success {
  background: rgba(5, 150, 105, 0.07);
  border: 1px solid rgba(5, 150, 105, 0.22);
  color: var(--p-success);
}

/* Button busy state — a drawn ring, no icon font. */
.pres-btn.is-busy { pointer-events: none; }
.pres-btn.is-busy span { opacity: 0.45; }
.pres-btn.is-busy::after {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #ffffff;
  animation: pres-spin 700ms linear infinite;
}
.pres-btn--secondary.is-busy::after {
  border-color: rgba(26, 26, 26, 0.25);
  border-top-color: var(--p-ink);
}
.pres-btn { position: relative; }
@keyframes pres-spin { to { transform: rotate(360deg); } }

/* Success panel replacing the form once an enquiry lands. */
.pres-form__done {
  text-align: center;
  padding: clamp(20px, 3vw, 34px) 0;
}
.pres-form__done-mark {
  width: 46px;
  height: 46px;
  margin: 0 auto 18px;
  border-radius: 50%;
  background: var(--p-ink);
  position: relative;
  animation: pres-pop 460ms var(--p-ease-soft) both;
}
.pres-form__done-mark::after {
  content: "";
  position: absolute;
  left: 17px;
  top: 13px;
  width: 10px;
  height: 18px;
  border: solid #ffffff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.pres-form__done-title {
  font-size: clamp(20px, 2vw, 26px);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.pres-form__done-body {
  font-size: var(--p-body);
  color: var(--p-muted);
  max-width: 44ch;
  margin: 0 auto;
}
@keyframes pres-pop {
  from { opacity: 0; transform: scale(0.7); }
  to { opacity: 1; transform: scale(1); }
}

/* -----------------------------------------------------------------------
   20. FOOTER
   -------------------------------------------------------------------- */
.pres-footer {
  padding: clamp(40px, 5vw, 64px) 0;
  padding-bottom: max(clamp(40px, 5vw, 64px), env(safe-area-inset-bottom));
  border-top: 1px solid var(--p-border);
  background: var(--p-bg);
  text-align: center;
}
.pres-footer__brand {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--p-ink);
  margin-bottom: 6px;
}
.pres-footer__meta {
  font-size: 12.5px;
  color: var(--p-faint);
}

/* Deliberately quieter than the line above: same size and family, a little
   more tracking and a lighter ink, so it sits as a colophon under the
   notice rather than reading as a second warning. */
.pres-footer__owner {
  margin-top: 6px;
  letter-spacing: 0.06em;
  color: rgba(26, 26, 26, 0.34);
}

/* -----------------------------------------------------------------------
   20b. SHOWCASE PRODUCT PAGE — /showcase/:slug
   Where the deck's "View Product" button lands: a real product page for a
   product that is deliberately not for sale.

   It shares this stylesheet rather than the storefront's so a viewer who
   clicks out of a deck stays inside the same design — same paper, same
   ink, same Montserrat, same radii. What it does NOT share is any part of the
   storefront's buy flow: there is no cart control on this page to style,
   because the markup never contains one (see routes/presentation.js).

   One column at every width. The page is a handful of blocks of prose and
   media read top to bottom; a second column would only give it somewhere
   to put things that do not need saying.
   -------------------------------------------------------------------- */
.shw-body { background: var(--p-bg); }

.shw {
  padding: clamp(48px, 8vh, 96px) 0 clamp(64px, 10vh, 120px);
}
.shw .pres-container { max-width: 860px; }

/* The not-for-sale notice. Pill, inked, and first in the flow — the page
   otherwise looks exactly like something you could buy. */
.shw-flag {
  display: inline-block;
  padding: 7px 16px;
  border-radius: var(--p-radius-pill);
  background: var(--p-ink);
  color: #ffffff;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.shw-head { margin: clamp(24px, 3vw, 36px) 0 clamp(28px, 3.4vw, 44px); }
.shw-logo {
  height: clamp(26px, 3.2vw, 34px);
  width: auto;
  display: block;
  margin-bottom: clamp(16px, 2vw, 22px);
}

.shw-title {
  font-size: clamp(34px, 5.4vw, 62px);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.04em;
  color: var(--p-ink);
  text-wrap: balance;
}
.shw-meta {
  margin-top: 14px;
  font-size: var(--p-small);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--p-faint);
}
.shw-lead {
  margin-top: clamp(18px, 2.2vw, 26px);
  font-size: var(--p-lead);
  line-height: 1.6;
  color: var(--p-muted);
  max-width: 62ch;
}

.shw-cover {
  margin: 0 0 clamp(32px, 4vw, 52px);
  border-radius: var(--p-radius-lg);
  overflow: hidden;
  border: 1px solid var(--p-border-soft);
  background: var(--p-surface-2);
  box-shadow: var(--p-shadow-lg);
}
/* Capped and contained rather than full-bleed. Cover art on this platform is
   whatever shape the creator uploaded — often portrait — and at 860px wide a
   portrait image became a two-screen-tall wall before the copy started.
   `contain` rather than `cover` because cropping a product cover tends to cut
   off the title, which is the one thing it exists to show. */
.shw-cover img {
  display: block;
  width: 100%;
  max-height: min(58vh, 520px);
  object-fit: contain;
  margin: 0 auto;
}

.shw-prose { margin-bottom: clamp(32px, 4vw, 52px); }
.shw-prose__p {
  font-size: var(--p-body);
  line-height: 1.72;
  color: var(--p-muted);
  max-width: 68ch;
}
.shw-prose__p + .shw-prose__p { margin-top: 1.1em; }

/* Gallery. One column on a phone, two from 640px — images, GIFs and video
   all sit in the same grid because they are all just "a demonstration of
   the product" and sorting them into separate sections would be a
   distinction the viewer does not care about. */
.shw-gallery {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(12px, 1.6vw, 20px);
  margin-bottom: clamp(32px, 4vw, 52px);
}
@media (min-width: 640px) {
  .shw-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.shw-media {
  margin: 0;
  border-radius: var(--p-radius);
  overflow: hidden;
  border: 1px solid var(--p-border-soft);
  background: var(--p-surface-2);
}
.shw-media img,
.shw-media video {
  display: block;
  width: 100%;
  height: auto;
}

.shw-block { margin-bottom: clamp(32px, 4vw, 52px); }
.shw-h2 {
  font-size: clamp(22px, 2.4vw, 32px);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--p-ink);
  margin-bottom: var(--p-gap);
}

.shw-list { border-top: 1px solid var(--p-border); }
.shw-list__item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px 0;
  border-bottom: 1px solid var(--p-border);
}
.shw-list__name {
  font-size: var(--p-body);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--p-ink);
}
.shw-list__meta { font-size: 13.5px; line-height: 1.55; color: var(--p-muted); }

.shw-faq + .shw-faq {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--p-border);
}
.shw-faq__q {
  font-size: var(--p-h3);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.shw-faq__a { font-size: var(--p-body); line-height: 1.65; color: var(--p-muted); }

.shw-foot {
  margin-top: clamp(48px, 7vw, 88px);
  padding-top: clamp(28px, 3.4vw, 40px);
  border-top: 1px solid var(--p-border);
  text-align: center;
}

/* -----------------------------------------------------------------------
   21. GATE SCREENS — password + expired
   Server-rendered (see renderGateScreen in routes/presentation.js) and
   sharing this stylesheet, so the first thing a visitor sees is already the
   deck's typography rather than a browser default.
   -------------------------------------------------------------------- */
.pres-body--gate {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
}
.pres-gate {
  width: 100%;
  padding: 40px max(20px, env(safe-area-inset-left)) 40px max(20px, env(safe-area-inset-right));
}
.pres-gate__card {
  width: 100%;
  max-width: 420px;
  margin: 0 auto;
  padding: clamp(32px, 5vw, 48px);
  background: var(--p-surface);
  border: 1px solid var(--p-border);
  border-radius: var(--p-radius-lg);
  box-shadow: var(--p-shadow-lg);
  text-align: center;
  animation: pres-rise 520ms var(--p-ease-soft) both;
}
/* Card is text-align:center, so the auto margins are what actually centre the
   mark — an inline-level img would only centre if it were still inline. */
.pres-gate__logo {
  height: 30px;
  width: auto;
  display: block;
  margin: 0 auto 24px;
}
.pres-gate__mark {
  width: 36px;
  height: 36px;
  margin: 0 auto 22px;
  border-radius: 9px;
  background: var(--p-ink);
  position: relative;
}
/* A drawn padlock: ::after is the shackle, ::before the body it sits on.
   Both are needed — the shackle alone reads as an arch, not a lock. No icon
   font is loaded on this page for the sake of one glyph. */
.pres-gate__mark::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 8px;
  width: 16px;
  height: 12px;
  transform: translateX(-50%);
  background: #ffffff;
  border-radius: 2px;
}
.pres-gate__mark::after {
  content: "";
  position: absolute;
  left: 50%;
  /* Bottom edge meets the top of the body above: 36 - 8 (inset) - 12 (body)
     = 16px, minus the 8px shackle height. */
  top: 8px;
  width: 11px;
  height: 8px;
  transform: translateX(-50%);
  border: 2px solid #ffffff;
  border-bottom: 0;
  border-radius: 6px 6px 0 0;
}
.pres-gate__title {
  font-size: clamp(21px, 3vw, 26px);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.25;
  color: var(--p-ink);
  margin-bottom: 10px;
}
.pres-gate__body {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--p-muted);
}
.pres-gate__form { margin-top: 26px; text-align: left; }
.pres-gate__label {
  display: block;
  font-size: var(--p-small);
  font-weight: 600;
  color: var(--p-ink);
  margin-bottom: 8px;
}
.pres-gate__input {
  display: block;
  width: 100%;
  min-height: 52px;
  padding: 14px 16px;
  border: 1px solid var(--p-border);
  border-radius: var(--p-radius);
  background: var(--p-bg);
  color: var(--p-ink);
  font-size: 16px;
  transition: border-color var(--p-dur-fast) var(--p-ease), box-shadow var(--p-dur-fast) var(--p-ease);
}
.pres-gate__input:focus {
  outline: none;
  border-color: var(--p-ink);
  box-shadow: 0 0 0 3px rgba(26, 26, 26, 0.08);
}
.pres-gate__error {
  margin-top: 10px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--p-danger);
}
.pres-gate__submit { width: 100%; margin-top: 18px; }
/* A wrong password shakes the card — short, small amplitude, once. */
.pres-gate__card.is-shake { animation: pres-shake 380ms var(--p-ease); }
@keyframes pres-shake {
  0%, 100% { transform: translateX(0); }
  22% { transform: translateX(-7px); }
  46% { transform: translateX(6px); }
  70% { transform: translateX(-3px); }
  88% { transform: translateX(2px); }
}

/* -----------------------------------------------------------------------
   22. NARROW-VIEWPORT CORRECTIONS
   The layout is fluid from 320px up, so these are corrections to specific
   things that stop being comfortable at the bottom of the range — not a
   separate mobile stylesheet.
   -------------------------------------------------------------------- */
@media (max-width: 400px) {
  /* At 320-390px the hero headline is the one string that can still overflow
     if it is a long single word; wrapping is preferable to a clipped
     wordmark or a horizontal scrollbar. */
  .pres-hero__title {
    font-size: clamp(40px, 12.5vw, 52px);
    overflow-wrap: break-word;
    hyphens: auto;
  }
  .pres-hero__subtitle { max-width: none; }
  /* Reclaim the indent under the step number: at this width the body copy
     needs the full measure more than it needs the alignment. */
  .pres-step__body-inner { padding-left: 0; }
  .pres-form { padding: 22px 18px; }
  /* The belt's edge fade is a fixed 44px, which at 320px eats a quarter of
     the strip. Halved here so a card is still legible at the edges. */
  .pres-belt__viewport {
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
  }
  .pres-journey__step { padding-left: 56px; }
  .pres-journey__node { width: 42px; height: 42px; }
  .pres-journey__line { left: 20px; }
}

/* -----------------------------------------------------------------------
   23. REDUCED MOTION
   Everything above is decoration over content that is already complete and
   readable, so the honest response to the preference is to remove the
   motion entirely rather than shorten it. That includes the drifting
   auras and the benefit belt, which are the two things on this page most
   likely to cause discomfort.

   The belt is the one case where removing motion is not enough on its
   own: a strip that is wider than the viewport and no longer moving is
   unreachable content. It becomes an ordinary scroll container here, so
   the same twelve cards stay available by whatever means the visitor
   already uses to scroll.
   -------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .pres-js [data-reveal] {
    opacity: 1;
    translate: none;
  }
  .pres-body [data-magnetic] { transform: none; }
  .pres-hero__cue-line { animation: none; transform: scaleX(1); }

  .pres-belt__viewport {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    cursor: auto;
    touch-action: auto;
    padding-inline: var(--p-shell-x);
  }
  .pres-belt__track { transform: none !important; }
  .pres-benefit { scroll-snap-align: center; }
  .pres-belt__hint { display: none; }

  /* The stage's height still has to change with the device, or the phone
     is cropped. Removing the animation, not the resize. */
  .pres-stage { transition: none; }
  .pres-journey__line-fill { transition: none; }

  /* Coming Soon: same problem as the belt, same answer. The rows are
     wider than the viewport by design, so stopping the drift without
     making them scrollable would hide two thirds of the roadmap. The
     iteration-count override above already halts the animation; these
     rules give the cards back. */
  .pres-soon {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .pres-soon__row {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-inline: var(--p-shell-x);
  }
  .pres-soon__track { animation: none !important; transform: none !important; }
  .pres-soon__card { scroll-snap-align: center; }

  /* The hero slideshow keeps working on click, swipe and keyboard — the
     autoplay timer is what the preference switches off (see the hero
     module in presentation.js). The panels themselves must still be able
     to appear, so the transition is removed rather than the visibility. */
  .pres-hs__slide { transition: none; }
  .pres-slogan__line { animation: none; opacity: 1; transform: none; }
  .mock__demo-figure { animation: none; }
  .mock__page { transition: none; }

  /* The pillar glyphs. The blanket rule above already collapses these to
     nothing, but they are looping decoration on a card a viewer is meant
     to read, so they are switched off by name too rather than left
     running for a millisecond. */
  .pres-pillar__pip { animation: none; }
  .pres-pillar__glyph,
  .pres-pillar__core,
  .pres-pillar__bar,
  .pres-pillar__bolt,
  .pres-pillar::before { transition: none; }
}

/* -----------------------------------------------------------------------
   24. PARTNER PROFILE FRAME

   The Partner Profile section embeds the REAL profile: the same markup from
   renderPartnerMain() and the same assets/css/partner.css that draw the
   public /partners/:slug page, both loaded unmodified (see the IFPARTNER
   block in private/presentation.html). Nothing about the profile's
   typography, buttons, imagery, spacing, card language, colours or
   interaction is restated here — restating any of it is exactly how the
   in-deck profile would start drifting from the public one.

   What this block does is the two things that are genuinely different about
   the deck as a HOST page, and nothing else:

   1. TOKENS. partner.css reads style.css's design tokens (--color-bg,
      --radius, --nav-ease, --span …). This deck deliberately does not load
      style.css — it re-derives the same monochrome system under its own
      --p-* names (see the file header). So the frame republishes the deck's
      --p-* values under the names partner.css asks for. Same values, one
      source: the profile inside a deck and the profile on the public site
      resolve to identical colours, radii and shadows.

   2. HEADER CLEARANCE. partner.css reserves 68px at the top of the profile
      for the site's fixed header (see the note in partner.css and
      [the 68px constant] used by every public inner page). The deck has no
      fixed header — the profile sits mid-page inside a section that already
      has its own vertical rhythm — so the clearance is zeroed. Left at 68px
      it prints as a band of empty space above the avatar, which on a phone
      is most of a screen of nothing.
   -------------------------------------------------------------------- */
.pres-partner__frame {
  /* ---- 1. Token bridge. Values, not new colours: each right-hand side is
     the deck's own token, which the file header already documents as being
     style.css's value. ---- */
  --color-bg: var(--p-bg);
  --color-surface: var(--p-surface);
  --color-surface-2: var(--p-surface-2);
  --color-text: var(--p-ink);
  --color-text-muted: var(--p-muted);
  --color-text-faint: var(--p-faint);
  --color-border: var(--p-border);
  --color-primary: var(--p-ink);
  --color-primary-hover: var(--p-ink-hover);
  --radius: var(--p-radius);
  --radius-pill: var(--p-radius-pill);
  --shadow-sm: var(--p-shadow-sm);
  --shadow-md: var(--p-shadow-md);
  --prosto-one: var(--p-font);
  --nav-ease: var(--p-ease);
  --nav-dur: var(--p-dur);

  /* style.css's global `span { color: var(--span) }` rule does not exist on
     this page, so --span is undefined here and every <span> inside the
     profile would fall back to unstyled. Bound to the profile's own text
     colour, which is what it resolves to on the public page. See the
     span-on-a-filled-surface trap in the project notes: partner.css already
     sets `color: inherit` where it matters, and this keeps the rest honest. */
  --span: var(--p-ink);

  /* ---- 2. No fixed header in a deck. ---- */
  --pprofile-header-clearance: 0px;

  margin-top: clamp(28px, 4vw, 48px);
}

/* The profile is a 34rem column that centres itself (partner.css). Inside the
   deck's wider container that is still exactly what it should be — a profile,
   not a landing page — so the only thing needed is to keep it centred and let
   its own padding continue to be what guarantees no horizontal overflow at
   320px. Width is never touched here. */
.pres-partner__frame .pprofile {
  margin-inline: auto;
}

/* The deck sets `img { max-width: 100% }` broadly, but .pmedia boxes inside
   the profile rely on media-container.css's own sizing; nothing to add. The
   one deck-level rule that must not leak in is any transform on links, which
   would fight the profile's press states. */
.pres-partner__frame a {
  text-decoration: none;
}

@media print {
  /* The profile's entrance animations are a screen affordance; on paper they
     must not leave anything at opacity 0. */
  .pres-partner__frame .pprofile__reveal {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }
}

/* -----------------------------------------------------------------------
   25. PRINT
   Decks get printed to PDF and emailed after the meeting — often by the
   person who was pitched, to circulate internally. Worth getting right.
   -------------------------------------------------------------------- */
@media print {
  .pres-progress,
  .pres-rail,
  .pres-hero__cue,
  .pres-hs__controls,
  .pres-pager,
  .pres-setup__nav,
  .pres-form { display: none !important; }

  .pres-body { background: #ffffff; }
  .pres-section,
  .pres-hero {
    min-height: 0;
    padding: 28px 0;
    break-inside: avoid;
  }
  .pres-section--tint { background: none; }
  .pres-aura,
  .pres-stage__glow,
  .pres-belt__hint,
  .pres-switch { display: none !important; }

  /* On paper the belt is a list, not a strip: the twelve cards wrap into
     a grid so none of them fall off the right edge of the page. */
  .pres-belt__viewport {
    overflow: visible;
    -webkit-mask-image: none;
    mask-image: none;
    padding: 0;
  }
  .pres-belt__track {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    width: auto;
    transform: none !important;
  }
  .pres-benefit { width: auto; box-shadow: none; break-inside: avoid; }
  /* Clones exist only to make the loop seamless; printing them would
     repeat every benefit twice. */
  .pres-benefit[data-clone] { display: none !important; }

  /* Both devices print, side by side, rather than whichever one happened
     to be selected when the reader hit Print. */
  .pres-stage { height: auto !important; display: flex; gap: 24px; align-items: flex-start; }
  .pres-pane {
    position: static;
    opacity: 1 !important;
    visibility: visible !important;
    transform: none !important;
    flex: 1 1 0;
  }
  .pres-shell { box-shadow: none; }

  .pres-journey__step.is-lit .pres-journey__node,
  .pres-journey__node { transform: none !important; }
  .pres-journey__line-fill { transform: scaleY(1) !important; }

  /* Every wizard panel prints, not just the open one. */
  .pres-step__body {
    max-height: none !important;
    opacity: 1 !important;
    visibility: visible !important;
  }

  /* ---- Hero slideshow ----
     Only the panel that was showing prints. Ten stacked drawings of a UI
     is not information on paper, and un-stacking them would turn a
     one-page hero into four. The caption of each carries the actual
     content, and the reader has the live link anyway. */
  .pres-hs__stage {
    box-shadow: none;
    break-inside: avoid;
  }

  /* ---- Showcase pages + setup wizard ----
     Both stack their panels absolutely. On paper they return to the flow
     so every page and every question prints, in order — a circulated PDF
     of the deck should contain the questions somebody was asked, not just
     whichever step the browser happened to be on. */
  .mock__pages,
  .pres-setup__steps {
    position: static;
    height: auto !important;
  }
  .mock__page,
  .pres-setup__step {
    position: static;
    opacity: 1 !important;
    visibility: visible !important;
    transform: none !important;
    break-inside: avoid;
  }
  .pres-setup__step + .pres-setup__step { margin-top: 22px; }
  .pres-setup__meter { break-inside: avoid; }

  /* ---- Coming Soon ----
     The two drifting rows become a wrapped grid, exactly as the belt
     does, and the duplicated set is dropped. */
  .pres-soon {
    -webkit-mask-image: none;
    mask-image: none;
    overflow: visible;
  }
  .pres-soon__row { overflow: visible; }
  .pres-soon__track {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    width: auto;
    animation: none !important;
    transform: none !important;
  }
  .pres-soon__card { width: auto; box-shadow: none; break-inside: avoid; }
  .pres-soon__card[data-clone] { display: none !important; }

  .pres-camp__card { break-inside: avoid; box-shadow: none; }
  .pres-value__item { break-inside: avoid; }

  .pres-js [data-reveal] { opacity: 1 !important; translate: none !important; }
}
