/* ============================================================================
   PG "Unlock Your Reward" portal — app.css
   Owner: agent C (front-end). Files: templates/index.html, static/app.css,
   static/journey.js. Never edit reveal.js or strings.json from here.
   ============================================================================

   THE :root BLOCK BELOW IS GENERATED. Do not hand-edit it, and do not invent a
   token name to match it — the generator is the authority on what exists:

       python3 tools/design_tokens.py --css --tokens tools/config/pg_tokens.json

   Paste that output over everything between the BEGIN/END markers when the
   token file changes. Nothing else in this file holds a colour literal.

   ⚠️ THIS FILE ALREADY SHIPPED 89 DEAD var() REFERENCES ONCE (2026-08-27).
   The names came from the plan's token table, which had drifted from what the
   generator emits — --pg-radius-card vs the real --pg-r-card, --pg-weight-bold
   vs --pg-font-weight-bold, and twenty more. A dead var() does not error and
   does not fall back to anything sane: it resolves to the INHERITED value, so
   the page looked nearly right and every radius, weight and tap target was
   silently wrong. Before committing any edit here, this must print nothing:

       python3 tools/design_tokens.py --css --tokens tools/config/pg_tokens.json \
         | grep -oE '^\s+--pg-[a-z0-9-]+' | tr -d ' ' | sort -u > /tmp/real.txt
       grep -oE 'var\(--pg-[a-z0-9-]+' tools/web/pg_ugc/static/app.css \
         | sed 's/var(//' | sort -u | comm -23 - /tmp/real.txt

   reveal.js reads these same --pg-* names off :root. If this block is missing
   or renamed, agent F's tins render unstyled.

   ── 2026-08-27, RESTYLE AGAINST THE REAL BRAND ──────────────────────────────
   Chris: "the aesthetic you replicated is really not a good match. Their
   website looks a lot higher quality as compared to what you gave me."

   What was wrong was not taste, it was the source. v1 of the token file read
   the Shopify theme's --color-base-* custom properties and a raw hex-frequency
   count, and landed on white/blue/yellow. The most frequent hex on PG's PDP is
   #F8F8F8 at 80 occurrences and every one of them belongs to the Kaching
   Bundles app widget — a vendor's card grey shipped as PG's page.

   PG's real design language lives in the twelve `#pg-*` sections their own
   designer hand-wrote, and in the product photography. It is:

     · a WARM CREAM ground (#FBF6F2 is 41-55% of every background region
       sampled out of the ingredients panel; their own CSS ships #F7F5EF ->
       #F2EFE8, #ECE8DF, #F4EADE, #E0DBD3),
     · deep navy ink (#022044 wordmark, #272F47 on nearly every heading and
       paragraph they set themselves),
     · near-white translucent cards carrying TWO shadow layers — an ambient
       drop cast in ink-muted rgba(39,47,71,0.05-0.10) AND an
       `inset 0 1px 0 rgba(255,255,255,0.72-0.86)` rim light — plus a hairline
       gradient topline and a radial corner glow,
     · headings on NEGATIVE tracking (-0.03em to -0.05em) at line-heights of
       0.96-1.08. The theme chrome declares +0.06rem and 1.23; v1 shipped that.

   Every rule below is written against those. Nothing here is retuned to taste:
   if a number is not in the token file it is not in this file either, and the
   token file records where each one was measured.
   ========================================================================= */

/* ── BEGIN GENERATED BLOCK ───────────────────────────────────────────────── */
/* GENERATED by tools/design_tokens.py --css. Do not hand-edit. */
/* ⚠️ THE GENERATED :root BLOCK USED TO BE INLINED HERE. It is not any more.
   There were two generated copies — this one and static/tokens.css — and a second copy
   is a second thing to forget. tokens.css went stale twice on 2026-08-27: once as a
   pasted copy in the dashboard (25 unknown hexes, commit gate red) and once 90 seconds
   after regeneration when a token was added.
   The ONE home is static/tokens.css, regenerated by deploy.sh on every deploy and
   <link>ed from index.html BEFORE this file. Do not paste it back. */
/* ── END GENERATED BLOCK ─────────────────────────────────────────────────── */

/* Derived locals. Deliberately NOT --pg-* prefixed: that namespace belongs to
   the generator, and keeping these out of it means the dead-var gate above can
   be run with zero allowed exceptions. Every value resolves through a real
   --pg-* token or is a plain length (lengths are not gated; only hexes are). */
:root {
  --c-pad: var(--pg-gutter);
  --c-column: var(--pg-reading-width);   /* PG's own measure for a prose column */
  --c-stack: 16px;                        /* the rhythm inside a screen */
  --c-cardgap: 14px;                      /* the rhythm inside a card */
}

/* ==========================================================================
   RESET
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; padding: 0; min-height: 100%; }
html { -webkit-text-size-adjust: 100%; font-size: 100%; }

body {
  font-family: var(--pg-font-family);
  font-weight: var(--pg-font-weight-body);
  color: var(--pg-ink-muted);
  /* THE PAGE IS CREAM. A cream ground under near-white cards is the single
     biggest reason PG's own pages read as considered rather than default —
     a white card on a white page has nothing to sit on.
     ⚠️ NOT `background-attachment: fixed`. That paints the gradient at
     viewport height and leaves the rest of a long document on the flat base
     colour, which lands as a hard seam partway down the page (caught in
     ds_shot-1440-09-reward.png). Let it stretch to the document instead. */
  background: var(--pg-surface);
  background-image: var(--pg-page-grad);
  letter-spacing: var(--pg-font-tracking-body);
  line-height: var(--pg-font-lh-body);
  overflow-x: hidden;
  padding-bottom: env(safe-area-inset-bottom, 0);
  -webkit-font-smoothing: antialiased;
}

