/* ============================================================
   Gentika - the PRODUCT page (product/ai-shopping-assistant/).
   Loaded only by that page, and every rule below is scoped under
   body[data-page="product"], so home / pricing / demo / contact / 404 are
   unreachable from here and keep Carlo's shared cross-site type scale
   (home.css:71) exactly as it is.

   TYPE IS THE SITE'S, NOT ITS OWN (Enrico): the page was first built to a
   display scale of its own (H1 72px / headings 48px at Bold 700), and that
   made it read as a different site. Every token below is now lifted VERBATIM
   from the homepage so the product page speaks in the same voice as
   everything else - Carlo's "one type scale across ALL pages" (home.css:71)
   now genuinely includes this page. The source of each value is named beside
   it; if the homepage's scale moves, move these with it.

   Nothing here sets weight 700 any more, so this page's font link no longer
   asks for it - the site stops at SemiBold 600.

   The light identity itself is INHERITED, not redefined: body.home-v3 (still
   on the page) supplies --ink, --bg, --line and the glass tokens, so a change
   to the palette in home.css still reaches this page.
   ============================================================ */

body[data-page="product"] {
  /* wider column than the rest of the site - the split sections need room for
     a ~60% visual without crushing the copy. The header's .wrap reads the
     same token, so the logo stays aligned with the headline. */
  --maxw: 1280px;

  /* ---------- the type scale, mirrored from the homepage ---------- */
  --pp-h1: clamp(2.2rem, 3.4vw, 3.2rem);   /* = .hero3d .hero-copy h1 / .page-title */
  --pp-h2: clamp(1.7rem, 2.3vw, 2.2rem);   /* = .beat h2 / .band h2            */
  --pp-h2-final: clamp(1.9rem, 2.6vw, 2.5rem); /* = .after-film h2 (closing CTA) */
  --pp-lede: 1rem;                         /* = .hero-copy .lede / .final-lede */
  --pp-secbody: 0.98rem;                   /* = .beat-inner > p                */
  --pp-cardtitle: 1.05rem;                 /* = .enterprise-card h3            */
  --pp-copy: 0.88rem;                      /* = .enterprise-card p             */
  --pp-label: 0.9rem;                      /* = .category-row li               */

  /* one rhythm for every section band */
  --pp-band: clamp(76px, 9vw, 136px);
  --pp-radius: 22px;
  --pp-shadow: 0 40px 90px -48px rgba(60, 30, 10, 0.44),
    0 8px 26px -16px rgba(60, 30, 10, 0.16);
  --pp-shadow-sm: 0 18px 40px -28px rgba(60, 30, 10, 0.34);
}

/* ============================================================
   TYPE
   ============================================================ */
/* h1 mirrors .hero3d .hero-copy h1; h2 mirrors .beat h2 - same size, same
   weight, same tracking and leading, so a reader moving from the home film
   to this page never crosses a type boundary */
body[data-page="product"] .pp-h1 {
  font-family: var(--font-display);
  font-size: var(--pp-h1);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.028em;
  margin: 0 0 18px;
  text-wrap: balance;
}
body[data-page="product"] .pp-h2 {
  font-family: var(--font-display);
  font-size: var(--pp-h2);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.024em;
  margin: 0 0 14px;
  text-wrap: balance;
}
body[data-page="product"] .pp-lede {
  font-size: var(--pp-lede);
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-2);
  margin: 0 0 24px;
  max-width: 46ch;
}
body[data-page="product"] .pp-body {
  font-size: var(--pp-secbody);
  font-weight: 400;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 0 0 10px;
  max-width: 48ch;
}
body[data-page="product"] .pp-body--quiet { color: var(--ink-3); font-size: var(--pp-copy); }
body[data-page="product"] .pp-body:last-child { margin-bottom: 0; }

/* the eyebrow: style.css's .eyebrow carries a dark-canvas rule + dash, so the
   product page uses its own label - sized like the home rail's .intel-label */
body[data-page="product"] .pp-eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 14px;
}

body[data-page="product"] .pp-actions {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
}
/* the CTAs carry NO size override: style.css .btn (15px/500, 13px 22px) is
   the same button the home hero and every other page uses */

/* ============================================================
   SECTION SHELL
   ============================================================ */
body[data-page="product"] .pp-sec { padding-block: var(--pp-band); position: relative; }
/* alternating bands give the long page a readable pulse without introducing a
   second palette - --bg-raised is home.css's own warm off-white */
body[data-page="product"] .pp-sec--alt { background: var(--bg-raised); }

body[data-page="product"] .pp-split {
  display: grid;
  grid-template-columns: minmax(0, 0.74fr) minmax(0, 1fr);   /* visual ≈ 58% */
  gap: clamp(36px, 5vw, 84px);
  align-items: center;
}
body[data-page="product"] .pp-copy { min-width: 0; }
body[data-page="product"] .pp-visual { min-width: 0; }

/* the stack diagram needs its cards top-aligned with the heading */
body[data-page="product"] .pp-split--stack { align-items: center; }

body[data-page="product"] .pp-head { max-width: 46ch; margin: 0 0 clamp(32px, 4vw, 56px); }
body[data-page="product"] .pp-sec--wide .pp-head .pp-body { max-width: 52ch; }

/* "No migrations. / No checkout replacement." - a statement, not a list */
body[data-page="product"] .pp-nolist {
  list-style: none; margin: 24px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 6px;
}
body[data-page="product"] .pp-nolist li {
  font-size: var(--pp-cardtitle);
  font-weight: 600;
  letter-spacing: -0.025em;   /* = style.css:95, as the card titles get */
  color: var(--ink);
}

/* ============================================================
   1 · HERO - copy vertically centered against a ~60% visual
   ============================================================ */
body[data-page="product"] .pp-hero {
  padding-top: clamp(40px, 5vw, 72px);
  padding-bottom: clamp(72px, 8vw, 120px);
}
body[data-page="product"] .pp-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.68fr) minmax(0, 1fr);   /* visual ≈ 60% */
  gap: clamp(32px, 4.5vw, 76px);
  align-items: center;
  min-height: min(76vh, 720px);
}
body[data-page="product"] .pp-hero-copy { min-width: 0; }
body[data-page="product"] .pp-hero-visual { min-width: 0; }

