/* ============================================================
   THE APP SHELL  —  /account once you're signed in.
   ============================================================
   Signed out, /account is a marketing page with a sign-in card on it.
   Signed in, it stops being a page and becomes an application: a fixed
   sidebar, a working area, and objects you operate.

   That split is deliberate. The old dashboard was the homepage's
   components (.card, .stat-num, .green-card) arranged down a 1080px
   column — which reads as a receipt, because a receipt is what a
   centred column of cards IS. Nothing on it could be acted on.

   The tokens are the store's own (--bg, --panel, --line, --green =
   electric cyan, Inter, 16px radii) so this is recognisably the same
   product as /twitch — only the composition changes. There is no new
   palette and no new typeface here on purpose.
   ============================================================ */

/* ── shell ───────────────────────────────────────────────────────
   Sidebar is fixed and the main column is pushed, rather than a
   two-column grid: the working area scrolls on its own and the nav
   never leaves, which is the single thing that makes a page feel
   like an app rather than a document.
   ──────────────────────────────────────────────────────────────── */
.app{--side:252px;--top:64px;--z:1}
/* site.css does `@media(min-width:1057px){body{zoom:1.1}}`. Under zoom, a vh
   is NOT divided by the zoom factor — 100vh resolves to the full viewport and
   is THEN scaled, so it renders 10% taller than the screen and forces ~90px
   of scroll on a page that should exactly fit. Mirror the same breakpoint and
   divide it back out. Keep this in step with site.css:653. */
@media(min-width:1057px){ .app{--z:1.1} }
body.in-app{background:var(--bg)}
/* the marketing backdrop belongs to the signed-out page only — a fixed
   aura + grid behind an app chrome just muddies every panel edge */
body.in-app::before,body.in-app::after{display:none}
/* marketing chrome is for the gate. Once you're in, the sidebar IS the nav. */
body.in-app > header.nav,body.in-app > footer,body.in-app > main.page{display:none}
.app{display:none}
body.in-app .app{display:block}

.app-side{position:fixed;top:0;left:0;bottom:0;width:var(--side);z-index:40;
  background:var(--panel);border-right:1px solid var(--line);
  display:flex;flex-direction:column;overflow-y:auto;overscroll-behavior:contain}
.app-main{margin-left:var(--side);min-height:calc(100vh / var(--z));display:flex;flex-direction:column}

/* ── sidebar: brand ─────────────────────────────────────────── */
.app-brand{display:flex;align-items:center;gap:9px;height:var(--top);flex:none;
  padding:0 18px;border-bottom:1px solid var(--line);text-decoration:none}
.app-brand img.logo-word{height:17px;width:auto;display:block}
.app-brand .logo-mark{width:22px;height:22px;display:block}