img, video { max-width: 100%; height: auto; display: block; }
button, input, textarea, select { font: inherit; color: inherit; }
h1, h2, h3, p, ul, ol, figure, figcaption { margin: 0; }
ul, ol { padding: 0; list-style: none; }

/* ==========================================================================
   A11Y PRIMITIVES
   ========================================================================== */
.pg-sr {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

:focus-visible {
  outline: var(--pg-border-focus) solid var(--pg-focus-ring);
  outline-offset: 3px;
  border-radius: var(--pg-r-button);
}
:focus:not(:focus-visible) { outline: none; }

/* show() moves focus to each screen's <h1> so the change is announced. That is
   a programmatic focus target, not a control, and Chrome paints :focus-visible
   on it — a box drawn around every heading. Suppress the ring here ONLY; every
   real control keeps its own. */
h1[tabindex="-1"]:focus, h1[tabindex="-1"]:focus-visible { outline: none; }

/* Every interactive control clears the 44px floor. */
button, .pg-tap, .pg-chip, .pg-answer, .pg-filelabel {
  min-height: var(--pg-touch-target-min);
  min-width: var(--pg-touch-target-min);
}

/* ==========================================================================
   THE SURFACE RECIPE
   Every raised thing in this portal is the SAME four layers, because every
   raised thing on pupganics.com is. Transcribed from pg-mechanism-split-v2
   __feature, pg-scoop-v2__card, pg-secondary-benefits-v6__item,
   pg-proof-v2__card and pg-guarantee-v5__inner — five different sections,
   one recipe:
       1  a white gradient 0.94 -> 0.78, so the card is translucent over cream
       2  a 1px rgba(39,47,71,0.08) edge — ink-muted, never grey, never black
       3  shadow-card: a wide ambient drop AND an inset 1px white rim light
       4  a hairline gradient topline inset from the corners, plus a radial
          corner glow bleeding off the bottom-right
   Layers 3 and 4 are the ones v1 had none of, and they are the whole
   difference between "a div with a border-radius" and PG's page.
   ========================================================================== */
.pg-surface {
  position: relative;
  overflow: hidden;
  background: var(--pg-card-grad);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  border-radius: var(--pg-r-card-sm);
  box-shadow: var(--pg-shadow-card);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.pg-surface::before {
  content: "";
  position: absolute;
  top: 0; left: 16px; right: 16px;
  height: 1px;
  background: var(--pg-topline);
  pointer-events: none;
}

.pg-surface::after {
  content: "";
  position: absolute;
  right: -34px; bottom: -36px;
  width: 120px; height: 120px;
  border-radius: var(--pg-r-pill);
  background: var(--pg-glow);
  pointer-events: none;
}

/* ==========================================================================
   SHELL
   ========================================================================== */
.pg-shell {
  width: 100%;
  max-width: var(--pg-page-width);
  margin: 0 auto;
  padding: 0 var(--c-pad);
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

/* ⚠️ WRAPS. At 390px the row is brand + "Checkpoint 3" + a two-word language
   toggle, and it does not fit — the first cut of this restyle let the toggle
   shrink below its content and "Español" ran off the right edge of the phone
   (ds_shot-390-01-play.png). Nothing in here may shrink; the row breaks
   instead. */
.pg-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  padding: var(--pg-section-y-mobile) 0 12px;
}

.pg-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  flex: 0 0 auto;
  font-weight: var(--pg-font-weight-bold);
  /* An uppercase micro-label is the ONE place the heading's negative tracking
     is wrong. 0.12em is what PG set on their own uppercase label
     (pg-routine-v3__eyebrow, 11px/600); the 0.18em eyebrow is for the
     terracotta section kicker, and it is too wide for a wordmark on a phone. */
  letter-spacing: var(--pg-font-tracking-label);
  text-transform: uppercase;
  font-size: var(--pg-font-size-micro);
  color: var(--pg-ink);
}

/* The amber chew, at 10px. PG's product is a golden chew in an amber jar; a
   yellow dot was the theme's outline-button swatch and appears nowhere. */
.pg-brand__dot {
  width: 10px; height: 10px;
  border-radius: var(--pg-r-pill);
  background: var(--pg-amber);
  box-shadow: 0 0 0 3px var(--pg-amber-soft), var(--pg-shadow-rim);
}

/* "Checkpoint 3" — the series IS the product, so it sits on the chrome. */
.pg-badge {
  flex: 0 0 auto;
  padding: 6px 11px;
  border-radius: var(--pg-r-pill);
  background: var(--pg-canvas);
  background-image: var(--pg-card-grad);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-xs);
  color: var(--pg-ink-muted);
  font-size: var(--pg-font-size-eyebrow);
  font-weight: var(--pg-font-weight-bold);
  letter-spacing: var(--pg-font-tracking-label);
  line-height: 1.6;
  text-transform: uppercase;
  white-space: nowrap;          /* "Checkpoint 3" wrapped to two lines at 390px */
}
.pg-badge[hidden] { display: none !important; }