/* the live agent screen (app.js fills [data-screen="hero"]). style.css sizes
   .screen-frame for the dark pages; here it simply fills its column, and
   home.css's body.home-v3 rule already supplies the light frame. */
body[data-page="product"] .pp-hero-visual .screen-frame {
  width: 100%;
  transform: none;
  border-radius: var(--pp-radius);
}
body[data-page="product"] .pp-hero-visual .gx-screen { height: clamp(440px, 60vh, 620px); }
body[data-page="product"] .hero-screen { min-height: 440px; }
/* The hero's demo is CENTRED ON THE COPY (cofounder, Aug 9). The stage region
   is already centred in the grid, but its idle content sat in the upper half -
   the empty band below the bar is the room the panel needs when it opens. So
   the hero re-seats its idle bar lower: both states (idle pill, and the open
   dialog+bar composite) then land on the same axis as the headline block. */
/* no manual seat or nudge here any more: app.js centres every step of the
   stage on the region's axis, and the hero grid already aligns that region
   with the headline column. */

/* ============================================================
   SHARED ANIMATION PANEL - one material for every [data-anim] host
   ============================================================ */
body[data-page="product"] .pp-anim { width: 100%; }
body[data-page="product"] .pp-panel {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--pp-radius);
  box-shadow: var(--pp-shadow);
  overflow: hidden;
  font-size: 14.5px;
}
body[data-page="product"] .pp-panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 16px;
  border-bottom: 0.5px solid var(--line);
  background: linear-gradient(180deg, rgba(255,255,255,0.55), rgba(248,246,243,0.35));
}
body[data-page="product"] .pp-panel-title {
  font-size: 12.5px; font-weight: 600; color: var(--ink);
  display: inline-flex; align-items: center; gap: 8px;
}
body[data-page="product"] .pp-panel-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--ink); box-shadow: 0 0 0 3px rgba(23, 23, 26, 0.08);
  flex: none;
}
body[data-page="product"] .pp-panel-meta { font-size: 11px; color: var(--ink-3); }

/* ===== THE PRODUCT'S MATERIAL (cofounder, Aug 9) =====
   Every explainer scene now speaks the shell's dialect: the panel's own
   frosted glass, its light rim, its radius - and the sizes above are the
   shell's NATIVE px at 1:1, since this column (~660px) is the chat panel's
   own width (680px). Solid fills stay the no-support fallback, exactly as
   in shell.css. */
/* THE LIGHT BEHIND THE GLASS.
   Frost is only visible when there is something to diffuse. Over a flat page
   backdrop-filter samples a single colour and the panel reads as PAINT, not
   glass - which is why the first pass still did not look like the product.
   The demo stage solves this with its neon bloom; every scene here gets the
   same device: a soft two-tone light laid behind the panel, which the frost
   then veils, spreads and cools. */
body[data-page="product"] .pp-anim { position: relative; }
body[data-page="product"] .pp-anim::before {
  content: "";
  position: absolute; inset: -46px -34px -38px;
  background:
    radial-gradient(46% 52% at 22% 18%, rgba(255, 226, 199, 0.80), transparent 72%),
    radial-gradient(44% 50% at 82% 80%, rgba(210, 224, 246, 0.72), transparent 74%),
    radial-gradient(72% 78% at 58% 46%, rgba(255, 255, 255, 0.86), transparent 78%);
  filter: blur(34px);
  pointer-events: none;
}
body[data-page="product"] .pp-panel { position: relative; z-index: 1; }

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body[data-page="product"] .pp-panel {
    /* the shell's veil: light enough that the bloom reads THROUGH it */
    background: color-mix(in srgb, #ffffff 52%, transparent);
    -webkit-backdrop-filter: blur(26px) saturate(1.65);
    backdrop-filter: blur(26px) saturate(1.65);
    border-color: rgba(255, 255, 255, 0.75);
    box-shadow: 0 34px 80px -44px rgba(60, 30, 10, 0.42),
      inset 0 1px 0 rgba(255, 255, 255, 0.95);
  }
  /* the head is one step firmer than the panel, like the shell's own */
  body[data-page="product"] .pp-panel-head {
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0.34));
  }
}

/* ============================================================
   2 · INTENT EXTRACTION
   The sentence types in, its entities light up one by one, and each lights a
   field card on the right. Panel height is reserved so the page never jumps
   between examples of different lengths.
   ============================================================ */
body[data-page="product"] .pp-intent { min-height: 440px; display: flex; flex-direction: column; }
body[data-page="product"] .pp-intent-body { padding: 16px; display: flex; flex-direction: column; gap: 14px; flex: 1; }

body[data-page="product"] .pp-utter-wrap { display: flex; gap: 12px; align-items: flex-start; }
body[data-page="product"] .pp-utter-av {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: var(--accent); color: var(--accent-ink);
  display: grid; place-items: center;
  font-size: 11px; font-weight: 500;
}
/* panel copy never out-sizes the section body it sits beside */
body[data-page="product"] .pp-utter {
  margin: 0;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--ink);
  min-height: 3.1em;
}
/* the caret rides the end of the typed text */
body[data-page="product"] .pp-utter.is-typing::after {
  content: ""; display: inline-block;
  width: 2px; height: 1.05em; margin-left: 2px;
  background: var(--ink); vertical-align: -0.16em;
  animation: pp-caret 1s steps(1) infinite;
}
@keyframes pp-caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* an entity inside the sentence: neutral until recognised, then lit */
body[data-page="product"] .pp-tok {
  border-radius: 6px;
  padding: 1px 3px;
  margin: 0 -3px;
  transition: background 0.45s ease, color 0.45s ease, box-shadow 0.45s ease;
}
body[data-page="product"] .pp-tok.is-lit {
  background: rgba(23, 23, 26, 0.07);
  box-shadow: inset 0 0 0 1px rgba(23, 23, 26, 0.14);
  color: var(--ink);
  font-weight: 500;
}

