    /* =========================================================
       Tokens — shared with the rest of the Fizook surface
       ========================================================= */
    *,*::before,*::after{box-sizing:border-box}
    :root{
      --bg:#0b1220;
      --surface:#ffffff;
      --surface-2:#f8fafc;
      --border:#e5e7eb;
      --text:#0f172a;
      --muted:#64748b;
      --primary:#2481bc;
      --primary-dark:#1a6494;
      --success:#16a34a;
      --success-light:#dcfce7;
      --danger:#dc2626;
      --warn:#f59e0b;
      --accent:#7c3aed;
      --shadow-sm:0 1px 3px rgba(15,23,42,.10);
      --shadow:0 6px 20px rgba(15,23,42,.14);
      --shadow-lg:0 20px 48px rgba(15,23,42,.24);
      --radius:14px;
      --radius-sm:10px;
      --radius-lg:20px;
      --radius-button:6px;
    }
    html,body{margin:0;padding:0;font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:var(--text);background:var(--surface-2);scroll-behavior:smooth}
    /* overflow-x:clip on html+body prevents the hero's decorative blobs
       from creating horizontal scroll on any screen. */
    html,body{overflow-x:clip}
    /* Layered radial-gradient background — same three-blob pattern as
       index.html and fizook-vendor-pizza-hut so the whole app looks like
       one cohesive surface. */
    body{
      min-height:100vh;
      background:
        radial-gradient(900px 500px at 10% -10%, rgba(124,58,237,.10), transparent 60%),
        radial-gradient(900px 500px at 110% 0%, rgba(36,129,188,.12), transparent 60%),
        radial-gradient(700px 420px at 50% 60%, rgba(22,163,74,.05), transparent 70%),
        #eef2f7;
    }
    img{max-width:100%;display:block}
    button{font-family:inherit}
    a{color:inherit}

    /* =========================================================
       Top bar — sticky nav matching vendor + history pages
       =========================================================
       Only three nav links here (History / Redeem / Vendor view) — this
       is a task page, not a marketing surface, so we keep the chrome
       minimal. .brand uses a gradient wordmark clipped to text. */
    .topbar{
      position:sticky;top:0;z-index:50;
      background:rgba(11,18,32,.92);backdrop-filter:saturate(1.4) blur(10px);
      color:#fff;border-bottom:1px solid rgba(255,255,255,.08);
    }
    .topbar-inner{max-width:1240px;margin:0 auto;display:flex;align-items:center;gap:16px;padding:12px 16px}
    .brand{
      font-weight:800;letter-spacing:.5px;font-size:1.25rem;
      display:flex;align-items:center;gap:8px;text-decoration:none;color:#fff;
      background:linear-gradient(135deg,#ffffff,#a5f3fc 60%,#fbcfe8);
      -webkit-background-clip:text;background-clip:text;color:transparent;
    }
    .brand .dot{
      width:10px;height:10px;border-radius:50%;flex:0 0 auto;
      background:linear-gradient(135deg,#22d3ee,#7c3aed) !important;
      box-shadow:0 0 16px rgba(34,211,238,.7);
    }
    .topnav{margin-left:auto;display:flex;gap:4px;align-items:center;flex-wrap:wrap}
    .nav-link{
      background:transparent;border:0;font-family:inherit;color:#cbd5e1;
      text-decoration:none;font-size:.92rem;padding:6px 10px;border-radius:8px;
      display:inline-flex;align-items:center;gap:6px;cursor:pointer;
    }
    .nav-link:hover{background:rgba(255,255,255,.14);color:#fff}
    .nav-link.active{background:rgba(36,129,188,.25);color:#fff}
    @media (max-width:559.98px){
      .nav-link span.lbl{display:none}
    }

    /* =========================================================
       Hero — "You won · ready to redeem" banner
       =========================================================
       Same gradient + floating cyan/pink blobs as the other Fizook
       hero surfaces. Compact ("You won") copy sets celebratory tone
       so this feels like a reward, not a task. */
    .hero{
      position:relative;overflow:hidden;
      background:linear-gradient(135deg,#0b1220,#1a6494 50%,#7c3aed 110%);
      color:#fff;padding:42px 20px 80px;
    }
    /* Two decorative gradient blobs — clipped by .hero{overflow:hidden}. */
    .hero::before,.hero::after{
      content:"";position:absolute;border-radius:50%;filter:blur(50px);opacity:.55;pointer-events:none;
    }
    .hero::before{width:340px;height:340px;background:#22d3ee;top:-100px;left:-80px;animation:floatA 9s ease-in-out infinite}
    .hero::after {width:380px;height:380px;background:#ec4899;bottom:-140px;right:-80px;animation:floatB 11s ease-in-out infinite}
    @keyframes floatA{0%,100%{transform:translateY(0)}50%{transform:translateY(20px)}}
    @keyframes floatB{0%,100%{transform:translate(0,0)}50%{transform:translate(-12px,-18px)}}
    .hero-inner{position:relative;z-index:1;max-width:1240px;margin:0 auto;display:flex;flex-direction:column;gap:14px}
    /* "You won · ready to redeem" pill above the headline. */
    .hero-tag{
      display:inline-flex;align-items:center;gap:8px;align-self:flex-start;
      background:rgba(255,255,255,.12);padding:5px 12px;border-radius:999px;
      font-size:.74rem;font-weight:800;letter-spacing:.6px;text-transform:uppercase;
    }
    .hero-tag .pulse-dot{width:8px;height:8px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 0 rgba(34,197,94,.7);animation:pulse 1.6s infinite}
    /* Green pulse dot — shared with status pills, sit-tag, live badges. */
    @keyframes pulse{
      0%{box-shadow:0 0 0 0 rgba(34,197,94,.6)}
      70%{box-shadow:0 0 0 12px rgba(34,197,94,0)}
      100%{box-shadow:0 0 0 0 rgba(34,197,94,0)}
    }
    .hero h1{margin:0;font-size:clamp(1.7rem,3.5vw,2.4rem);font-weight:800;letter-spacing:-.6px;line-height:1.1}
    /* Gradient-text on the accent phrase ("redeem your meal.") — same
       background-clip:text trick used throughout the Fizook surface. */
    .hero h1 .grad{
      background:linear-gradient(135deg,#22d3ee 0%,#a78bfa 50%,#fbcfe8 100%);
      -webkit-background-clip:text;background-clip:text;color:transparent;
    }
    .hero p.lead{margin:0;font-size:1rem;line-height:1.55;opacity:.92;max-width:640px}

    /* =========================================================
       Layout — .wrap is a 2-column grid on ≥880px
       =========================================================
       margin-top:-44px lifts the first card up into the hero's bottom
       edge so the transition reads as one composition. Stacks to a
       single column on phones. Left column carries the redemption
       card (star of the show); right column is aux details. */
    .wrap{
      max-width:1100px;margin:-44px auto 0;padding:0 16px 40px;position:relative;z-index:2;
      display:grid;gap:18px;
      grid-template-columns:minmax(0,1fr);
    }
    @media (min-width:880px){
      .wrap{grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);align-items:start;gap:22px}
    }

    /* =========================================================
       Card primitives — shared shell for every content card
       =========================================================
       Base .card is a white surface with a colored left accent stripe
       (::before, 4px wide). Variant classes (.accent-success/-warn/
       -danger/-purple) swap the stripe color. Cards fade + slide up
       on load, staggered so they animate in sequence. */
    .card{
      position:relative;
      background:var(--surface);border:1px solid var(--border);
      border-radius:var(--radius);box-shadow:var(--shadow);
      padding:18px 18px 20px 22px;
      animation:cardIn .6s ease both;opacity:0;
      overflow:hidden;
    }
    /* Left accent stripe. Default = blue primary, variants below swap the gradient. */
    .card::before{
      content:"";position:absolute;left:0;top:0;bottom:0;width:4px;
      background:linear-gradient(180deg,#2481bc,#1a6494);
      border-top-left-radius:var(--radius);border-bottom-left-radius:var(--radius);
    }
    .card.accent-success::before{background:linear-gradient(180deg,#16a34a,#15803d)}
    .card.accent-warn::before{background:linear-gradient(180deg,#f59e0b,#b45309)}
    .card.accent-danger::before{background:linear-gradient(180deg,#dc2626,#991b1b)}
    .card.accent-purple::before{background:linear-gradient(180deg,#7c3aed,#5b21b6)}
    /* Lift real children above the accent stripe pseudo. */
    .card > *{position:relative;z-index:1}
    /* Stagger animation delays so cards cascade in on load. */
    .card:nth-child(1){animation-delay:.05s}
    .card:nth-child(2){animation-delay:.12s}
    .card:nth-child(3){animation-delay:.19s}
    @keyframes cardIn{0%{opacity:0;transform:translateY(14px)}100%{opacity:1;transform:translateY(0)}}
    /* Card header: icon square + title stack. */
    .card-head{display:flex;align-items:center;gap:12px;margin-bottom:10px}
    .card-icon{
      width:38px;height:38px;border-radius:10px;flex:0 0 auto;
      display:grid;place-items:center;color:#fff;font-size:1.05rem;
      background:linear-gradient(135deg,#2481bc,#1a6494);
      box-shadow:0 4px 10px rgba(36,129,188,.32);
    }
    .card.accent-success .card-icon{background:linear-gradient(135deg,#16a34a,#15803d);box-shadow:0 4px 10px rgba(22,163,74,.32)}
    .card.accent-purple .card-icon{background:linear-gradient(135deg,#7c3aed,#5b21b6);box-shadow:0 4px 10px rgba(124,58,237,.32)}
    /* Title stack — flex:1 so it takes remaining width; min-width:0 lets
       the h2/p ellipsis correctly when they're too long. */
    .card-titles{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}
    .card-titles h2{margin:0;font-size:1.05rem;font-weight:800;letter-spacing:-.2px}
    .card-titles p{margin:0;font-size:.82rem;color:var(--muted)}

    /* =========================================================
       Redemption card — the star of the show (3D flip on redeem)
       =========================================================
       CSS 3D flip pattern: .redeem-card sets perspective; .redeem-inner
       is the flippable container (transform-style:preserve-3d so its
       children live in the 3D space). Two children:
         - .redeem-face: the pending state (QR + code + steps)
         - .redeem-back: the "Redeemed!" green confirmation
       Adding .is-redeemed to .redeem-card rotates -inner 180° on Y,
       so the front hides (backface-visibility:hidden) and the back
       (pre-rotated 180°) faces forward.
       Trigger: the vendor page marks the token redeemed in localStorage,
       and refreshFromStorage() adds the class in real time. */
    .redeem-card{position:relative;perspective:1200px}
    .redeem-inner{
      position:relative;transition:transform .9s cubic-bezier(.7,.0,.2,1);
      transform-style:preserve-3d;
    }
    .redeem-card.is-redeemed .redeem-inner{transform:rotateY(180deg)}
    /* Front face — the pending redemption UI. */
    .redeem-face{
      backface-visibility:hidden;-webkit-backface-visibility:hidden;
    }
    /* Back face — green success card, positioned absolute so it
       overlays the front. Pre-rotated 180° so it faces backward until
       the flip completes. */
    .redeem-back{
      position:absolute;inset:0;
      transform:rotateY(180deg);
      backface-visibility:hidden;-webkit-backface-visibility:hidden;
      display:grid;place-items:center;text-align:center;
      background:linear-gradient(160deg,#dcfce7,#bbf7d0);
      border:1px solid #86efac;border-radius:var(--radius);
      padding:30px 24px;
    }
    /* Big green check circle — pops in with an overshoot bounce
       (cubic-bezier(.34,1.56,.64,1) is the "back-out" easing). */
    .redeem-back .check{
      width:88px;height:88px;border-radius:50%;
      background:linear-gradient(135deg,#16a34a,#15803d);
      color:#fff;display:grid;place-items:center;font-size:2.4rem;
      box-shadow:0 18px 40px rgba(22,163,74,.45);
      margin:0 auto 14px;
      animation:popIn .7s cubic-bezier(.34,1.56,.64,1) both;
    }
    @keyframes popIn{0%{transform:scale(.2);opacity:0}60%{transform:scale(1.1)}100%{transform:scale(1);opacity:1}}
    .redeem-back h3{margin:0 0 4px;font-size:1.5rem;font-weight:800;color:#15803d;letter-spacing:-.4px}
    .redeem-back p{margin:0;font-size:.95rem;color:#166534;max-width:340px}
    /* "✓ Confirmed by vendor" pill under the success message. */
    .redeem-back .stamp{
      display:inline-block;margin-top:14px;
      padding:6px 14px;border-radius:999px;
      background:rgba(21,128,61,.12);color:#15803d;
      font-size:.8rem;font-weight:800;letter-spacing:.4px;text-transform:uppercase;
    }

    /* =========================================================
       Status pill (active / expired / redeemed)
       =========================================================
       Sits above the QR + code group. Base = green "Awaiting" pill.
       Variant classes are applied by paintStatus() in the JS:
         .warn   = amber (nearing expiry — not currently used)
         .danger = red   (expired)
         .done   = blue  (redeemed)
       The dot inside pulses green on active, static on other states. */
    .status-row{
      display:flex;align-items:center;gap:10px;margin-bottom:14px;
      flex-wrap:wrap;
    }
    .status-pill{
      display:inline-flex;align-items:center;gap:7px;
      padding:5px 12px;border-radius:999px;
      font-size:.74rem;font-weight:800;letter-spacing:.5px;text-transform:uppercase;
      background:linear-gradient(135deg,#dcfce7,#bbf7d0);color:#166534;
      border:1px solid #86efac;
    }
    .status-pill .dot{width:8px;height:8px;border-radius:50%;background:#22c55e;animation:pulse 1.6s infinite}
    .status-pill.warn{background:linear-gradient(135deg,#fef3c7,#fde68a);color:#92400e;border-color:#fcd34d}
    .status-pill.warn .dot{background:#f59e0b;animation:none}
    .status-pill.danger{background:linear-gradient(135deg,#fee2e2,#fecaca);color:#991b1b;border-color:#fca5a5}
    .status-pill.danger .dot{background:#dc2626;animation:none}
    .status-pill.done{background:linear-gradient(135deg,#dbeafe,#bfdbfe);color:#1e40af;border-color:#93c5fd}
    .status-pill.done .dot{background:#2481bc;animation:none}
    /* Live "Expires in Xh Ym" countdown on the right. Tabular-nums so
       digits don't jitter as they update every second. */
    .countdown{
      margin-left:auto;font-size:.85rem;font-weight:700;color:var(--muted);
      font-variant-numeric:tabular-nums;
    }
    .countdown strong{color:var(--text)}

    /* =========================================================
       QR stage — dashed-border stage containing QR + code + hint
       =========================================================
       Soft blue radial glow at the top for depth. Contents stack:
       QR frame → "SCAN OR TYPE" divider → fallback code pill + hint. */
    .qr-stage{
      display:flex;flex-direction:column;align-items:center;gap:18px;
      padding:18px 12px 6px;
      background:
        radial-gradient(420px 220px at 50% 0%, rgba(36,129,188,.06), transparent 60%),
        var(--surface);
      border:1px dashed var(--border);
      border-radius:var(--radius);
    }
    /* QR frame — square white card with four blue L-shaped corner
       accents that mimic a camera scan overlay. */
    .qr-frame{
      position:relative;width:260px;height:260px;padding:14px;
      background:#fff;border-radius:18px;
      box-shadow:0 14px 36px rgba(15,23,42,.18), inset 0 0 0 1px rgba(15,23,42,.04);
    }
    /* Four L-corners. Two come from ::before/::after (top-left/bottom-left)
       and two come from real <span> children (top-right/bottom-right)
       because CSS only allows two pseudos per element. Each corner is a
       22×22 square with three borders hidden — the two visible borders
       form the L. */
    .qr-frame::before,.qr-frame::after,
    .qr-frame > .qr-corner-tr,.qr-frame > .qr-corner-br{
      content:"";position:absolute;width:22px;height:22px;
      border:3px solid var(--primary);
    }
    .qr-frame::before{top:-2px;left:-2px;border-right:0;border-bottom:0;border-top-left-radius:8px}
    .qr-frame::after{bottom:-2px;left:-2px;border-right:0;border-top:0;border-bottom-left-radius:8px}
    .qr-frame .qr-corner-tr{top:-2px;right:-2px;border-left:0;border-bottom:0;border-top-right-radius:8px}
    .qr-frame .qr-corner-br{bottom:-2px;right:-2px;border-left:0;border-top:0;border-bottom-right-radius:8px}
    /* qrcodejs library injects an <img> or <canvas> into #qrcode.
       !important overrides its inline width/height so the code fills the frame. */
    .qr-frame #qrcode{width:100%;height:100%;display:grid;place-items:center}
    .qr-frame #qrcode img,
    .qr-frame #qrcode canvas{width:100% !important;height:100% !important;display:block}

    /* "SCAN OR TYPE" divider between QR and fallback code. */
    .qr-or{
      display:flex;align-items:center;gap:10px;width:100%;max-width:300px;
      color:var(--muted);font-size:.72rem;font-weight:800;letter-spacing:.7px;text-transform:uppercase;
    }
    .qr-or::before,.qr-or::after{content:"";flex:1;height:1px;background:var(--border)}

    /* =========================================================
       Fallback 6-char code — big, monospaced, tap-to-copy
       =========================================================
       Alternative to the QR when the vendor's scanner is unavailable.
       user-select:all so tap-and-hold on iOS selects the whole thing.
       cursor:pointer + click handler in JS also copies via Clipboard API.
       .copied class briefly shows a "Copied ✓" toast above the pill. */
    .code-block{
      width:100%;max-width:300px;display:flex;flex-direction:column;align-items:center;gap:6px;
    }
    .code-label{
      font-size:.7rem;font-weight:800;letter-spacing:.7px;text-transform:uppercase;color:var(--muted);
    }
    .code-pill{
      font-family:'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
      font-size:2.2rem;font-weight:800;letter-spacing:.32em;
      color:var(--text);
      background:linear-gradient(180deg,#f8fafc,#eef2f7);
      border:1px solid var(--border);border-radius:14px;
      padding:14px 22px 14px 26px;
      box-shadow:inset 0 -2px 0 rgba(15,23,42,.05);
      cursor:pointer;user-select:all;
      transition:transform .15s ease, box-shadow .15s ease;
      position:relative;
    }
    .code-pill:hover{transform:translateY(-1px);box-shadow:0 6px 16px rgba(15,23,42,.10)}
    /* Toast pseudo — content is set from the class, position is absolute
       above the pill so it briefly appears without shifting layout. */
    .code-pill.copied::after{
      content:"Copied ✓";
      position:absolute;left:50%;top:-26px;transform:translateX(-50%);
      background:var(--success);color:#fff;padding:3px 9px;border-radius:6px;
      font-family:'Inter',sans-serif;font-size:.7rem;font-weight:700;letter-spacing:.3px;
      box-shadow:var(--shadow-sm);
    }
    .code-hint{font-size:.78rem;color:var(--muted);text-align:center;max-width:280px}

    /* =========================================================
       Offer summary — vendor emoji / meal info / prices
       =========================================================
       3-column grid: 54px emoji square | flex-grow info | fixed prices.
       Info cell uses text-overflow:ellipsis on the meal name + vendor
       location so long strings don't force the row wider than its parent. */
    .offer-summary{
      display:grid;gap:10px;
      grid-template-columns:54px minmax(0,1fr) auto;
      align-items:center;
      padding:12px;border:1px solid var(--border);border-radius:var(--radius-sm);
      background:var(--surface-2);
    }
    /* Warm-amber emoji square. Inset shadow gives it a subtle inner border. */
    .offer-emoji{
      width:54px;height:54px;border-radius:12px;display:grid;place-items:center;
      background:linear-gradient(135deg,#fff7ed,#fed7aa);font-size:1.8rem;
      box-shadow:inset 0 0 0 1px rgba(245,158,11,.25);
    }
    .offer-info{min-width:0;display:flex;flex-direction:column;gap:1px}
    .offer-info .v-name{font-size:.74rem;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.4px}
    .offer-info .m-name{font-size:1rem;font-weight:800;letter-spacing:-.1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .offer-info .v-loc{font-size:.78rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    /* Price stack: big current price → strike-through original → green save pill. */
    .offer-prices{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
    .offer-prices .now{font-weight:800;color:var(--primary);font-size:1.15rem;line-height:1}
    .offer-prices .was{color:var(--muted);text-decoration:line-through;font-size:.78rem;display:block;margin-top:2px}
    .offer-prices .save{
      display:inline-block;margin-top:6px;font-size:.7rem;font-weight:800;
      color:var(--success);background:var(--success-light);
      padding:2px 8px;border-radius:999px;letter-spacing:.3px;
    }

    /* =========================================================
       "How to redeem" steps — numbered walkthrough
       =========================================================
       Three horizontal rows, each with a colored number circle + text. */
    .steps{display:grid;gap:10px;margin-top:14px}
    .step{
      display:grid;grid-template-columns:34px minmax(0,1fr);gap:12px;align-items:flex-start;
      padding:10px 12px;border-radius:10px;
      background:linear-gradient(180deg,rgba(36,129,188,.04),rgba(124,58,237,.04));
      border:1px solid rgba(36,129,188,.12);
    }
    /* Numbered blue-gradient circle. */
    .step-num{
      width:30px;height:30px;border-radius:50%;
      background:linear-gradient(135deg,#2481bc,#1a6494);color:#fff;
      display:grid;place-items:center;font-weight:800;font-size:.92rem;
      box-shadow:0 4px 10px rgba(36,129,188,.32);
    }
    .step-text{font-size:.9rem;line-height:1.4}
    .step-text strong{font-weight:800}

    /* =========================================================
       Actions row + button system
       =========================================================
       Base .btn is a white outlined pill; .primary is the blue-gradient
       CTA; .ghost is transparent (for tertiary actions). All lift 1px
       on hover for consistent feedback. Used on both the left redeem
       card (Directions/Share/New demo code) and the right demo card
       (Open vendor scanner / Reset status). */
    .actions-row{
      display:flex;flex-wrap:wrap;gap:8px;margin-top:12px;
    }
    .btn{
      appearance:none;border:1px solid var(--border);background:var(--surface);
      padding:9px 14px;border-radius:10px;font-weight:700;font-size:.88rem;
      color:var(--text);cursor:pointer;display:inline-flex;align-items:center;gap:7px;
      text-decoration:none;font-family:inherit;
      transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease,background .15s ease;
    }
    .btn:hover{transform:translateY(-1px);box-shadow:var(--shadow-sm);border-color:#cbd5e1}
    .btn.primary{
      background:linear-gradient(135deg,#2481bc,#1a6494);color:#fff;border-color:transparent;
      box-shadow:0 8px 22px rgba(36,129,188,.32);
    }
    .btn.primary:hover{box-shadow:0 12px 28px rgba(36,129,188,.42)}
    .btn.ghost{background:transparent}
    .btn i{font-size:.92rem}

    /* =========================================================
       Side card — key/value details rows on the right column
       =========================================================
       Used by the "Redemption details" card. Each .row is a
       label-left / value-right pair with a top dashed separator
       (except the first row). .v.mono uses the monospaced code font. */
    .side-card .row{
      display:flex;align-items:center;justify-content:space-between;gap:10px;
      padding:9px 0;border-top:1px dashed var(--border);
    }
    .side-card .row:first-of-type{border-top:0}
    .side-card .row .k{font-size:.85rem;color:var(--muted);font-weight:600}
    .side-card .row .v{font-size:.92rem;font-weight:700}
    .side-card .row .v.mono{font-family:'JetBrains Mono',monospace;font-size:.85rem}

    /* Dashed-border explainer card — used inside "Try the live demo"
       to explain the two-tab workflow. */
    .ghost-card{
      background:linear-gradient(180deg,rgba(36,129,188,.06),rgba(124,58,237,.04));
      border:1px dashed rgba(36,129,188,.3);border-radius:var(--radius);
      padding:14px;font-size:.86rem;color:var(--text);line-height:1.5;
    }
    .ghost-card strong{color:var(--primary)}

    /* =========================================================
       Footer + confetti overlay
       ========================================================= */
    footer{
      max-width:1240px;margin:24px auto 0;padding:18px 16px 28px;
      color:var(--muted);font-size:.82rem;text-align:center;
    }
    footer a{color:var(--primary);text-decoration:none;font-weight:600}
    footer a:hover{text-decoration:underline}

    /* Confetti — 80 falling colored squares fired by fireConfetti()
       the moment the redemption flips to done. position:fixed so it
       covers the viewport; pointer-events:none so it never blocks a
       click; z-index:90 keeps it under modals (z-index:200). */
    .confetti{
      position:fixed;inset:0;pointer-events:none;z-index:90;overflow:hidden;
    }
    .confetti .piece{
      position:absolute;top:-20px;width:9px;height:14px;border-radius:2px;
      opacity:.95;animation:fall 2.6s ease-in forwards;
    }
    @keyframes fall{
      0%{transform:translateY(-30px) rotate(0);opacity:0}
      10%{opacity:1}
      100%{transform:translateY(110vh) rotate(720deg);opacity:0}
    }

    /* =========================================================
       Responsive breakpoints
       ========================================================= */
    /* Phone portrait: shrink hero + QR + code, tighten offer summary. */
    @media (max-width:559.98px){
      .hero{padding:32px 16px 70px}
      .qr-frame{width:230px;height:230px}
      .code-pill{font-size:1.8rem;padding:12px 18px 12px 22px}
      .offer-summary{grid-template-columns:46px minmax(0,1fr) auto;gap:8px;padding:10px}
      .offer-emoji{width:46px;height:46px;font-size:1.5rem}
    }

    /* Landscape phone: keep the QR + code visible without excessive scroll.
       Trims hero padding, shrinks the QR to fit within a short viewport,
       and hides the animated blobs so the code stays the focal point. */
    @media (orientation:landscape) and (max-height:500px){
      .topbar-inner{padding:8px 14px}
      .hero{padding:20px 16px 44px}
      .hero::before,.hero::after{display:none}
      .hero h1{font-size:1.4rem;line-height:1.15;letter-spacing:-.4px}
      .hero p.lead{font-size:.85rem;line-height:1.45}
      .wrap{margin-top:-28px;padding:0 14px 24px;gap:14px}
      .card{padding:14px 14px 16px 18px}
      .card-titles h2{font-size:.98rem}
      .card-titles p{font-size:.76rem}
      .qr-stage{padding:12px 10px 4px;gap:12px}
      .qr-frame{width:180px;height:180px;padding:10px}
      .code-pill{font-size:1.35rem;padding:10px 16px 10px 20px;letter-spacing:.14em;word-break:break-all}
      .code-hint{font-size:.72rem}
      .offer-summary{grid-template-columns:44px minmax(0,1fr) auto;gap:8px;padding:9px}
      .offer-emoji{width:44px;height:44px;font-size:1.4rem}
      .status-row{margin-bottom:10px}
    }
