// ══════════════════════════════════════════════════════════════════════════════
// GUSTAI LAB — SUBSCRIPTION SCREENS
// lib/screens-subscription.jsx
//
// Screens: GLSubscribeOnboard | GLSubscriberDash | GLSwapBox | GLSubscribeConfirmed
// Requires: design-system.jsx (GL, GLFonts, GLImage, GLButton, GLTabBar, GLTopBar, GL_WEEKLY_MENU)
// ══════════════════════════════════════════════════════════════════════════════

// ── Shared helpers ────────────────────────────────────────────────────────────
function fmtThb(n) { return '฿' + Number(n).toLocaleString(); }

function dayLabel(d) {
  return ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'][d] || 'Thu';
}

function msUntil(isoStr) {
  return new Date(isoStr).getTime() - Date.now();
}

function fmtCountdown(ms) {
  if (ms <= 0) return 'Locked';
  const h = Math.floor(ms / 3600000);
  const d = Math.floor(h / 24);
  const rh = h % 24;
  if (d > 0) return `${d}d ${rh}h`;
  const m = Math.floor((ms % 3600000) / 60000);
  return `${h}h ${m}m`;
}

function calcDropDates(deliveryDay, frequency) {
  const DNAMES = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
  const today = new Date();
  const todayIdx = (today.getDay() + 6) % 7; // 0=Mon
  let diff = deliveryDay - todayIdx;
  if (diff <= 0) diff += 7;
  const first = new Date(today);
  first.setDate(today.getDate() + diff);
  first.setHours(0, 0, 0, 0);
  const drops = [];
  for (let i = 0; i < 3; i++) {
    let d;
    if (frequency === 'monthly') {
      d = new Date(first);
      d.setMonth(d.getMonth() + i);
    } else {
      const gap = frequency === 'biweekly' ? 14 : 7;
      d = new Date(first);
      d.setDate(first.getDate() + gap * i);
    }
    drops.push(DNAMES[(d.getDay() + 6) % 7] + ' ' + d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' }));
  }
  return drops;
}

// Badge chip for menu items
function FeatureBadge({ badge }) {
  if (!badge) return null;
  const colors = {
    'CHEF PICK':  { bg: GL.teal,    color: GL.ink },
    'NEW':        { bg: '#A8E10C',  color: GL.ink },
    'LIMITED':    { bg: '#FF4D8D',  color: '#fff'  },
    'BESTSELLER': { bg: GL.ink,     color: '#fff'  },
    'CLASSIC':    { bg: '#E8ECEC',  color: GL.ink  },
  };
  const style = colors[badge] || { bg: '#E8ECEC', color: GL.ink };
  return (
    <span style={{
      display: 'inline-block', padding: '2px 7px', borderRadius: 4,
      fontSize: 9, fontWeight: 800, letterSpacing: '0.12em',
      background: style.bg, color: style.color,
    }}>{badge}</span>
  );
}

// Quantity stepper
function QtyStepper({ qty, onInc, onDec, disabled }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
      <button onClick={onDec} disabled={qty <= 0} style={{
        width: 28, height: 28, borderRadius: '50%', border: '1.5px solid ' + GL.line,
        background: qty <= 0 ? '#f5f5f5' : '#fff',
        display: 'grid', placeItems: 'center', cursor: qty <= 0 ? 'not-allowed' : 'pointer',
        fontSize: 16, color: qty <= 0 ? GL.muted : GL.ink, fontWeight: 700,
      }}>−</button>
      <span style={{ fontSize: 15, fontWeight: 700, minWidth: 18, textAlign: 'center', fontFamily: GL.mono }}>{qty}</span>
      <button onClick={onInc} disabled={disabled} style={{
        width: 28, height: 28, borderRadius: '50%', border: 'none',
        background: disabled ? '#ccc' : GL.teal, color: GL.ink,
        display: 'grid', placeItems: 'center', cursor: disabled ? 'not-allowed' : 'pointer',
        fontSize: 16, fontWeight: 700,
      }}>+</button>
    </div>
  );
}

// Status chip for order cards
function StatusChip({ status }) {
  const map = {
    upcoming:   { bg: '#e4efff', color: '#1d4ed8', label: 'UPCOMING' },
    locked:     { bg: '#fff2d6', color: '#945200', label: 'LOCKED'   },
    billed:     { bg: '#deffcd', color: '#1f5f04', label: 'BILLED'   },
    dispatched: { bg: '#ece6ff', color: '#5a3ad6', label: 'OUT'      },
    delivered:  { bg: '#eef0f3', color: '#4a5160', label: 'DELIVERED'},
    skipped:    { bg: '#fde2e7', color: '#a61b30', label: 'SKIPPED'  },
    failed:     { bg: '#fde2e7', color: '#a61b30', label: 'FAILED'   },
  };
  const s = map[status] || map.upcoming;
  return (
    <span style={{
      display: 'inline-block', padding: '3px 9px', borderRadius: 999,
      fontSize: 10, fontWeight: 700, letterSpacing: '0.08em',
      background: s.bg, color: s.color,
    }}>{s.label}</span>
  );
}


// ══════════════════════════════════════════════════════════════════════════════
// SCREEN 1: GLSubscribeOnboard — 2-step onboarding wizard
// (Thailand prepay model: items already chosen via shop mode →
//  Step 1: Schedule + Duration combined with live calendar preview
//  Step 2: Prepay for the full term via PromptPay QR.)
// Per-product diet labels live on each item card (no wizard step).
// Frequency caps total run at ~6 months: weekly 24 / biweekly 12 / monthly 6.
// ══════════════════════════════════════════════════════════════════════════════

// Compute every drop date for the full subscription run
function calcAllDropDates(deliveryDay, frequency, count) {
  const today = new Date();
  const todayIdx = (today.getDay() + 6) % 7; // 0=Mon
  let diff = deliveryDay - todayIdx;
  if (diff <= 0) diff += 7;
  const first = new Date(today);
  first.setDate(today.getDate() + diff);
  first.setHours(0, 0, 0, 0);
  const drops = [];
  for (let i = 0; i < count; i++) {
    const d = new Date(first);
    if (frequency === 'monthly')      d.setMonth(d.getMonth() + i);
    else if (frequency === 'biweekly') d.setDate(first.getDate() + 14 * i);
    else                               d.setDate(first.getDate() + 7 * i);
    drops.push(d);
  }
  return drops;
}

function fmtDateShort(d) {
  return d.toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' });
}