body[data-page="product"] .pp-fields-label {
  font-size: 11px; font-weight: 500; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-3);
  margin: 0 0 8px;
  display: flex; align-items: center; gap: 8px;
}
body[data-page="product"] .pp-fields-label::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}
/* the label + fields share one column so the grid can still take the slack */
body[data-page="product"] .pp-intent-fieldwrap {
  flex: 1;
  display: flex; flex-direction: column;
  min-height: 0;
}
body[data-page="product"] .pp-fields {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  align-content: start;
  flex: 1;
}
/* the inferred goal is not lifted from a phrase - it's read off the whole
   sentence, so it spans the row and carries a slightly firmer bed */
body[data-page="product"] .pp-field--infer {
  grid-column: 1 / -1;
  background: rgba(23, 23, 26, 0.045);
  border-color: rgba(23, 23, 26, 0.16);
}
body[data-page="product"] .pp-field {
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 11px 13px;
  background: color-mix(in srgb, #ffffff 72%, transparent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  display: flex; flex-direction: column; gap: 3px;
  min-width: 0;
  /* trigger-and-complete, like the home rail: a field is never half-arrived */
  opacity: 0; transform: translateY(10px) scale(0.97);
  transition: opacity 0.4s ease,
    transform 0.55s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1));
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
body[data-page="product"] .pp-field.is-in { opacity: 1; transform: none; }
body[data-page="product"] .pp-field-k {
  font-size: 11px; font-weight: 500; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-3);
}
/* the quick-answer question - the product's ask_followups, at the shell's
   chip scale: one line, chips, one tap. It arrives AFTER the extraction, so
   the scene shows both halves of its claim: what was read, and what had to
   be asked. */
body[data-page="product"] .pp-ask {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--line);
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.4s ease,
    transform 0.55s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1));
}
body[data-page="product"] .pp-ask.is-in { opacity: 1; transform: none; }
body[data-page="product"] .pp-ask[hidden] { display: none; }
body[data-page="product"] .pp-ask-q { font-size: 13.5px; font-weight: 600; color: var(--ink); }
body[data-page="product"] .pp-ask-chips { display: flex; gap: 7px; flex-wrap: wrap; }
body[data-page="product"] .pp-ask .pp-chip { padding: 6px 12px; font-size: 12.5px; }
body[data-page="product"] .pp-ask.is-answered .pp-chip:not([aria-selected="true"]) { opacity: 0.45; }

body[data-page="product"] .pp-field-v {
  font-size: 13.5px; font-weight: 600; color: var(--ink);
  line-height: 1.3;
  overflow-wrap: anywhere;
}

/* ============================================================
   HEIGHT IS RESERVED FOR THE SCENE'S TALLEST STATE (cofounder's video,
   Aug 9). The split grid centres both columns, so a panel that GROWS during
   its cycle re-centres the row and drags the headline beside it down the
   page - the copy visibly drifted as basket rows landed. Each min-height
   below is the measured final height, so the panel is a fixed frame that
   fills up (exactly like the real cart card) and nothing beside it moves.
   ============================================================ */

/* ============================================================
   3 · BASKET CONSTRUCTION - the basket visibly grows
   ============================================================ */