/* Language toggle — two real buttons, never a select. A true pill, which is
   what every segmented control on PG's PDP is (.pg-vr-arrow, .pg-rv-dot). */
.pg-lang {
  display: inline-flex;
  flex: 0 0 auto;               /* never shrink — see .pg-topbar */
  margin-left: auto;
  border: var(--pg-border-hairline) solid var(--pg-edge);
  border-radius: var(--pg-r-pill);
  background: var(--pg-canvas);
  background-image: var(--pg-card-grad);
  box-shadow: var(--pg-shadow-xs);
  overflow: hidden;
  padding: 3px;
  gap: 2px;
}

.pg-lang button {
  /* 44px, not the 36px a grouped control tempts you into — the spec's tap
     floor has no exceptions, and these two were the only controls under it. */
  flex: 0 0 auto;
  white-space: nowrap;
  min-height: var(--pg-touch-target-min);
  min-width: var(--pg-touch-target-min);
  padding: 0 14px;
  border: 0;
  border-radius: var(--pg-r-pill);
  background: transparent;
  color: var(--pg-ink-subtle);
  font-size: var(--pg-font-size-micro);
  font-weight: var(--pg-font-weight-bold);
  letter-spacing: var(--pg-font-tracking-pill);
  cursor: pointer;
  transition: background-color var(--pg-dur-fast) var(--pg-ease-out),
              color var(--pg-dur-fast) var(--pg-ease-out);
}

.pg-lang button[aria-pressed="true"] {
  background: var(--pg-accent);
  color: var(--pg-accent-ink);
  box-shadow: var(--pg-shadow-rim);
}

.pg-main {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  max-width: var(--c-column);
  margin: 0 auto;
  padding: 8px 0 116px;          /* clears the sticky reward bar */
}

/* The stage is LAST in the DOM so the play step reads greeting-then-tins.
   On the upload and reward steps the prize card has to lead instead, so
   journey.js adds .stage-first and order does the move. One element, two
   positions, and reveal.js is never re-mounted. */
#pg-stage { order: 2; }
/* the scroll anchor for a return visit (see show() in journey.js) */
#pg-checkin-headline { scroll-margin-top: 12px; }
/* -1, not 0: the screens are flex children too and default to order 0, so an
   equal value leaves the stage where the DOM put it — last. */
.pg-main.stage-first #pg-stage { order: -1; }

.pg-foot {
  padding: var(--pg-section-y-mobile) 0 24px;
  text-align: center;
  color: var(--pg-ink-subtle);
  font-size: var(--pg-font-size-micro);
}

/* ==========================================================================
   THE REVEAL STAGE — reveal.js (agent F) owns #pg-reveal entirely.
   It is a PERSISTENT element, not part of any one screen: the same component
   shows three closed tins on the play step, the blurred prize on the upload
   step, and the unblurred prize on the reward step. Moving it between screens
   would tear down its DOM and lose the animation state.
   ========================================================================== */
.pg-stage { margin-bottom: var(--c-stack); }
.pg-stage[hidden] { display: none !important; }

/* ==========================================================================
   SCREENS — exactly one visible; [hidden] is the single source of truth
   ========================================================================== */
.pg-screen[hidden] { display: none !important; }

.pg-screen {
  display: flex;
  flex-direction: column;
  gap: var(--c-stack);
  animation: pg-rise var(--pg-dur-base) var(--pg-ease-out) both;
}

.pg-stack { display: flex; flex-direction: column; gap: var(--c-stack); }

@keyframes pg-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

.pg-stagger > * { animation: pg-rise var(--pg-dur-base) var(--pg-ease-out) both; }
.pg-stagger > *:nth-child(1) { animation-delay: 0ms; }
.pg-stagger > *:nth-child(2) { animation-delay: var(--pg-dur-stagger); }
.pg-stagger > *:nth-child(3) { animation-delay: calc(var(--pg-dur-stagger) * 2); }
.pg-stagger > *:nth-child(4) { animation-delay: calc(var(--pg-dur-stagger) * 3); }
.pg-stagger > *:nth-child(5) { animation-delay: calc(var(--pg-dur-stagger) * 4); }

/* ==========================================================================
   TYPE — PG's own ramp, not the theme's.
   Their sections set headings at -0.03em to -0.05em with line-heights of
   0.96-1.08 and weight 600. The theme chrome declares +0.06rem tracking and a
   1.23 line-height; v1 shipped that on every heading here, and airy positive
   tracking on a 20px heading is precisely what reads as "template".
   ========================================================================== */
.pg-h1 {
  font-size: var(--pg-font-size-h1);
  font-weight: var(--pg-font-weight-heading);
  letter-spacing: var(--pg-font-tracking-h1);
  line-height: var(--pg-font-lh-h1);
  color: var(--pg-ink);
  text-wrap: balance;
}

.pg-h2 {
  font-size: var(--pg-font-size-h2);
  font-weight: var(--pg-font-weight-heading);
  letter-spacing: var(--pg-font-tracking-h2);
  line-height: var(--pg-font-lh-h2);
  color: var(--pg-ink);
  text-wrap: balance;
}

.pg-lede {
  font-size: var(--pg-font-size-lg);
  line-height: var(--pg-font-lh-snug);
  color: var(--pg-ink-muted);
  text-wrap: pretty;
}

