/* ============================================================================
   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 */

  /* ── THE RHYTHM RAMP ─────────────────────────────────────────────────────
     Chris, 2026-08-27, on the upload screen (fb/f_320s.png): "There's a
     padding issue between checkpoint one, the actual headline and then the
     copy. You definitely want to increase the padding and MAKE IT LIKE
     STANDARD. Right now it's just everything crammed."

     "Standard" is the instruction, not "add 8px here". Before this there was
     one spacing value (--c-stack: 16px) plus a scatter of one-off literals,
     and any cluster that lived inside a plain <div> — the whole head of the
     upload screen — fell OUT of it and rendered at 0.

     So there are now exactly four steps, and every vertical gap in this file
     is one of them. They are 4px multiples anchored on the token file's own
     layout numbers (--pg-card-pad 24, --pg-section-y-mobile 26,
     --pg-grid-gap-mobile 20), which is where the ramp gets its authority:

        --c-s2   8px   inside a cluster, under an eyebrow
        --c-s4  16px   inside a cluster, headline -> body
        --c-s5  20px   between blocks in a screen        (= --c-stack)
        --c-s8  32px   between SECTIONS: stage <-> screen (= --c-section)

     Read as an outline it is 8 / 16 / 20 / 32 — each step clearly bigger than
     the one it nests inside, which is the only thing that makes a page read
     as structured rather than as a list of paragraphs. */
  --c-s2: 8px;
  --c-s4: 16px;
  --c-s5: var(--pg-grid-gap-mobile);      /* 20 */
  --c-s8: 32px;

  --c-stack: var(--c-s5);                 /* the rhythm inside a screen */
  --c-section: var(--c-s8);               /* the rhythm BETWEEN screen and stage */
  --c-cardgap: var(--c-s4);               /* 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 the logo + a two-word language toggle. It fits
   now that the badge is gone, but the wrap stays: nothing in here may shrink,
   because 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). 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 var(--c-s4);
}

.pg-brand {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}

/* ══ THE REAL LOGO ═══════════════════════════════════════════════════════════
   Chris, 2026-08-27: "The logo that you used is no Pupganics logo, and I don't
   know about this yellow dot that you used in the very top left corner."

   He was right on both counts. What shipped was the STRING "Pupganics" set in
   Poppins 800 uppercase at 12px with +0.12em tracking, beside a 10px amber
   disc we invented. PG's wordmark is neither uppercase nor Poppins nor
   letter-spaced, and there is no dot in their mark anywhere.

   static/img/pg-logo.webp is their own header asset, lifted at 448x107.

   ⚠️ THE ASSET IS NOT TRANSPARENT. It was handed over described as "navy
   wordmark on transparent"; it is not, and the first shot of this header
   proved it (hdr_shot-390-01-play.png, first pass) — a hard WHITE rectangle
   around the wordmark on a cream page.

   ⚠️ THE ASSET WAS OPAQUE WHITE, and it has been FIXED (2026-08-27). The version
   pulled from PG's header was `mode RGB, 448x107, all four corners (255,255,255)` —
   no alpha at all, despite being reported as transparent. It rendered a hard white
   rectangle on the cream ground.

   The first fix was `mix-blend-mode: multiply`, which works (white x cream = cream)
   but is fragile in two opposite directions: on a navy surface multiply turns the
   whole tile navy and the wordmark vanishes, and ANY ancestor with `isolation`,
   `opacity < 1`, a `filter` or a `transform` creates a stacking context that the
   blend resolves against, silently restoring the white plate.

   So the ASSET was given a real alpha channel instead — near-white knocked out with
   a proportional feather on the antialiased edge so the navy stays smooth. 33,628
   pixels transparent, all four corners alpha 0, 8,614 bytes. The blend hack is gone
   and the logo now works on any ground.

   Height, not width: 24px puts the cap-height of the wordmark on the same optical
   line as the 44px language pill beside it, and the intrinsic width/height on the
   <img> reserves the 100x24 box so the topbar does not reflow when the asset lands. */