body[data-page="product"] .pp-basket { min-height: 700px; display: flex; flex-direction: column; }
body[data-page="product"] .pp-basket-ask {
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  font-size: 13.5px;
  color: var(--ink-2);
}
body[data-page="product"] .pp-basket-ask b { color: var(--ink); font-weight: 500; }
body[data-page="product"] .pp-basket-rows {
  padding: 8px 12px;
  display: flex; flex-direction: column; gap: 2px;
  flex: 1;
  align-content: start;
}
body[data-page="product"] .pp-row {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr) auto auto;
  align-items: center; gap: 10px;
  padding: 8px;
  border-radius: 10px;
  opacity: 0; transform: translateY(12px);
  transition: opacity 0.4s ease,
    transform 0.55s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1)),
    background 0.4s ease;
}
body[data-page="product"] .pp-row.is-in { opacity: 1; transform: none; }
/* a row that has just changed flashes its bed, so quantity edits are visible */
body[data-page="product"] .pp-row.is-bump { background: rgba(23, 23, 26, 0.05); }
body[data-page="product"] .pp-row.is-gone { opacity: 0; transform: translateX(-14px); }
body[data-page="product"] .pp-row-thumb {
  width: 40px; height: 40px; border-radius: 8px; flex: none;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  display: grid; place-items: center; overflow: hidden;
}
body[data-page="product"] .pp-row-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
body[data-page="product"] .pp-row-info { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
body[data-page="product"] .pp-row-name {
  font-size: 13.5px; font-weight: 600; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* the sub-line pairs an optional reason tag with the detail text; only the
   detail truncates, so the tag can never be clipped to nothing */
body[data-page="product"] .pp-row-sub {
  font-size: 11.5px; color: var(--ink-3);
  display: flex; align-items: center; gap: 6px; min-width: 0;
}
body[data-page="product"] .pp-row-subt {
  min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* "Often bought together" / "Substituted" - the reason a row is here */
body[data-page="product"] .pp-tag {
  display: inline-block;
  flex: none;
  font-size: 10.5px; font-weight: 600;
  padding: 2px 7px; border-radius: 999px;
  background: #eceef1;
  color: var(--ink-2);
  border: 1px solid rgba(23, 23, 26, 0.08);
}
/* a class selector and [hidden] carry the SAME specificity, so the later rule
   wins - without this the untagged rows show an empty pill */
body[data-page="product"] .pp-tag[hidden] { display: none; }
body[data-page="product"] .pp-row-qty {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 600; color: var(--ink-2);
  padding: 3px 8px; border-radius: 999px;
  border: 1px solid var(--line);
  background: color-mix(in srgb, #ffffff 74%, transparent);
  transition: transform 0.45s var(--spring-pop, cubic-bezier(0.22, 1, 0.36, 1)),
    border-color 0.4s ease;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
body[data-page="product"] .pp-row.is-bump .pp-row-qty {
  transform: scale(1.14);
  border-color: rgba(23, 23, 26, 0.34);
}
body[data-page="product"] .pp-row-price {
  font-size: 13px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
  min-width: 56px; text-align: right;
}
body[data-page="product"] .pp-basket-foot {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px;
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(248,246,243,0.35), rgba(255,255,255,0.55));
}
body[data-page="product"] .pp-basket-count { font-size: 11.5px; color: var(--ink-3); }
body[data-page="product"] .pp-basket-total {
  font-size: 16px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
body[data-page="product"] .pp-basket-cta {
  margin: 0 10px 12px;
  padding: 11px 12px; border-radius: 8px;
  background: var(--accent); color: var(--accent-ink);
  font-size: 12.5px; font-weight: 600; text-align: center;
  opacity: 0; transform: translateY(8px);
  transition: opacity 0.4s ease,
    transform 0.5s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1));
}
body[data-page="product"] .pp-basket-cta.is-in { opacity: 1; transform: none; }

/* ============================================================
   4 · ADAPTIVE CONVERSATIONS - thread above, live basket below
   ============================================================ */
body[data-page="product"] .pp-adapt { min-height: 580px; display: flex; flex-direction: column; }
body[data-page="product"] .pp-thread {
  padding: 16px 16px 6px;
  display: flex; flex-direction: column; gap: 12px;
  /* the thread fills from the BOTTOM (justify-content), so this reserve is
     the empty space above the first bubble - keep it just tall enough for the
     four-bubble window rather than leaving a void early in the cycle */
  min-height: 196px;
  justify-content: flex-end;
}
body[data-page="product"] .pp-bub {
  max-width: 82%;
  padding: 11px 15px;
  border-radius: 16px;
  font-size: 14.5px;
  line-height: 1.45;
  opacity: 0; transform: translateY(9px) scale(0.98);
  transition: opacity 0.35s ease,
    transform 0.5s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1));
}
body[data-page="product"] .pp-bub.is-in { opacity: 1; transform: none; }
body[data-page="product"] .pp-bub--user {
  align-self: flex-end;
  background: color-mix(in srgb, var(--user-bubble, #2e2e33) 66%, transparent); color: var(--user-ink, #fff);
  border-bottom-right-radius: 5px;
}
body[data-page="product"] .pp-bub--agent {
  align-self: flex-start;
  background: color-mix(in srgb, #eeeef1 78%, transparent);
  border: 1px solid var(--line);
  color: var(--ink);
  border-bottom-left-radius: 5px;
}
/* a system notice (out of stock) reads as neither party */
body[data-page="product"] .pp-bub--sys {
  align-self: center;
  background: rgba(196, 106, 30, 0.09);
  border: 1px solid rgba(196, 106, 30, 0.22);
  color: #8a4d13;
  font-size: 12.5px;
  max-width: 92%;
  text-align: center;
}
body[data-page="product"] .pp-typing {
  align-self: flex-start;
  display: inline-flex; gap: 4px;
  padding: 11px 15px; border-radius: 16px;
  background: color-mix(in srgb, #eeeef1 78%, transparent); border: 1px solid var(--line);
  opacity: 0; transition: opacity 0.3s ease;
  border-bottom-left-radius: 5px;
}
body[data-page="product"] .pp-typing.is-in { opacity: 1; }
body[data-page="product"] .pp-typing i {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--ink-3);
  animation: pp-blink 1.1s ease-in-out infinite;
}
body[data-page="product"] .pp-typing i:nth-child(2) { animation-delay: 0.16s; }
body[data-page="product"] .pp-typing i:nth-child(3) { animation-delay: 0.32s; }
@keyframes pp-blink { 0%, 60%, 100% { opacity: 0.28; } 30% { opacity: 1; } }

body[data-page="product"] .pp-adapt-basket {
  margin-top: auto;
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(248, 246, 243, 0.45), rgba(255, 255, 255, 0.9));
}
body[data-page="product"] .pp-adapt-basket .pp-basket-rows { padding: 6px 8px; }
body[data-page="product"] .pp-adapt-basket .pp-row { padding: 6px 8px; }
body[data-page="product"] .pp-adapt-basket .pp-basket-foot { background: none; }

/* ============================================================
   5 · ONE ENGINE - chips + the morphing scene
   ============================================================ */
body[data-page="product"] .pp-chips { display: flex; flex-wrap: wrap; gap: 8px; }
body[data-page="product"] .pp-chips--pick { margin-top: 28px; }
body[data-page="product"] .pp-chip {
  font-family: inherit;
  /* Chrome's UA sheet forces letter-spacing:normal on form controls, so a
     <button> chip would track wider than the home page's <li> chips */
  letter-spacing: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  padding: 8px 13px;
  border-radius: 999px;
  border: 1px solid var(--line-2);
  background: rgba(255, 255, 255, 0.6);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  cursor: pointer;
  transition: transform 0.45s var(--spring-bouncy, cubic-bezier(0.22, 1, 0.36, 1)),
    background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
}
body[data-page="product"] .pp-chip:hover { border-color: rgba(23, 23, 26, 0.34); background: rgba(255, 255, 255, 0.86); }
body[data-page="product"] .pp-chip:active { transform: scale(0.96); }
body[data-page="product"] .pp-chip[aria-selected="true"] {
  background: var(--accent); color: var(--accent-ink);
  border-color: rgba(0, 0, 0, 0.7);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 10px 24px -14px rgba(23, 23, 26, 0.5);
}
/* platform chips are labels, not controls - monochrome, no affordance */
body[data-page="product"] .pp-chip--static { cursor: default; color: var(--ink-2); }
body[data-page="product"] .pp-chip--static:hover { border-color: var(--line-2); background: rgba(255, 255, 255, 0.6); }

body[data-page="product"] .pp-vert { min-height: 448px; display: flex; flex-direction: column; }
body[data-page="product"] .pp-vert-body { padding: 16px; display: flex; flex-direction: column; gap: 14px; flex: 1; }
body[data-page="product"] .pp-vert-q {
  font-size: 14.5px; line-height: 1.45; color: var(--ink);
  margin: 0;
}
body[data-page="product"] .pp-vert-q::before,
body[data-page="product"] .pp-vert-q::after { content: "\201C"; }
body[data-page="product"] .pp-vert-q::after { content: "\201D"; }
body[data-page="product"] .pp-vert-a { font-size: 13px; color: var(--ink-2); margin: -8px 0 0; }
/* --pp-cols is written per vertical by product.js - the supplements scene
   carries three products, not four, and must not leave a hole in the row */
body[data-page="product"] .pp-vert-grid {
  display: grid; grid-template-columns: repeat(var(--pp-cols, 4), minmax(0, 1fr));
  gap: 10px;
  flex: 1; align-content: center;
}
body[data-page="product"] .pp-tile {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: color-mix(in srgb, #ffffff 72%, transparent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9);
  padding: 10px;
  display: flex; flex-direction: column; gap: 8px;
  min-width: 0;
  /* the morph: tiles leave and arrive on their own stagger (JS sets the
     per-tile delay), so switching vertical reads as a dissolve, not a cut */
  opacity: 0; transform: translateY(12px) scale(0.96);
  transition: opacity 0.42s ease,
    transform 0.6s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1));
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
body[data-page="product"] .pp-tile.is-in { opacity: 1; transform: none; }
body[data-page="product"] .pp-tile-img {
  aspect-ratio: 1; border-radius: 9px; overflow: hidden;
  background: var(--bg-raised);
  display: grid; place-items: center;
}
body[data-page="product"] .pp-tile-img img { width: 100%; height: 100%; object-fit: contain; display: block; }
body[data-page="product"] .pp-tile-name {
  font-size: 12.5px; font-weight: 600; color: var(--ink);
  line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
body[data-page="product"] .pp-tile-price { font-size: 12.5px; color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 700; }
body[data-page="product"] .pp-vert-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 0.8125rem; color: var(--ink-3);
}
body[data-page="product"] .pp-vert-foot b { color: var(--ink); font-weight: 600; font-size: 1rem; font-variant-numeric: tabular-nums; }

/* ============================================================
   6 · COMMERCE STACK - the architecture diagram
   The wires are an SVG laid UNDER the cards; product.js measures the real
   card boxes and rewrites the paths, so any reflow (resize, font swap,
   wrapped label) redraws correctly instead of drifting.
   ============================================================ */
body[data-page="product"] .pp-stack { position: relative; }
body[data-page="product"] .pp-stack-wires {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  overflow: visible;
  z-index: 0;
}
body[data-page="product"] .pp-wire {
  fill: none;
  stroke: rgba(23, 23, 26, 0.16);
  stroke-width: 1.5;
}
/* the travelling pulse - one per wire, offset so the diagram breathes rather
   than strobing in unison. The dash geometry and the motion are driven from
   product.js (Web Animations): the dash length is per-path, and only a real
   Animation object can be paused outright when the section scrolls away. */
/* the phone fan: hairline grey (Enrico, Aug 11 - 'minimize the
   thickness, make the color grey') */
body[data-page="product"] .pp-wire-stacked {
  stroke: rgba(23, 23, 26, 0.30);
  stroke-width: 1.25;
  stroke-linecap: round;
}
body[data-page="product"] .pp-pulse {
  fill: none;
  stroke: rgba(23, 23, 26, 0.62);
  stroke-width: 2;
  stroke-linecap: round;
  opacity: 0;
}

/* HUB AND SPOKE, three components either side of the core. A 2-column grid
   beside the core looked tidier but was a lie: the wires to the far column
   ran BEHIND the near cards, so half the stack read as unconnected. Fanning
   both ways gives every component its own unobstructed spoke. */
body[data-page="product"] .pp-stack-grid {
  position: relative; z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: clamp(16px, 2.4vw, 34px);
  align-items: center;
}
body[data-page="product"] .pp-stack-col {
  display: grid; gap: 10px; align-content: center;
}
/* The hub is the app icon itself. It brings its own squircle, ink field and
   glyph, so the container contributes nothing but size — no background, no
   padding, no radius, and NO box-shadow: a box-shadow would trace the
   element's square edge and betray the rounded corners. */
body[data-page="product"] .pp-stack-core {
  justify-self: center;
  width: 88px;
  padding: 0;
  background: none;
  border-radius: 0;
  box-shadow: none;
}
body[data-page="product"] .pp-stack-core img {
  width: 100%; height: auto; display: block;
  /* drop-shadow reads the PNG's alpha, so the shadow follows the squircle */
  filter: drop-shadow(0 16px 24px rgba(23, 23, 26, 0.28));
}
body[data-page="product"] .pp-stack-core span {
  font-size: 0.8125rem; font-weight: 500; letter-spacing: -0.01em;
}
/* the premium card the spec asks for instead of a bullet: white glass, a
   hairline, a specular top edge and a real lift */
body[data-page="product"] .pp-node {
  background: color-mix(in srgb, #ffffff 70%, transparent);   /* the card layer's glass */
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 12px 14px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.95), var(--pp-shadow-sm);
  display: flex; flex-direction: column; gap: 4px;
  min-width: 0;
  opacity: 0; transform: translateY(12px);
  transition: opacity 0.45s ease,
    transform 0.6s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1));
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
body[data-page="product"] .pp-node.is-in { opacity: 1; transform: none; }
body[data-page="product"] .pp-node-name {
  font-size: 0.9375rem; font-weight: 500; color: var(--ink); letter-spacing: -0.01em;
}
body[data-page="product"] .pp-node-sub { font-size: 0.75rem; color: var(--ink-3); line-height: 1.35; }

/* ============================================================
   7 · MERCHANT INTELLIGENCE - the dashboard highlights one metric at a time
   ============================================================ */
body[data-page="product"] .pp-dash { min-height: 446px; display: flex; flex-direction: column; }
body[data-page="product"] .pp-dash-grid {
  padding: 14px 16px;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  flex: 1;
}
body[data-page="product"] .pp-metric {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 11px 12px;
  background: color-mix(in srgb, #ffffff 70%, transparent);
  display: flex; flex-direction: column; gap: 6px;
  min-width: 0;
  transition: transform 0.5s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1)),
    box-shadow 0.45s ease, border-color 0.45s ease, background 0.45s ease;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
/* the lit tile: raised, ringed and opaque - the rest recede rather than dim,
   so the dashboard never looks broken */
body[data-page="product"] .pp-metric.is-lit {
  background: color-mix(in srgb, #ffffff 92%, transparent);
  border-color: rgba(23, 23, 26, 0.3);
  transform: translateY(-4px);
  box-shadow: 0 22px 44px -26px rgba(60, 30, 10, 0.5),
    inset 0 0 0 1px rgba(23, 23, 26, 0.06);
}
body[data-page="product"] .pp-metric-k {
  font-size: 10px; font-weight: 500; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-3);
  line-height: 1.3;
}
body[data-page="product"] .pp-metric-v {
  font-size: 19px; font-weight: 600; color: var(--ink);
  letter-spacing: -0.025em; font-variant-numeric: tabular-nums;
  line-height: 1;
}
body[data-page="product"] .pp-metric-d { font-size: 10.5px; color: #2b7a4d; font-weight: 500; }
/* mini bars - height is written by JS so they can draw when the tile lights */
body[data-page="product"] .pp-spark { display: flex; align-items: flex-end; gap: 3px; height: 26px; margin-top: auto; }
body[data-page="product"] .pp-spark i {
  flex: 1; border-radius: 2px 2px 0 0;
  background: rgba(23, 23, 26, 0.16);
  height: 12%;
  transition: height 0.6s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1)),
    background 0.45s ease;
}
body[data-page="product"] .pp-metric.is-lit .pp-spark i { background: rgba(23, 23, 26, 0.42); }
body[data-page="product"] .pp-dash-note {
  padding: 10px 16px;
  border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--ink-2);
  background: linear-gradient(180deg, rgba(248, 246, 243, 0.45), rgba(255, 255, 255, 0.9));
  min-height: 44px;
  display: flex; align-items: center;
  transition: opacity 0.3s ease;
}
body[data-page="product"] .pp-dash-note.is-fading { opacity: 0; }
/* the named gaps - what customers asked for and you don't stock. A number
   cannot carry that claim; the phrases can. (The backoffice's own
   knowledge-gaps component, at the shell's chip scale.) */