.pg-note {
  font-size: var(--pg-font-size-xs);
  line-height: var(--pg-font-lh-snug);
  color: var(--pg-ink-subtle);
}

/* PG put a terracotta eyebrow above five of their twelve sections, at 10px /
   700 / +0.18em uppercase. It is the only warm-red in their system and it is
   the thing that stops a page of navy-on-cream going monotone.

   ⚠️ 0.18em (--pg-font-tracking-eyebrow) IS THIS ELEMENT AND NOTHING ELSE.
   Every other uppercase micro-label here — .pg-brand, .pg-badge, .pg-label,
   .pg-compare__tag, .pg-reference__badge — uses --pg-font-tracking-label
   (0.12em), which is what PG set on their own dense label
   (pg-routine-v3__eyebrow, 11px/600). 0.18em on a form label or a wordmark
   is wide enough to read as broken on a 390px phone. */
.pg-eyebrow {
  font-size: var(--pg-font-size-eyebrow);
  font-weight: var(--pg-font-weight-bold);
  letter-spacing: var(--pg-font-tracking-eyebrow);
  line-height: 1;
  text-transform: uppercase;
  color: var(--pg-attention);
}

/* ==========================================================================
   CARD / BUTTONS
   ========================================================================== */
.pg-card {
  position: relative;
  overflow: hidden;
  background: var(--pg-card-grad);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  border-radius: var(--pg-r-card-sm);
  box-shadow: var(--pg-shadow-card);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  padding: var(--pg-card-pad-mobile);
  display: flex;
  flex-direction: column;
  gap: var(--c-cardgap);
}

/* The topline and the corner glow. Absolutely positioned, so they are out of
   flex flow and cannot become phantom children. */
.pg-card::before {
  content: "";
  position: absolute;
  top: 0; left: 16px; right: 16px;
  height: 1px;
  background: var(--pg-topline);
  pointer-events: none;
}

.pg-card::after {
  content: "";
  position: absolute;
  right: -34px; bottom: -36px;
  width: 120px; height: 120px;
  border-radius: var(--pg-r-pill);
  background: var(--pg-glow);
  pointer-events: none;
}

/* A well, not a card: recessed into the cream rather than raised off it. */
.pg-card--flat {
  background: var(--pg-wash);
  border-color: var(--pg-edge);
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.pg-card--flat::before, .pg-card--flat::after { display: none; }

.pg-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 14px 22px;
  border-radius: var(--pg-r-button);
  border: var(--pg-border-button) solid transparent;
  font-weight: var(--pg-font-weight-bold);
  font-size: var(--pg-font-size-lg);
  letter-spacing: var(--pg-font-tracking-body);
  line-height: var(--pg-font-lh-flat);
  cursor: pointer;
  text-decoration: none;
  transition: transform var(--pg-dur-fast) var(--pg-ease-out),
              box-shadow var(--pg-dur-quick) var(--pg-ease-out),
              background-color var(--pg-dur-fast) var(--pg-ease-out),
              border-color var(--pg-dur-fast) var(--pg-ease-out),
              color var(--pg-dur-fast) var(--pg-ease-out);
}

.pg-btn:active { transform: translateY(1px); }
.pg-btn[hidden] { display: none !important; }

/* Navy fill, white label — what PG's own Add-to-Cart is (--color-button is
   literally 2,32,68). The rim light is the same inset white PG put on every
   raised surface; without it a flat navy rectangle reads as a form control. */
.pg-btn--primary {
  background: var(--pg-accent);
  color: var(--pg-accent-ink);
  border-color: var(--pg-accent);
  box-shadow: var(--pg-shadow-xs);
}
.pg-btn--primary:hover {
  background: var(--pg-accent-hover);
  border-color: var(--pg-accent-hover);
  box-shadow: var(--pg-shadow-lift);
  transform: translateY(-1px);
}
.pg-btn--primary:active {
  background: var(--pg-accent-active);
  border-color: var(--pg-accent-active);
  box-shadow: var(--pg-shadow-xs);
  transform: translateY(1px);
}

.pg-btn--secondary {
  background: var(--pg-card-grad);
  color: var(--pg-accent);
  border-color: var(--pg-accent-border);
  box-shadow: var(--pg-shadow-xs);
}
.pg-btn--secondary:hover { background: var(--pg-surface-hover); border-color: var(--pg-accent); }
.pg-btn--secondary:active { background: var(--pg-surface-active); }

.pg-btn--ghost {
  background: transparent;
  color: var(--pg-ink-subtle);
  border-color: transparent;
  box-shadow: none;
  font-weight: var(--pg-font-weight-body);
  font-size: var(--pg-font-size-sm);
  width: auto;
  align-self: center;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}
.pg-btn--ghost:hover { color: var(--pg-ink-muted); }

.pg-btn[disabled], .pg-btn[aria-disabled="true"] {
  background: var(--pg-disabled-bg);
  border-color: var(--pg-disabled-bg);
  color: var(--pg-disabled-fg);
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
  pointer-events: none;
}

/* ==========================================================================
   CONSENT — one line of plain text above the upload control. NOT a checkbox.
   The rendered key's id is stamped onto submission.consent_version.
   ========================================================================== */