function GLSubscribeOnboard({ go, app }) {
  const [step, setStep] = React.useState(0);
  const [freq, setFreq]  = React.useState('weekly');
  const [delDay, setDelDay] = React.useState(3); // Thu
  const [duration, setDuration] = React.useState(8); // # of deliveries
  const [paid, setPaid] = React.useState(false);

  // Items come from the global Subscribe-mode template
  const subItems = (app && app.subTemplate && app.subTemplate.length > 0)
    ? app.subTemplate.map(it => ({ ...it, qty: it.qty || 1 }))
    : [];

  const perBoxTotal = subItems.reduce((s, it) => s + (it.price || 0) * (it.qty || 1), 0);

  // Per-frequency tiers — cap each at ~6 months
  // weekly: 4/8/12/24 (max 24 wks ≈ 6mo)
  // biweekly: 4/8/12 (max 12 × 2wk = 24wks ≈ 6mo)
  // monthly: 2/4/6 (max 6 × 1mo = 6mo)
  const TIER_MAP = {
    weekly: [
      { deliveries: 4,  discount: 0,  label: 'Starter',     desc: '~1 month'  },
      { deliveries: 8,  discount: 3,  label: 'Regular',     desc: '~2 months' },
      { deliveries: 12, discount: 7,  label: 'Committed',   desc: '~3 months' },
      { deliveries: 24, discount: 12, label: 'Best Value',  desc: '~6 months' },
    ],
    biweekly: [
      { deliveries: 4,  discount: 0,  label: 'Starter',     desc: '~2 months' },
      { deliveries: 8,  discount: 5,  label: 'Regular',     desc: '~4 months' },
      { deliveries: 12, discount: 12, label: 'Best Value',  desc: '~6 months' },
    ],
    monthly: [
      { deliveries: 2,  discount: 0,  label: 'Starter',     desc: '2 months'  },
      { deliveries: 4,  discount: 5,  label: 'Regular',     desc: '4 months'  },
      { deliveries: 6,  discount: 12, label: 'Best Value',  desc: '6 months'  },
    ],
  };
  const tiers = TIER_MAP[freq];

  // Re-snap duration when frequency changes so it stays valid
  React.useEffect(() => {
    if (!tiers.find(t => t.deliveries === duration)) {
      setDuration(tiers[Math.min(1, tiers.length - 1)].deliveries);
    }
  }, [freq]);

  const tier = tiers.find(t => t.deliveries === duration) || tiers[0];
  const grossTotal   = perBoxTotal * duration;
  const discountAmt  = Math.round(grossTotal * (tier.discount / 100));
  const prepayTotal  = grossTotal - discountAmt;
  const perDeliveryCap = duration > 0 ? Math.round(prepayTotal / duration) : 0;

  // Live calendar preview
  const allDrops = calcAllDropDates(delDay, freq, duration);
  const firstDrop = allDrops[0];
  const lastDrop  = allDrops[allDrops.length - 1];
  // Coverage (matches tier desc): each box covers one cycle.
  // weekly: count × 7d, biweekly: count × 14d, monthly: count × 30d.
  const cycleDays = freq === 'monthly' ? 30 : freq === 'biweekly' ? 14 : 7;
  const coverageDays = duration * cycleDays;
  const totalMonths = Math.max(1, Math.round(coverageDays / 30));

  const FREQS = [
    { id: 'weekly',   label: 'Weekly',    sub: 'Every week'    },
    { id: 'biweekly', label: 'Bi-weekly', sub: 'Every 2 weeks' },
    { id: 'monthly',  label: 'Monthly',   sub: 'Once a month'  },
  ];
  const DAYS = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];

  const titles = ['SCHEDULE & DURATION', 'PREPAY VIA QR'];
  const canNext = [duration > 0 && perBoxTotal > 0, paid][step];
  const progress = ((step + 1) / 2) * 100;

  function handleSubscribe() {
    if (window.GLSubscription) {
      window.GLSubscription.createPlan({
        frequency: freq,
        delivery_day: delDay,
        duration_deliveries: duration,
        per_delivery_cap: perDeliveryCap,
        prepay_total: prepayTotal,
        discount_pct: tier.discount,
        first_drop: firstDrop ? firstDrop.toISOString() : null,
        last_drop: lastDrop ? lastDrop.toISOString() : null,
        standing_items: subItems.map(it => ({ id: it.id, qty: it.qty, price: it.price, name: it.name, diet_labels: it.diet_labels || [] })),
        payment_method: 'promptpay_qr',
      });
    }
    if (app && app.clearSubTemplate) app.clearSubTemplate();
    go('subscribe-confirmed');
  }

  return (
    <div style={{ position: 'absolute', inset: 0, background: '#fff', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      {/* Header */}
      <div style={{
        paddingTop: 56, paddingBottom: 12, paddingLeft: 20, paddingRight: 20,
        background: '#fff', borderBottom: `1px solid ${GL.line}`,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
          {step > 0
            ? <button onClick={() => setStep(s => s - 1)} style={{ padding: '4px 0', fontSize: 13, color: GL.tealDim, fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer' }}>← Back</button>
            : <button onClick={() => go('box')} style={{ padding: '4px 0', fontSize: 13, color: GL.tealDim, fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer' }}>← Box</button>}
          <span style={{ fontSize: 11, fontWeight: 700, color: GL.muted, letterSpacing: '0.12em' }}>{step + 1} / 2</span>
        </div>
        <div style={{ height: 3, background: GL.line, borderRadius: 2, overflow: 'hidden' }}>
          <div style={{ height: '100%', width: progress + '%', background: GL.teal, borderRadius: 2, transition: 'width 0.3s' }}/>
        </div>
        <div className="gl-display" style={{ fontSize: 20, marginTop: 12, letterSpacing: '0.04em', color: GL.ink }}>
          {titles[step]}
        </div>
      </div>

      {/* Body */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '16px 20px 160px' }} className="gl-noscroll">

        {/* Step 1: Schedule + Duration combined */}
        {step === 0 && (
          <div>
            {perBoxTotal === 0 ? (
              <div style={{ padding: 20, textAlign: 'center', background: '#fff7e6', border: '1px solid #ffd591', borderRadius: 10, color: '#874d00' }}>
                <div style={{ fontWeight: 700, marginBottom: 6 }}>No items in your standing order</div>
                <div style={{ fontSize: 12, marginBottom: 12 }}>Go back and add at least one pasta set to your subscription.</div>
                <button onClick={() => go('home')} style={{ padding: '8px 16px', background: GL.ink, color: '#fff', border: 'none', borderRadius: 8, fontSize: 12, fontWeight: 700, cursor: 'pointer' }}>Browse Menu</button>
              </div>
            ) : (
              <>
                {/* Standing order recap */}
                <div style={{ padding: '12px 14px', background: '#f8f9fa', borderRadius: 10, marginBottom: 18 }}>
                  <div style={{ fontSize: 10, fontWeight: 800, color: GL.muted, letterSpacing: '0.12em', marginBottom: 6 }}>EVERY BOX CONTAINS</div>
                  {subItems.map((it, i) => (
                    <div key={i} style={{ display: 'flex', justifyContent: 'space-between', padding: '4px 0', fontSize: 12 }}>
                      <span style={{ color: GL.ink }}>{it.name} <span style={{ color: GL.muted }}>× {it.qty || 1}</span></span>
                      <span style={{ fontWeight: 700 }}>{fmtThb((it.price || 0) * (it.qty || 1))}</span>
                    </div>
                  ))}
                  <div style={{ display: 'flex', justifyContent: 'space-between', paddingTop: 8, marginTop: 4, borderTop: `1px dashed ${GL.line}`, fontSize: 13, fontWeight: 800, color: GL.ink }}>
                    <span>Per box</span><span>{fmtThb(perBoxTotal)}</span>
                  </div>
                </div>

                {/* Frequency */}
                <div style={{ marginBottom: 18 }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: GL.muted, letterSpacing: '0.1em', marginBottom: 8 }}>HOW OFTEN?</div>
                  <div style={{ display: 'flex', gap: 8 }}>
                    {FREQS.map(f => {
                      const on = freq === f.id;
                      return (
                        <button key={f.id} onClick={() => setFreq(f.id)}
                          style={{
                            flex: 1, padding: '12px 6px', borderRadius: 10,
                            border: `2px solid ${on ? GL.teal : GL.line}`,
                            background: on ? (GL.teal + '18') : '#fff',
                            cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 2,
                          }}>
                          <span style={{ fontWeight: 800, fontSize: 13, color: GL.ink }}>{f.label}</span>
                          <span style={{ fontSize: 10, color: GL.muted }}>{f.sub}</span>
                        </button>
                      );
                    })}
                  </div>
                </div>

                {/* Delivery day */}
                <div style={{ marginBottom: 18 }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: GL.muted, letterSpacing: '0.1em', marginBottom: 8 }}>DELIVERY DAY</div>
                  <div style={{ display: 'flex', gap: 6, overflowX: 'auto', paddingBottom: 4 }} className="gl-noscroll">
                    {DAYS.map((d, i) => (
                      <button key={i} onClick={() => setDelDay(i)}
                        style={{
                          minWidth: 44, height: 44, borderRadius: 8, border: 'none',
                          background: delDay === i ? GL.teal : GL.line,
                          color: delDay === i ? GL.ink : GL.muted,
                          fontWeight: 700, fontSize: 11, cursor: 'pointer', flexShrink: 0,
                        }}>{d}</button>
                    ))}
                  </div>
                </div>

                {/* Duration tier picker */}
                <div style={{ marginBottom: 18 }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: GL.muted, letterSpacing: '0.1em', marginBottom: 8 }}>HOW MANY DELIVERIES?</div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                    {tiers.map(t => {
                      const on = t.deliveries === duration;
                      const tGross = perBoxTotal * t.deliveries;
                      const tDisc = Math.round(tGross * (t.discount / 100));
                      const tNet = tGross - tDisc;
                      return (
                        <button key={t.deliveries} onClick={() => setDuration(t.deliveries)}
                          style={{
                            display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px',
                            border: `2px solid ${on ? GL.teal : GL.line}`, borderRadius: 12,
                            background: on ? GL.teal + '18' : '#fff',
                            cursor: 'pointer', textAlign: 'left',
                          }}>
                          <div style={{
                            width: 22, height: 22, borderRadius: '50%', flexShrink: 0,
                            border: `2px solid ${on ? GL.teal : GL.line}`,
                            background: on ? GL.teal : '#fff',
                            display: 'grid', placeItems: 'center',
                          }}>
                            {on && <span style={{ fontSize: 11, color: GL.ink, fontWeight: 900, lineHeight: 1 }}>✓</span>}
                          </div>
                          <div style={{ flex: 1, minWidth: 0 }}>
                            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 2 }}>
                              <span style={{ fontWeight: 800, fontSize: 14, color: GL.ink }}>{t.deliveries} deliveries</span>
                              {t.discount > 0 && (
                                <span style={{ fontSize: 10, fontWeight: 800, padding: '2px 7px', borderRadius: 4, background: GL.teal, color: GL.ink, letterSpacing: '0.08em' }}>SAVE {t.discount}%</span>
                              )}
                            </div>
                            <div style={{ fontSize: 11, color: GL.muted }}>{t.label} · {t.desc}</div>
                          </div>
                          <div style={{ textAlign: 'right', flexShrink: 0 }}>
                            <div style={{ fontSize: 14, fontWeight: 800, color: GL.ink }}>{fmtThb(tNet)}</div>
                            {tDisc > 0 && (
                              <div style={{ fontSize: 10, color: GL.muted, textDecoration: 'line-through' }}>{fmtThb(tGross)}</div>
                            )}
                          </div>
                        </button>
                      );
                    })}
                  </div>
                  <div style={{ marginTop: 8, fontSize: 11, color: GL.muted, lineHeight: 1.5 }}>
                    Subscriptions cap at ~6 months so you come back to see new pasta drops.
                  </div>
                </div>

                {/* LIVE CALENDAR PREVIEW */}
                <div style={{ padding: '14px 14px 16px', borderRadius: 12, background: GL.teal + '0d', border: `1.5px solid ${GL.teal}` }}>
                  <div style={{ fontSize: 11, fontWeight: 800, color: GL.tealDim, letterSpacing: '0.12em', marginBottom: 10 }}>YOUR SCHEDULE</div>

                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 10 }}>
                    <div>
                      <div style={{ fontSize: 10, fontWeight: 700, color: GL.muted, letterSpacing: '0.08em' }}>FIRST DROP</div>
                      <div style={{ fontSize: 14, fontWeight: 800, color: GL.ink, marginTop: 2 }}>{firstDrop ? fmtDateShort(firstDrop) : '—'}</div>
                    </div>
                    <div style={{ textAlign: 'right' }}>
                      <div style={{ fontSize: 10, fontWeight: 700, color: GL.muted, letterSpacing: '0.08em' }}>LAST DROP</div>
                      <div style={{ fontSize: 14, fontWeight: 800, color: GL.ink, marginTop: 2 }}>{lastDrop ? fmtDateShort(lastDrop) : '—'}</div>
                    </div>
                  </div>

                  <div style={{ display: 'flex', justifyContent: 'space-between', padding: '10px 0', borderTop: `1px dashed ${GL.teal}55`, borderBottom: `1px dashed ${GL.teal}55`, marginBottom: 10 }}>
                    <span style={{ fontSize: 12, color: GL.muted }}>Total run</span>
                    <span style={{ fontSize: 13, fontWeight: 800, color: GL.ink }}>~{totalMonths} {totalMonths === 1 ? 'month' : 'months'} · {duration} boxes</span>
                  </div>

                  {/* Mini drop list — first 6, then "+N more" */}
                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                    {allDrops.slice(0, 6).map((d, i) => (
                      <span key={i} style={{
                        fontSize: 10, fontWeight: 700, padding: '4px 8px', borderRadius: 6,
                        background: i === 0 ? GL.teal : '#fff',
                        color: i === 0 ? GL.ink : GL.tealDim,
                        border: `1px solid ${GL.teal}`,
                      }}>{fmtDateShort(d)}</span>
                    ))}
                    {allDrops.length > 6 && (
                      <span style={{ fontSize: 10, color: GL.muted, fontWeight: 700, padding: '4px 4px' }}>+{allDrops.length - 6} more</span>
                    )}
                  </div>

                  <div style={{ marginTop: 12, paddingTop: 10, borderTop: `1px dashed ${GL.teal}55`, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                    <div>
                      <div style={{ fontSize: 10, fontWeight: 700, color: GL.muted, letterSpacing: '0.08em' }}>PAY TODAY</div>
                      <div style={{ fontSize: 11, color: GL.muted, marginTop: 2 }}>{duration} × {fmtThb(perDeliveryCap)} cap/box</div>
                    </div>
                    <div style={{ fontSize: 22, fontWeight: 900, color: GL.ink }}>{fmtThb(prepayTotal)}</div>
                  </div>
                </div>
              </>
            )}
          </div>
        )}

        {/* Step 2: Prepay via QR */}
        {step === 1 && (
          <div>
            {/* Summary */}
            <div style={{ padding: '14px 16px', background: '#f8f9fa', borderRadius: 12, marginBottom: 16 }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: GL.muted, letterSpacing: '0.08em', marginBottom: 10 }}>YOUR PLAN</div>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                <span style={{ fontSize: 13, color: GL.muted }}>Schedule</span>
                <span style={{ fontSize: 13, fontWeight: 600, color: GL.ink, textTransform: 'capitalize' }}>{freq} · {DAYS[delDay]}</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                <span style={{ fontSize: 13, color: GL.muted }}>First / last drop</span>
                <span style={{ fontSize: 13, fontWeight: 600, color: GL.ink }}>{firstDrop ? fmtDateShort(firstDrop) : '—'} → {lastDrop ? fmtDateShort(lastDrop) : '—'}</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                <span style={{ fontSize: 13, color: GL.muted }}>Duration</span>
                <span style={{ fontSize: 13, fontWeight: 600, color: GL.ink }}>{duration} deliveries · ~{totalMonths}mo</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span style={{ fontSize: 13, color: GL.muted }}>Per-box cap</span>
                <span style={{ fontSize: 13, fontWeight: 700, color: GL.tealDim }}>{fmtThb(perDeliveryCap)}</span>
              </div>
            </div>

            {/* Charge breakdown */}
            <div style={{ marginBottom: 16, padding: '12px 14px', border: `1px solid ${GL.line}`, borderRadius: 10 }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: GL.muted, letterSpacing: '0.08em', marginBottom: 10 }}>CHARGE BREAKDOWN</div>
              <div style={{ display: 'flex', justifyContent: 'space-between', padding: '4px 0', fontSize: 13 }}>
                <span>{fmtThb(perBoxTotal)} × {duration} boxes</span>
                <span>{fmtThb(grossTotal)}</span>
              </div>
              {tier.discount > 0 && (
                <div style={{ display: 'flex', justifyContent: 'space-between', padding: '4px 0', fontSize: 13, color: GL.tealDim, fontWeight: 700 }}>
                  <span>Tier discount ({tier.discount}%)</span>
                  <span>−{fmtThb(discountAmt)}</span>
                </div>
              )}
              <div style={{ display: 'flex', justifyContent: 'space-between', padding: '10px 0 0', marginTop: 4, borderTop: `1px solid ${GL.line}`, fontSize: 16, fontWeight: 800, color: GL.ink }}>
                <span>Pay today</span>
                <span>{fmtThb(prepayTotal)}</span>
              </div>
            </div>

            {/* PromptPay QR placeholder */}
            <div style={{ marginBottom: 16, padding: '20px 14px', textAlign: 'center', border: `1px solid ${GL.line}`, borderRadius: 12, background: '#fff' }}>
              <div style={{ fontSize: 11, fontWeight: 800, color: GL.muted, letterSpacing: '0.12em', marginBottom: 8 }}>PROMPTPAY · ONE-TIME SCAN</div>
              <div style={{
                width: 180, height: 180, margin: '0 auto 12px',
                background: paid ? GL.teal + '10' : '#fff', border: `2px ${paid ? 'solid' : 'dashed'} ${paid ? GL.teal : GL.line}`,
                borderRadius: 14, display: 'grid', placeItems: 'center', position: 'relative', overflow: 'hidden',
              }}>
                {paid ? (
                  <div style={{ textAlign: 'center' }}>
                    <div style={{ fontSize: 44, color: GL.tealDim, lineHeight: 1 }}>✓</div>
                    <div style={{ fontSize: 11, fontWeight: 800, color: GL.tealDim, marginTop: 4, letterSpacing: '0.1em' }}>PAID</div>
                  </div>
                ) : (
                  <div style={{
                    width: 140, height: 140,
                    background: 'repeating-linear-gradient(45deg, #0a0a0a 0 3px, #fff 3px 6px)',
                    borderRadius: 6,
                  }}/>
                )}
              </div>
              <div style={{ fontSize: 12, color: GL.muted, marginBottom: 6 }}>Scan with your bank app to pay</div>
              <div style={{ fontSize: 16, fontWeight: 800, color: GL.ink, marginBottom: 12 }}>{fmtThb(prepayTotal)}</div>
              {!paid && (
                <button onClick={() => setPaid(true)} style={{
                  padding: '8px 16px', background: GL.ink, color: '#fff', border: 'none', borderRadius: 8,
                  fontSize: 11, fontWeight: 800, letterSpacing: '0.1em', cursor: 'pointer',
                }}>SIMULATE PAID</button>
              )}
            </div>

            <div style={{ padding: '10px 14px', background: '#f8f9fa', borderRadius: 8, fontSize: 11.5, color: GL.muted, lineHeight: 1.5 }}>
              We'll remind you 7 days before your last delivery to renew. Cancel anytime — unused balance becomes account credit (never expires).
            </div>
          </div>
        )}
      </div>

      {/* Footer CTA */}
      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0,
        padding: '12px 20px 32px',
        background: 'rgba(255,255,255,0.96)', backdropFilter: 'blur(12px)',
        borderTop: `1px solid ${GL.line}`,
      }}>
        {step === 0 && perBoxTotal > 0 && (
          <div style={{ fontSize: 13, fontWeight: 700, color: GL.ink, marginBottom: 8, display: 'flex', justifyContent: 'space-between' }}>
            <span>Pay today {fmtThb(prepayTotal)}</span>
            <span style={{ color: GL.muted }}>{duration} × {fmtThb(perDeliveryCap)}/box</span>
          </div>
        )}
        <GLButton
          onClick={step < 1 ? () => setStep(s => s + 1) : handleSubscribe}
          disabled={!canNext}
          variant="teal"
        >
          {step < 1
            ? (perBoxTotal === 0 ? 'Add items first' : 'Continue to payment')
            : (paid ? 'Activate Subscription' : 'Awaiting payment…')}
        </GLButton>
      </div>
    </div>
  );
}