.pg-brand__logo {
  display: block;
  height: 24px;
  width: auto;
  /* the asset is 4.19:1, so this is ~100px — a ceiling, never a stretch */
  max-width: 116px;
}

/* 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);
}

/* ⚠️ `gap`, not a margin on the stage. Chris, 2026-08-27 (fb/f_45s.png):
   "There's not enough padding between step three and the sub-headline, which
   is 'Pick a tin'. So add more padding there."

   "Step three" is the last row of .pg-steps, at the bottom of #s-play. "Pick a
   tin" is reveal.js's own headline at the top of #pg-stage. They are two
   SIBLING flex children of this element, and this element had no gap — so the
   distance between a screen and the stage was whatever the two of them
   happened to leave, which on the play step was nothing at all. The stage
   carried a margin-BOTTOM only, which is the wrong end for the play order and
   the right one for stage-first, so it helped on exactly one of the two.

   One `gap` fixes both orders at once, and it is the section step of the ramp
   (32px) rather than a number chosen for this one screen. #pg-stage's own
   margin is gone with it — a margin plus a gap is how a rhythm drifts. */
.pg-main {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--c-section);
  width: 100%;
  max-width: var(--c-column);
  margin: 0 auto;
  padding: var(--c-s2) 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.
   ========================================================================== */
/* ⚠️ NO MARGIN. .pg-main's `gap: var(--c-section)` owns the distance on both
   sides of this element, in both orders. Re-adding a margin here reintroduces
   the asymmetry Chris filmed in fb/f_45s.png. */
.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); }

/* ══ THE STANDARD HEAD CLUSTER ═══════════════════════════════════════════════
   Chris, 2026-08-27, filming the upload screen (fb/f_320s.png): "There's a
   padding issue between checkpoint one, the actual headline and then the copy.
   You definitely want to increase the padding and make it like standard. Right
   now it's just everything crammed."

   It was crammed at literally 0px. The reset zeroes every h1/p margin, that
   screen's eyebrow + headline + lede lived inside a bare <div>, and a bare div
   is not a flex container — so the .pg-stack gap stopped at its edge and the
   three of them stacked with nothing between them. The other screens looked
   different from that one for no better reason than that their head elements
   happened to be direct children of the stack.

   So the cluster gets a name and every screen uses it. Two steps off the ramp:

     eyebrow -> headline      --c-s2   8px   (they are one label, near-touching)
     headline -> body/anything --c-s4  16px   (--pg-font-lh-h1 is 0.98, so the
                                              optical gap is smaller than the
                                              number; 16 is what 12 looks like)

   Both sit UNDER --c-stack (20) and well under --c-section (32), which is what
   makes the head read as one object rather than three loose paragraphs. */
.pg-headgroup { display: flex; flex-direction: column; }
.pg-headgroup > * + * { margin-top: var(--c-s4); }
.pg-headgroup > .pg-eyebrow + * { margin-top: var(--c-s2); }

@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-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;
}

/* ==========================================================================
   CATEGORIES — the three ways to log, then "Got it, let's start".
   Chris, 2026-08-27: "they would be able to SEE the three options on the page
   and then be able to click on 'got it, let's start'."

   "See the three options ON THE PAGE" is the constraint that shapes this, and
   it is a constraint about a 390px phone. Three stacked cards each carrying a
   16:9 example clip is roughly two and a half screens of scrolling, so on
   mobile the media is a 92px SQUARE beside the text rather than a banner above
   it — all three fit inside one viewport and the choice is an actual choice
   rather than a scroll. At >=768px there is room for the honest layout, so the
   cards go three-up with the clip full width on top.
   ========================================================================== */
.pg-catlist { display: flex; flex-direction: column; gap: var(--c-s4); }