.pg-consent {
  font-size: var(--pg-font-size-xs);
  line-height: var(--pg-font-lh-snug);
  color: var(--pg-ink-subtle);
  padding: 12px 14px;
  border-left: var(--pg-border-focus) solid var(--pg-amber-border);
  background: var(--pg-amber-soft);
  border-radius: 0 var(--pg-r-badge) var(--pg-r-badge) 0;
}

/* ==========================================================================
   REFERENCE SHOT — the previous checkpoint, on a return visit.
   ⚠️ LARGE, and directly above the capture buttons. A file input cannot
   overlay the live viewfinder, so the second before the tap is the only
   framing aid this build gets. Never collapse it behind a tap.
   ========================================================================== */
.pg-reference {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
  border-radius: var(--pg-r-popup);
  background: var(--pg-card-grad-soft);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-popup);
}

/* PG frame their hero media the same way: an outer panel at 30px radius with
   10px of padding, and an inner 1px white rim drawn INSIDE that padding
   (pg-secondary-benefits-v6__media-frame). It is why their images look
   mounted rather than dropped in. */
.pg-reference::before {
  content: "";
  position: absolute;
  inset: 10px;
  border-radius: var(--pg-r-picker);
  border: var(--pg-border-hairline) solid var(--pg-edge-soft);
  pointer-events: none;
  z-index: 2;
}

.pg-reference[hidden] { display: none !important; }

.pg-reference__cap {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0 6px 4px;
}

.pg-reference__img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--pg-r-picker);
  background: var(--pg-surface-deep);
}

.pg-reference__img--sm { aspect-ratio: 16 / 9; max-height: 200px; }
.pg-reference__img[hidden] { display: none !important; }

.pg-reference__badge {
  position: absolute;
  top: 20px; left: 20px;
  z-index: 3;
  padding: 5px 11px;
  border-radius: var(--pg-r-pill);
  background: var(--pg-canvas);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-float);
  color: var(--pg-ink-muted);
  font-size: var(--pg-font-size-eyebrow);
  font-weight: var(--pg-font-weight-bold);
  letter-spacing: var(--pg-font-tracking-label);
  line-height: 1.6;
  text-transform: uppercase;
}

/* ==========================================================================
   FRAMING — first visit only, its own step. The highest-leverage copy on the
   whole page: checkpoint 1 sets the frame for the entire series.
   ========================================================================== */
.pg-tips { display: flex; flex-direction: column; gap: 11px; }

.pg-tips li {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  font-size: var(--pg-font-size-body);
  line-height: var(--pg-font-lh-snug);
  color: var(--pg-ink-muted);
}

.pg-tips li span[aria-hidden] { flex: 0 0 auto; }

/* An amber well, not a yellow alert. This is a framing tip, and PG's own
   emphasis surface is #F4EADE (pg-serving-standout__icon,
   pg-guarantee-v5__media) — warm cream, ink-muted edge, the xs shadow. */
.pg-callout {
  padding: 14px 16px;
  border-radius: var(--pg-r-card-sm);
  background: var(--pg-amber-soft);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-xs);
  color: var(--pg-ink);
  font-weight: var(--pg-font-weight-heading);
  font-size: var(--pg-font-size-sm);
  line-height: var(--pg-font-lh-snug);
  letter-spacing: var(--pg-font-tracking-body);
}

/* The three-step "here's what happens" list on the play screen. */
.pg-steps { display: flex; flex-direction: column; gap: 10px; }

.pg-steps li {
  display: flex;
  align-items: center;
  gap: 11px;
  font-size: var(--pg-font-size-sm);
  line-height: var(--pg-font-lh-snug);
  color: var(--pg-ink-muted);
}

/* The numeral disc, copied off pg-serving-standout__icon: amber well, 1px
   ink-muted edge, xs shadow with its inset rim. */
.pg-steps li b {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  display: grid;
  place-items: center;
  border-radius: var(--pg-r-pill);
  background: var(--pg-amber-soft);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-xs);
  color: var(--pg-amber-deep);
  font-size: var(--pg-font-size-micro);
  font-weight: var(--pg-font-weight-bold);
  line-height: 1;
}

/* ==========================================================================
   UPLOAD CONTROLS
   ========================================================================== */
.pg-slot { display: flex; flex-direction: column; gap: 12px; }
.pg-slot__head { display: flex; flex-direction: column; gap: 4px; }

/* The real inputs are visually hidden but focusable — the <label> is the
   target, so the 44px floor and the focus ring both live on the label. */
.pg-file { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }

.pg-filelabel {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: var(--pg-touch-target-min);
  padding: 14px 22px;
  border-radius: var(--pg-r-button);
  border: var(--pg-border-button) solid transparent;
  font-weight: var(--pg-font-weight-bold);
  font-size: var(--pg-font-size-lg);
  letter-spacing: var(--pg-font-tracking-body);
  line-height: var(--pg-font-lh-flat);
  cursor: pointer;
  text-align: center;
  transition: background-color var(--pg-dur-fast) var(--pg-ease-out),
              border-color var(--pg-dur-fast) var(--pg-ease-out),
              box-shadow var(--pg-dur-quick) var(--pg-ease-out),
              transform var(--pg-dur-fast) var(--pg-ease-out);
}