// ══════════════════════════════════════════════════════════════════════════════
// SCREEN 2: GLSubscriberDash — subscriber account home
// ══════════════════════════════════════════════════════════════════════════════

// Mock upcoming orders data
const MOCK_UPCOMING_ORDERS = [
  {
    id: 'so-1', week_label: 'Week of 21 Apr', dispatch_date: '2026-04-24',
    status: 'upcoming', estimated_total: 960,
    cutoff_at: '2026-04-22T18:00:00+07:00',
    items: [
      { name: 'Truffle Mafaldine',    img: 'https://images.unsplash.com/photo-1621996346565-e3dbc646d9a9?w=100' },
      { name: 'Squid Ink Tagliolini', img: 'https://images.unsplash.com/photo-1563379926898-05f4575a45d8?w=100' },
      { name: 'Burrata & Pomodoro',   img: 'https://images.unsplash.com/photo-1598866594230-a7c12756260f?w=100' },
      { name: 'Cacio e Pepe',         img: 'https://images.unsplash.com/photo-1612874742237-6526221588e3?w=100' },
    ],
  },
  {
    id: 'so-2', week_label: 'Week of 28 Apr', dispatch_date: '2026-05-01',
    status: 'locked', estimated_total: 680,
    cutoff_at: '2026-04-29T18:00:00+07:00',
    items: [
      { name: 'Cacio e Pepe',         img: 'https://images.unsplash.com/photo-1612874742237-6526221588e3?w=100' },
      { name: 'Pesto alla Genovese',  img: 'https://images.unsplash.com/photo-1473093295043-cdd812d0e601?w=100' },
    ],
  },
  {
    id: 'so-3', week_label: 'Week of 5 May', dispatch_date: '2026-05-08',
    status: 'upcoming', estimated_total: 580,
    cutoff_at: '2026-05-06T18:00:00+07:00',
    items: [
      { name: 'Pesto alla Genovese',  img: 'https://images.unsplash.com/photo-1473093295043-cdd812d0e601?w=100' },
      { name: 'Truffle Mafaldine',    img: 'https://images.unsplash.com/photo-1621996346565-e3dbc646d9a9?w=100' },
    ],
  },
  {
    id: 'so-4', week_label: 'Week of 12 May', dispatch_date: '2026-05-15',
    status: 'skipped', estimated_total: 0,
    cutoff_at: '2026-05-13T18:00:00+07:00',
    items: [],
  },
];