body[data-page="product"] .pp-gaps {
  display: flex; flex-wrap: wrap; gap: 7px;
  padding: 0 16px 14px;
}
body[data-page="product"] .pp-gaps[hidden] { display: none; }
body[data-page="product"] .pp-gap {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; color: var(--ink-2);
  padding: 5px 10px; border-radius: 999px;
  background: color-mix(in srgb, #f6e2d8 62%, transparent);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  animation: pp-gap-in 0.42s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1)) backwards;
}
body[data-page="product"] .pp-gap:nth-child(2) { animation-delay: 0.07s; }
body[data-page="product"] .pp-gap:nth-child(3) { animation-delay: 0.14s; }
body[data-page="product"] .pp-gap b { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
@keyframes pp-gap-in { from { opacity: 0; transform: translateY(6px); } }

/* ============================================================
   8 · ENTERPRISE
   ============================================================ */
body[data-page="product"] .pp-cards { display: grid; gap: clamp(14px, 1.6vw, 20px); }
body[data-page="product"] .pp-cards--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
body[data-page="product"] .pp-card {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: clamp(22px, 2.4vw, 32px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.95), var(--pp-shadow-sm);
  display: flex; flex-direction: column; gap: 10px;
}
/* = home.css .card-eyebrow, on the same enterprise cards */
body[data-page="product"] .pp-card-eyebrow {
  font-family: var(--font-mono);
  font-size: 10.5px; font-weight: 500; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--ink-3);
  margin: 0;
}
/* = home.css .enterprise-card h3, which sets only size + weight and lets
   style.css:95 supply the -0.025em tracking - so this does the same */
body[data-page="product"] .pp-card-title {
  font-size: var(--pp-cardtitle);
  font-weight: 600;
  line-height: 1.25;
  margin: 0;
  color: var(--ink);
}
body[data-page="product"] .pp-card p {
  font-size: var(--pp-copy);
  line-height: 1.5;
  color: var(--ink-2);
  margin: 0;
}
body[data-page="product"] .pp-card-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 8px;
}
body[data-page="product"] .pp-card-list li {
  font-size: var(--pp-copy); color: var(--ink-2);
  display: flex; align-items: baseline; gap: 10px;
}
body[data-page="product"] .pp-card-list li::before {
  content: ""; flex: none;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--ink-3);
  transform: translateY(-2px);
}