.pg-filelabel--primary {
  background: var(--pg-accent);
  color: var(--pg-accent-ink);
  border-color: var(--pg-accent);
  box-shadow: var(--pg-shadow-xs);
}
.pg-filelabel--primary:hover {
  background: var(--pg-accent-hover);
  border-color: var(--pg-accent-hover);
  box-shadow: var(--pg-shadow-lift);
  transform: translateY(-1px);
}

.pg-filelabel--secondary {
  background: var(--pg-card-grad);
  color: var(--pg-accent);
  border-color: var(--pg-accent-border);
  box-shadow: var(--pg-shadow-xs);
}
.pg-filelabel--secondary:hover { background: var(--pg-surface-hover); border-color: var(--pg-accent); }

/* The focus ring has to follow the hidden input onto its label. */
.pg-file:focus-visible + .pg-filelabel {
  outline: var(--pg-border-focus) solid var(--pg-focus-ring);
  outline-offset: 3px;
}

.pg-dropwords { display: none; flex-direction: column; gap: 4px; }
.pg-dropwords small {
  font-weight: var(--pg-font-weight-body);
  font-size: var(--pg-font-size-xs);
  color: var(--pg-ink-subtle);
}

.pg-filelabel.is-dragover {
  background: var(--pg-accent-soft);
  border-color: var(--pg-accent);
  border-style: solid;
  box-shadow: var(--pg-shadow-lift);
}

/* Per-slot transfer status. Informational only — it NEVER gates the next screen. */
.pg-xfer {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--pg-font-size-xs);
  font-weight: var(--pg-font-weight-heading);
  color: var(--pg-ink-subtle);
  min-height: 22px;
}

.pg-xfer[hidden] { display: none !important; }
.pg-xfer__label { flex: 0 0 auto; white-space: nowrap; }

.pg-xfer__bar {
  flex: 1 1 auto;
  height: 6px;
  border-radius: var(--pg-r-pill);
  background: var(--pg-wash);
  box-shadow: var(--pg-shadow-rim);
  overflow: hidden;
}

.pg-xfer__fill {
  display: block;
  height: 100%;
  width: 0%;
  border-radius: var(--pg-r-pill);
  background: var(--pg-accent-mid);
  transition: width 300ms linear;
}

.pg-xfer[data-status="done"] { color: var(--pg-success-fg); }
.pg-xfer[data-status="done"] .pg-xfer__fill { background: var(--pg-success-fg); }
.pg-xfer[data-status="failed"] { color: var(--pg-danger-fg); }
.pg-xfer[data-status="failed"] .pg-xfer__fill { background: var(--pg-danger-fg); }

.pg-thumb {
  width: 56px; height: 56px;
  border-radius: var(--pg-r-picker);
  object-fit: cover;
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-xs);
  background: var(--pg-surface-deep);
}
.pg-thumb[hidden] { display: none !important; }

/* ==========================================================================
   JOURNEY
   ========================================================================== */
.pg-answers { display: flex; flex-direction: column; gap: 10px; }

.pg-answer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 15px 18px;
  text-align: left;
  border-radius: var(--pg-r-button);
  border: var(--pg-border-button) solid var(--pg-edge);
  background: var(--pg-card-grad);
  box-shadow: var(--pg-shadow-xs);
  color: var(--pg-ink-muted);
  font-weight: var(--pg-font-weight-heading);
  font-size: var(--pg-font-size-lg);
  letter-spacing: var(--pg-font-tracking-body);
  cursor: pointer;
  transition: border-color var(--pg-dur-fast) var(--pg-ease-out),
              background-color var(--pg-dur-fast) var(--pg-ease-out),
              box-shadow var(--pg-dur-quick) var(--pg-ease-out),
              transform var(--pg-dur-fast) var(--pg-ease-out);
}

.pg-answer:hover {
  border-color: var(--pg-accent-border);
  box-shadow: var(--pg-shadow-card);
  transform: translateY(-1px);
}
.pg-answer[aria-pressed="true"] {
  border-color: var(--pg-accent);
  background: var(--pg-accent-soft);
  color: var(--pg-ink);
  box-shadow: var(--pg-shadow-card);
}

.pg-chips { display: flex; flex-wrap: wrap; gap: 10px; }

.pg-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 18px;
  border-radius: var(--pg-r-pill);
  border: var(--pg-border-button) solid var(--pg-edge);
  background: var(--pg-card-grad);
  box-shadow: var(--pg-shadow-xs);
  color: var(--pg-ink-muted);
  font-size: var(--pg-font-size-sm);
  font-weight: var(--pg-font-weight-heading);
  letter-spacing: var(--pg-font-tracking-body);
  cursor: pointer;
  transition: border-color var(--pg-dur-fast) var(--pg-ease-out),
              background-color var(--pg-dur-fast) var(--pg-ease-out),
              color var(--pg-dur-fast) var(--pg-ease-out),
              box-shadow var(--pg-dur-quick) var(--pg-ease-out);
}

.pg-chip:hover { border-color: var(--pg-accent-border); }

.pg-chip[aria-pressed="true"] {
  background: var(--pg-accent);
  border-color: var(--pg-accent);
  color: var(--pg-accent-ink);
  box-shadow: var(--pg-shadow-float), var(--pg-shadow-rim);
}

/* The tick is a real character, so selection is never colour-only. */
.pg-chip__tick { font-weight: var(--pg-font-weight-bold); }
.pg-chip[aria-pressed="false"] .pg-chip__tick { visibility: hidden; }