const MOCK_PAST_ORDERS = [
  { id: 'so-p1', week_label: 'Week of 14 Apr', dispatch_date: '2026-04-17', status: 'delivered', billed_total: 760, items: [{ name: 'Truffle Mafaldine' }, { name: 'Rigatoni' }] },
  { id: 'so-p2', week_label: 'Week of 7 Apr',  dispatch_date: '2026-04-10', status: 'delivered', billed_total: 580, items: [{ name: 'Cacio e Pepe' }, { name: 'Pesto' }] },
  { id: 'so-p3', week_label: 'Week of 31 Mar', dispatch_date: '2026-04-03', status: 'delivered', billed_total: 680, items: [{ name: 'Squid Ink' }] },
];

function GLSubscriberDash({ go, app }) {
  const [skipped, setSkipped] = React.useState({});
  const [orders, setOrders] = React.useState(MOCK_UPCOMING_ORDERS);
  const [loading, setLoading] = React.useState(false);
  const [discontinued, setDiscontinued] = React.useState([]); // product ids
  const [replaceFor, setReplaceFor] = React.useState(null);   // {oldId, name} | null

  const plan = { status: 'ACTIVE', frequency: 'Weekly', day: 'Thu' };

  // Mock standing items for this subscriber (used by discontinuation check).
  // In production this comes from the active plan record.
  const standingItemIds = React.useMemo(() => {
    const set = new Set();
    orders.forEach(o => (o.items || []).forEach(i => {
      // map demo names back to product ids
      const map = {
        'Squid Ink Tagliolini': 'squid',
        'Truffle Mafaldine':    'truffle',
        'Burrata & Pomodoro':   'burrata',
        'Cacio e Pepe':         'cacio',
        'Pesto alla Genovese':  'pesto',
        'Classic Rigatoni':     'rigatoni',
      };
      const id = map[i.name];
      if (id) set.add(id);
    }));
    return Array.from(set);
  }, [orders]);

  React.useEffect(() => {
    let alive = true;
    if (window.GLSubscription && standingItemIds.length > 0) {
      window.GLSubscription.getDiscontinuedInOrder(standingItemIds).then(ids => {
        if (alive) setDiscontinued(ids);
      });
    }
    return () => { alive = false; };
  }, [standingItemIds.join(',')]);

  // Replacement candidates: same category (sets), within per-delivery cap,
  // not already in standing order, not discontinued.
  const PER_BOX_CAP = 1200; // mock cap from active plan
  const replacementOptions = React.useMemo(() => {
    if (!replaceFor) return [];
    const pool = (window.GL_PASTA || []).filter(p =>
      p.id !== replaceFor.oldId &&
      !discontinued.includes(p.id) &&
      p.price <= PER_BOX_CAP
    );
    return pool;
  }, [replaceFor, discontinued]);

  function pickReplacement(newItem) {
    if (window.GLSubscription && replaceFor) {
      window.GLSubscription.replaceDiscontinuedItem('so-1', replaceFor.oldId, newItem.id);
    }
    // Optimistically update local state: drop the discontinued name from upcoming
    setOrders(os => os.map(o => ({
      ...o,
      items: (o.items || []).map(i => {
        const nameMap = { 'squid': 'Squid Ink Tagliolini' };
        if (i.name === nameMap[replaceFor.oldId]) return { ...i, name: newItem.name, img: newItem.img };
        return i;
      }),
    })));
    setDiscontinued(d => d.filter(id => id !== replaceFor.oldId));
    setReplaceFor(null);
  }

  const toggleSkip = (orderId) => {
    setOrders(os => os.map(o => {
      if (o.id !== orderId) return o;
      return { ...o, status: o.status === 'skipped' ? 'upcoming' : 'skipped' };
    }));
  };

  return (
    <div style={{ position: 'absolute', inset: 0, background: '#f6f8f8', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      <GLTopBar title="MY SUBSCRIPTION" onBack={() => go('me')} dark={false}/>

      <div style={{ flex: 1, overflowY: 'auto', paddingBottom: 100 }} className="gl-noscroll">

        {/* Plan status bar */}
        <div style={{ margin: 16, padding: '14px 16px', background: '#fff', borderRadius: 12, border: `1px solid ${GL.line}` }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
            <div>
              <div style={{ fontSize: 11, fontWeight: 700, color: GL.tealDim, letterSpacing: '0.1em' }}>
                {plan.status} SUBSCRIPTION
              </div>
              <div style={{ fontSize: 13, fontWeight: 600, color: GL.ink, marginTop: 2 }}>
                {plan.frequency} · {plan.day} delivery
              </div>
            </div>
            <div style={{
              width: 10, height: 10, borderRadius: '50%', background: '#22c55e',
              boxShadow: '0 0 0 3px rgba(34,197,94,0.2)',
            }}/>
          </div>
          <div style={{ display: 'flex', gap: 12 }}>
            <button onClick={() => {}} style={{ fontSize: 12, color: GL.tealDim, fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}>
              Pause subscription
            </button>
            <button onClick={() => {}} style={{ fontSize: 12, color: '#a61b30', fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer', padding: 0 }}>
              Cancel
            </button>
          </div>
        </div>

        {/* Discontinuation banner — surfaces items removed from the menu */}
        {discontinued.length > 0 && (
          <div style={{ margin: '0 16px 12px', padding: '14px 16px', background: '#fff7e6', border: '1.5px solid #ffb84d', borderRadius: 12 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
              <span style={{ fontSize: 18 }}>⚠️</span>
              <div style={{ fontWeight: 800, fontSize: 13, color: '#874d00', letterSpacing: '0.04em' }}>ACTION NEEDED</div>
            </div>
            <div style={{ fontSize: 12.5, color: '#874d00', lineHeight: 1.5, marginBottom: 10 }}>
              {discontinued.length === 1 ? 'An item' : `${discontinued.length} items`} in your standing order {discontinued.length === 1 ? 'is' : 'are'} no longer on the menu. Pick a replacement so your next box ships on time.
            </div>
            {discontinued.map(id => {
              const product = (window.GL_PASTA || []).find(p => p.id === id);
              const name = product ? product.name : id;
              return (
                <div key={id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 10px', background: '#fff', borderRadius: 8, marginBottom: 6 }}>
                  <div>
                    <div style={{ fontSize: 13, fontWeight: 700, color: GL.ink, textDecoration: 'line-through', opacity: 0.6 }}>{name}</div>
                    <div style={{ fontSize: 10, color: '#945200', fontWeight: 700, letterSpacing: '0.06em' }}>NO LONGER AVAILABLE</div>
                  </div>
                  <button onClick={() => setReplaceFor({ oldId: id, name })} style={{
                    padding: '8px 14px', borderRadius: 8, background: GL.ink, color: '#fff',
                    fontSize: 11, fontWeight: 800, letterSpacing: '0.08em', border: 'none', cursor: 'pointer',
                  }}>PICK REPLACEMENT</button>
                </div>
              );
            })}
          </div>
        )}

        {/* Upcoming deliveries */}
        <div style={{ padding: '0 16px', marginBottom: 8 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: GL.muted, letterSpacing: '0.12em', marginBottom: 10 }}>UPCOMING DELIVERIES</div>
        </div>

        {orders.map((order) => {
          const cutoffMs = msUntil(order.cutoff_at);
          const lockingSoon = cutoffMs > 0 && cutoffMs < 72 * 3600000;
          const isUpcoming = order.status === 'upcoming';

          return (
            <div key={order.id} style={{
              margin: '0 16px 12px', padding: 16,
              background: order.status === 'skipped' ? '#f8f9fa' : '#fff',
              borderRadius: 12, border: `1px solid ${GL.line}`,
              opacity: order.status === 'skipped' ? 0.65 : 1,
            }}>
              {/* Week header */}
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
                <div>
                  <div style={{ fontWeight: 700, fontSize: 13, color: GL.ink }}>{order.week_label}</div>
                  <div style={{ fontSize: 11, color: GL.muted }}>Ships {new Date(order.dispatch_date).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' })}</div>
                </div>
                <StatusChip status={order.status}/>
              </div>

              {/* Items preview */}
              {order.items.length > 0 && (
                <div style={{ display: 'flex', gap: 6, marginBottom: 10, alignItems: 'center' }}>
                  {order.items.slice(0, 3).map((item, i) => (
                    <div key={i} style={{ width: 36, height: 36, borderRadius: 6, overflow: 'hidden', background: GL.line, flexShrink: 0 }}>
                      {item.img && <img src={item.img} alt={item.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>}
                    </div>
                  ))}
                  {order.items.length > 3 && (
                    <span style={{ fontSize: 11, color: GL.muted, fontWeight: 600 }}>+{order.items.length - 3} more</span>
                  )}
                  {order.items.length === 0 && (
                    <span style={{ fontSize: 12, color: GL.muted, fontStyle: 'italic' }}>No items yet</span>
                  )}
                </div>
              )}

              {/* Estimated total + lock countdown */}
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
                {order.estimated_total > 0
                  ? <span style={{ fontSize: 14, fontWeight: 800, color: GL.ink }}>~{fmtThb(order.estimated_total)}</span>
                  : <span style={{ fontSize: 13, color: GL.muted }}>—</span>
                }
                {cutoffMs > 0 && order.status === 'upcoming' && (
                  <span style={{
                    fontSize: 11, fontWeight: 700, padding: '3px 8px', borderRadius: 6,
                    background: lockingSoon ? '#fff2d6' : '#f0f0f0',
                    color: lockingSoon ? '#945200' : GL.muted,
                  }}>
                    {lockingSoon ? '⚡ ' : ''}Locks in {fmtCountdown(cutoffMs)}
                  </span>
                )}
              </div>

              {/* Actions */}
              <div style={{ display: 'flex', gap: 8 }}>
                {isUpcoming && (
                  <button onClick={() => go('swap')} style={{
                    flex: 1, padding: '8px 0', borderRadius: 8,
                    border: `1.5px solid ${GL.ink}`, background: '#fff',
                    fontWeight: 700, fontSize: 12, cursor: 'pointer',
                  }}>Edit Box</button>
                )}
                <button onClick={() => toggleSkip(order.id)} style={{
                  padding: '8px 12px', borderRadius: 8,
                  border: `1.5px solid ${GL.line}`, background: '#fff',
                  fontWeight: 600, fontSize: 12, cursor: 'pointer',
                  color: order.status === 'skipped' ? GL.tealDim : '#a61b30',
                }}>
                  {order.status === 'skipped' ? 'Unskip' : 'Skip'}
                </button>
              </div>
            </div>
          );
        })}

        {/* Past orders */}
        <div style={{ padding: '8px 16px', marginBottom: 8 }}>
          <div style={{ fontSize: 11, fontWeight: 800, color: GL.muted, letterSpacing: '0.12em' }}>PAST ORDERS</div>
        </div>

        {MOCK_PAST_ORDERS.map(order => (
          <div key={order.id} style={{
            margin: '0 16px 10px', padding: '12px 14px',
            background: '#fff', borderRadius: 10, border: `1px solid ${GL.line}`,
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          }}>
            <div>
              <div style={{ fontSize: 13, fontWeight: 600, color: GL.ink }}>{order.week_label}</div>
              <div style={{ fontSize: 11, color: GL.muted, marginTop: 2 }}>
                {order.items.map(i => i.name).join(', ')}
              </div>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div style={{ fontSize: 14, fontWeight: 800 }}>{fmtThb(order.billed_total)}</div>
              <button style={{ fontSize: 11, color: GL.tealDim, fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer', marginTop: 2, padding: 0 }}>
                Reorder
              </button>
            </div>
          </div>
        ))}
      </div>

      <GLTabBar active="me" onNav={go} dark={false} shopMode={app && app.shopMode} cartCount={app ? ((app.shopMode === 'sub' ? (app.subTemplate || []) : app.cart) || []).length : 0}/>

      {/* Replacement picker modal */}
      {replaceFor && (
        <div onClick={() => setReplaceFor(null)} style={{
          position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.55)', zIndex: 100,
          display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
        }}>
          <div onClick={e => e.stopPropagation()} style={{
            background: '#fff', borderTopLeftRadius: 20, borderTopRightRadius: 20,
            maxHeight: '85%', display: 'flex', flexDirection: 'column',
          }}>
            <div style={{ padding: '16px 20px', borderBottom: `1px solid ${GL.line}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <div>
                <div className="gl-display" style={{ fontSize: 16, color: GL.ink, letterSpacing: '0.04em' }}>PICK A REPLACEMENT</div>
                <div style={{ fontSize: 11, color: GL.muted, marginTop: 4 }}>Replacing <strong>{replaceFor.name}</strong> · cap ฿{PER_BOX_CAP.toLocaleString()}/box</div>
              </div>
              <button onClick={() => setReplaceFor(null)} style={{ width: 32, height: 32, borderRadius: '50%', background: GL.line, color: GL.ink, fontSize: 18, fontWeight: 700, border: 'none', cursor: 'pointer' }}>×</button>
            </div>
            <div style={{ flex: 1, overflowY: 'auto', padding: 16 }} className="gl-noscroll">
              {replacementOptions.length === 0 ? (
                <div style={{ padding: 30, textAlign: 'center', color: GL.muted }}>No eligible replacements available right now. Please contact support.</div>
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {replacementOptions.map(p => (
                    <button key={p.id} onClick={() => pickReplacement(p)} style={{
                      display: 'flex', gap: 12, padding: 12,
                      border: `1.5px solid ${GL.line}`, borderRadius: 12, background: '#fff',
                      textAlign: 'left', cursor: 'pointer', alignItems: 'center',
                    }}>
                      <div style={{ width: 64, height: 64, borderRadius: 8, overflow: 'hidden', flexShrink: 0, background: GL.line }}>
                        {p.img && <img src={p.img} alt={p.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>}
                      </div>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontWeight: 800, fontSize: 13, color: GL.ink, marginBottom: 2 }}>{p.name}</div>
                        <div style={{ fontSize: 11, color: GL.muted, marginBottom: 4 }}>{p.pair}</div>
                        {p.diet_labels && <GLDietBadges labels={p.diet_labels} mode="compact"/>}
                      </div>
                      <div style={{ textAlign: 'right', flexShrink: 0 }}>
                        <div style={{ fontSize: 14, fontWeight: 800, color: GL.ink }}>฿{p.price}</div>
                        <div style={{ fontSize: 10, color: GL.tealDim, fontWeight: 700, marginTop: 2 }}>SELECT →</div>
                      </div>
                    </button>
                  ))}
                </div>
              )}
            </div>
            <div style={{ padding: '12px 20px 28px', borderTop: `1px solid ${GL.line}`, fontSize: 11, color: GL.muted, lineHeight: 1.5, textAlign: 'center' }}>
              Your replacement applies to all upcoming undelivered boxes. Cancel anytime.
            </div>
          </div>
        </div>
      )}
    </div>
  );
}


// ══════════════════════════════════════════════════════════════════════════════
// SCREEN 3: GLSwapBox — edit items for an upcoming week
// ══════════════════════════════════════════════════════════════════════════════
function GLSwapBox({ go, app }) {
  const menu = window.GL_WEEKLY_MENU || { items: [], theme: 'This Week', week_label: 'This Week', cutoff_at: '' };

  // Pre-populate with 2 items
  const [basket, setBasket] = React.useState({
    'wmi-1': 1,
    'wmi-3': 1,
  });
  const [saved, setSaved] = React.useState(false);
  const [currentPanelOpen, setCurrentPanelOpen] = React.useState(true);

  const cutoffMs = msUntil(menu.cutoff_at);
  const lockingSoon = cutoffMs > 0 && cutoffMs < 72 * 3600000;

  const selectedItems = menu.items.filter(i => (basket[i.id] || 0) > 0);
  const total = selectedItems.reduce((s, i) => s + i.price * (basket[i.id] || 0), 0);
  const count = Object.values(basket).reduce((s, q) => s + q, 0);

  const setQty = (id, q) => {
    setSaved(false);
    setBasket(b => {
      const n = { ...b };
      if (q <= 0) delete n[id];
      else n[id] = q;
      return n;
    });
  };

  const handleSave = async () => {
    if (window.GLSubscription) {
      await window.GLSubscription.updateOrderItems('so-1', basket);
    }
    setSaved(true);
    setTimeout(() => { go('subs'); }, 1200);
  };

  return (
    <div style={{ position: 'absolute', inset: 0, background: '#f6f8f8', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      {/* Header */}
      <div style={{
        paddingTop: 54, paddingBottom: 10, paddingLeft: 16, paddingRight: 16,
        background: '#fff', borderBottom: `1px solid ${GL.line}`,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6 }}>
          <button onClick={() => go('subs')} style={{ width: 32, height: 32, display: 'grid', placeItems: 'center', background: 'none', border: 'none', cursor: 'pointer' }}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke={GL.ink} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M19 12H5M12 19l-7-7 7-7"/>
            </svg>
          </button>
          <div>
            <div className="gl-display" style={{ fontSize: 14, color: GL.ink, letterSpacing: '0.04em' }}>
              {menu.week_label.toUpperCase()} · EDIT YOUR BOX
            </div>
          </div>
          {cutoffMs > 0 && (
            <span style={{
              marginLeft: 'auto', fontSize: 10, fontWeight: 700, padding: '3px 8px', borderRadius: 6,
              background: lockingSoon ? '#fff2d6' : '#f0f0f0',
              color: lockingSoon ? '#945200' : GL.muted,
              flexShrink: 0,
            }}>
              Locks {fmtCountdown(cutoffMs)}
            </span>
          )}
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', paddingBottom: 80 }} className="gl-noscroll">

        {/* Current selections (collapsible) */}
        <div style={{ margin: '12px 16px 0', background: '#fff', borderRadius: 12, border: `1px solid ${GL.line}`, overflow: 'hidden' }}>
          <button onClick={() => setCurrentPanelOpen(o => !o)} style={{
            width: '100%', padding: '12px 14px', display: 'flex', justifyContent: 'space-between',
            alignItems: 'center', background: 'none', border: 'none', cursor: 'pointer',
          }}>
            <div style={{ fontWeight: 700, fontSize: 13, color: GL.ink }}>
              Current Box  <span style={{ color: GL.muted, fontWeight: 500 }}>({count} item{count !== 1 ? 's' : ''})</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <span style={{ fontSize: 14, fontWeight: 800, color: GL.ink }}>{fmtThb(total)}</span>
              <span style={{ fontSize: 16, color: GL.muted }}>{currentPanelOpen ? '▲' : '▼'}</span>
            </div>
          </button>
          {currentPanelOpen && (
            <div style={{ borderTop: `1px solid ${GL.line}` }}>
              {selectedItems.length === 0 && (
                <div style={{ padding: '12px 14px', fontSize: 13, color: GL.muted, fontStyle: 'italic' }}>No items selected yet</div>
              )}
              {selectedItems.map(item => (
                <div key={item.id} style={{ padding: '10px 14px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: `1px solid ${GL.line}` }}>
                  <div>
                    <div style={{ fontSize: 13, fontWeight: 600 }}>{item.name}</div>
                    <div style={{ fontSize: 11, color: GL.muted }}>{item.variant}</div>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                    <span style={{ fontSize: 13, fontWeight: 700 }}>{fmtThb(item.price * basket[item.id])}</span>
                    <QtyStepper qty={basket[item.id]} onInc={() => setQty(item.id, basket[item.id] + 1)} onDec={() => setQty(item.id, basket[item.id] - 1)}/>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>

        {/* Week theme header */}
        <div style={{ padding: '16px 16px 8px' }}>
          <div style={{ fontWeight: 800, fontSize: 16, color: GL.ink }}>{menu.theme}</div>
          <div style={{ fontSize: 12, color: GL.muted }}>{menu.week_label}</div>
        </div>

        {/* Menu items */}
        <div style={{ padding: '0 16px', display: 'flex', flexDirection: 'column', gap: 10 }}>
          {menu.items.map(item => {
            const qty = basket[item.id] || 0;
            const remaining = (item.available_qty || 0) - (item.reserved_qty || 0);
            const outOfStock = remaining <= 0;
            const inBox = qty > 0;

            return (
              <div key={item.id} style={{
                background: inBox ? (GL.teal + '0f') : '#fff',
                border: `1.5px solid ${inBox ? GL.teal : GL.line}`,
                borderRadius: 12, overflow: 'hidden',
                transition: 'border-color .15s, background .15s',
              }}>
                <div style={{ display: 'flex', gap: 0 }}>
                  {/* Image 2:3 ratio */}
                  <div style={{ width: 80, flexShrink: 0, background: GL.line, position: 'relative' }}>
                    {item.img && <img src={item.img} alt={item.name} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block', minHeight: 110 }}/>}
                    {item.feature_badge && (
                      <div style={{ position: 'absolute', top: 6, left: 6 }}>
                        <FeatureBadge badge={item.feature_badge}/>
                      </div>
                    )}
                  </div>

                  <div style={{ flex: 1, padding: '12px 12px', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
                    <div>
                      <div style={{ fontSize: 13, fontWeight: 700, color: GL.ink, marginBottom: 2 }}>{item.name}</div>
                      <div style={{ fontSize: 11, color: GL.muted, marginBottom: 4 }}>{item.variant}</div>
                      {remaining < 15 && remaining > 0 && (
                        <div style={{ fontSize: 10, fontWeight: 700, color: '#945200', marginBottom: 4 }}>{remaining} left</div>
                      )}
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                      <span style={{ fontSize: 14, fontWeight: 800, color: GL.ink }}>{fmtThb(item.price)}</span>
                      {outOfStock
                        ? <span style={{ fontSize: 11, color: '#a61b30', fontWeight: 600 }}>Sold out</span>
                        : inBox
                          ? <QtyStepper qty={qty} onInc={() => setQty(item.id, qty + 1)} onDec={() => setQty(item.id, qty - 1)}/>
                          : <button onClick={() => setQty(item.id, 1)} style={{
                              padding: '6px 14px', borderRadius: 999, background: GL.ink, color: '#fff',
                              fontWeight: 700, fontSize: 12, border: 'none', cursor: 'pointer',
                            }}>+ Add</button>
                      }
                    </div>
                    {inBox && (
                      <div style={{ marginTop: 6, fontSize: 11, color: GL.tealDim, fontWeight: 600 }}>✓ In your box</div>
                    )}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {/* Sticky footer */}
      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0,
        padding: '12px 16px 32px',
        background: 'rgba(255,255,255,0.97)', backdropFilter: 'blur(14px)',
        borderTop: `1px solid ${GL.line}`,
        display: 'flex', alignItems: 'center', gap: 12,
      }}>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 15, fontWeight: 800, color: GL.ink }}>{fmtThb(total)} estimated</div>
          <div style={{ fontSize: 11, color: GL.muted }}>{count} item{count !== 1 ? 's' : ''}</div>
        </div>
        {saved
          ? <div style={{ padding: '12px 24px', borderRadius: 10, background: '#deffcd', color: '#1f5f04', fontWeight: 800, fontSize: 14 }}>✓ Saved</div>
          : <button onClick={handleSave} style={{
              padding: '12px 24px', borderRadius: 10, background: GL.ink, color: '#fff',
              fontWeight: 800, fontSize: 14, border: 'none', cursor: 'pointer',
            }}>Save Box</button>
        }
      </div>
    </div>
  );
}


// ══════════════════════════════════════════════════════════════════════════════
// SCREEN 4: GLSubscribeConfirmed — post-signup success
// ══════════════════════════════════════════════════════════════════════════════
function GLSubscribeConfirmed({ go }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, background: '#fff',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      padding: '32px 28px', textAlign: 'center',
    }}>
      <div style={{ fontSize: 64, marginBottom: 16 }}>🍝</div>
      <div className="gl-display" style={{ fontSize: 28, color: GL.ink, marginBottom: 12 }}>YOU'RE IN!</div>
      <div style={{ fontSize: 15, color: GL.muted, lineHeight: 1.6, marginBottom: 32, maxWidth: 280 }}>
        Your pasta subscription is live. We'll send you a reminder before each cutoff so you can swap your box.
      </div>
      <div style={{ width: '100%', marginBottom: 12 }}>
        <GLButton onClick={() => go('subs')} variant="teal">View My Subscription</GLButton>
      </div>
      <button onClick={() => go('home')} style={{ fontSize: 13, color: GL.muted, fontWeight: 600, background: 'none', border: 'none', cursor: 'pointer', padding: 8 }}>
        Back to Home
      </button>
    </div>
  );
}

// ── Register all screens on window ────────────────────────────────────────────
Object.assign(window, {
  GLSubscribeOnboard,
  GLSubscriberDash,
  GLSwapBox,
  GLSubscribeConfirmed,
});

console.log('%cSubscription screens loaded ✓', 'color:#59DFE8;font-weight:bold');