body[data-page="product"] .pp-platforms {
  margin-top: clamp(30px, 3.5vw, 48px);
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
}
body[data-page="product"] .pp-platforms-label {
  font-size: 0.75rem; font-weight: 500; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
  margin: 0;
}

/* ============================================================
   9 · GO LIVE - horizontal timeline
   The rule is a scaleX transform on a pseudo-element rather than a width, so
   the draw stays on the compositor.
   ============================================================ */
body[data-page="product"] .pp-timeline {
  --pp-gap: clamp(20px, 3vw, 44px);
  --pp-dot: 44px;
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--pp-gap);
  position: relative;
}
/* The rule joins the FIRST and LAST milestone centres. With the milestones
   centred in their columns those dots sit at 12.5% and 87.5%, so BOTH insets
   are half a column - (100% − 3 gaps) / 8 - and the rule's midpoint lands on
   exactly 50%. (Left-aligned dots put the midpoint at ~38%, which is what
   made the row lean.) Running it to the container edges instead would leave
   a tail hanging past "Go live". */
body[data-page="product"] .pp-timeline::before {
  content: "";
  position: absolute;
  left: calc((100% - 3 * var(--pp-gap)) / 8);
  right: calc((100% - 3 * var(--pp-gap)) / 8);
  top: calc(var(--pp-dot) / 2);
  height: 1px; background: var(--line-2);
  transform: scaleX(0); transform-origin: left;
  transition: transform 1.5s cubic-bezier(0.22, 1, 0.36, 1);
}
body[data-page="product"] .pp-timeline.is-in::before { transform: scaleX(1); }
body[data-page="product"] .pp-step {
  display: flex; flex-direction: column; gap: 18px;
  align-items: center; text-align: center;   /* dot + title centred on the column */
  position: relative;
  opacity: 0; transform: translateY(14px);
  transition: opacity 0.5s ease,
    transform 0.7s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1));
}
body[data-page="product"] .pp-timeline.is-in .pp-step { opacity: 1; transform: none; }
/* the milestones land in order, chasing the rule as it draws */
body[data-page="product"] .pp-timeline.is-in .pp-step:nth-child(1) { transition-delay: 0.12s; }
body[data-page="product"] .pp-timeline.is-in .pp-step:nth-child(2) { transition-delay: 0.42s; }
body[data-page="product"] .pp-timeline.is-in .pp-step:nth-child(3) { transition-delay: 0.72s; }
body[data-page="product"] .pp-timeline.is-in .pp-step:nth-child(4) { transition-delay: 1.02s; }
body[data-page="product"] .pp-step-n {
  width: var(--pp-dot); height: var(--pp-dot); border-radius: 50%;
  display: grid; place-items: center;
  background: var(--bg-card);
  border: 1px solid var(--line-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.95), var(--pp-shadow-sm);
  font-size: 1rem; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;
  position: relative; z-index: 1;
}
body[data-page="product"] .pp-step-t {
  font-size: var(--pp-cardtitle);
  font-weight: 600;
  line-height: 1.25;
  margin: 0;
  color: var(--ink);
  max-width: 16ch;
}