/* ⚠️ A <button>, and therefore the play control inside it CANNOT be a button
   too — nested interactive content is invalid and screen readers flatten it.
   Tapping the card is what selects the category AND plays its example; the
   glyph is decorative and `categories.example.play` binds to the <video>'s
   aria-label instead.
   The button carries its own name and description as visible text, which IS
   its accessible name. That is why `categories.aria.option` is unbound here —
   an aria-label would override the same words with the same words. */
.pg-catcard {
  display: grid;
  grid-template-columns: 92px 1fr;
  grid-template-areas:
    "media name"
    "media desc"
    "cap   cap";
  align-items: start;
  column-gap: var(--c-s4);
  row-gap: 6px;
  width: 100%;
  padding: var(--pg-card-pad-mobile);
  text-align: left;
  border-radius: var(--pg-r-card-sm);
  border: var(--pg-border-button) solid var(--pg-edge);
  background: var(--pg-card-grad);
  box-shadow: var(--pg-shadow-xs);
  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-catcard:hover {
  border-color: var(--pg-accent-border);
  box-shadow: var(--pg-shadow-card);
  transform: translateY(-1px);
}

/* Selected reads as navy EDGE on the soft accent, not as a navy fill: unlike a
   one-word chip these cards carry a paragraph, and white body copy on navy at
   --pg-font-size-xs is the smallest thing on the page reversed out. */
.pg-catcard[aria-pressed="true"] {
  border-color: var(--pg-accent);
  background: var(--pg-accent-soft);
  box-shadow: var(--pg-shadow-card);
}

.pg-catcard__name {
  grid-area: name;
  align-self: end;
  font-size: var(--pg-font-size-h4);
  font-weight: var(--pg-font-weight-heading);
  letter-spacing: var(--pg-font-tracking-h3);
  line-height: var(--pg-font-lh-h3);
  color: var(--pg-ink);
}

.pg-catcard__desc {
  grid-area: desc;
  font-size: var(--pg-font-size-sm);
  font-weight: var(--pg-font-weight-body);
  line-height: var(--pg-font-lh-snug);
  color: var(--pg-ink-muted);
  text-wrap: pretty;
}

/* The clip's caption. It is a caption for MEDIA THAT IS NOT THERE YET, so it
   sits under the whole card rather than under an empty well, where it reads as
   a promise instead of as a broken figure. */
.pg-catcard__cap {
  grid-area: cap;
  margin-top: var(--c-s2);
  padding-top: var(--c-s2);
  border-top: var(--pg-border-hairline) solid var(--pg-edge);
  font-size: var(--pg-font-size-micro);
  font-weight: var(--pg-font-weight-body);
  line-height: var(--pg-font-lh-snug);
  color: var(--pg-ink-subtle);
}

/* ⚠️ AN EMPTY SLOT ON PURPOSE. Chris is supplying one real customer clip per
   category. Until then this is a WELL — recessed, with a play glyph — and not
   a broken <video>: a media element with no src paints a black rectangle on
   iOS, which looks like a failed load rather than like something pending. */
.pg-example__media {
  grid-area: media;
  position: relative;
  display: grid;
  place-items: center;
  width: 92px;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--pg-r-picker);
  background: var(--pg-surface-deep);
  border: var(--pg-border-hairline) solid var(--pg-edge);
  box-shadow: var(--pg-shadow-rim);
}

.pg-example__video {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.pg-example__video[hidden] { display: none !important; }

.pg-example__play {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  padding-left: 3px;                 /* a triangle is optically off-centre */
  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-accent);
  font-size: var(--pg-font-size-micro);
  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; }

/* ⚠️ NO `gap` ON THIS ELEMENT. The tick's spacing is a margin ON THE TICK, so
   it collapses to nothing with the tick. A flex gap does not collapse when a
   child hits zero width, which is the entire bug below. */