/* ── sidebar: nav ───────────────────────────────────────────── */
.app-nav{flex:1;padding:16px 12px 8px}
.app-grp{display:block;margin:16px 0 7px;padding:0 10px;font-size:10px;font-weight:700;
  letter-spacing:.13em;text-transform:uppercase;color:#5e5e68}
.app-nav > .app-grp:first-child{margin-top:0}
.app-link{display:flex;align-items:center;gap:11px;position:relative;
  padding:9px 10px;margin-bottom:2px;border-radius:10px;
  font-size:14px;font-weight:600;color:var(--muted-2);text-decoration:none;
  border:0;background:none;font-family:inherit;width:100%;text-align:left;cursor:pointer;
  transition:background .15s ease,color .15s ease}
.app-link svg{width:17px;height:17px;flex:none;color:#65656f;transition:color .15s ease}
.app-link:hover{background:var(--panel-2);color:var(--text)}
.app-link:hover svg{color:var(--muted-2)}
.app-link.on{background:color-mix(in srgb,var(--green) 13%,transparent);color:var(--text)}
.app-link.on svg{color:var(--green)}
/* the active marker reads as "you are here" without needing a colour change
   the eye has to decode — it is the only cyan bar in the sidebar */
.app-link.on:before{content:"";position:absolute;left:-12px;top:50%;transform:translateY(-50%);
  width:3px;height:19px;border-radius:0 3px 3px 0;background:var(--green)}
.app-link .tag{margin-left:auto;font-size:10.5px;font-weight:700;padding:2px 7px;border-radius:100px;
  background:var(--panel-2);color:var(--muted);font-variant-numeric:tabular-nums}
.app-link.on .tag{background:color-mix(in srgb,var(--green) 22%,transparent);color:var(--green)}

/* ── sidebar: foot ──────────────────────────────────────────── */
.app-foot{flex:none;padding:12px;border-top:1px solid var(--line)}
.app-me{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:11px;background:var(--panel-2)}
.app-av{width:31px;height:31px;flex:none;border-radius:9px;display:grid;place-items:center;
  background:linear-gradient(135deg,var(--green),var(--green-2));color:var(--ink);
  font-size:12px;font-weight:800;letter-spacing:.02em}
.app-me .mail{flex:1;min-width:0;font-size:12px;font-weight:600;color:var(--muted-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.app-out{flex:none;display:grid;place-items:center;width:27px;height:27px;border-radius:8px;
  border:0;background:none;color:var(--muted);cursor:pointer;transition:background .15s,color .15s}
.app-out svg{width:15px;height:15px}
.app-out:hover{background:#2a2a2f;color:var(--text)}

/* ── topbar ──────────────────────────────────────────────────── */
.app-top{position:sticky;top:0;z-index:30;height:var(--top);flex:none;
  display:flex;align-items:center;gap:14px;padding:0 28px;
  background:color-mix(in srgb,var(--bg) 86%,transparent);
  -webkit-backdrop-filter:saturate(1.6) blur(12px);backdrop-filter:saturate(1.6) blur(12px);
  border-bottom:1px solid var(--line)}
.app-top h1{margin:0;min-width:0;font-size:17px;font-weight:700;letter-spacing:-.01em;color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.app-top .sp{flex:1}
.app-burger{display:none;width:34px;height:34px;flex:none;border:1px solid var(--line);border-radius:9px;
  background:var(--panel);color:var(--text);cursor:pointer;place-items:center}
.app-burger svg{width:17px;height:17px}

/* buttons — .btn is the app's own, deliberately squarer and quieter than the
   storefront's .pill, which is a marketing CTA and shouts in here */
.btn{display:inline-flex;align-items:center;gap:7px;height:36px;padding:0 14px;border-radius:10px;
  font:inherit;font-size:13.5px;font-weight:650;cursor:pointer;text-decoration:none;white-space:nowrap;
  border:1px solid var(--line);background:var(--panel);color:var(--text);
  transition:transform .16s cubic-bezier(.23,1,.32,1),background .15s,border-color .15s}
.btn svg{width:15px;height:15px}
.btn:hover{background:var(--panel-2);border-color:#33333a}
.btn:active{transform:scale(.97)}
.btn.pri{background:var(--green);border-color:var(--green);color:var(--ink);font-weight:700}
.btn.pri:hover{background:var(--green-2);border-color:var(--green-2)}
.btn.sm{height:31px;padding:0 11px;font-size:12.5px;border-radius:9px}
.btn.gho{background:none;border-color:transparent;color:var(--muted-2)}
.btn.gho:hover{background:var(--panel);color:var(--text)}
.btn[disabled]{opacity:.5;pointer-events:none}

/* ── working area ───────────────────────────────────────────── */
.app-body{flex:1;padding:26px 28px 72px;max-width:1180px;width:100%}
.app-view{display:none}
.app-view.on{display:block}
.sec-h{display:flex;align-items:center;gap:12px;margin:30px 0 13px}
.sec-h:first-child{margin-top:0}
.sec-h h2{margin:0;font-size:13px;font-weight:700;letter-spacing:.02em;color:var(--text)}
.sec-h .sp{flex:1}

/* entrance — one orchestrated load, staggered by index, not scattered
   per-component effects. Skipped entirely for reduced-motion. */
.rise{opacity:0;transform:translateY(9px);animation:appRise .42s cubic-bezier(.23,1,.32,1) forwards}
@keyframes appRise{to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){
  .rise{animation:none;opacity:1;transform:none}
  .btn:active{transform:none}
}

/* ── get started ─────────────────────────────────────────────
   Only rendered while something on it is still undone. A permanent
   checklist of ticks is clutter — the moment it's all green it has
   nothing left to say, so it removes itself.
   ──────────────────────────────────────────────────────────── */
.gs{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);overflow:hidden;margin-bottom:26px}
.gs-h{display:flex;align-items:center;gap:13px;padding:17px 18px}
.gs-ic{width:36px;height:36px;flex:none;border-radius:10px;display:grid;place-items:center;
  background:color-mix(in srgb,var(--green) 15%,transparent);color:var(--green)}
.gs-ic svg{width:18px;height:18px}
.gs-h b{display:block;font-size:14.5px;font-weight:700;color:var(--text)}
.gs-h span{display:block;font-size:12.5px;color:var(--muted);margin-top:1px}
.gs-steps{padding:0 10px 10px;display:flex;flex-direction:column;gap:5px}
.gs-step{display:flex;align-items:center;gap:12px;padding:12px 12px;border-radius:11px;background:var(--panel-2)}
.gs-step.done{background:color-mix(in srgb,#3ddc84 8%,transparent)}
.gs-n{width:23px;height:23px;flex:none;border-radius:50%;display:grid;place-items:center;
  font-size:11.5px;font-weight:700;background:#2a2a30;color:var(--muted-2)}
.gs-step.done .gs-n{background:color-mix(in srgb,#3ddc84 20%,transparent);color:#3ddc84}
.gs-n svg{width:12px;height:12px}
.gs-tx{flex:1;min-width:0}
.gs-tx b{display:block;font-size:13.5px;font-weight:650;color:var(--text)}
.gs-tx span{display:block;font-size:12.5px;color:var(--muted);margin-top:1px}
.gs-step.done .gs-tx b{color:var(--muted-2)}

/* ── state tabs ─────────────────────────────────────────────── */
.tabs{display:flex;gap:6px;flex-wrap:wrap}
.tab{display:inline-flex;align-items:center;gap:7px;height:34px;padding:0 13px;border-radius:10px;
  border:1px solid transparent;background:var(--panel);color:var(--muted-2);
  font:inherit;font-size:13.5px;font-weight:650;cursor:pointer;
  transition:background .15s,color .15s,border-color .15s}
.tab:hover{background:var(--panel-2);color:var(--text)}
.tab .c{font-size:11px;font-weight:700;padding:1.5px 6px;border-radius:100px;
  background:#2a2a30;color:var(--muted);font-variant-numeric:tabular-nums}
.tab.on{background:color-mix(in srgb,var(--green) 14%,transparent);border-color:color-mix(in srgb,var(--green) 34%,transparent);color:var(--text)}
.tab.on .c{background:var(--green);color:var(--ink)}

/* ── channel chips (dashboard strip) ────────────────────────── */
.chan-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(238px,1fr));gap:12px}
.chan{display:flex;align-items:center;gap:11px;padding:13px;border-radius:13px;
  border:1px solid var(--line);background:var(--panel);min-width:0;
  transition:border-color .15s ease}
.chan:hover{border-color:#33333a}
.chan.live{border-color:color-mix(in srgb,#ff4747 40%,transparent);
  background:linear-gradient(180deg,color-mix(in srgb,#ff4747 7%,transparent),transparent)}
.chan-av{width:38px;height:38px;flex:none;border-radius:11px;object-fit:cover;background:#1f1f24;
  display:grid;place-items:center;font-size:14px;font-weight:800;color:var(--muted)}
.chan-tx{flex:1;min-width:0}
.chan-nm{display:block;font-size:13.5px;font-weight:700;color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.chan-st{display:inline-flex;align-items:center;gap:5px;margin-top:2px;font-size:11.5px;font-weight:600;color:var(--muted)}
.chan-st .d{width:6px;height:6px;border-radius:50%;background:#4d4d56}
.chan.live .chan-st{color:#ff8080}
.chan.live .chan-st .d{background:#ff4747;box-shadow:0 0 7px #ff4747;animation:appPulse 1.5s infinite}
@keyframes appPulse{0%,100%{opacity:1}50%{opacity:.35}}
.chan-x{flex:none;width:26px;height:26px;border-radius:8px;border:0;background:none;color:#55555e;
  cursor:pointer;display:grid;place-items:center;transition:background .15s,color .15s}
.chan-x svg{width:13px;height:13px}
.chan-x:hover{background:#2a2a30;color:#ff8080}

/* ── the session card — an order you can act on ──────────────
   Grid, not a stacked column: what you bought reads left, the state
   reads right, actions sit on their own rail. The old .ord card put
   a 4-segment progress bar across the full width, which is why the
   thing looked like parcel tracking.
   ──────────────────────────────────────────────────────────── */
.ses-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:14px}
.ses{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:var(--r);
  background:var(--panel);overflow:hidden;transition:border-color .15s ease}
.ses:hover{border-color:#33333a}
.ses.is-run{border-color:color-mix(in srgb,var(--green) 32%,transparent)}
.ses.is-rdy{border-color:color-mix(in srgb,#ffb340 30%,transparent)}

.ses-top{display:flex;align-items:center;gap:10px;padding:13px 15px;border-bottom:1px solid var(--line)}
.ses-no{font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:#5e5e68;
  font-variant-numeric:tabular-nums}
.ses-top .sp{flex:1}
.pill-st{display:inline-flex;align-items:center;gap:6px;font-size:10px;font-weight:800;
  letter-spacing:.07em;text-transform:uppercase;padding:4px 9px;border-radius:100px;
  background:#2a2a30;color:var(--muted-2);white-space:nowrap}
.pill-st .d{width:5px;height:5px;border-radius:50%;background:currentColor}
.pill-st.run{background:color-mix(in srgb,var(--green) 17%,transparent);color:var(--green)}
.pill-st.run .d{animation:appPulse 1.5s infinite}
.pill-st.rdy{background:color-mix(in srgb,#ffb340 16%,transparent);color:#ffb340}
.pill-st.done{background:color-mix(in srgb,#3ddc84 14%,transparent);color:#3ddc84}
.pill-st.bad{background:color-mix(in srgb,#ff5f57 16%,transparent);color:#ff8080}

/* Cards in a row are equal height (grid), so a short one gets stretched. Left
   alone that opens a hole between the facts and the action bar. Pinning the
   fact row to the bottom instead means every card reads the same way whatever
   its height: what you bought at the top, where it's got to at the bottom. */
.ses-mid{padding:15px;flex:1;display:flex;flex-direction:column}
.ses-what{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:3px}
.ses-what b{font-size:21px;font-weight:800;letter-spacing:-.02em;color:var(--text);
  font-variant-numeric:tabular-nums;line-height:1}
.ses-what span{font-size:13.5px;font-weight:600;color:var(--muted-2)}
.ses-sub{font-size:12.5px;color:var(--muted)}
.ses-extra{margin-top:9px;display:flex;flex-direction:column;gap:4px}
.ses-extra i{font-style:normal;font-size:12.5px;color:var(--muted)}

/* the two-slot fact row — CHANNEL and the state fact beside it, the
   shape ViewBotter uses for CHANNEL / EXPIRES / AUTOSTART */
.ses-facts{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:auto;padding-top:14px}
.fact{padding:10px 11px;border-radius:11px;background:var(--panel-2);min-width:0}
.fact .k{display:block;font-size:9.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:#5e5e68}
.fact .v{display:block;margin-top:3px;font-size:13px;font-weight:650;color:var(--text);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fact .v.dim{color:var(--muted);font-weight:600}
.fact.wide{grid-column:1/-1}

/* live progress — a single filled bar, because "how much of my session is
   left" is one number. Four labelled segments answered a question about
   parcels that nobody buying a stream session was asking. */
.ses-bar{margin-top:13px;flex:none}
.ses-bar .track{height:5px;border-radius:100px;background:#2a2a30;overflow:hidden}
.ses-bar .fill{height:100%;border-radius:100px;background:linear-gradient(90deg,var(--green-2),var(--green));
  transition:width .5s cubic-bezier(.23,1,.32,1)}
.ses-bar .lab{display:flex;justify-content:space-between;gap:8px;margin-top:7px;font-size:11.5px;color:var(--muted)}
.ses-bar .lab b{color:var(--green);font-weight:700}

.ses-act{display:flex;align-items:center;gap:8px;padding:12px 15px;border-top:1px solid var(--line);
  background:color-mix(in srgb,var(--panel-2) 55%,transparent)}
.ses-act .sp{flex:1}

/* ── empty states ────────────────────────────────────────────
   Every empty view says what will appear here and gives the one
   action that fills it. A bare "nothing yet" is a dead end.
   ──────────────────────────────────────────────────────────── */
.empty{border:1px dashed #2e2e34;border-radius:var(--r);background:color-mix(in srgb,var(--panel) 55%,transparent);
  padding:42px 24px;text-align:center}
.empty .ic{width:44px;height:44px;margin:0 auto 13px;border-radius:13px;display:grid;place-items:center;
  background:var(--panel-2);color:#55555e}
.empty .ic svg{width:21px;height:21px}
.empty b{display:block;font-size:15px;font-weight:700;color:var(--text)}
.empty p{margin:5px auto 0;max-width:360px;font-size:13.5px;line-height:1.55;color:var(--muted)}
.empty .btn{margin-top:16px}

/* ── add-channel form ───────────────────────────────────────── */
.addch{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);padding:17px;max-width:720px}
.addch-row{display:flex;gap:9px;flex-wrap:wrap}
.addch .sel{display:flex;gap:4px;padding:4px;border-radius:11px;background:var(--panel-2);border:1px solid var(--line)}
.addch .sel button{border:0;background:none;font:inherit;font-size:13px;font-weight:650;color:var(--muted-2);
  padding:0 13px;height:32px;border-radius:8px;cursor:pointer;transition:background .15s,color .15s}
.addch .sel button.on{background:var(--green);color:var(--ink);font-weight:700}
.addch .inp{flex:1;min-width:190px;display:flex;align-items:center;height:42px;padding:0 13px;
  border-radius:11px;background:var(--panel-2);border:1px solid var(--line);
  transition:border-color .16s,box-shadow .16s}
.addch .inp:focus-within{border-color:var(--green);box-shadow:0 0 0 3px color-mix(in srgb,var(--green) 20%,transparent)}
.addch .inp .pre{font-size:13.5px;color:var(--muted);font-weight:500;flex:none}
.addch .inp input{flex:1;min-width:0;background:none;border:0;outline:none;color:var(--text);
  font:inherit;font-size:14.5px;font-weight:600;padding:0 0 0 2px}
.addch .inp input::placeholder{color:#55555e;font-weight:500}
.addch .btn{height:42px;padding:0 18px}
.addch .msg{margin-top:10px;font-size:12.5px;color:#ff8080;display:none}
.addch .msg.show{display:block}

/* ── responsive ──────────────────────────────────────────────
   Under 900 the sidebar becomes a slide-over. It keeps its own
   scroll and the scrim closes it; the burger only exists here.
   ──────────────────────────────────────────────────────────── */
@media(max-width:900px){
  .app-main{margin-left:0}
  .app-burger{display:grid}
  .app-side{transform:translateX(-100%);transition:transform .3s cubic-bezier(.32,.72,0,1);width:262px}
  body.side-open .app-side{transform:none}
  .app-scrim{position:fixed;inset:0;z-index:39;background:rgba(4,4,6,.62);
    opacity:0;pointer-events:none;transition:opacity .3s ease}
  body.side-open .app-scrim{opacity:1;pointer-events:auto}
  .app-body{padding:20px 16px 64px}
  .app-top{padding:0 16px}
  .ses-grid,.chan-grid{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  .app-side,.app-scrim{transition:none}
}
@media(max-width:344px){
  .ses-facts{grid-template-columns:1fr}
  .addch-row{flex-direction:column}
  .addch .inp{min-width:0}
}

/* ════════════════════════════════════════════════════════════════
   ROUND 2 — the parts that make it operable rather than readable.
   ════════════════════════════════════════════════════════════════ */

/* ── topbar live strip ───────────────────────────────────────────
   The one fact worth the topbar's space: you are live RIGHT NOW, and
   that is exactly when buying viewers makes sense. Only rendered when
   a channel actually resolves live — never as a placeholder.
   ──────────────────────────────────────────────────────────────── */
.app-live{display:inline-flex;align-items:center;gap:9px;height:34px;padding:0 6px 0 12px;
  border-radius:100px;background:color-mix(in srgb,#ff4747 12%,transparent);
  border:1px solid color-mix(in srgb,#ff4747 30%,transparent);min-width:0}
.app-live .d{width:6px;height:6px;flex:none;border-radius:50%;background:#ff4747;
  box-shadow:0 0 7px #ff4747;animation:appPulse 1.5s infinite}
.app-live .tx{font-size:13px;font-weight:650;color:#ffb3b3;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;min-width:0}
.app-live .tx b{color:#fff;font-weight:700}
.app-live .btn{height:26px;padding:0 10px;font-size:12px;border-radius:100px}
@media(max-width:640px){
  .app-live .tx{display:none}
  .app-live{padding-left:10px}
  /* Two buy CTAs cannot both fit next to the burger and the title at 390 —
     the row overflowed and Chrome widened the layout viewport to 455, which
     then made every fixed overlay (scrim, drawer) 455 wide too. When a
     channel is live the strip's own CTA is the better of the two, because it
     is the one with a reason attached, so the static button stands down. */
  body.has-live .app-top > .btn.pri{display:none}
}

/* ── clickable channel cards ─────────────────────────────────── */
.chan.tap{cursor:pointer;text-align:left;font:inherit;width:100%;
  transition:border-color .15s ease,transform .16s cubic-bezier(.23,1,.32,1)}
.chan.tap:hover{border-color:#3b3b43}
.chan.tap:active{transform:scale(.99)}
.chan .go{flex:none;color:#4a4a53;display:grid;place-items:center;width:20px;height:20px}
.chan .go svg{width:15px;height:15px}

/* ── channel detail ──────────────────────────────────────────── */
.ch-hero{display:flex;align-items:center;gap:16px;padding:19px;border-radius:var(--r);
  border:1px solid var(--line);background:var(--panel);flex-wrap:wrap}
.ch-hero.live{border-color:color-mix(in srgb,#ff4747 38%,transparent);
  background:linear-gradient(180deg,color-mix(in srgb,#ff4747 8%,transparent),transparent)}
.ch-hero .av{width:60px;height:60px;flex:none;border-radius:16px;object-fit:cover;background:#1f1f24;
  display:grid;place-items:center;font-size:22px;font-weight:800;color:var(--muted)}
.ch-hero .who{flex:1;min-width:170px}
.ch-hero .nm{font-size:22px;font-weight:800;letter-spacing:-.02em;color:var(--text);line-height:1.15;
  word-break:break-word}
.ch-hero .url{display:block;margin-top:2px;font-size:12.5px;color:var(--muted)}
.ch-hero .st{display:inline-flex;align-items:center;gap:6px;margin-top:8px;font-size:12.5px;
  font-weight:650;color:var(--muted);padding:4px 10px;border-radius:100px;background:var(--panel-2)}
.ch-hero .st .d{width:6px;height:6px;border-radius:50%;background:#4d4d56}
.ch-hero.live .st{background:color-mix(in srgb,#ff4747 15%,transparent);color:#ff9c9c}
.ch-hero.live .st .d{background:#ff4747;box-shadow:0 0 7px #ff4747;animation:appPulse 1.5s infinite}
.ch-hero .acts{display:flex;gap:8px;flex-wrap:wrap}
.ch-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:12px;margin-top:14px}
.ch-stat{padding:14px 15px;border-radius:13px;border:1px solid var(--line);background:var(--panel)}
.ch-stat b{display:block;font-size:23px;font-weight:800;letter-spacing:-.025em;color:var(--text);
  line-height:1;font-variant-numeric:tabular-nums}
.ch-stat span{display:block;margin-top:5px;font-size:11px;font-weight:650;letter-spacing:.06em;
  text-transform:uppercase;color:#5e5e68}
.back{display:inline-flex;align-items:center;gap:6px;margin-bottom:14px;padding:0;border:0;background:none;
  font:inherit;font-size:13px;font-weight:650;color:var(--muted);cursor:pointer;transition:color .15s}
.back svg{width:15px;height:15px}
.back:hover{color:var(--text)}

/* ── the order drawer ────────────────────────────────────────────
   A card in a grid cannot hold a full breakdown without wrecking the
   grid, and a modal in the middle of the screen loses the list behind
   it. A right-hand drawer keeps the list in place and slides the
   detail in over it — iOS's own curve, because it is the one that
   feels attached to the gesture rather than timed.
   ──────────────────────────────────────────────────────────────── */
.dr-scrim{position:fixed;inset:0;z-index:60;background:rgba(4,4,6,.66);
  opacity:0;pointer-events:none;transition:opacity .3s ease}
body.dr-open .dr-scrim{opacity:1;pointer-events:auto}
.dr{position:fixed;top:0;right:0;bottom:0;z-index:61;width:min(440px,100%);
  background:var(--panel);border-left:1px solid var(--line);
  display:flex;flex-direction:column;
  transform:translateX(100%);transition:transform .34s cubic-bezier(.32,.72,0,1)}
body.dr-open .dr{transform:none}
.dr-top{display:flex;align-items:center;gap:11px;flex:none;height:var(--top);padding:0 16px;
  border-bottom:1px solid var(--line)}
.dr-top b{font-size:15px;font-weight:700;color:var(--text)}
.dr-top .sp{flex:1}
.dr-x{width:32px;height:32px;flex:none;border-radius:9px;border:0;background:var(--panel-2);
  color:var(--muted-2);cursor:pointer;display:grid;place-items:center;transition:background .15s,color .15s}
.dr-x svg{width:15px;height:15px}
.dr-x:hover{background:#2f2f36;color:var(--text)}
.dr-body{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:18px 16px 26px}
.dr-h{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;margin-bottom:4px}
.dr-h b{font-size:25px;font-weight:800;letter-spacing:-.025em;color:var(--text);font-variant-numeric:tabular-nums}
.dr-h span{font-size:14px;font-weight:600;color:var(--muted-2)}
.dr-sec{margin-top:20px}
.dr-sec > h4{margin:0 0 9px;font-size:10px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:#5e5e68}
.dr-line{display:flex;align-items:flex-start;gap:12px;padding:11px 0;border-bottom:1px solid var(--line)}
.dr-line:last-child{border-bottom:0}
.dr-line .k{flex:none;width:104px;font-size:12.5px;color:var(--muted)}
.dr-line .v{flex:1;min-width:0;font-size:13.5px;font-weight:600;color:var(--text);
  word-break:break-word;text-align:right}
.dr-line .v.mono{font-variant-numeric:tabular-nums}
.dr-item{display:flex;align-items:center;gap:11px;padding:11px 12px;border-radius:11px;
  background:var(--panel-2);margin-bottom:7px}
.dr-item .q{flex:none;min-width:44px;font-size:15px;font-weight:800;color:var(--text);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.dr-item .n{flex:1;min-width:0;font-size:13px;font-weight:600;color:var(--muted-2);line-height:1.4}
.dr-item .s{flex:none;font-size:9.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  padding:3px 8px;border-radius:100px;background:#2c2c33;color:var(--muted)}
.dr-item .s.ok{background:color-mix(in srgb,#3ddc84 14%,transparent);color:#3ddc84}
.dr-item .s.go{background:color-mix(in srgb,var(--green) 16%,transparent);color:var(--green)}
.dr-foot{flex:none;display:flex;gap:8px;padding:13px 16px;border-top:1px solid var(--line);
  background:color-mix(in srgb,var(--panel-2) 55%,transparent)}
.dr-foot .btn{flex:1;justify-content:center}
@media(prefers-reduced-motion:reduce){ .dr,.dr-scrim{transition:none} }

/* ── settings ────────────────────────────────────────────────── */
.set{max-width:660px}
.set-card{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);
  overflow:hidden;margin-bottom:16px}
.set-row{display:flex;align-items:center;gap:14px;padding:16px 17px;border-bottom:1px solid var(--line);
  flex-wrap:wrap}
.set-row:last-child{border-bottom:0}
.set-tx{flex:1;min-width:180px}
.set-tx b{display:block;font-size:13.5px;font-weight:650;color:var(--text)}
.set-tx span{display:block;margin-top:2px;font-size:12.5px;line-height:1.5;color:var(--muted)}
.set-val{font-size:13.5px;font-weight:650;color:var(--muted-2);word-break:break-all}
.seg{display:flex;gap:4px;padding:4px;border-radius:11px;background:var(--panel-2);border:1px solid var(--line)}
.seg button{border:0;background:none;font:inherit;font-size:13px;font-weight:650;color:var(--muted-2);
  padding:0 14px;height:30px;border-radius:8px;cursor:pointer;
  transition:background .15s ease,color .15s ease}
.seg button:hover{color:var(--text)}
.seg button.on{background:var(--green);color:var(--ink);font-weight:700}

/* ── section subhead used above split lists ──────────────────── */
.sec-h .n{font-size:11px;font-weight:700;padding:2px 8px;border-radius:100px;
  background:var(--panel-2);color:var(--muted);font-variant-numeric:tabular-nums}

@media(max-width:900px){
  .dr{width:100%}
  .ch-hero .acts{width:100%}
  .ch-hero .acts .btn{flex:1;justify-content:center}
}

/* ── starting a held order ───────────────────────────────────────
   Only ever rendered for status 'ready'. The channel is asked again
   here because a held order may have been bought days before the
   customer decided which channel to stream on — and this is the last
   moment the answer can still change anything.
   ──────────────────────────────────────────────────────────────── */
.dr-start{margin-top:20px;padding:15px;border-radius:14px;
  border:1px solid color-mix(in srgb,#ffb340 32%,transparent);
  background:color-mix(in srgb,#ffb340 7%,transparent)}
.dr-start h4{margin:0 0 9px;font-size:10px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:#ffb340}
.dr-start .inp{display:flex;align-items:center;height:46px;padding:0 13px;border-radius:12px;
  background:var(--panel-2);border:1px solid var(--line);
  transition:border-color .16s ease,box-shadow .16s ease}
.dr-start .inp:focus-within{border-color:#ffb340;box-shadow:0 0 0 3px color-mix(in srgb,#ffb340 22%,transparent)}
.dr-start .inp .pre{flex:none;font-size:13.5px;color:var(--muted);font-weight:500}
.dr-start .inp input{flex:1;min-width:0;background:none;border:0;outline:none;color:var(--text);
  font:inherit;font-size:15px;font-weight:600;padding:0 0 0 2px}
.dr-start .inp input::placeholder{color:#55555e;font-weight:500}
.dr-start p{margin:10px 0 0;font-size:12.5px;line-height:1.55;color:var(--muted)}
.dr-start .err{margin-top:9px;font-size:12.5px;color:#ff8080;display:none}
.dr-start .err.show{display:block}
.btn.warn{background:#ffb340;border-color:#ffb340;color:#231603;font-weight:700}
.btn.warn:hover{background:#ffc266;border-color:#ffc266}

/* ── framed pages ────────────────────────────────────────────────
   Store, guides, support and legal open INSIDE the shell instead of
   navigating away. Each one is the real page in a same-origin iframe,
   not a copy of its markup, which matters for three reasons:

     · those pages are indexed and rank — they keep their own URLs,
       their own <head> and their own SEO, untouched;
     · the PDP wizards, the cart and the channel lookups are inline
       scripts and heavy CSS. Injecting that markup into this document
       would mean re-running the scripts by hand and refereeing a
       collision between their stylesheet and this one;
     · the frame is a hard boundary — a page can be redesigned without
       any chance of it reaching in and breaking the dashboard.

   The iframe FILLS the content area and scrolls internally rather than
   auto-sizing to its content. Auto-height would make every position:fixed
   element inside it (the cart drawer, the order modal) position against a
   3000px-tall viewport and fly off-screen.
   ──────────────────────────────────────────────────────────────── */
.pg-wrap{position:relative;height:calc(100vh / var(--z) - var(--top));background:var(--bg)}
.pg-wrap iframe{display:block;width:100%;height:100%;border:0;background:var(--bg)}
/* the framed page owns the full width — .app-body's padding and reading
   width belong to the app's own views */
body.pg-view .app-body{padding:0;max-width:none}

.pg-load{position:absolute;inset:0;display:grid;place-items:center;gap:12px;
  background:var(--bg);transition:opacity .25s ease}
.pg-load.gone{opacity:0;pointer-events:none}
.pg-load .sp{width:26px;height:26px;border-radius:50%;border:2.5px solid rgba(255,255,255,.12);
  border-top-color:var(--green);animation:pgspin .7s linear infinite}
@keyframes pgspin{to{transform:rotate(360deg)}}
.pg-load span{font-size:13px;color:var(--muted)}
@media(prefers-reduced-motion:reduce){ .pg-load .sp{animation-duration:2s} }

/* Shown only if the frame cannot load — never instead of trying. */
.pg-fail{position:absolute;inset:0;display:grid;place-items:center;padding:24px;text-align:center}
.pg-fail b{display:block;font-size:15px;font-weight:700;color:var(--text)}
.pg-fail p{margin:6px auto 14px;max-width:340px;font-size:13.5px;line-height:1.55;color:var(--muted)}

/* an "open the real page" escape hatch in the topbar while framing */
.pg-out{display:inline-flex;align-items:center;gap:6px}
@media(max-width:640px){ .pg-out span{display:none} }

/* ── packages ────────────────────────────────────────────────────
   The monthly plans. These already existed on the hub — four tiers
   with live Stripe prices — and nothing on the site sold them:
   assets/plans.js was written and then never linked from any page.
   This is the surface that makes them buyable.

   A plan here is one thing and the card says only that thing: N live
   viewers held on one channel for a month. There are no chat bots,
   no follower allowance and no VOD views in a BuyLiveViewers plan, so
   there are no rows for them.
   ──────────────────────────────────────────────────────────────── */
/* Exactly four tiers, so pick column counts that DIVIDE evenly rather than
   whatever auto-fit lands on. At 1160px minus gaps each column came to 253px
   against a 252px minimum — auto-fit took the safer 3 and left Pro alone on a
   row of its own, which reads as an afterthought rather than the top tier. */
.pk-grid{display:grid;grid-template-columns:1fr;gap:14px}
@media(min-width:620px){ .pk-grid{grid-template-columns:repeat(2,1fr)} }
@media(min-width:1120px){ .pk-grid{grid-template-columns:repeat(4,1fr)} }
.pk{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:var(--r);
  background:var(--panel);overflow:hidden;transition:border-color .16s ease,transform .16s cubic-bezier(.23,1,.32,1)}
.pk:hover{border-color:#33333a}
.pk.on{border-color:color-mix(in srgb,var(--green) 45%,transparent)}

.pk-head{padding:18px 18px 16px;background:var(--panel-2);border-bottom:1px solid var(--line)}
.pk-name{font-size:14.5px;font-weight:700;letter-spacing:-.01em;color:var(--text)}
.pk-price{display:flex;align-items:baseline;gap:5px;margin-top:7px}
.pk-price b{font-size:29px;font-weight:800;letter-spacing:-.03em;color:var(--text);
  line-height:1;font-variant-numeric:tabular-nums}
.pk-price span{font-size:13px;font-weight:600;color:var(--muted)}
.pk-blurb{margin-top:8px;font-size:12.5px;line-height:1.5;color:var(--muted)}

.pk-body{flex:1;padding:16px 18px}
.pk-row{display:flex;align-items:center;gap:11px;margin-bottom:11px}
.pk-ic{width:29px;height:29px;flex:none;border-radius:9px;display:grid;place-items:center}
.pk-ic svg{width:15px;height:15px}
.pk-ic.a{background:color-mix(in srgb,var(--green) 15%,transparent);color:var(--green)}
.pk-ic.b{background:color-mix(in srgb,var(--purple) 20%,transparent);color:#a894ff}
.pk-ic.c{background:color-mix(in srgb,#ff9f45 15%,transparent);color:#ff9f45}
.pk-row .t{font-size:13.5px;color:var(--muted-2);line-height:1.35}
.pk-row .t b{color:var(--text);font-weight:700}

.pk-checks{margin-top:14px;padding-top:13px;border-top:1px solid var(--line);
  display:flex;flex-direction:column;gap:7px}
.pk-check{display:flex;align-items:flex-start;gap:8px;font-size:12.5px;color:var(--muted)}
.pk-check svg{width:13px;height:13px;flex:none;margin-top:2px;color:#3ddc84}

.pk-foot{padding:0 18px 18px}
.pk-foot .btn{width:100%;justify-content:center;height:40px}
.pk-sub{margin-top:8px;text-align:center;font-size:11.5px;color:var(--muted)}
.pk-or{margin-top:13px;padding-top:13px;border-top:1px solid var(--line);text-align:center}
.pk-or a{font-size:12.5px;font-weight:600;color:var(--green);text-decoration:none}
.pk-or a:hover{text-decoration:underline}

/* the channel this plan will run on — asked once, above the tiers, because
   the hub refuses a plan without one and refuses a second plan on a channel
   that already has one */
.pk-setup{border:1px solid var(--line);border-radius:var(--r);background:var(--panel);
  padding:17px;margin-bottom:18px;max-width:720px}
.pk-setup .k{display:block;margin-bottom:9px;font-size:10px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:#5e5e68}
.pk-setup .row{display:flex;gap:9px;flex-wrap:wrap;align-items:center}
.pk-chips{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
.pk-chip{border:1px solid var(--line);background:var(--panel-2);color:var(--muted-2);
  font:inherit;font-size:12.5px;font-weight:600;padding:5px 11px;border-radius:100px;cursor:pointer;
  transition:border-color .15s,color .15s}
.pk-chip:hover{border-color:#3a3a41;color:var(--text)}
.pk-msg{margin-top:11px;font-size:12.5px;line-height:1.5;color:#ff8080;display:none}
.pk-msg.show{display:block}
.pk-msg.ok{color:var(--muted-2)}


/* ── modal ───────────────────────────────────────────────────────
   A short, blocking decision taken after a choice has been made —
   "which channel is this for", once you have picked a package.

   Deliberately NOT the order drawer. A drawer is for reading a thing
   that already exists beside the list it came from; this interrupts,
   wants one answer, and hands it straight to checkout. It is also
   built generically (openModal) because the same shape is wanted for
   every other product surface, not just packages.

   Scales from .96 rather than 0 — nothing in the real world appears
   out of nothing — and keeps transform-origin at centre, because
   unlike a popover it is not anchored to the thing that opened it.
   ──────────────────────────────────────────────────────────────── */
.mo-scrim{position:fixed;inset:0;z-index:70;background:rgba(4,4,6,.7);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);
  opacity:0;pointer-events:none;transition:opacity .2s ease}
body.mo-open .mo-scrim{opacity:1;pointer-events:auto}
.mo-wrap{position:fixed;inset:0;z-index:71;display:grid;place-items:center;padding:20px;
  pointer-events:none}
.mo{width:min(460px,100%);max-height:calc(100vh / var(--z) - 40px);
  display:flex;flex-direction:column;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:0 24px 64px rgba(0,0,0,.5);
  opacity:0;transform:scale(.96);transform-origin:center;pointer-events:auto;
  transition:opacity .2s ease,transform .2s cubic-bezier(.23,1,.32,1)}
body.mo-open .mo{opacity:1;transform:none}

.mo-head{display:flex;align-items:flex-start;gap:12px;padding:17px 18px 15px;border-bottom:1px solid var(--line)}
.mo-head .tx{flex:1;min-width:0}
.mo-head b{display:block;font-size:15.5px;font-weight:700;letter-spacing:-.01em;color:var(--text)}
.mo-head span{display:block;margin-top:3px;font-size:12.5px;line-height:1.5;color:var(--muted)}
.mo-x{flex:none;width:30px;height:30px;border-radius:9px;border:0;background:var(--panel-2);
  color:var(--muted-2);cursor:pointer;display:grid;place-items:center;transition:background .15s,color .15s}
.mo-x svg{width:14px;height:14px}
.mo-x:hover{background:#2f2f36;color:var(--text)}

.mo-body{padding:17px 18px;overflow-y:auto;overscroll-behavior:contain}
.mo-foot{flex:none;display:flex;gap:9px;padding:14px 18px;border-top:1px solid var(--line);
  background:color-mix(in srgb,var(--panel-2) 55%,transparent)}
.mo-foot .btn{flex:1;justify-content:center;height:40px}

/* what you picked, restated so the decision is never made blind */
.mo-pick{display:flex;align-items:center;gap:12px;padding:12px 13px;border-radius:12px;
  background:var(--panel-2);border:1px solid var(--line);margin-bottom:16px}
.mo-pick .n{flex:1;min-width:0}
.mo-pick .n b{display:block;font-size:14px;font-weight:700;color:var(--text)}
.mo-pick .n span{display:block;margin-top:2px;font-size:12.5px;color:var(--muted)}
.mo-pick .p{flex:none;text-align:right}
.mo-pick .p b{display:block;font-size:16px;font-weight:800;letter-spacing:-.02em;color:var(--text);
  font-variant-numeric:tabular-nums}
.mo-pick .p span{display:block;font-size:11px;color:var(--muted)}

/* ── the chat-bot add-on ─────────────────────────────────────────
   Same row as .mo-pick so the modal reads as one stack: what you
   picked, who it is for, and what you can add to it. Collapsed to a
   single line until it is switched on, because most orders do not
   want it and an open panel of message options reads as a required
   step rather than an extra.
   ──────────────────────────────────────────────────────────────── */
.mo-addon{border:1px solid var(--line);border-radius:12px;background:var(--panel-2);
  margin-top:14px;overflow:hidden;transition:border-color .16s}
.mo-addon.on{border-color:rgba(74,214,255,.42)}
.mo-addon-hd{display:flex;align-items:center;gap:11px;width:100%;padding:12px 13px;
  background:none;border:0;text-align:left;cursor:pointer;color:inherit;font:inherit}
.mo-box{flex:none;width:18px;height:18px;border-radius:6px;border:1.6px solid var(--line);
  position:relative;transition:background .16s,border-color .16s}
.mo-addon.on .mo-box{background:var(--green,#4ad6ff);border-color:transparent}
.mo-addon.on .mo-box::after{content:"";position:absolute;left:5px;top:1.5px;width:5px;height:10px;
  border:solid #08080a;border-width:0 2px 2px 0;transform:rotate(45deg)}
.mo-addon-tx{flex:1;min-width:0}
.mo-addon-tx b{display:block;font-size:14px;font-weight:700;color:var(--text)}
.mo-addon-tx span{display:block;margin-top:2px;font-size:12.5px;color:var(--muted)}
.mo-addon-pr{flex:none;font-size:14px;font-weight:800;letter-spacing:-.02em;color:var(--text);
  font-variant-numeric:tabular-nums}

.mo-addon-opts{padding:0 13px 13px}
.mo-chips{display:flex;flex-wrap:wrap;gap:8px}
.mo-chip{appearance:none;border:1px solid var(--line);background:transparent;color:var(--muted);
  border-radius:999px;padding:7px 13px;font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;
  transition:border-color .16s,color .16s,background .16s}
.mo-chip:hover{color:var(--text);border-color:rgba(255,255,255,.24)}
.mo-chip.on{background:var(--green,#4ad6ff);border-color:transparent;color:#08080a}
.mo-comments{display:block;width:100%;box-sizing:border-box;margin-top:10px;min-height:88px;
  padding:10px 12px;border-radius:10px;border:1px solid var(--line);background:var(--panel);
  color:var(--text);font:inherit;font-size:13px;line-height:1.5;resize:vertical}
.mo-comments:focus{outline:2px solid var(--green,#4ad6ff);outline-offset:1px;border-color:transparent}

@media(prefers-reduced-motion:reduce){ .mo,.mo-scrim{transition:none}
  .mo-addon,.mo-box,.mo-chip{transition:none} }
@media(max-width:520px){
  .mo-wrap{padding:0;place-items:end stretch}
  .mo{width:100%;max-height:88vh;border-radius:18px 18px 0 0;border-bottom:0;
    transform:translateY(16px) scale(1)}
  body.mo-open .mo{transform:none}
}

/* ── one-off packages ────────────────────────────────────────────
   Same card as the monthly tiers on purpose: a customer choosing
   between "500 viewers for 3 hours" and "the Regular package" is
   making one decision, and two different card designs would make it
   look like two different shops.
   ──────────────────────────────────────────────────────────────── */
.qp-grid{display:grid;grid-template-columns:1fr;gap:12px}
@media(min-width:560px){ .qp-grid{grid-template-columns:repeat(2,1fr)} }
@media(min-width:900px){ .qp-grid{grid-template-columns:repeat(3,1fr)} }
@media(min-width:1240px){ .qp-grid{grid-template-columns:repeat(3,1fr)} }

.qp{display:flex;align-items:center;gap:14px;padding:15px 16px;border-radius:var(--r);
  border:1px solid var(--line);background:var(--panel);width:100%;text-align:left;font:inherit;
  cursor:pointer;transition:border-color .15s ease,background .15s ease,transform .16s cubic-bezier(.23,1,.32,1)}
.qp:hover{border-color:color-mix(in srgb,var(--green) 38%,transparent);background:var(--panel-2)}
.qp:active{transform:scale(.99)}
.qp .n{flex:1;min-width:0}
.qp .n b{display:block;font-size:21px;font-weight:800;letter-spacing:-.025em;color:var(--text);
  line-height:1;font-variant-numeric:tabular-nums}
.qp .n span{display:block;margin-top:5px;font-size:12.5px;color:var(--muted)}
.qp .p{flex:none;text-align:right}
.qp .p b{display:block;font-size:17px;font-weight:800;letter-spacing:-.02em;color:var(--green);
  font-variant-numeric:tabular-nums}
.qp .p span{display:block;margin-top:3px;font-size:10.5px;color:#5e5e68;text-transform:uppercase;letter-spacing:.07em}

/* duration tabs reuse .tabs from the order lists */
.qp-note{margin:14px 0 0;font-size:12.5px;line-height:1.6;color:var(--muted);max-width:640px}