/* ============================================================
   10 · FINAL CTA - full-width white, centered, lots of air
   ============================================================ */
body[data-page="product"] .pp-final {
  background: #ffffff;
  border-top: 1px solid var(--line);
  padding-block: clamp(90px, 12vw, 176px);
  text-align: center;
}
body[data-page="product"] .pp-final-col {
  display: flex; flex-direction: column; align-items: center;
  max-width: 640px;   /* = home .after-film-col (560) + room for the wider CTA */
}
/* the closing head takes home's .after-film h2 - a step up from a section
   heading, exactly as the home page's own closing section does */
body[data-page="product"] .pp-final .pp-h2 {
  font-size: var(--pp-h2-final);
  letter-spacing: -0.028em;
  line-height: 1.04;
  margin-bottom: 20px;
  max-width: 18ch;
}
body[data-page="product"] .pp-final .pp-lede { max-width: 52ch; margin-bottom: 32px; }

/* ============================================================
   REVEAL - the page's own one-shot entrance (app.js's reveal selector is
   written for the old .band/.benefit markup and never matches pp-*)
   ============================================================ */
/* Copy ASSEMBLES rather than appearing: --pp-d (written by product.js) is
   baked into the shorthand so eyebrow, heading and body arrive in reading
   order. Opacity keeps a plain ease and finishes early - a spring on opacity
   makes text wobble in brightness. */
body[data-page="product"] .pp-reveal {
  opacity: 0; transform: translateY(20px);
  transition: opacity 0.5s ease var(--pp-d, 0s),
    transform 0.85s var(--spring-soft, cubic-bezier(0.22, 1, 0.36, 1)) var(--pp-d, 0s);
  will-change: opacity, transform;
}
body[data-page="product"] .pp-reveal.is-in { opacity: 1; transform: none; will-change: auto; }

/* ============================================================
   RESPONSIVE - the split collapses to one column; the visual keeps its
   proportions but stops trying to be 60% of anything.
   ============================================================ */