.pg-field { display: flex; flex-direction: column; gap: 7px; }

.pg-label {
  font-size: var(--pg-font-size-eyebrow);
  font-weight: var(--pg-font-weight-bold);
  letter-spacing: var(--pg-font-tracking-label);
  text-transform: uppercase;
  color: var(--pg-ink-subtle);
}

.pg-input, .pg-textarea {
  width: 100%;
  min-height: var(--pg-touch-target-min);
  padding: 13px 15px;
  border-radius: var(--pg-r-input);
  border: var(--pg-border-button) solid var(--pg-field-border);
  background: var(--pg-field-bg);
  box-shadow: var(--pg-shadow-rim);
  color: var(--pg-ink);
  font-size: var(--pg-font-size-lg);   /* 16px — anything smaller zooms the viewport on iOS */
  letter-spacing: var(--pg-font-tracking-body);
  transition: border-color var(--pg-dur-fast) var(--pg-ease-out);
}

.pg-textarea { min-height: 96px; resize: vertical; line-height: var(--pg-font-lh-snug); }
.pg-input::placeholder, .pg-textarea::placeholder { color: var(--pg-ink-subtle); }
.pg-input:hover, .pg-textarea:hover { border-color: var(--pg-field-border-hover); }
.pg-input:focus-visible, .pg-textarea:focus-visible { border-color: var(--pg-accent); }

/* ==========================================================================
   COMPARISON — their own series, on the reward screen. The payoff.
   ========================================================================== */
.pg-compare {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
  border-radius: var(--pg-r-popup);
  background: var(--pg-card-grad);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-lift);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.pg-compare::before {
  content: "";
  position: absolute;
  top: 0; left: 16px; right: 16px;
  height: 1px;
  background: var(--pg-topline);
  pointer-events: none;
}

.pg-compare[hidden] { display: none !important; }
.pg-compare__cap { display: flex; flex-direction: column; gap: 4px; }

/* Two up at EVERY width — a side-by-side that stacks is not a comparison. */
.pg-compare__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.pg-compare__grid.is-single { grid-template-columns: 1fr; }

.pg-compare__cell { display: flex; flex-direction: column; gap: 7px; }
.pg-compare__cell[hidden] { display: none !important; }

.pg-compare__cell img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--pg-r-card-sm);
  background: var(--pg-surface-deep);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-rim);
}

.pg-compare__grid.is-single .pg-compare__cell img { aspect-ratio: 4 / 3; }

.pg-compare__tag {
  font-size: var(--pg-font-size-eyebrow);
  font-weight: var(--pg-font-weight-bold);
  letter-spacing: var(--pg-font-tracking-label);
  text-transform: uppercase;
  color: var(--pg-ink-subtle);
  text-align: center;
}

/* ==========================================================================
   REWARD CODE — the payoff surface. Amber, because the payoff should look
   like the product and not like an info panel.
   ========================================================================== */
/* ⚠️ WRAPS, and the code is the thing that must not break. At 390px a
   13-character code beside a "Copy code" button does not fit on one line, and
   the first cut broke it mid-token — "PG-ROCKY-9F / 2K" — which is a code the
   customer then mistypes (ds_shot-390-09-reward.png). The row wraps and the
   button drops instead. */
.pg-code {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  padding: 16px 18px;
  border-radius: var(--pg-r-card-sm);
  border: var(--pg-border-button) dashed var(--pg-amber-border);
  background: var(--pg-amber-soft);
  box-shadow: var(--pg-shadow-xs);
}

.pg-code[hidden] { display: none !important; }

.pg-code__value {
  flex: 1 1 auto;
  font-weight: var(--pg-font-weight-bold);
  font-size: var(--pg-font-size-h4);
  letter-spacing: var(--pg-font-tracking-pill);
  line-height: var(--pg-font-lh-flat);
  /* `anywhere`, not `break-all`: break-all splits a word the moment the line
     is tight, `anywhere` only when there is genuinely no other option. */
  overflow-wrap: anywhere;
  color: var(--pg-ink);
}

.pg-code__copy {
  flex: 0 0 auto;
  width: auto;
  padding: 11px 16px;
  font-size: var(--pg-font-size-sm);
}

/* ==========================================================================
   STICKY SKIP BAR — persistent, lights up on prize set AND first asset done
   ========================================================================== */
.pg-skipbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 20;
  padding: 12px var(--c-pad) calc(12px + env(safe-area-inset-bottom, 0px));
  /* ⚠️ OPAQUE. --pg-card-grad is white at 0.94 -> 0.78 alpha, which is right
     for a card sitting ON the page and wrong for a bar sitting OVER it: the
     first cut let the page's own headings and card edges read straight
     through the bar (ds_shot-390-04-upload-first.png). The solid canvas goes
     underneath and the gradient rides on top of it. */
  background: var(--pg-canvas);
  background-image: var(--pg-card-grad);
  border-top: var(--pg-border-hairline) solid var(--pg-edge-strong);
  box-shadow: var(--pg-shadow-rim);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
}

.pg-skipbar[hidden] { display: none !important; }

.pg-skipbar__inner {
  max-width: var(--c-column);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: stretch;
}

.pg-skipbar__hint {
  text-align: center;
  font-size: var(--pg-font-size-micro);
  color: var(--pg-ink-subtle);
}
.pg-skipbar__hint[hidden] { display: none !important; }