.pg-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  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 ════════════════════════════════════════════════════════════════
   It is a real character, so selection is never colour-only — a chip that
   changes only from cream to navy is unreadable to the ~8% of men with a
   red-green deficiency and invisible in a forced-colours mode.

   ⚠️ BUT IT MUST NOT RESERVE ITS SPACE. Chris, 2026-08-27, filming the area
   step (fb/f_140s.png unselected vs fb/f_165s.png selected): "When I do select
   one thing I see a checkmark appearing on the left... but whenever the item is
   NOT selected there was a huge gap between the word and the actual left
   border. So I don't want to have that gap. Either do an animation that would
   turn a standard unselected block into a selected one, but then the unselected
   one wouldn't have such a huge gap."

   What did it was `visibility: hidden`, which hides a box WITHOUT removing it:
   the glyph kept its ~13px of advance width and the chip's 8px flex gap kept
   its 8px, so every unselected chip carried 21px of dead space on its left and
   its label sat visibly off-centre. `display: none` would fix the gap and lose
   the animation he asked for.

   So the tick animates its own width instead. max-width 1em -> 0 with
   overflow: hidden, and the spacing carried as margin-inline-end on the tick
   rather than as gap on the chip, so BOTH go to zero together. Unselected, the
   label is centred by the chip's symmetric 18px padding and there is nothing
   to its left at all. Selected, the tick expands into place.

   `margin-inline-end`, not `margin-right`: this page ships EN and ES today and
   the property is direction-correct for free.
   Under prefers-reduced-motion the global transition-duration override at the
   foot of this file collapses the expansion to an instant swap — the end
   states are identical, so nothing is lost but the motion. */
.pg-chip__tick {
  display: inline-flex;
  overflow: hidden;
  max-width: 1em;
  margin-inline-end: var(--c-s2);
  opacity: 1;
  font-weight: var(--pg-font-weight-bold);
  transition: max-width var(--pg-dur-quick) var(--pg-ease-out),
              margin-inline-end var(--pg-dur-quick) var(--pg-ease-out),
              opacity var(--pg-dur-fast) var(--pg-ease-out);
}

.pg-chip[aria-pressed="false"] .pg-chip__tick {
  max-width: 0;
  margin-inline-end: 0;
  opacity: 0;
}

.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) {
  /* The ramp steps up as a UNIT — redefine the two variable rungs and every
     gap in the file follows, because nothing declares a raw length. 8 / 16 /
     20 / 32 becomes 8 / 16 / 24 / 40, which is the same proportion with more
     air, and 40 is --pg-grid-gap: PG's own desktop section step. */
  :root {
    --c-pad: var(--pg-grid-gap);
    --c-s5: var(--pg-card-pad);          /* 24 */
    --c-s8: var(--pg-grid-gap);          /* 40 */
  }

  .pg-topbar { padding: var(--pg-section-y) 0 var(--c-s4); }
  .pg-main { padding: var(--c-s5) 0 132px; }
  .pg-brand__logo { height: 30px; max-width: 140px; }

  /* Three-up, and the example clip gets the width it deserves. The 92px
     square was a concession to a 390px viewport; there is no reason to
     carry it here. */
  .pg-catlist { display: grid; grid-template-columns: repeat(3, 1fr); }
  .pg-catcard {
    grid-template-columns: 1fr;
    grid-template-areas: "media" "name" "desc" "cap";
    align-content: start;
    padding: var(--pg-card-pad);
    border-radius: var(--pg-r-card);
    row-gap: var(--c-s2);
  }
  .pg-catcard__name { align-self: start; }
  .pg-catcard__cap { margin-top: auto; }
  .pg-example__media { width: 100%; aspect-ratio: 4 / 3; margin-bottom: 6px; }
  .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-catcard: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, .pg-catcard { border: 2px solid ButtonText; }
  .pg-example__media, .pg-example__play { border: 1px solid CanvasText; }
  .pg-card, .pg-compare, .pg-reference, .pg-code, .pg-callout,
  .pg-noscript, .pg-lang, .pg-state__mark { border: 1px solid CanvasText; }
  /* Images are exempt from forced colours, so the wordmark arrives as authored.
     The asset now carries real alpha (see .pg-brand__logo above), so it composites
     correctly against a forced Canvas of any colour and needs no blend override.
     forced-color-adjust stays: it stops the UA repainting the navy wordmark. */
  .pg-brand__logo { forced-color-adjust: none; }
  .pg-card::before, .pg-card::after,
  .pg-compare::before, .pg-surface::before, .pg-surface::after { display: none; }
  .pg-skipbar { border-top: 1px solid CanvasText; }
}