@media (max-width: 1080px) {
  body[data-page="product"] .pp-hero-grid,
  body[data-page="product"] .pp-split {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(28px, 5vw, 48px);
  }
  body[data-page="product"] .pp-hero-grid { min-height: 0; }
  body[data-page="product"] .pp-copy .pp-body,
  body[data-page="product"] .pp-hero-copy .pp-lede { max-width: 60ch; }
  body[data-page="product"] .pp-cards--three { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body[data-page="product"] .pp-timeline { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 36px; }
  /* two rows of milestones - a single rule can no longer join them */
  body[data-page="product"] .pp-timeline::before { display: none; }
}

@media (max-width: 760px) {
  body[data-page="product"] { --pp-band: 64px; }
  body[data-page="product"] .pp-fields { grid-template-columns: minmax(0, 1fr); }
  body[data-page="product"] .pp-vert-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body[data-page="product"] .pp-dash-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body[data-page="product"] .pp-cards--three { grid-template-columns: minmax(0, 1fr); }
  /* single column and no rule to centre on - the milestones fall back into
     the page's left-aligned reading column like everything else */
  /* TIMELINE, phones (Enrico, Aug 11): a real vertical timeline instead of
     four dot-over-title blocks. Dots hold the left rail, titles sit beside
     them, and the joining rule - hidden since 1080px because it was drawn
     horizontal - returns VERTICAL, drawing top-down through the dot centres
     as the milestones land. The dots paint over it (bg-card fill, z:1). */
  body[data-page="product"] .pp-timeline { grid-template-columns: minmax(0, 1fr); row-gap: 20px; --pp-dot: 40px; }
  body[data-page="product"] .pp-timeline::before {
    display: block;
    left: calc(var(--pp-dot) / 2 - 0.5px); right: auto; width: 1px;
    top: calc(var(--pp-dot) / 2); bottom: calc(var(--pp-dot) / 2); height: auto;
    transform: scaleY(0); transform-origin: top;
  }
  body[data-page="product"] .pp-timeline.is-in::before { transform: scaleY(1); }
  body[data-page="product"] .pp-step { flex-direction: row; align-items: center; gap: 14px; text-align: left; }
  body[data-page="product"] .pp-step-t { max-width: none; }
  body[data-page="product"] .pp-stack-grid { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  /* the wires STAY on phones (Enrico's mock, Aug 11): product.js draws
     the stacked fan - one ink trunk through the hub, a measured bezier
     sweeping into each card - with the same measure-and-redraw system
     as the desktop diagram */
  /* THE TREE, take 2 (Enrico, Aug 11): the hub icon rides the left rail
     VERTICALLY CENTRED on the six cards - the desktop hub's posture, kept
     in a column - and the trunk runs from the first card's centre to the
     last card's, passing BEHIND the icon (the icon paints over it, like
     the timeline dots over their rule). Take 1 hung the icon on top and
     drew alpha strokes per segment; the compositing boundaries read as a
     DASHED line on a real phone. Everything here is solid-filled: trunk
     segments butt at card edges at the same x with the same solid colour,
     so no seam can show, and the branches are straight solid bars.
     Geometry: 44px rail + 12px column gap puts card edges 56px in; the
     trunk sits at the rail's centre, x=22, so branches span 34px. */
  body[data-page="product"] .pp-stack-grid {
    /* take 4 (Enrico, Aug 11): fit-content cards left every card a
       different width and the block hugging the left edge. ONE width for
       all six - the column is max-content, so the longest line sizes it
       and every card stretches to match, right edges aligned - and the
       whole diagram (rail + cards) centres itself in the viewport. */
    /* the cards end just past the longest line - '...the moment of
       asking' - as the figure has it: the column is max-content, every
       card stretches to that one width, and the whole diagram centres
       itself with even margins (Enrico, Aug 11) */
    grid-template-columns: 54px minmax(0, max-content);
    grid-template-rows: repeat(6, auto);   /* one row per STACK_NODES entry */
    column-gap: 12px; row-gap: 12px;
    align-items: stretch;
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
  }
  body[data-page="product"] .pp-stack-col { display: contents; }
  body[data-page="product"] .pp-stack-core {
    order: 0; grid-column: 1; grid-row: 1 / -1;
    align-self: center; justify-self: center;   /* centred in the rail = on the trunk */
    width: 40px; padding: 0;
    position: relative; z-index: 1;   /* over the trunk */
  }
  /* take 3 (Enrico, Aug 11, red-marker pass): the cards shrink-wrap
     their sentences instead of dragging empty glass to the screen edge,
     the branches are CURVES that peel off the trunk toward the hub -
     no straight horizontal stubs - and the entrance loses its slide:
     the marked-up screenshot flagged the leftover motion, so the cards
     only fade. Cards above the hub bend down toward it (border-top +
     top-left radius: horizontal arm at the card's centre, vertical arm
     dropping to the trunk); cards below mirror it. */
  body[data-page="product"] .pp-node {
    grid-column: 2; position: relative; padding: 14px 16px;
    /* the items animate in one by one again (Enrico, Aug 11): the base
       rule's rise-and-fade returns, staggered by product.js */
    /* take 7 = Enrico's final mock verbatim (Aug 11): NO outlines - the
       soft borderless glass card - the ink lives only in the connectors
       (product.js's measured SVG fan) */
    border: 0;
    border-radius: 14px;
  }
  body[data-page="product"] .pp-row { grid-template-columns: 32px minmax(0, 1fr) auto; }
  body[data-page="product"] .pp-row-qty { display: none; }
  body[data-page="product"] .pp-hero-visual .gx-screen { height: 420px; }
  body[data-page="product"] .pp-intent,
  body[data-page="product"] .pp-basket,
  body[data-page="product"] .pp-adapt,
  body[data-page="product"] .pp-vert,
  body[data-page="product"] .pp-dash { min-height: 0; }
}

/* ============================================================
   REDUCED MOTION - every scene renders its finished state and stays there
   (product.js also stops scheduling loops; this covers the CSS half)
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  body[data-page="product"] .pp-field,
  body[data-page="product"] .pp-row,
  body[data-page="product"] .pp-bub,
  body[data-page="product"] .pp-tile,
  body[data-page="product"] .pp-node,
  body[data-page="product"] .pp-step,
  body[data-page="product"] .pp-reveal,
  body[data-page="product"] .pp-basket-cta {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  body[data-page="product"] .pp-timeline::before { transform: scaleX(1); transition: none; }
  body[data-page="product"] .pp-utter.is-typing::after { display: none; }
  body[data-page="product"] .pp-typing i { animation: none; }
}

/* ============================================================
   WHITE CARDS CARRY NO OUTLINE (Enrico, site-wide - see home.css)

   Same rule as the rest of the site: content surfaces separate by elevation,
   not by a drawn edge. Two families here, needing different answers:

     · surfaces sitting on the PAGE (panels, enterprise cards, stack nodes)
       already carry a real drop shadow, so the border simply goes;
     · surfaces nested INSIDE a panel (intent fields, vertical tiles, metric
       tiles) cannot use a shadow - a shadow inside a shadowed panel reads as
       mud - so they separate by a whisper of ink fill instead.

   Controls keep their edge: .pp-chip, .pp-row-qty and the .pp-step-n
   timeline markers stay outlined - an interactive or marker surface with no
   edge has no affordance.

   THIS BLOCK MUST STAY LAST. Every selector here has the same specificity as
   the base rule it overrides, so it only wins by source order.
   ============================================================ */
body[data-page="product"] .pp-panel,
body[data-page="product"] .pp-card,
body[data-page="product"] .pp-node { border-color: transparent; }

body[data-page="product"] .pp-field,
body[data-page="product"] .pp-tile,
body[data-page="product"] .pp-metric {
  border-color: transparent;
  background: rgba(23, 23, 26, 0.035);
}
/* the lit metric still has to lift clear of its neighbours */
body[data-page="product"] .pp-metric.is-lit {
  background: #ffffff;
  border-color: transparent;
}