/* ==========================================================================
   STATE SCREENS (cooldown / banned / ineligible / error)
   ========================================================================== */
.pg-state { text-align: center; gap: var(--c-stack); }
.pg-state .pg-lede { color: var(--pg-ink-muted); }

/* pg-guarantee-v5__mark-ring, verbatim: a 52px disc on the card gradient with
   an ink-muted edge, the lift shadow, and a SECOND ring drawn 6px inside it. */
.pg-state__mark {
  position: relative;
  width: 60px; height: 60px;
  margin: 0 auto;
  border-radius: var(--pg-r-pill);
  display: grid;
  place-items: center;
  background: var(--pg-card-grad);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-lift);
  font-size: var(--pg-font-size-h2);
  font-weight: var(--pg-font-weight-heading);
  color: var(--pg-accent);
}

.pg-state__mark::before {
  content: "";
  position: absolute;
  inset: 6px;
  border-radius: var(--pg-r-pill);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  pointer-events: none;
}

/* The cooldown is a countdown to the next check-in, not a closed door. */
.pg-countdown {
  font-size: var(--pg-font-size-h1);
  font-weight: var(--pg-font-weight-heading);
  color: var(--pg-ink);
  letter-spacing: var(--pg-font-tracking-h1);
  line-height: var(--pg-font-lh-h1);
  font-variant-numeric: tabular-nums;
}

.pg-lastshot {
  max-width: 280px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pg-lastshot[hidden] { display: none !important; }

.pg-lastshot img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--pg-r-card-sm);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-card);
  background: var(--pg-surface-deep);
}

/* ==========================================================================
   NOSCRIPT
   ========================================================================== */
.pg-noscript {
  max-width: var(--c-column);
  margin: var(--pg-section-y) auto;
  padding: var(--pg-card-pad-mobile);
  border-radius: var(--pg-r-card-sm);
  background: var(--pg-card-grad);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-card);
  color: var(--pg-ink-muted);
  text-align: center;
}

/* ==========================================================================
   DESKTOP — media queries only. There is no device sniffing in this build.
   PG's own sections step their radii and padding at the same breakpoint:
   cards go 16 -> 20px, panels 22 -> 28px, section padding 26 -> 44px.
   ========================================================================== */
@media (min-width: 768px) {
  :root {
    --c-pad: var(--pg-grid-gap);
    --c-stack: var(--pg-grid-gap-mobile);
    --c-cardgap: 16px;
  }

  .pg-topbar { padding: var(--pg-section-y) 0 16px; }
  .pg-main { padding: var(--pg-grid-gap-mobile) 0 132px; }
  .pg-foot { padding: var(--pg-section-y) 0 var(--pg-grid-gap-mobile); }

  .pg-card {
    padding: var(--pg-card-pad);
    border-radius: var(--pg-r-card);
  }
  .pg-compare, .pg-reference { border-radius: var(--pg-r-card-lg); }
  .pg-noscript { padding: var(--pg-card-pad); border-radius: var(--pg-r-card); }
  .pg-code { border-radius: var(--pg-r-card); }
  .pg-code__value { font-size: var(--pg-font-size-h3); letter-spacing: 0.06em; }

  /* The SECOND control (library, no `capture`) becomes the drop zone. */
  .pg-filelabel--secondary {
    flex-direction: column;
    gap: 6px;
    min-height: 148px;
    border-style: dashed;
    background: var(--pg-wash);
    box-shadow: none;
    font-weight: var(--pg-font-weight-body);
  }
  .pg-filelabel--secondary:hover { background: var(--pg-surface-hover); }
  .pg-dropwords { display: flex; }
  .pg-pickwords { display: none; }
}

/* A single ~620px column is the right measure at any width — it is PG's own
   (pg-mechanism-split-v2__inner). The shell centres rather than stretches, so
   1440px is not a different layout. */
@media (min-width: 1200px) {
  .pg-main { max-width: var(--pg-reading-width); }
}

/* ==========================================================================
   REDUCED MOTION — the tin animation collapses to a cross-fade (spec § A11y).
   reveal.js honours prefers-reduced-motion itself; this covers app.css.
   The transform suppressions are load-bearing: a 0.01ms translate still MOVES
   the element, so shortening a duration is not the same as removing motion.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .pg-screen, .pg-stagger > * { animation: pg-fade 160ms linear both !important; }
  .pg-btn:hover, .pg-btn:active,
  .pg-filelabel:hover,
  .pg-answer:hover { transform: none !important; }
}

@keyframes pg-fade { from { opacity: 0; } to { opacity: 1; } }

/* ==========================================================================
   FORCED COLORS
   The translucent gradients, the alpha edges and the inset rim lights all
   vanish in a forced-colours mode, so every affordance is redeclared as a
   real border against the system palette.
   ========================================================================== */
@media (forced-colors: active) {
  .pg-btn, .pg-chip, .pg-answer, .pg-filelabel { border: 2px solid ButtonText; }
  .pg-card, .pg-compare, .pg-reference, .pg-code, .pg-callout,
  .pg-noscript, .pg-badge, .pg-lang, .pg-state__mark { border: 1px solid CanvasText; }
  .pg-card::before, .pg-card::after,
  .pg-compare::before, .pg-surface::before, .pg-surface::after { display: none; }
  .pg-skipbar { border-top: 1px solid CanvasText; }
}