/* ── Upload rejection notice ────────────────────────────────────────────────
   Chris, 2026-08-27: "Have an error message if the pic isn't approved. I don't
   want the user to submit and then be stuck with no discount and no way to get
   one." Agent FLOW renders this above the capture buttons; it inherited
   `pg-note` (muted grey) and read as a hint rather than as a rejection.

   ⚠️ Deliberately NOT the danger red at full strength. This fires on the one
   screen where someone has already done the work, and a loud red on a warm
   cream page reads as "you broke it" when the actual message is "that file
   didn't go through, here's what will". Tinted surface, danger-coloured rule
   and text, no icon shouting. It has to be unmissable AND recoverable-looking.
   ⚠️ role="alert" is set in journey.js, so screen readers announce it without
   the styling doing any of that work. */
.pg-upload-error {
  margin: var(--c-s2) 0 0;
  padding: var(--c-s2) var(--c-s4);
  border-radius: var(--pg-r-input);
  border: 1px solid var(--pg-danger-border);
  border-inline-start: 3px solid var(--pg-danger-fg);
  background: var(--pg-danger-bg);
  color: var(--pg-danger-fg);
  font-weight: var(--pg-font-weight-body);
  text-align: start;
}
.pg-upload-error:empty { display: none; }


/* ── Three 9:16 example slots on the framing screen ─────────────────────────
   Chris, 2026-08-27: "three side-by-side placeholders that would be vertical in
   9:16, just so we can later add videos there ... just so people know that they
   can very much grab pictures from their computer and it would be okay."

   ⚠️ THREE ACROSS AT EVERY WIDTH, on purpose. At 390px that is ~104px per slot,
   which is too small to watch and exactly right to scan: the job of this row is
   "here are the kinds of thing that count", read in one glance before the ask.
   Stacking them on mobile would push the CTA below the fold and turn a glance
   into a scroll.
   ⚠️ aspect-ratio does the sizing, so a slot is the right shape while EMPTY —
   no layout shift when the real clips are dropped in.
   ⚠️ EVERY SELECTOR HERE IS SCOPED TO #s-framing. The inert #s-categories screen
   reuses the same .pg-example__media class name, and unscoped rules forced 9:16
   onto its 92px square thumbs — a collision that only showed up because a live
   measurement returned SIX slots when the markup has three. */
#s-framing .pg-examples {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--c-s2);
  list-style: none;
  margin: 0;
  padding: 0;
}
#s-framing .pg-example { margin: 0; min-width: 0; }
#s-framing .pg-example__media {
  position: relative;
  aspect-ratio: 9 / 16;
  border-radius: var(--pg-r-card);
  border: 1px solid var(--pg-hairline);
  background: var(--pg-surface-2);
  overflow: hidden;
  display: grid;
  place-items: center;
}
#s-framing .pg-example__media video,
#s-framing .pg-example__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* The play glyph only shows while the slot is empty; a real clip covers it. */
#s-framing .pg-example__empty {
  font-size: 20px;
  line-height: 1;
  color: var(--pg-ink-subtle);
  opacity: .55;
}
#s-framing .pg-example__cap {
  margin: var(--c-s2) 0 0;
  font-size: var(--pg-font-size-xs);
  line-height: 1.3;
  color: var(--pg-ink-muted);
  text-align: center;
}
@media (min-width: 768px) {
  #s-framing .pg-examples { gap: var(--c-s4); }
  #s-framing .pg-example__cap { font-size: var(--pg-font-size-sm); }
}
