:root{
  --bg:#0b0e14; --bg2:#10151f; --card:#151b27; --line:#222c3d;
  --txt:#e6edf6; --muted:#8aa0bd; --brand:#66c0f4; --brand2:#1b6ca8;
  /* 'live' was green; it is orange now. Kept as its own pair rather than
     reusing --orange so the live state can be tuned without touching buttons. */
  --live:#e2711c; --live2:#ff8a2b; --red:#e05a4a; --amber:#e0a23a;
  /* Primary action: Loopcast blue, the same gradient the marketing site's CTA
     uses, deepened at the light end so white stays legible across the whole
     button rather than only its dark half. */
  --btn-blue:#2f8fd6; --btn-blue2:#1b6ca8;
  /* Ink for the ORANGE ghost hover only: dark Loopcast blue, 4.5:1 on orange. */
  --btn-ink:#082c48;
  --orange:#ff8a2b; --orange2:#e2711c;   /* accent, matches the .io in the wordmark */
  --radius:14px; --maxw:1120px;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  font-family:'Motiva Sans',Inter,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
  background:radial-gradient(1200px 600px at 70% -10%,#1a2740 0%,var(--bg) 55%);
  color:var(--txt);line-height:1.55;-webkit-font-smoothing:antialiased;
}
a{color:var(--brand);text-decoration:none}
.container{max-width:var(--maxw);margin:0 auto;padding:0 22px}
.btn{display:inline-block;padding:12px 22px;border-radius:10px;font-weight:600;cursor:pointer;
  border:1px solid transparent;font-size:15px;transition:.15s;text-align:center}
/* Primary action: Loopcast blue, matching the marketing site's CTA. Orange is
   the secondary action and the live state, so the primary never competes. */
.btn-primary{background:linear-gradient(90deg,var(--btn-blue),var(--btn-blue2));color:#fff}
.btn-primary:hover{filter:brightness(1.08)}
/* Secondary action: orange, picking up the .io accent. Primary is blue, so
   the two never compete for the same click. */
.btn-ghost{background:rgba(255,138,43,.10);border:1px solid rgba(255,138,43,.55);color:var(--orange)}
.btn-ghost:hover{background:var(--orange);border-color:var(--orange);color:var(--btn-ink)}
/* Genuinely neutral actions (cancel, back, close) stay quiet instead of shouting */
.btn-quiet{background:#1e2738;border:1px solid #38465f;color:var(--txt)}
.btn-quiet:hover{border-color:var(--brand);background:#26324a}
.btn-block{display:block;width:100%}
.btn-sm{padding:7px 14px;font-size:13px;border-radius:8px}
.muted{color:var(--muted)}

/* nav */
header.nav{position:sticky;top:0;z-index:30;backdrop-filter:blur(10px);
  background:rgba(11,14,20,.72);border-bottom:1px solid var(--line)}
.nav-in{display:flex;align-items:center;justify-content:space-between;height:64px}
.logo{display:flex;align-items:center;gap:10px;font-weight:800;font-size:19px;letter-spacing:.3px}
/* Wordmark SVG. Height drives it; width follows the 9:1 viewBox. */
.logo-img{height:24px;width:auto;display:block}
@media(max-width:520px){.logo-img{height:20px}}
.logo .dot{width:26px;height:26px;border-radius:7px;background:linear-gradient(135deg,var(--brand),var(--brand2));
  display:grid;place-items:center;color:#04101c;font-size:15px}
.nav-links{display:flex;gap:26px;align-items:center}
.nav-links a{color:var(--muted);font-weight:500}
.nav-links a:hover{color:var(--txt)}
/* A button in the nav keeps its own label colour: .nav-links a is more
   specific than .btn-primary and was painting the label muted-on-orange. */
.nav-links a.btn-primary{color:#fff}
.nav-links a.btn-ghost{color:var(--orange)}
.nav-links a.btn-ghost:hover{color:var(--btn-ink)}
.nav-links a.btn-quiet{color:var(--txt)}

/* hero */
.hero{padding:90px 0 60px;text-align:center}
.badge{display:inline-block;padding:6px 14px;border:1px solid var(--line);border-radius:999px;
  color:var(--brand);font-size:13px;margin-bottom:22px;background:var(--card)}
.hero h1{font-size:clamp(34px,5vw,58px);line-height:1.05;margin:0 0 18px;font-weight:800;letter-spacing:-.5px}
.hero h1 .hl{background:linear-gradient(90deg,var(--brand),var(--live2));-webkit-background-clip:text;background-clip:text;color:transparent}
.hero p.sub{font-size:clamp(16px,2vw,21px);color:var(--muted);max-width:680px;margin:0 auto 32px}
.hero-cta{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
.price-pill{margin-top:26px;color:var(--muted);font-size:14px}
.price-pill b{color:var(--live2);font-size:17px}

/* sections */
section{padding:70px 0}
section h2{font-size:clamp(26px,3.5vw,38px);text-align:center;margin:0 0 12px;font-weight:800}
section .lead{text-align:center;color:var(--muted);max-width:640px;margin:0 auto 48px}
.grid{display:grid;gap:22px}
.grid-3{grid-template-columns:repeat(3,1fr)}
.grid-4{grid-template-columns:repeat(4,1fr)}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);padding:26px}
.card h3{margin:0 0 8px;font-size:18px}
.card p{margin:0;color:var(--muted);font-size:15px}
.step-n{width:36px;height:36px;border-radius:9px;background:var(--bg2);border:1px solid var(--line);
  display:grid;place-items:center;color:var(--brand);font-weight:800;margin-bottom:14px}
.feature-ico{margin-bottom:14px;width:44px;height:44px;border-radius:11px;display:grid;place-items:center;
  background:linear-gradient(135deg,rgba(102,192,244,.16),rgba(27,108,168,.12));border:1px solid var(--line)}
.feature-ico svg{width:23px;height:23px;stroke:var(--brand);fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}

/* pricing */
.pricewrap{display:flex;justify-content:center}
.pricecard{max-width:420px;width:100%;text-align:center;border:1px solid var(--brand);
  box-shadow:0 0 0 1px rgba(102,192,244,.15),0 20px 60px -20px rgba(102,192,244,.35)}
.bigprice{font-size:58px;font-weight:800;margin:8px 0 0}
.bigprice small{font-size:18px;color:var(--muted);font-weight:500}
.pricecard ul{list-style:none;padding:0;margin:24px 0;text-align:left}
.pricecard li{padding:9px 0;border-bottom:1px solid var(--line);color:var(--muted)}
.pricecard li::before{content:"✓";color:var(--live2);font-weight:800;margin-right:10px}

/* faq */
.faq details{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px 20px;margin-bottom:12px}
.faq summary{cursor:pointer;font-weight:600;font-size:16px}
.faq p{color:var(--muted);margin:12px 0 0}

footer{border-top:1px solid var(--line);padding:40px 0;color:var(--muted);text-align:center;font-size:14px}

/* auth + panel */
.auth-wrap{min-height:calc(100vh - 64px);display:grid;place-items:center;padding:40px 20px}
.auth-card{width:100%;max-width:400px}
.field{margin-bottom:16px}
.field label{display:block;font-size:13px;color:var(--muted);margin-bottom:6px}
input,select,textarea{width:100%;padding:11px 13px;background:var(--bg2);border:1px solid var(--line);
  border-radius:9px;color:var(--txt);font-size:15px;font-family:inherit}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--brand)}
.tabs{display:flex;gap:8px;margin-bottom:22px}
.tabs button{flex:1;background:var(--bg2);border:1px solid var(--line);color:var(--muted);padding:10px;border-radius:9px;cursor:pointer}
.tabs button.active{background:var(--card);color:var(--txt);border-color:var(--brand)}
.err{color:var(--red);font-size:13px;margin-top:10px;min-height:18px}
.ok{color:var(--live2)}

.panel-top{display:flex;justify-content:space-between;align-items:center;padding:18px 0;border-bottom:1px solid var(--line);margin-bottom:26px;flex-wrap:wrap;gap:12px}
/* Nothing on the left any more: the balance and the account moved to the header */
.panel-top-solo{justify-content:flex-end}
.balance{display:flex;gap:18px;align-items:center;flex-wrap:wrap}
.chip{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:8px 14px;font-size:14px}
.chip b{color:var(--live2)}

/* ---- signed-in header: balance + account menu ---- */
.nav-authed{display:flex;align-items:center;gap:10px;margin-left:auto}
/* Balance chip in the header. Morphs into "Buy Minutes" on hover so topping up is
   always one click away; only active once buying is unlocked (.topup-ready). */
/* Both labels are stacked in ONE grid cell, so the chip is as wide as the wider of
   the two and hovering swaps the text without the chip changing size. */
.nav-balance{position:relative;display:grid;place-items:center;background:var(--card);
  border:1px solid var(--line);border-radius:10px;padding:8px 14px;font:inherit;font-size:14px;
  color:var(--muted);white-space:nowrap;cursor:default;
  transition:background .12s,border-color .12s,color .12s}
.nav-balance>.balance-label,.nav-balance>.balance-buy{grid-area:1/1}
.nav-balance b{color:var(--live2);font-weight:700}
.nav-balance .balance-unit-sm{display:none}
.nav-balance .balance-buy{visibility:hidden;font-weight:600}
.nav-balance.topup-ready{cursor:pointer}
.nav-balance.topup-ready:hover{background:linear-gradient(90deg,var(--btn-blue),var(--btn-blue2));
  border-color:transparent;color:#fff}
.nav-balance.topup-ready:hover .balance-label{visibility:hidden}
.nav-balance.topup-ready:hover .balance-buy{visibility:visible}
.nav-balance:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* Low balance lives on the balance itself: amber chip, and the reason on hover.
   The tooltip is absolutely positioned, so it never becomes a grid item. */
.nav-balance.low{border-color:rgba(224,162,58,.55);background:rgba(224,162,58,.10);color:var(--amber)}
.nav-balance.low b{color:var(--amber)}
.nav-balance.low::after{content:attr(data-tip);position:absolute;top:calc(100% + 9px);right:0;
  width:max-content;max-width:290px;padding:10px 12px;border-radius:9px;background:var(--card);
  border:1px solid rgba(224,162,58,.5);color:var(--txt);font-size:12.5px;font-weight:500;
  line-height:1.45;white-space:normal;text-align:left;box-shadow:0 14px 34px rgba(0,0,0,.5);
  opacity:0;visibility:hidden;transform:translateY(-4px);pointer-events:none;z-index:50;
  transition:opacity .14s,visibility .14s,transform .14s}
.nav-balance.low:hover::after,.nav-balance.low:focus-visible::after{opacity:1;visibility:visible;transform:none}
/* Hovering a low chip still offers the top-up, so the amber has to give way to the
   blue button the same as any other state. */
.nav-balance.low.topup-ready:hover{border-color:transparent;color:#fff}
.acct{position:relative}
.acct-btn{display:grid;place-items:center;width:38px;height:38px;padding:0;background:var(--card);
  border:1px solid var(--line);border-radius:50%;color:var(--muted);cursor:pointer;transition:.15s}
.acct-btn svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.acct-btn:hover,.acct-btn[aria-expanded="true"]{border-color:var(--brand);color:var(--txt)}
.acct-btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.acct-menu{position:absolute;top:calc(100% + 8px);right:0;z-index:40;min-width:224px;
  background:var(--card);border:1px solid var(--line);border-radius:11px;padding:6px;
  box-shadow:0 16px 40px rgba(0,0,0,.5)}
.acct-who{padding:9px 11px 10px;font-size:12.5px;color:var(--muted);border-bottom:1px solid var(--line);
  margin-bottom:5px}
.acct-who b{display:block;color:var(--brand);font-size:13.5px;font-weight:600;word-break:break-all}
.acct-item{display:block;padding:9px 11px;border-radius:8px;color:var(--txt);font-size:14px;font-weight:500}
.acct-item:hover{background:#26324a;color:var(--txt)}
#navLogout{color:var(--muted)}
#navLogout:hover{color:var(--red);background:rgba(224,90,74,.12)}
/* An answer is waiting: a dot on the closed menu, the number inside it. The dot
   rides .acct's position:relative and its ring is the header's own colour, so it
   reads as sitting on the icon rather than beside it. */
.acct-dot{position:absolute;top:-2px;right:-2px;width:10px;height:10px;border-radius:50%;
  background:var(--live2);border:2px solid var(--bg);pointer-events:none}
/* Sits after the word Support inside the menu item, so it is inline: the admin
   rail's .adm-badge pushes itself to the far edge, which here would leave the
   count floating away from what it counts. */
.acct-count{display:inline-block;margin-left:7px;min-width:19px;padding:0 6px;border-radius:999px;
  background:rgba(255,138,43,.16);border:1px solid rgba(255,138,43,.5);color:var(--live2);
  font-size:11px;font-weight:700;line-height:16px;text-align:center;vertical-align:1px}
/* On a phone the wordmark, the balance and the icon have to share 375px, so the
   chip drops the word "Balance" and abbreviates the unit rather than wrapping. */
@media(max-width:560px){
  .nav-authed{gap:8px}
  .nav-balance{padding:7px 11px;font-size:13px}
  .nav-balance .balance-word,.nav-balance .balance-unit,.nav-balance .balance-plus{display:none}
  .nav-balance .balance-unit-sm{display:inline}
  .acct-btn{width:34px;height:34px}
  .acct-btn svg{width:19px;height:19px}
}
/* One tab per stream, sitting on top of the stream card */
.stream-tabs{display:flex;gap:6px;overflow-x:auto;padding:0 2px 0;margin-bottom:-1px;scrollbar-width:thin}
.stream-tab{display:flex;align-items:center;gap:8px;flex:0 0 auto;max-width:230px;padding:10px 16px;
  background:var(--bg2);border:1px solid var(--line);border-bottom-color:transparent;
  border-radius:10px 10px 0 0;color:var(--muted);font:inherit;font-size:14px;font-weight:600;
  cursor:pointer;white-space:nowrap;transition:background .15s,color .15s,border-color .15s}
.stream-tab:hover{color:var(--txt);background:var(--card)}
.stream-tab.active{background:var(--card);color:var(--txt);border-color:var(--line);
  border-bottom-color:var(--card);box-shadow:inset 0 2px 0 var(--brand)}
.stream-tab .tab-label{overflow:hidden;text-overflow:ellipsis}
.tab-close{flex:0 0 auto;margin:-4px -8px -4px 2px;padding:4px 6px;border-radius:6px;
  font-size:11px;line-height:1;color:var(--muted);opacity:.55;transition:.15s}
.stream-tab:hover .tab-close,.stream-tab.active .tab-close{opacity:1}
.tab-close:hover{background:rgba(224,90,74,.18);color:var(--red)}
.tab-dot{width:8px;height:8px;flex:0 0 auto;border-radius:50%;background:var(--muted)}
.tab-dot.d-live{background:var(--live2);box-shadow:0 0 0 3px rgba(255,138,43,.18)}
.tab-dot.d-ready{background:var(--brand)}
.tab-dot.d-transcoding{background:var(--amber)}
.tab-dot.d-error,.tab-dot.d-no_credit{background:var(--red)}
/* Media leads the strip, then a gap, then the streams: two groups of tabs that
   read as two places rather than one long row. "+ New stream" closes the row on
   the right, as a button: the strip scrolls, the action never scrolls away. */
.tab-bar{display:flex;align-items:flex-end;gap:22px;margin-bottom:-1px}
.tab-bar .stream-tabs{margin-bottom:0;overflow:visible;flex:0 0 auto}
.tab-bar .stream-tabs+.stream-tabs{flex:1 1 auto;min-width:0;overflow-x:auto}
.tab-new{flex:0 0 auto;margin-left:auto;margin-bottom:6px;white-space:nowrap}
@media(max-width:560px){
  .tab-bar{gap:12px}
  .tab-new{margin-bottom:5px;padding:7px 11px}
  /* A scrollbar under two tabs is more chrome than the row can afford at this
     width; the strip is still swipeable. */
  .tab-bar .stream-tabs+.stream-tabs{scrollbar-width:none}
  .tab-bar .stream-tabs+.stream-tabs::-webkit-scrollbar{display:none}
}
.tab-dot.d-media{background:var(--brand)}
/* The open panel is welded to its tab, whichever section is showing. */
.tab-bar+#mediaView .stream,.tab-bar+#mediaView+.streamlist .stream,
.stream.tabbed{border-top-left-radius:0;border-top-right-radius:0}

/* ---- media library ---- */
.media-list{display:grid;gap:0;margin-top:16px;border:1px solid var(--line);border-radius:10px;overflow:hidden}
.media-list:empty{display:none}
.media-row{display:flex;align-items:center;gap:12px;padding:12px 14px;border-top:1px solid var(--line);
  background:var(--bg2)}
.media-row:first-child{border-top:0}
/* Same still and runtime badge the source cards use, at row scale */
.media-thumb{position:relative;flex:0 0 auto;width:76px;aspect-ratio:16/9;border-radius:7px;
  overflow:hidden;background:#05080d;border:1px solid var(--line)}
.media-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.media-thumb .src-dur{right:3px;bottom:3px;padding:0 4px;font-size:10.5px}
.media-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
.media-size{flex:0 0 auto;font-size:13px}
.media-used{flex:0 0 auto;font-size:12px}
.media-state{flex:0 0 auto;display:flex;align-items:center;gap:8px;min-width:132px;justify-content:flex-end;
  font-size:13px;font-weight:600}
/* s-ready / s-preparing are shared with the stream status PILL, which brings a
   tinted background with it. Here the state is plain text in a column, so the
   pill's background is turned back off — and it has to out-specify .s-ready,
   which is declared further down the file. */
.media-row .media-state{background:none}
.media-state.s-ready{color:var(--brand)}
.media-state.s-preparing{color:var(--amber)}
.media-state.s-failed{color:var(--red)}
.media-state.s-none{color:var(--muted)}
.media-mini{width:74px;height:6px;border-radius:999px;background:var(--bg);overflow:hidden;border:1px solid var(--line)}
.media-mini i{display:block;height:100%;width:0;background:var(--amber);border-radius:999px;transition:width .4s}

/* ---- source picker on a stream ----
   Cards, not rows: a loop is recognised by the frame it opens on and how long it
   runs, so the picker shows both and reads like a row of video previews. */
.src{margin-bottom:18px}
.src-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:9px}
.src-title{font-weight:700;font-size:14px}
.src-cards{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(168px,1fr))}
.src-cards:empty{display:none}
.src-card{display:flex;flex-direction:column;gap:7px;padding:8px;text-align:left;font:inherit;
  color:var(--txt);background:var(--bg2);border:1px solid var(--line);border-radius:12px;
  cursor:pointer;transition:border-color .15s,background .15s,transform .15s}
.src-card:hover{border-color:var(--brand);background:var(--card);transform:translateY(-1px)}
.src-card:focus-visible{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px rgba(102,192,244,.22)}
.src-card.on{border-color:var(--brand);background:rgba(102,192,244,.09);
  box-shadow:0 0 0 1px rgba(102,192,244,.45)}
.src-card.busy{cursor:default;opacity:.55}
.src-card.busy:hover{transform:none;border-color:var(--line);background:var(--bg2)}
/* 16:9 frame: the poster fills it, and the placeholder shows through whenever
   there is no poster yet (still encoding, or the file went missing). */
.src-thumb{position:relative;display:block;aspect-ratio:16/9;border-radius:8px;overflow:hidden;
  background:#05080d;border:1px solid var(--line)}
.src-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;z-index:1}
.src-fallback{position:absolute;inset:0;display:grid;place-items:center}
.src-fallback svg{width:30px;height:30px;fill:none;stroke:var(--line);stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}
.src-dur{position:absolute;right:5px;bottom:5px;z-index:2;padding:1px 6px;border-radius:5px;
  background:rgba(5,8,13,.82);color:#e6edf6;font-size:11.5px;font-weight:700;
  font-variant-numeric:tabular-nums;line-height:1.5}
.src-tick{position:absolute;top:5px;left:5px;z-index:2;width:19px;height:19px;border-radius:50%;
  background:rgba(5,8,13,.7);border:1px solid rgba(230,237,246,.35);display:grid;place-items:center}
.src-tick::after{content:'';width:9px;height:5px;margin-top:-2px;border-left:2px solid transparent;
  border-bottom:2px solid transparent;transform:rotate(-45deg)}
.src-card.on .src-tick{background:var(--brand);border-color:var(--brand)}
.src-card.on .src-tick::after{border-left-color:#04101c;border-bottom-color:#04101c}
.src-file{font-size:13px;font-weight:600;line-height:1.35;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.src-note{font-size:12px;color:var(--muted)}
.src-card.on .src-note{color:var(--brand)}

/* Library pane and chosen-video pane. Only one is in flow at a time, and the one
   arriving slides in from the side it came from: library from the left, video
   from the right. .anim is added only on a real switch, never on a poll redraw. */
@keyframes src-in-left{from{opacity:0;transform:translateX(-24px)}to{opacity:1;transform:none}}
@keyframes src-in-right{from{opacity:0;transform:translateX(24px)}to{opacity:1;transform:none}}
.src-pane.p-pick.anim{animation:src-in-left .26s ease}
.src-pane.p-view.anim{animation:src-in-right .26s ease}
.preview-back{position:absolute;top:10px;left:10px;z-index:3;display:inline-flex;align-items:center;gap:6px;
  padding:7px 12px;border-radius:9px;background:rgba(5,8,13,.72);border:1px solid rgba(230,237,246,.22);
  color:#e6edf6;font:inherit;font-size:12.5px;font-weight:600;line-height:1.2;cursor:pointer;
  backdrop-filter:blur(6px);transition:.15s}
.preview-back:hover{background:rgba(5,8,13,.92);border-color:var(--brand);color:var(--brand)}
.preview-back:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.preview-back svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2.4;
  stroke-linecap:round;stroke-linejoin:round}
.preview-meta{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;margin-top:9px;font-size:13px;
  line-height:1.4}
.preview-meta b{font-weight:600}
.preview-meta .muted{font-size:12.5px}
.link{color:var(--brand);cursor:pointer;text-decoration:underline}

.stream-tabs:not(.hidden)+.streamlist .stream{border-top-left-radius:0;border-top-right-radius:0}
.streamlist{display:grid;gap:16px}
.stream{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:18px}
.stream-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
/* The name and its pen behave as one control: hovering either highlights both,
   and clicking either opens the inline editor. */
.name-wrap{display:flex;align-items:center;gap:2px;margin:-3px -8px}
.stream-name{font-size:17px;font-weight:700;cursor:text;padding:3px 8px;border-radius:7px;border:1px solid transparent;transition:background .15s,border-color .15s}
.stream-name:hover,.stream-name:focus-visible,.name-wrap:hover .stream-name{background:rgba(102,192,244,.10);border-color:var(--line);outline:none}
.name-pen{flex:0 0 auto;display:grid;place-items:center;width:28px;height:28px;padding:0;
  background:none;border:1px solid transparent;border-radius:7px;color:var(--muted);
  cursor:pointer;opacity:.75;transition:.15s}
.name-pen svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.name-wrap:hover .name-pen{opacity:1;color:var(--brand)}
.name-pen:hover{background:rgba(102,192,244,.12);border-color:var(--line);color:var(--brand)}
.name-pen:focus-visible{outline:none;border-color:var(--brand);color:var(--brand);opacity:1}
.name-edit{display:flex;align-items:center;gap:6px}
.name-input{font-size:17px;font-weight:700;color:var(--txt);background:var(--bg2);border:1px solid var(--brand);border-radius:7px;padding:3px 8px;min-width:180px;max-width:100%;font-family:inherit}
.name-input:focus{outline:none;box-shadow:0 0 0 3px rgba(102,192,244,.18)}
.name-btn{width:30px;height:30px;flex:0 0 auto;display:grid;place-items:center;font-size:14px;line-height:1;cursor:pointer;border-radius:7px;border:1px solid var(--line);background:var(--bg2);color:var(--muted);transition:background .15s,color .15s,border-color .15s}
.name-btn:hover{color:var(--txt)}
.name-ok:hover{background:rgba(255,138,43,.16);border-color:var(--live2);color:var(--live2)}
.name-cancel:hover{background:rgba(224,90,74,.16);border-color:var(--red);color:var(--red)}
.status{font-size:12px;font-weight:700;padding:4px 11px;border-radius:999px;text-transform:uppercase;letter-spacing:.4px}
.s-live{background:rgba(255,138,43,.16);color:var(--live2)}
.s-ready{background:rgba(102,192,244,.16);color:var(--brand)}
.s-transcoding{background:rgba(224,162,58,.16);color:var(--amber)}
.s-error,.s-no_credit{background:rgba(224,90,74,.16);color:var(--red)}
.s-new,.s-stopped{background:#222c3d;color:var(--muted)}
.stream-body{margin-top:16px;display:grid;gap:12px}
.row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
/* Destination fields: hiding App ID lets URL + Key pair up on one row (YouTube/Kick);
   with App ID (Steam) the key spans full width beneath [App ID | URL]. */
/* Masked like a password, without being a password field (see panel.html) */
.masked{-webkit-text-security:disc;text-security:disc;letter-spacing:.08em}
.netfields{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.netfields .field{margin-bottom:0}
.netfields.has-appid .f-key-field{grid-column:1 / -1}
.actions{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap}

/* source-video drag & drop */
.dropzone{position:relative;border:2px dashed var(--line);border-radius:12px;background:var(--bg2);
  padding:28px 20px;text-align:center;cursor:pointer;transition:.15s}
.dropzone:hover{border-color:var(--brand);background:rgba(102,192,244,.05)}
.dropzone.dragover{border-color:var(--brand);background:rgba(102,192,244,.10);box-shadow:0 0 0 3px rgba(102,192,244,.12)}
.dropzone.has-file{border-style:solid;border-color:var(--brand);background:rgba(102,192,244,.06)}
.dropzone.has-file .dz-inner{display:none}
.dz-inner{display:flex;flex-direction:column;align-items:center;gap:5px;pointer-events:none}
.dz-ico{width:46px;height:46px;border-radius:12px;display:grid;place-items:center;margin-bottom:4px;
  background:linear-gradient(135deg,rgba(102,192,244,.16),rgba(27,108,168,.12));border:1px solid var(--line)}
.dz-ico svg{width:22px;height:22px;stroke:var(--brand);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.dz-title{font-weight:700;font-size:15px}
.dz-sub{font-size:13px}
.dz-browse{color:var(--brand);text-decoration:underline}
.dz-file{display:inline-flex;align-items:center;gap:10px;max-width:100%}
.dz-ico-sm{color:var(--brand);font-size:13px}
.dz-fname{font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:min(70vw,420px)}
.dz-clear{background:none;border:none;color:var(--muted);cursor:pointer;font-size:14px;line-height:1;
  padding:3px 7px;border-radius:6px;transition:.15s}
.dz-clear:hover{color:var(--red);background:var(--bg)}
.card-close{position:absolute;top:12px;right:12px;width:30px;height:30px;display:grid;place-items:center;
  background:none;border:1px solid transparent;border-radius:8px;color:var(--muted);
  font-size:14px;line-height:1;cursor:pointer;transition:.15s}
.card-close:hover{color:var(--red);border-color:var(--line);background:var(--bg2)}
.dz-progress{width:min(100%,460px);margin:14px auto 0;display:flex;flex-direction:column;gap:7px}
.dz-bar{height:7px;border-radius:999px;background:var(--bg);overflow:hidden;border:1px solid var(--line)}
.dz-bar i{display:block;height:100%;width:0;border-radius:999px;
  background:linear-gradient(90deg,var(--brand),var(--live2));transition:width .18s ease}
.dz-pct{display:flex;gap:8px;justify-content:center;font-size:12.5px}
.dz-pct b{color:var(--txt)}
/* Encoding state is shown on the video (Media list and source cards), not per
   destination; all that is left here is the pulse on a platform chip mid-encode. */
@keyframes prep-pulse{0%,100%{opacity:1}50%{opacity:.55}}
.dz-preview{position:relative;border:1px solid var(--line);border-radius:12px;overflow:hidden;
  background:#000;line-height:0}
.preview-video{display:block;width:100%;max-height:230px;background:#000;object-fit:contain}
.dz-actions{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-top:-2px}
.dz-platforms{display:flex;gap:10px;flex-wrap:wrap}
.plat-btn{position:relative;display:flex;align-items:center;gap:8px;background:var(--bg2);
  border:1px solid var(--line);border-radius:10px;padding:9px 14px;cursor:pointer;
  color:var(--txt);font-size:13px;font-weight:600;transition:.15s}
.plat-btn:hover{border-color:var(--brand)}
.plat-ico{width:20px;height:20px;display:block;flex:none}
.plat-name{white-space:nowrap}
.plat-check{position:absolute;top:-7px;right:-7px;width:18px;height:18px;border-radius:50%;
  background:var(--bg2);border:1px solid var(--line);display:none;place-items:center;
  line-height:1;transition:.15s}
/* The badge says what this destination will do:
     grey pause   = details saved, switched off
     amber play   = switched on, starts on the next Go live
     orange play  = broadcasting right now                                   */
.plat-btn.idle .plat-check,.plat-btn.on .plat-check,.plat-btn.livenow .plat-check{display:grid}
.plat-btn.idle{border-color:var(--line);background:var(--bg2)}
.plat-btn.idle .plat-check{background:var(--muted);border-color:var(--muted)}
.plat-btn.idle .plat-check::after{content:"";width:7px;height:8px;
  border-left:2px solid #0b0e14;border-right:2px solid #0b0e14}
.plat-btn.on{border-color:var(--amber);background:rgba(224,162,58,.12)}
.plat-btn.on .plat-check{background:var(--amber);border-color:var(--amber)}
.plat-btn.on .plat-check::after{content:"";width:0;height:0;margin-left:2px;
  border-left:7px solid #0b0e14;border-top:4.5px solid transparent;border-bottom:4.5px solid transparent}
.plat-btn.active{box-shadow:0 0 0 2px rgba(102,192,244,.55)}
/* "+ Add" control: platforms show only once added; the rest live behind this menu */
.plat-add-wrap{position:relative}
.plat-add{display:flex;align-items:center;gap:6px;background:transparent;border:1px dashed #38465f;
  border-radius:10px;padding:9px 14px;cursor:pointer;color:var(--muted);font-size:13px;font-weight:600;transition:.15s}
.plat-add:hover{border-color:var(--brand);color:var(--txt)}
.plat-add-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:30;display:flex;flex-direction:column;gap:4px;
  background:var(--card);border:1px solid var(--line);border-radius:10px;padding:6px;
  box-shadow:0 12px 30px rgba(0,0,0,.45);min-width:150px}
.plat-add-item{display:flex;align-items:center;gap:8px;background:transparent;border:none;border-radius:7px;
  padding:8px 10px;cursor:pointer;color:var(--txt);font-size:13px;font-weight:600;text-align:left;
  transform:scale(.85);opacity:0;animation:plat-add-in .18s ease forwards}
@keyframes plat-add-in{to{transform:none;opacity:1}}
.plat-add-item:hover{background:#26324a}
.plat-add-item svg{width:20px;height:20px;flex:none}

/* The destination form is one block: which destination, its fields, autosaved */
.dest-form{display:grid;gap:12px;padding:14px;border:1px solid var(--line);border-radius:12px;background:var(--bg2)}
.dest-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
/* Remove sits top-right of the destination card, away from the switch it used
   to sit beside: a destructive action should not share a row with a toggle. */
.dest-head .f-dest-remove{margin-left:auto}
.dest-title{font-size:14px;color:var(--muted)}
.dest-title b{color:var(--txt)}
.save-hint{font-size:12.5px;min-height:16px}
.save-hint.dirty{color:var(--amber)}
.save-hint.saving{color:var(--brand)}
.save-hint.saved{color:var(--live2)}
.save-hint.failed{color:var(--red);cursor:pointer;text-decoration:underline}
.logbox{background:#05080d;border:1px solid var(--line);border-radius:9px;padding:12px;font-family:ui-monospace,Menlo,monospace;
  font-size:12px;color:#9fb6d2;white-space:pre-wrap;max-height:240px;overflow:auto;margin-top:12px}
table{width:100%;border-collapse:collapse;font-size:14px}
th,td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--line)}
th{color:var(--muted);font-weight:600}
.hidden{display:none !important}   /* utility: must beat any later display rule */
.toast{position:fixed;bottom:24px;right:24px;background:var(--card);border:1px solid var(--brand);
  padding:14px 18px;border-radius:10px;z-index:100;max-width:340px}
@media(max-width:820px){.grid-3,.grid-4{grid-template-columns:1fr}.row,.netfields{grid-template-columns:1fr}.nav-links{display:none}}

/* ---- UX pass: head actions, errors, modal, balance affordance ---- */
.stream-head{gap:10px}
.head-actions{display:flex;gap:8px;margin-left:auto}
.stream-err{margin:10px 0 0;padding:10px 14px;border-radius:10px;font-size:13px;
  border:1px solid rgba(224,90,74,.45);background:rgba(224,90,74,.10);color:var(--red);
  white-space:pre-wrap;word-break:break-word}
.balance-plus{margin-left:8px;color:var(--live2);font-weight:800;opacity:.75}
/* Destructive confirmations get a real dialog, not the browser's one-click confirm */
.modal-backdrop{position:fixed;inset:0;z-index:80;background:rgba(6,9,14,.72);
  display:grid;   /* .hidden keeps it off screen until confirmAction() opens it */
  place-items:center;padding:20px;backdrop-filter:blur(2px)}
.modal{width:100%;max-width:440px;padding:22px}
.modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:18px}
.btn-danger{background:var(--red);color:#fff;border-color:var(--red)}
.btn-danger:hover{filter:brightness(1.08)}
.btn-danger:disabled{opacity:.45;cursor:not-allowed;filter:none}
/* A primary action that cannot succeed yet: dimmed, unclickable, and the
   reason is shown beside it rather than hidden in a title attribute. */
.btn-primary:disabled{opacity:.4;cursor:not-allowed;filter:none}
.start-why{font-size:12.5px;white-space:nowrap}
/* Video library: uploads belong to the account, so one file can feed several
   streams. Collapsed by default; the usage line shows without opening it. */
.lib{margin:12px 0 4px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.lib-toggle{background:none;border:0;padding:0;cursor:pointer;color:var(--brand);
  font-size:13.5px;font-weight:600}
.lib-toggle::before{content:'\25B8 ';display:inline-block;transition:transform .15s ease}
.lib-toggle.on::before{transform:rotate(90deg)}
.lib-usage{font-size:12.5px}
.lib-list{flex:1 0 100%;margin-top:6px;border:1px solid var(--line);border-radius:9px;
  background:var(--bg2);overflow:hidden}
.lib-row{display:flex;align-items:center;gap:10px;padding:9px 12px;border-top:1px solid var(--line)}
.lib-row:first-child{border-top:0}
.lib-row-on{background:rgba(102,192,244,.07)}
.lib-name{font-size:13.5px;color:var(--txt);max-width:230px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.lib-meta{font-size:12px;margin-right:auto}
@media (max-width:560px){.lib-meta{flex:1 0 100%;order:3;margin:2px 0 0}}

/* Simulcast: one destination per switch, all live together */
.dest-toggle{display:flex;align-items:center;gap:12px}
.st-text{font-size:14px;color:var(--muted)}
.st-text b{color:var(--txt)}
.st-state{margin-left:8px;font-size:12px;color:var(--muted)}
.switch{position:relative;width:46px;height:26px;border-radius:999px;background:var(--line);
  border:1px solid var(--line);cursor:pointer;padding:0;flex:none;transition:.15s}
.switch-knob{position:absolute;top:2px;left:2px;width:20px;height:20px;border-radius:50%;
  background:#e6edf6;transition:.15s}
.switch.on{background:var(--live2);border-color:var(--live2)}
.switch.on .switch-knob{left:22px;background:#0b0e14}
.switch.disabled{opacity:.45;cursor:not-allowed}
.plat-btn.preparing{border-color:var(--amber);animation:prep-pulse 1.4s ease-in-out infinite}
/* Live wins over armed: orange background, same play mark. Declared after .on so
   a destination that is both switched on and pushing reads as live. */
.plat-btn.livenow{border-color:var(--live2);background:rgba(255,138,43,.12);
  box-shadow:0 0 0 2px rgba(255,138,43,.35)}
.plat-btn.livenow .plat-check{background:var(--live2);border-color:var(--live2)}
.plat-btn.livenow .plat-check::after{content:"";width:0;height:0;margin-left:2px;
  border-left:7px solid #0b0e14;border-top:4.5px solid transparent;border-bottom:4.5px solid transparent}
.dest-live{font-size:12px;color:var(--live2);font-weight:700}
/* Account deletion sits apart from the rest of the account page */
.danger-card{border-color:rgba(224,90,74,.45)}
.danger-card h3,.danger-card .err{margin-top:0}

/* ---- panel header on small screens ----
   The signed-in bar is two controls (balance + account icon), which fits every
   width, so the panel no longer needs a hamburger. The marketing page's own link
   row still collapses below 820px. */
@media(max-width:820px){
  .nav-links{display:none!important}
}

/* ---- support: the ticket list ----
   Same row idiom as the media library, because it is the same kind of object:
   one line per thing, all four columns landing in the same place down the list. */
.ticket-list{display:grid;gap:0;border:1px solid var(--line);border-radius:10px;overflow:hidden}
/* The empty and the loading copy are the paragraph below this list, so an empty
   list has to leave rather than sit there as a bordered strip (see .media-list). */
.ticket-list:empty{display:none}
.ticket-row{display:flex;align-items:center;gap:12px;padding:12px 14px;border-top:1px solid var(--line);
  background:var(--bg2);cursor:pointer;transition:background .15s}
.ticket-row:first-child{border-top:0}
.ticket-row:hover{background:var(--card)}
/* The list clips its corners, which would clip an outline too, so the keyboard
   ring is drawn inside the row. */
.ticket-row:focus-visible{outline:none;background:var(--card);box-shadow:inset 0 0 0 2px var(--brand)}
.ticket-state{flex:0 0 auto;display:inline-flex;justify-content:center;min-width:118px;padding:3px 10px;
  border-radius:999px;font-size:11.5px;font-weight:700;white-space:nowrap}
.ticket-subject{flex:1 1 auto;min-width:0;font-weight:600;font-size:14px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* A sibling of the subject rather than a child of it: the subject truncates,
   and the one thing on the row that must never be truncated away is the flag
   saying there is an answer sitting in there unread. */
.ticket-new{flex:0 0 auto;padding:1px 7px;border-radius:999px;background:rgba(255,138,43,.16);
  border:1px solid rgba(255,138,43,.5);color:var(--live2);font-size:11px;font-weight:700}
.ticket-meta{flex:0 0 auto;font-size:12.5px}
.ticket-when{flex:0 0 auto;font-size:12px;white-space:nowrap}
/* One set of state colours for two shapes: the badge in a row above, and
   #threadStatus, which takes .status for the uppercase pill and only its colour
   from here. Neither one carries the meaning alone: the word is written out. */
.t-open{background:rgba(102,192,244,.16);color:var(--brand)}
.t-pending{background:rgba(224,162,58,.16);color:var(--amber)}
.t-resolved{background:rgba(255,138,43,.16);color:var(--live2)}
.t-closed{background:#222c3d;color:var(--muted)}
/* A thread that has not loaded yet has a status pill with nothing in it, and an
   empty pill is still 22px of padding. */
#threadStatus:empty{display:none}
/* .status uppercases, which suits the one-word stream states it was written for.
   A ticket state is a sentence ("Waiting on you") and has to read as the same
   words the list showed, not as shouting. */
#threadStatus{text-transform:none;letter-spacing:0}

/* ---- support: the conversation ----
   Read before it is read: your own messages take the right edge and the brand
   tint, theirs stay left and neutral, and each one still says who and when. The
   admin drawer renders the same markup with the sides swapped, so nothing here
   may be scoped to either side of the product. */
.msg-list{display:flex;flex-direction:column;gap:10px}
.msg{align-self:flex-start;max-width:min(88%,620px);padding:10px 13px;border-radius:12px;
  border-top-left-radius:4px;background:var(--bg2);border:1px solid var(--line)}
.msg.mine{align-self:flex-end;background:rgba(102,192,244,.09);border-color:rgba(102,192,244,.35);
  border-top-left-radius:12px;border-top-right-radius:4px}
.msg-head{display:flex;align-items:baseline;gap:9px;margin-bottom:4px;font-size:12px}
.msg-head b{font-weight:700;color:var(--brand);overflow-wrap:anywhere}
.msg.mine .msg-head b{color:var(--txt)}
.msg-head .muted{margin-left:auto;font-size:11.5px;white-space:nowrap}
/* Whatever they typed, including the line breaks: a stack trace or a pasted RTMP
   URL is most of what arrives here. */
.msg-body{font-size:14px;line-height:1.6;white-space:pre-wrap;overflow-wrap:anywhere}
.msg-input{min-height:96px;font-size:14px;line-height:1.6;resize:vertical}
/* At phone width the four columns become three lines: what it is about, then
   where it stands, then the detail. */
@media(max-width:560px){
  .ticket-row{flex-wrap:wrap;gap:7px 10px;padding:12px}
  .ticket-subject{order:1;flex:1 1 100%;white-space:normal}
  /* Same order as the subject it belongs to, so it leads the second line with
     the state rather than jumping ahead of everything at order 0. */
  .ticket-new{order:1}
  .ticket-state{order:2;min-width:0}
  .ticket-when{order:3;margin-left:auto}
  .ticket-meta{order:4;flex:1 1 100%}
  .msg{max-width:94%}
}

/* ============================================================================
   ADMIN PANEL
   Built around the three questions an operator actually opens it with:
   is anything wrong (alerts), what is happening right now (on air), and what
   does this one customer look like (the drawer). Everything else is a table,
   and every table is the same table.
   ========================================================================= */
.adm{padding-bottom:70px}
/* Sub-bar: section title, one search box, and proof the numbers are fresh. */
.adm-bar{position:sticky;top:64px;z-index:25;background:rgba(11,14,20,.86);backdrop-filter:blur(10px);
  border-bottom:1px solid var(--line)}
.adm-bar-in{display:flex;align-items:center;justify-content:space-between;gap:16px;
  min-height:66px;padding-top:10px;padding-bottom:10px;flex-wrap:wrap}
.adm-kicker{display:block;font-size:11px;font-weight:700;letter-spacing:.13em;text-transform:uppercase;
  color:var(--muted)}
.adm-brand h2{margin:1px 0 0;font-size:21px;font-weight:800;letter-spacing:-.2px}
.adm-tools{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.adm-find{position:relative;display:flex;align-items:center}
.adm-find svg{position:absolute;left:11px;width:15px;height:15px;fill:none;stroke:var(--muted);
  stroke-width:2;stroke-linecap:round;pointer-events:none}
.adm-find input{width:230px;padding:9px 40px 9px 33px;font-size:13.5px;background:var(--bg2);
  border:1px solid var(--line);border-radius:9px}
.adm-find input::-webkit-search-cancel-button{display:none}
.adm-find kbd{position:absolute;right:9px;padding:1px 6px;border-radius:5px;background:var(--card);
  border:1px solid var(--line);color:var(--muted);font-size:11px;font-family:inherit;pointer-events:none}
.adm-find input:focus+kbd{opacity:0}
/* Freshness indicator doubles as the pause switch: a paused admin panel showing
   two-minute-old live data is worse than no live data. */
.adm-pulse{display:flex;align-items:center;gap:7px;padding:8px 12px;border-radius:9px;cursor:pointer;
  background:var(--bg2);border:1px solid var(--line);color:var(--muted);font:inherit;font-size:12.5px;
  font-weight:600;font-variant-numeric:tabular-nums;min-width:104px;transition:.15s}
.adm-pulse:hover{border-color:var(--brand);color:var(--txt)}
.pulse-dot{width:7px;height:7px;border-radius:50%;background:var(--muted);flex:none}
.adm-pulse[aria-pressed="true"] .pulse-dot{background:var(--live2);animation:pulse-beat 2s ease-in-out infinite}
@keyframes pulse-beat{0%,100%{opacity:1;box-shadow:0 0 0 0 rgba(255,138,43,.45)}
  50%{opacity:.65;box-shadow:0 0 0 5px rgba(255,138,43,0)}}

/* Shell: rail + content. The rail is the only navigation; sections never nest. */
.adm-shell{display:grid;grid-template-columns:196px minmax(0,1fr);gap:26px;padding-top:22px}
.adm-nav{display:flex;flex-direction:column;gap:2px;position:sticky;top:150px;align-self:start}
.adm-nav button{display:flex;align-items:center;gap:10px;width:100%;padding:9px 12px;border-radius:9px;
  background:none;border:1px solid transparent;color:var(--muted);font:inherit;font-size:13.5px;
  font-weight:600;text-align:left;cursor:pointer;transition:.13s}
.adm-nav button svg{width:17px;height:17px;flex:none;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.adm-nav button:hover{background:var(--bg2);color:var(--txt)}
.adm-nav button.active{background:var(--card);border-color:var(--line);color:var(--txt);
  box-shadow:inset 2px 0 0 var(--brand)}
.adm-nav button.active svg{stroke:var(--brand)}
.adm-badge{margin-left:auto;min-width:20px;padding:1px 6px;border-radius:999px;background:var(--bg2);
  border:1px solid var(--line);color:var(--muted);font-size:11px;font-weight:700;text-align:center}
.adm-badge.hot{background:rgba(255,138,43,.16);border-color:rgba(255,138,43,.5);color:var(--live2)}
.adm-badge.bad{background:rgba(224,90,74,.16);border-color:rgba(224,90,74,.5);color:var(--red)}
.adm-main{min-width:0}
/* The marketing page styles every <section> with 70px of vertical padding; the
   admin's sections are layout, not landing-page bands. */
.adm-sec{padding:0}
.adm-h{margin:26px 0 12px;font-size:13px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted)}
.adm-h:first-child{margin-top:0}
.adm-h .muted{font-weight:500;letter-spacing:0;text-transform:none;font-size:12.5px;margin-left:6px}
.adm-note{font-size:12.5px;margin:12px 2px 0;line-height:1.5}

/* ---- alerts: only ever rendered for something that is actually wrong ---- */
/* auto-FILL, not auto-fit: one alert should be one card, not a banner stretched
   across the width because it happens to be alone. */
.adm-alerts{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(268px,1fr))}
.adm-alerts:empty{display:none}
.alert-card{display:flex;align-items:center;gap:12px;padding:13px 15px;border-radius:12px;cursor:pointer;
  background:var(--card);border:1px solid var(--line);text-align:left;font:inherit;transition:.15s}
.alert-card:hover{transform:translateY(-1px);box-shadow:0 10px 26px -14px rgba(0,0,0,.8)}
.alert-ico{width:34px;height:34px;flex:none;border-radius:10px;display:grid;place-items:center;font-size:15px}
.alert-n{font-size:19px;font-weight:800;line-height:1.1;color:var(--txt)}
.alert-txt{font-size:13px;color:var(--muted);line-height:1.35}
.alert-go{margin-left:auto;color:var(--muted);font-size:15px}
.alert-card.crit{border-color:rgba(224,90,74,.5);background:linear-gradient(180deg,rgba(224,90,74,.11),transparent 70%)}
.alert-card.crit .alert-ico{background:rgba(224,90,74,.16);color:var(--red)}
.alert-card.warn{border-color:rgba(224,162,58,.5);background:linear-gradient(180deg,rgba(224,162,58,.10),transparent 70%)}
.alert-card.warn .alert-ico{background:rgba(224,162,58,.16);color:var(--amber)}
.alert-card.info{border-color:rgba(102,192,244,.4)}
.alert-card.info .alert-ico{background:rgba(102,192,244,.14);color:var(--brand)}
.all-clear{display:flex;align-items:center;gap:11px;padding:13px 16px;border-radius:12px;
  border:1px solid rgba(102,192,244,.32);background:rgba(102,192,244,.06);font-size:13.5px}
.all-clear b{color:var(--brand)}

/* ---- stat tiles ---- */
.adm-tiles{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(184px,1fr))}
.tile{padding:14px 16px 13px;border-radius:12px;background:var(--card);border:1px solid var(--line);
  display:flex;flex-direction:column;gap:3px;min-width:0}
.tile-label{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.tile-val{font-size:25px;font-weight:800;letter-spacing:-.5px;line-height:1.15;
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.tile-sub{font-size:12.5px;color:var(--muted);line-height:1.4}
.tile-val .unit{font-size:14px;font-weight:600;color:var(--muted);margin-left:3px}
.tile.hot .tile-val{color:var(--live2)}
.tile.bad .tile-val{color:var(--red)}
.tile-spark{margin-top:7px;line-height:0}
.tile-spark svg{width:100%;height:32px;display:block;overflow:visible}
.spark-line{fill:none;stroke:var(--brand);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.spark-fill{fill:rgba(102,192,244,.13);stroke:none}
.spark-dot{fill:var(--brand);stroke:var(--card);stroke-width:2}

/* ---- system card ---- */
.adm-split{display:grid;grid-template-columns:minmax(0,1fr) 290px;gap:12px;align-items:start}
.adm-sys{padding:16px 18px}
.adm-sys h4{margin:0 0 12px;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted)}
.sys-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:6px 0;
  border-top:1px solid var(--line);font-size:13px}
.sys-row:first-of-type{border-top:0}
.sys-row b{font-weight:600;font-variant-numeric:tabular-nums}
.sys-meter{height:6px;border-radius:999px;background:var(--bg);border:1px solid var(--line);
  overflow:hidden;margin:8px 0 2px}
.sys-meter i{display:block;height:100%;background:linear-gradient(90deg,var(--brand),var(--brand2));
  transition:width .4s}
.sys-meter.warn i{background:linear-gradient(90deg,var(--amber),var(--live2))}
.sys-meter.bad i{background:var(--red)}

/* ---- section toolbar ---- */
.adm-secbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:13px}
.adm-chips{display:flex;gap:6px;flex-wrap:wrap}
.chip-btn{padding:6px 12px;border-radius:999px;background:var(--bg2);border:1px solid var(--line);
  color:var(--muted);font:inherit;font-size:12.5px;font-weight:600;cursor:pointer;transition:.13s}
.chip-btn:hover{color:var(--txt);border-color:#38465f}
.chip-btn.active{background:rgba(102,192,244,.14);border-color:rgba(102,192,244,.55);color:var(--brand)}
.adm-summary{margin-left:auto;font-size:12.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.adm-select{width:auto;min-width:170px;padding:7px 11px;font-size:13px}

/* ---- the one table ---- */
.adm-tablewrap{border:1px solid var(--line);border-radius:12px;overflow:auto;background:var(--card);
  max-height:none}
/* Fixed layout: the columns are declared, not negotiated. Auto layout let a long
   stream name or email decide the width of the whole table and push the actions
   off the right edge; here every column keeps its share and the text truncates. */
.adm-table{width:100%;table-layout:fixed;border-collapse:separate;border-spacing:0;font-size:13px}
.adm-table th{position:sticky;top:0;z-index:2;background:#131926;padding:10px 13px;text-align:left;
  font-size:11px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--muted);
  border-bottom:1px solid var(--line);white-space:nowrap}
.adm-table th.th-sort{cursor:pointer;user-select:none}
.adm-table th.th-sort:hover{color:var(--txt)}
.th-arrow{display:inline-block;width:0;height:0;margin-left:6px;vertical-align:middle;opacity:0;
  border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:5px solid currentColor}
.adm-table th.th-sort:hover .th-arrow{opacity:.35}
.adm-table th.on{color:var(--brand)}
.adm-table th.on .th-arrow{opacity:1}
.adm-table th.on.desc .th-arrow{transform:rotate(180deg)}
.adm-table td{padding:10px 13px;border-bottom:1px solid rgba(34,44,61,.7);vertical-align:middle}
.adm-table tbody tr:last-child td{border-bottom:0}
.adm-table tbody tr.clickable{cursor:pointer}
.adm-table tbody tr:hover td{background:rgba(102,192,244,.045)}
.adm-table tbody tr.row-bad td{background:rgba(224,90,74,.06)}
.adm-table tbody tr.row-bad:hover td{background:rgba(224,90,74,.1)}
.adm-table td.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.adm-table td.nowrap{white-space:nowrap}
.adm-table td.mono{font-family:ui-monospace,Menlo,monospace;font-size:11.5px;color:var(--muted)}
.adm-table td.wrap{font-size:12.5px;color:var(--muted);line-height:1.45;overflow-wrap:anywhere}
/* Long single-line values (message ids, session ids, user agents) get one line
   and an ellipsis; the full value is in the title attribute. */
.adm-table td.trunc{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;color:var(--muted)}
.adm-table .tr-empty td{padding:34px 14px;text-align:center;color:var(--muted);font-size:13.5px}
/* One line per row: a wrapping name makes rows different heights, and uneven
   rows are what makes a long table hard to scan. Long names truncate instead. */
.cell-main{display:block;max-width:100%;font-weight:600;color:var(--txt);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cell-sub{display:block;font-size:11.5px;color:var(--muted);margin-top:2px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.cell-act{display:flex;gap:6px;justify-content:flex-end;white-space:nowrap}
/* Title and summary cells exist only for the phone layout below. */
.adm-table .cell-title,.adm-table .cell-sum{display:none}
.sum-dot{margin:0 7px;opacity:.45}
.mini-btn{padding:5px 10px;border-radius:7px;background:var(--bg2);border:1px solid var(--line);
  color:var(--muted);font:inherit;font-size:12px;font-weight:600;cursor:pointer;transition:.13s}
.mini-btn:hover{color:var(--txt);border-color:var(--brand)}
.mini-btn.danger:hover{color:var(--red);border-color:var(--red);background:rgba(224,90,74,.12)}

/* ---- state pills: colour never carries the meaning on its own, the word does ---- */
.pill{display:inline-flex;align-items:center;gap:5px;padding:2.5px 9px;border-radius:999px;font-size:11.5px;
  font-weight:700;letter-spacing:.02em;white-space:nowrap;border:1px solid transparent}
.pill-live{background:rgba(255,138,43,.15);color:var(--live2);border-color:rgba(255,138,43,.4)}
.pill-live::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--live2);
  animation:pulse-beat 2s ease-in-out infinite}
.pill-ok{background:rgba(102,192,244,.14);color:var(--brand);border-color:rgba(102,192,244,.35)}
.pill-warn{background:rgba(224,162,58,.15);color:var(--amber);border-color:rgba(224,162,58,.4)}
.pill-bad{background:rgba(224,90,74,.15);color:var(--red);border-color:rgba(224,90,74,.4)}
.pill-mute{background:#1e2738;color:var(--muted);border-color:#2b374d}
.pill-plat{background:var(--bg2);border-color:var(--line);color:var(--txt);font-weight:600}
.pill-plat svg,.pill-plat img{width:13px;height:13px}
.pill-off{opacity:.55}

/* ---- drawer: the customer 360 ---- */
.adm-scrim{position:fixed;inset:0;z-index:60;background:rgba(6,9,14,.6);backdrop-filter:blur(2px);
  animation:scrim-in .18s ease}
@keyframes scrim-in{from{opacity:0}to{opacity:1}}
@keyframes row-open{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}
.adm-drawer{position:fixed;top:0;right:0;bottom:0;z-index:61;width:min(560px,100vw);display:flex;
  flex-direction:column;background:var(--bg2);border-left:1px solid var(--line);
  box-shadow:-24px 0 60px rgba(0,0,0,.55);animation:drawer-in .22s cubic-bezier(.2,.7,.3,1)}
@keyframes drawer-in{from{transform:translateX(26px);opacity:.4}to{transform:none;opacity:1}}
.adm-drawer-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;
  padding:18px 20px 14px;border-bottom:1px solid var(--line);background:var(--card)}
.adm-drawer-head h3{margin:2px 0 0;font-size:17px;font-weight:700;word-break:break-all}
.adm-drawer-x{width:32px;height:32px;flex:none;border-radius:8px;background:var(--bg2);
  border:1px solid var(--line);color:var(--muted);font-size:13px;cursor:pointer;transition:.14s}
.adm-drawer-x:hover{color:var(--txt);border-color:var(--brand)}
/* One explicit column: a grid's implicit column is max-content sized, which let
   the widest row (a long email, a log line) push the whole drawer sideways. */
.adm-drawer-body{flex:1;overflow-y:auto;overflow-x:hidden;padding:18px 20px 40px;
  display:grid;grid-template-columns:minmax(0,1fr);gap:20px;align-content:start}
.adm-drawer-body>*{min-width:0}
.dw-grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(112px,1fr))}
.dw-stat{padding:11px 13px;border-radius:10px;background:var(--card);border:1px solid var(--line)}
.dw-stat .tile-label{font-size:10.5px}
.dw-stat .v{font-size:19px;font-weight:800;font-variant-numeric:tabular-nums;margin-top:3px}
.dw-sec h4{margin:0 0 9px;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted)}
.dw-list{border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--card)}
.dw-item{display:flex;align-items:center;gap:10px;padding:9px 12px;border-top:1px solid var(--line);
  font-size:12.5px}
.dw-item:first-child{border-top:0}
.dw-item .grow{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dw-item .when{color:var(--muted);font-size:11.5px;white-space:nowrap}
.dw-empty{padding:14px 12px;color:var(--muted);font-size:12.5px}
.dw-form{display:grid;gap:9px;padding:13px;border-radius:10px;background:var(--card);border:1px solid var(--line)}
.dw-form .row2{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.dw-form input{padding:9px 11px;font-size:13.5px}
.dw-form label{display:block;font-size:11.5px;color:var(--muted);margin-bottom:4px}
.dw-quick{display:flex;gap:6px;flex-wrap:wrap}
.dw-log{background:#05080d;border:1px solid var(--line);border-radius:10px;padding:12px;
  font-family:ui-monospace,Menlo,monospace;font-size:11.5px;color:#9fb6d2;white-space:pre-wrap;
  max-height:340px;overflow:auto;line-height:1.5}

/* ---- responsive ---- */
@media(max-width:1080px){
  .adm-split{grid-template-columns:1fr}
  .adm-sys{order:-1}
}
@media(max-width:900px){
  .adm-shell{grid-template-columns:1fr;gap:16px}
  .adm-nav{position:static;flex-direction:row;overflow-x:auto;scrollbar-width:none;padding-bottom:2px}
  .adm-nav::-webkit-scrollbar{display:none}
  .adm-nav button{width:auto;flex:0 0 auto;border:1px solid var(--line);background:var(--bg2)}
  .adm-nav button.active{box-shadow:inset 0 -2px 0 var(--brand)}
  .adm-bar{top:64px}
  .adm-find input{width:170px}
}
/* Under 760px a table stops being a grid and becomes one card per row: the
   header is gone, so every cell carries its own label. */
@media(max-width:760px){
  .adm-bar-in{min-height:0}
  .adm-brand h2{font-size:19px}
  .adm-tools{width:100%}
  .adm-find{flex:1}
  .adm-find input{width:100%}
  .adm-tablewrap{border:0;background:none;border-radius:0;overflow:visible}
  .adm-table,.adm-table tbody,.adm-table tr,.adm-table td{display:block;width:100%}
  .adm-table thead{display:none}
  .adm-table tr{margin-bottom:10px;border:1px solid var(--line);border-radius:12px;background:var(--card);
    overflow:hidden}
  .adm-table td{display:flex;align-items:baseline;justify-content:space-between;gap:14px;
    padding:9px 13px;border-bottom:1px solid rgba(34,44,61,.6);text-align:right}
  .adm-table td::before{content:attr(data-label);flex:0 0 auto;font-size:10.5px;font-weight:700;
    letter-spacing:.08em;text-transform:uppercase;color:var(--muted);text-align:left}
  .adm-table td:empty{display:none}
  /* A phone row is a summary that opens. Collapsed it shows two lines: what this
     row is, and the few numbers worth scanning. Tapping reveals the rest, which
     is the same cells the desktop table shows, each with its own label. */
  .adm-table .cell-head{display:block;position:relative;text-align:left;padding:11px 34px 9px 13px;
    background:rgba(102,192,244,.05);border-bottom:0;font-size:14px}
  .adm-table .cell-head::before{display:none}
  .adm-table .cell-head b{font-weight:600}
  .adm-table .cell-head .pill{margin-right:7px;vertical-align:1px}
  .adm-table .cell-head .cell-main{max-width:100%;font-size:14px;display:inline-block;vertical-align:bottom}
  .adm-table .cell-head .cell-sub{white-space:normal}
  /* Chevron, and the only moving part: it turns when the row opens. */
  .adm-table .cell-head::after{content:'';position:absolute;right:14px;top:18px;width:7px;height:7px;
    border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
    transform:rotate(45deg);transition:transform .2s ease}
  .adm-table tr.open .cell-head::after{transform:rotate(-135deg) translate(-3px,-3px)}
  /* Block, not flex: a flex container turns every text run into an item and eats
     the spaces between words ("1,440min"). */
  .adm-table td.cell-sum{display:block;padding:0 13px 11px;background:rgba(102,192,244,.05);
    text-align:left;font-size:12.5px;line-height:1.7;color:var(--muted);border-bottom:0}
  .adm-table td.cell-sum::before{display:none}
  .adm-table td.cell-sum .pill{margin:1px 0}
  /* Collapsed: everything except the header and the summary is out of the flow. */
  .adm-table.has-sum tr:not(.open) td:not(.cell-head):not(.cell-sum){display:none}
  .adm-table tr.open td.cell-sum{border-bottom:1px solid rgba(34,44,61,.7)}
  .adm-table tr.open td:not(.cell-head):not(.cell-sum){animation:row-open .2s ease both}
  .adm-table tr.tr-empty td::after{display:none}
  .adm-table td.hide-sm{display:none!important}
  .adm-table.no-expand .cell-head{padding-right:13px}
  .adm-table.no-expand .cell-head::after{display:none}
  .adm-table .cell-head .cell-sub{margin-top:3px;font-size:11.5px;font-weight:400}
  .adm-table td.wrap,.adm-table td.trunc{max-width:none;white-space:normal;text-align:right}
  .adm-table .cell-act{justify-content:flex-end}
  .adm-table .tr-empty td{justify-content:center}
  .adm-table .tr-empty td::before{display:none}
  .adm-drawer{width:100vw}
}

/* A destination that refused the broadcast says why, where its settings are */
.dest-err{padding:11px 13px;border-radius:10px;font-size:13px;line-height:1.55;
  border:1px solid rgba(224,90,74,.45);background:rgba(224,90,74,.10);color:var(--red);
  white-space:pre-wrap;word-break:break-word}
/* Connected, waiting on the platform to accept the first frame */
.plat-btn.starting{border-color:var(--amber);animation:prep-pulse 1.4s ease-in-out infinite}
.plat-btn.failed{border-color:rgba(224,90,74,.55);background:rgba(224,90,74,.10)}
.plat-btn.failed .plat-check{display:grid;background:var(--red);border-color:var(--red)}
.plat-btn.failed .plat-check::after{content:"!";color:#0b0e14;font-size:11px;font-weight:800;line-height:1}
