/* ==========================================================================
   9teen88 Technology Services XMLTV Studio — UI stylesheet

   Theme is lifted directly from 9teen88.net, not invented: the palette,
   type scale and accent below were sampled off the live site's computed
   styles so this app reads as the same product, not a lookalike.

     type      Sora (400-800) + JetBrains Mono, both variable
     canvas    #0B0C0E body, #05060 8 / #08090A / #0A0B0D layered surfaces
     accent    #7FB3E8 (site link colour)
     ink       #F3F6FA headings, #AAB6C4 body, #9AA3AD muted

   Fonts are SELF-HOSTED in assets/fonts/. The site pulls them from Google
   Fonts; this app must not. It runs offline on a customer's machine behind a
   strict `default-src 'self'` CSP, and an external font request would both
   break with no network and leak every launch to a third party.
   ========================================================================== */

/* Two variable files cover every weight — Google serves Sora and JetBrains
   Mono as variable fonts, so the per-weight URLs are byte-identical. Declaring
   the full range against one file is 55 KB total instead of 7 near-copies. */
@font-face{
  font-family:"Sora"; font-style:normal; font-weight:400 800; font-display:swap;
  src:url("assets/fonts/sora-var.woff2") format("woff2");
}
@font-face{
  font-family:"JetBrains Mono"; font-style:normal; font-weight:400 600; font-display:swap;
  src:url("assets/fonts/jetbrainsmono-var.woff2") format("woff2");
}

/* The HTML `hidden` attribute must always win. Author rules like
   .counter{display:flex} silently override the UA stylesheet's
   [hidden]{display:none}, which left the channel counter visible on
   step 1 before any channels existed. */
[hidden]{ display:none !important; }

:root{
  /* surfaces — sampled from 9teen88.net section backgrounds. The site stacks
     four near-blacks a point or two apart; that restraint is what makes it
     read as premium rather than as a generic dark theme. */
  --bg:        #0b0c0e;   /* body */
  --bg-2:      #08090a;   /* recessed bands */
  --panel:     #101215;   /* cards */
  --panel-2:   #15171b;   /* raised / hover */
  --panel-3:   #1b1e23;   /* inputs, code */
  --line:      #23262c;
  --line-2:    #2f333a;

  /* type — site values verbatim */
  --text:      #f3f6fa;   /* headings */
  --text-2:    #cbd5e1;
  --muted:     #aab6c4;   /* body copy */
  --faint:     #9aa3ad;

  /* accent + semantics. #7fb3e8 is the site's link colour; #cc3366 is its
     secondary mark. The pink is reserved for genuine destructive intent so it
     keeps its weight — it is not a decorative second accent. */
  --accent:    #7fb3e8;
  --accent-hi: #a3cbf2;
  --accent-lo: #4a7fb5;
  --accent-a:  rgba(127,179,232,.12);
  --accent-b:  rgba(127,179,232,.28);
  --ok:        #4ec98a;
  --ok-a:      rgba(78,201,138,.12);
  --warn:      #e8b055;
  --warn-a:    rgba(232,176,85,.12);
  --danger:    #cc3366;
  --danger-a:  rgba(204,51,102,.14);
  --danger-hi: #e284a8;

  /* Ink used ON a filled accent surface. #7fb3e8 is light, so this must be
     near-black to stay readable — it was previously hardcoded in 7 places,
     which is exactly why the old blue could not be swapped in one edit. */
  --on-accent: #071019;

  --r-sm: 7px;
  --r:    11px;
  --r-lg: 16px;

  --sh-1: 0 1px 2px rgba(0,0,0,.4);
  --sh-2: 0 8px 24px -6px rgba(0,0,0,.55);
  --sh-3: 0 20px 60px -12px rgba(0,0,0,.7);

  --hdr-h: 58px;
  --rail-h: 62px;
  --foot-h: 64px;
  --row-h: 44px;

  /* One left edge for the whole app. The rail numerals used to sit 4px off the
     step headings below them (rail-inner's 18px + rail-step's 12px = 30px,
     against .wrap's 26px), which read as a wobble rather than a mistake --
     the kind of thing you feel without being able to name it. */
  --gutter: 26px;

  --font: "Sora", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;

  --t: 150ms cubic-bezier(.4,0,.2,1);
}

*,*::before,*::after{ box-sizing:border-box; }

/* Form controls do NOT inherit font or colour from the page — the UA stylesheet
   gives them their own, and `button` in particular gets `color: buttontext`,
   which is BLACK. On a near-black canvas any text inside a <button> that does
   not set its own colour is invisible. That is exactly what happened to the
   provider-card titles ("Xtream Codes" et al): `.pcard span` set a colour but
   `.pcard b` did not, so the headings rendered pure black on #0b0c0e at a
   contrast ratio of 1.07.
   Resetting this once, globally, kills the whole class of bug instead of
   patching each control as it is discovered. Specific rules still win. */
button,input,select,textarea,optgroup{ font-family:inherit; font-size:inherit; color:inherit; }

html,body{ height:100%; }
body{
  margin:0;
  /* One cool wash off the top-right, matching the site's restrained hero
     lighting. The old build had a second green glow fighting it. */
  background:
    radial-gradient(1200px 640px at 78% -12%, rgba(127,179,232,.055), transparent 62%),
    var(--bg);
  color:var(--muted);
  font-family:var(--font);
  /* LEGIBILITY. Sora is a geometric sans with a noticeably smaller x-height
     than Segoe UI, so it reads ~1px smaller at the same nominal size. The old
     14px/1.5 in a system font became genuinely hard to read once the brand
     face went in. 15px/1.62 restores it and matches the site's 16px/1.5 body
     setting at this app's slightly denser scale. */
  font-size:15px;
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
  padding-top:calc(var(--hdr-h) + var(--rail-h));
  padding-bottom:var(--foot-h);
}
body.no-foot{ padding-bottom:0; }

/* Headings carry the site's weight-700 / tight-tracking treatment and the
   brightest ink, so hierarchy comes from contrast rather than size alone. */
h1,h2,h3{ margin:0; color:var(--text); font-weight:700; letter-spacing:-.021em; line-height:1.25; }
p{ margin:0; }
code{ font-family:var(--mono); font-size:.9em; background:var(--panel-3); padding:1.5px 6px; border-radius:5px; color:var(--text-2); }
.mono{ font-family:var(--mono); font-feature-settings:"zero" 1; }
.small{ font-size:13px; }
.muted{ color:var(--faint); }

::selection{ background:var(--accent-b); color:#fff; }

/* scrollbars */
*{ scrollbar-width:thin; scrollbar-color:#33373f transparent; }
::-webkit-scrollbar{ width:11px; height:11px; }
::-webkit-scrollbar-track{ background:transparent; }
::-webkit-scrollbar-thumb{ background:#2f333a; border-radius:8px; border:3px solid transparent; background-clip:content-box; }
::-webkit-scrollbar-thumb:hover{ background:#454a52; background-clip:content-box; }

/* focus */
:focus{ outline:none; }
:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; border-radius:4px; }

/* inline svg icon defaults */
.ico{ width:17px; height:17px; flex:none; fill:none; stroke:currentColor; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; }
.ico-sm{ width:14px; height:14px; }
.spin{ animation:spin .8s linear infinite; }
@keyframes spin{ to{ transform:rotate(360deg); } }

/* ============================ layout shells ============================= */
.wrap{ max-width:1240px; margin:0 auto; padding:30px var(--gutter) 60px; }

/* Steps 1 and 2 hold a single short column.
   This was briefly VERTICALLY CENTRED to kill the dead space on a 1080p
   screen. That was a mistake: the column's own height changes the instant a
   provider is picked and the credentials form appears, so centring made the
   whole page lurch ~180px upward on the click -- trading static dead space for
   motion, which is worse.
   Top-anchored with a viewport-proportional lead-in instead: generous on a
   tall screen, tight on a laptop, and completely stationary while the form
   grows underneath it. */
.wrap-narrow{
  max-width:880px;
  padding-top:clamp(40px, 11vh, 132px);
  padding-bottom:clamp(32px, 6vh, 72px);
}

/* ============================== top bar ================================ */
.topbar{
  position:fixed; inset:0 0 auto 0; height:var(--hdr-h); z-index:60;
  display:flex; align-items:center; gap:14px; padding:0 18px;
  background:rgba(11,12,14,.86);
  backdrop-filter:blur(14px) saturate(150%);
  border-bottom:1px solid var(--line);
}
.brand{ display:flex; align-items:center; gap:11px; min-width:0; }
/* The 9teen88 mark is a detailed multi-colour illustrated badge, not a flat
   monochrome glyph. So: no filter, no tint, no recolour — anything applied to
   it turns the artwork to mud. It only gets a faint rim to seat it on the dark
   canvas, and a slightly larger box than a vector glyph would need so the
   detail survives at header scale. */
.brand-mark{ display:flex; flex:none; }
.brand-mark img{
  width:34px; height:34px; display:block; border-radius:8px;
  box-shadow:0 0 0 1px rgba(255,255,255,.06), 0 1px 3px rgba(0,0,0,.5);
}
.brand-text{ display:flex; flex-direction:column; line-height:1.18; min-width:0; }
.brand-text strong{ font-size:15px; font-weight:700; letter-spacing:-.017em; color:var(--text); }
.brand-text em{ font-style:normal; font-size:11px; color:var(--faint); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Company eyebrow above the product mark: a standard brand lockup reads far
   better than one long run-on string, and keeps "XMLTV Studio" the dominant
   element while still crediting 9teen88 Technology Services. */
.brand-text .brand-co{
  font-size:9.5px; font-weight:640; letter-spacing:.085em; text-transform:uppercase;
  color:var(--accent); opacity:.92; margin-bottom:1px;
}
.brand-text .brand-tag{ color:var(--faint); }
.topbar-spacer{ flex:1; }
.topbar-actions{ display:flex; align-items:center; gap:8px; }

/* ============================== buttons ================================ */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  font:inherit; font-size:13px; font-weight:560; color:var(--text-2);
  background:var(--panel-2); border:1px solid var(--line-2); border-radius:var(--r-sm);
  padding:8px 13px; cursor:pointer; white-space:nowrap;
  transition:background var(--t), border-color var(--t), color var(--t), transform var(--t), box-shadow var(--t);
}
.btn:hover{ background:var(--panel-3); border-color:#3a3f47; color:var(--text); }
.btn:active{ transform:translateY(1px); }
.btn:disabled{ opacity:.4; cursor:not-allowed; transform:none; }
.btn-ghost{ background:transparent; border-color:transparent; }
.btn-ghost:hover{ background:var(--panel-2); border-color:var(--line-2); }
.btn-accent{
  background:linear-gradient(180deg, var(--accent-hi), var(--accent));
  border-color:transparent; color:var(--on-accent); font-weight:640;
  box-shadow:0 1px 0 rgba(255,255,255,.22) inset, 0 6px 18px -8px rgba(127,179,232,.55);
}
.btn-accent:hover{ background:linear-gradient(180deg,var(--accent-hi),var(--accent-lo)); color:var(--on-accent); }
.btn-danger{ color:var(--danger); border-color:rgba(204,51,102,.34); background:var(--danger-a); }
.btn-danger:hover{ background:rgba(204,51,102,.24); color:var(--danger-hi); }
.btn-lg{ padding:12px 22px; font-size:14.5px; }
.btn-sm{ padding:6px 10px; font-size:12.5px; }
.btn-mini{ padding:3px 8px; font-size:11.5px; border-radius:6px; }
.btn-block{ width:100%; margin-top:14px; }
.caret{ opacity:.6; margin-left:-2px; }

/* chips used in the bulk bar */
.btn-chip{
  padding:5px 11px; font-size:12.5px; border-radius:999px;
  background:var(--panel-2); border-color:var(--line-2); color:var(--text-2);
}
.btn-chip:hover{ background:var(--panel-3); }
.btn-chip-accent{ color:var(--accent-hi); border-color:rgba(127,179,232,.30); background:var(--accent-a); }
.btn-chip-accent:hover{ background:var(--accent-b); color:var(--accent-hi); }

/* ============================== step rail ============================== */
.rail{
  position:fixed; top:var(--hdr-h); left:0; right:0; height:var(--rail-h); z-index:55;
  background:rgba(8,9,10,.80); backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line);
}
.rail-inner{
  display:flex; gap:6px; align-items:stretch; height:100%;
  /* .rail-step adds its own 12px left padding, so subtract it here to land the
     numeral on the same edge as .wrap's content. */
  max-width:1240px; margin:0 auto; padding:0 calc(var(--gutter) - 12px); list-style:none;
}
.rail-step{
  display:flex; align-items:center; gap:11px; padding:0 16px 0 12px;
  position:relative; cursor:default; color:var(--faint); border-radius:var(--r-sm);
  transition:color var(--t), background var(--t); flex:0 1 auto; min-width:0;
}
.rail-step + .rail-step::before{
  content:""; width:26px; height:1px; background:var(--line-2); margin-left:-16px; margin-right:6px; flex:none;
}
.rail-num{
  width:26px; height:26px; flex:none; border-radius:50%; display:grid; place-items:center;
  font-size:12.5px; font-weight:700; background:var(--panel-2); border:1px solid var(--line-2); color:var(--faint);
  transition:all var(--t);
}
.rail-txt{ display:flex; flex-direction:column; line-height:1.2; min-width:0; }
.rail-txt b{ font-size:13px; font-weight:600; }
.rail-txt i{ font-style:normal; font-size:11px; color:var(--faint); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.rail-step.is-done{ color:var(--text-2); cursor:pointer; }
.rail-step.is-done .rail-num{ background:var(--ok-a); border-color:rgba(78,201,138,.42); color:var(--ok); }
.rail-step.is-done:hover{ background:var(--panel-2); }
.rail-step.is-active{ color:var(--text); }
.rail-step.is-active .rail-num{
  background:linear-gradient(180deg,var(--accent-hi),var(--accent)); border-color:transparent; color:var(--on-accent);
  box-shadow:0 0 0 4px var(--accent-a);
}
.rail-step.is-active .rail-txt i{ color:var(--muted); }

/* =============================== steps ================================= */
.step{ animation:fade .28s ease both; }
@keyframes fade{ from{ opacity:0; transform:translateY(6px);} to{ opacity:1; transform:none; } }
.step-head{ margin-bottom:24px; }
.step-head h1{ font-size:25px; margin-bottom:7px; }
.step-head p{ color:var(--muted); max-width:70ch; font-size:13.5px; }

/* =============================== panels ================================ */
.panel{
  background:linear-gradient(180deg, var(--panel), var(--bg-2));
  border:1px solid var(--line); border-radius:var(--r-lg);
  box-shadow:var(--sh-2); margin-bottom:20px; overflow:hidden;
}
.panel-head{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:15px 20px; border-bottom:1px solid var(--line); background:rgba(255,255,255,.012);
}
.panel-head h2{ font-size:14.5px; }
.panel-sub{ color:var(--faint); font-size:12px; margin-left:auto; }
.panel-body{ padding:20px; }
.panel-sticky{ position:sticky; top:calc(var(--hdr-h) + var(--rail-h) + 20px); }
.center-col{ display:flex; flex-direction:column; align-items:center; gap:18px; padding:34px 20px; }

/* =========================== provider cards ============================ */
.cards{ display:grid; grid-template-columns:repeat(auto-fit,minmax(215px,1fr)); gap:14px; margin-bottom:24px; }
.pcard{
  position:relative; text-align:left; display:flex; flex-direction:column; gap:7px;
  padding:17px 17px 16px; border-radius:var(--r); cursor:pointer;
  background:linear-gradient(180deg,var(--panel),var(--bg-2)); border:1px solid var(--line);
  transition:border-color var(--t), transform var(--t), box-shadow var(--t), background var(--t);
}
.pcard:hover{ border-color:#3a3f47; transform:translateY(-2px); box-shadow:var(--sh-2); }
.pcard .pc-ico{ width:30px; height:30px; border-radius:8px; display:grid; place-items:center; background:var(--panel-3); color:var(--accent); margin-bottom:3px; }
.pcard b{ font-size:14px; font-weight:700; color:var(--text); }
.pcard span{ font-size:12px; color:var(--muted); line-height:1.45; }
.pcard.is-sel{ border-color:var(--accent); background:linear-gradient(180deg, rgba(127,179,232,.11), var(--bg-2)); box-shadow:0 0 0 1px var(--accent-b), var(--sh-2); }
.pcard.is-sel .pc-ico{ background:var(--accent); color:var(--on-accent); }
.pcard .pc-check{ position:absolute; top:13px; right:13px; color:var(--accent); opacity:0; transform:scale(.7); transition:all var(--t); }
.pcard.is-sel .pc-check{ opacity:1; transform:none; }
.skeleton-card{ height:118px; border-radius:var(--r); background:linear-gradient(90deg,var(--panel) 25%,var(--panel-2) 37%,var(--panel) 63%); background-size:400% 100%; animation:shimmer 1.4s infinite; border:1px solid var(--line); }
@keyframes shimmer{ 0%{background-position:100% 0} 100%{background-position:0 0} }

/* =============================== forms ================================= */
.form-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:16px; }
.field{ display:flex; flex-direction:column; gap:6px; min-width:0; }
.field-wide{ grid-column:1 / -1; }
.lbl{ font-size:12px; font-weight:600; color:var(--text-2); letter-spacing:.01em; }
.fhint{ font-size:11.5px; color:var(--faint); }
.input{
  font:inherit; font-size:13.5px; color:var(--text);
  background:var(--bg-2); border:1px solid var(--line-2); border-radius:var(--r-sm);
  padding:9px 11px; width:100%; transition:border-color var(--t), box-shadow var(--t), background var(--t);
}
.input::placeholder{ color:#5b626c; }
.input:hover{ border-color:#3a3f47; }
.input:focus{ border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-a); background:var(--bg); outline:none; }
.input-sm{ padding:6px 9px; font-size:12.5px; }
select.input{ appearance:none; cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239aa3ad' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 9px center; background-size:15px; padding-right:30px;
}
select.input option{ background:#15171b; color:var(--text); }
.input-suffix{ position:relative; display:flex; align-items:center; }
.input-suffix i{ position:absolute; right:11px; font-style:normal; font-size:11.5px; color:var(--faint); pointer-events:none; }
.input-suffix .input{ padding-right:40px; }
input[type=number]{ -moz-appearance:textfield; }
input[type=number]::-webkit-outer-spin-button,input[type=number]::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }
.form-actions{ display:flex; align-items:center; gap:12px; margin-top:20px; flex-wrap:wrap; }
.hint{ font-size:12px; color:var(--faint); }

/* switch */
.switch{ display:inline-flex; align-items:center; gap:9px; cursor:pointer; margin-left:auto; user-select:none; }
.switch input{ position:absolute; opacity:0; width:0; height:0; }
.switch-track{ width:38px; height:21px; border-radius:999px; background:var(--panel-3); border:1px solid var(--line-2); position:relative; transition:all var(--t); }
.switch-thumb{ position:absolute; top:2px; left:2px; width:15px; height:15px; border-radius:50%; background:var(--faint); transition:all var(--t); }
.switch input:checked + .switch-track{ background:var(--accent); border-color:transparent; }
.switch input:checked + .switch-track .switch-thumb{ left:19px; background:var(--on-accent); }
.switch input:focus-visible + .switch-track{ box-shadow:0 0 0 3px var(--accent-a); }
.switch-lbl{ font-size:12.5px; color:var(--text-2); font-weight:560; }

/* ============================== callouts =============================== */
.result-slot:empty{ display:none; }
.result-slot{ margin-top:18px; }
.callout{
  display:flex; gap:12px; padding:13px 15px; border-radius:var(--r);
  border:1px solid var(--line-2); background:var(--panel-2); font-size:13px; line-height:1.5;
}
.callout .ico{ margin-top:1px; }
.callout div{ display:flex; flex-direction:column; gap:3px; min-width:0; }
.callout b{ font-weight:640; }
.callout span{ color:var(--muted); }
.callout-ok{ border-color:rgba(78,201,138,.32); background:var(--ok-a); color:var(--ok); }
.callout-ok span{ color:#9fdcc2; }
.callout-warn{ border-color:rgba(232,176,85,.32); background:var(--warn-a); color:var(--warn); }
.callout-warn span{ color:#d9c095; }
.callout-err{ border-color:rgba(204,51,102,.34); background:var(--danger-a); color:var(--danger); }
.callout-err span{ color:#e0aeb3; }

/* account panel */
.acct{ margin-top:14px; display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:1px; background:var(--line); border:1px solid var(--line); border-radius:var(--r); overflow:hidden; }
.acct-cell{ background:var(--panel); padding:12px 14px; display:flex; flex-direction:column; gap:3px; }
.acct-cell span{ font-size:11px; color:var(--faint); text-transform:uppercase; letter-spacing:.05em; }
.acct-cell b{ font-size:13.5px; font-weight:600; }
.acct-cell b.ok{ color:var(--ok); } .acct-cell b.bad{ color:var(--danger); } .acct-cell b.warn{ color:var(--warn); }

/* ============================== load step ============================== */
.loadbar{ width:100%; max-width:520px; display:flex; flex-direction:column; gap:9px; }
.bar{ height:7px; border-radius:999px; background:var(--panel-3); overflow:hidden; position:relative; }
.bar-fill{ height:100%; border-radius:999px; background:linear-gradient(90deg,var(--accent-lo),var(--accent-hi)); transition:width .3s ease; }
.bar-fill.indeterminate{ width:35%; animation:slide 1.15s ease-in-out infinite; }
@keyframes slide{ 0%{ transform:translateX(-100%);} 100%{ transform:translateX(320%);} }
.loadbar-txt{ font-size:12.5px; color:var(--muted); text-align:center; }

/* Exactly six tiles, always. auto-fit resolved to five-plus-an-orphan at every
   width, because the 880px column cap makes the available space effectively
   viewport-independent -- so "fit as many as will go" had only one answer, and
   it was the wrong one. A fixed 3x2 is the shape the content actually is. */
.stat-grid{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; margin-bottom:20px; }
@media (max-width:720px){ .stat-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
.stat{ background:linear-gradient(180deg,var(--panel),var(--bg-2)); border:1px solid var(--line); border-radius:var(--r); padding:15px 16px; }
.stat b{ display:block; font-size:24px; font-weight:660; letter-spacing:-.02em; line-height:1.15; }
.stat span{ font-size:11.5px; color:var(--faint); text-transform:uppercase; letter-spacing:.05em; }
.stat.accent b{ color:var(--accent-hi); }
.stat.warn b{ color:var(--warn); }

.facet-summary{ display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:22px; }
.fs-block h3{ font-size:11.5px; text-transform:uppercase; letter-spacing:.06em; color:var(--faint); margin-bottom:10px; }
.fs-row{ display:grid; grid-template-columns:1fr auto; gap:8px; align-items:center; margin-bottom:7px; font-size:12.5px; }
.fs-name{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text-2); }
.fs-count{ color:var(--faint); font-family:var(--mono); font-size:11.5px; }
.fs-track{ grid-column:1 / -1; height:4px; border-radius:999px; background:var(--panel-3); overflow:hidden; margin-top:-3px; margin-bottom:4px; }
.fs-fill{ height:100%; background:linear-gradient(90deg,var(--accent-lo),var(--accent)); border-radius:999px; }

/* ============================ curate step ============================== */
.step-full{ height:calc(100vh - var(--hdr-h) - var(--rail-h) - var(--foot-h)); }
.curate{ display:grid; grid-template-columns:252px 1fr; height:100%; min-height:0; }
.curate.side-hidden{ grid-template-columns:0 1fr; }
.curate.side-hidden .side{ display:none; }

.side{ border-right:1px solid var(--line); display:flex; flex-direction:column; min-height:0; background:rgba(255,255,255,.008); }
.side-head{ display:flex; align-items:center; gap:8px; padding:12px 14px; border-bottom:1px solid var(--line); }
.side-head h2{ font-size:12px; text-transform:uppercase; letter-spacing:.07em; color:var(--faint); }
.side-head .btn{ margin-left:auto; }
.side-body{ overflow-y:auto; flex:1; padding:6px 0 24px; }

.facet{ border-bottom:1px solid var(--line); }
.facet-head{
  display:flex; align-items:center; gap:8px; width:100%; padding:10px 14px;
  background:none; border:0; color:var(--text-2); font:inherit; font-size:12px; font-weight:640;
  letter-spacing:.04em; text-transform:uppercase; cursor:pointer; transition:color var(--t);
}
.facet-head:hover{ color:var(--text); }
.facet-head .chev{ margin-left:auto; transition:transform var(--t); opacity:.5; }
.facet.collapsed .chev{ transform:rotate(-90deg); }
.facet.collapsed .facet-list, .facet.collapsed .facet-search{ display:none; }
.facet-badge{ background:var(--accent); color:var(--on-accent); font-size:10px; font-weight:700; border-radius:999px; padding:0 6px; height:15px; display:grid; place-items:center; }
.facet-search{ padding:0 12px 8px; }
.facet-list{ max-height:230px; overflow-y:auto; padding:0 6px 10px; }
.fitem{
  display:flex; align-items:center; gap:8px; padding:5px 8px; border-radius:6px; cursor:pointer;
  font-size:12.5px; color:var(--text-2); transition:background var(--t);
}
.fitem:hover{ background:var(--panel-2); }
.fitem input{ flex:none; }
.fitem .fname{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.fitem .fcount{ margin-left:auto; font-size:11px; color:var(--faint); font-family:var(--mono); }
.fitem.on{ color:var(--text); }

/* checkboxes */
input[type=checkbox]{
  appearance:none; width:15px; height:15px; border-radius:4px; cursor:pointer;
  border:1.5px solid #454a52; background:var(--bg-2); position:relative; transition:all var(--t); flex:none;
}
input[type=checkbox]:hover{ border-color:var(--accent); }
input[type=checkbox]:checked{ background:var(--accent); border-color:var(--accent); }
input[type=checkbox]:checked::after{
  content:""; position:absolute; left:4.4px; top:1.2px; width:4px; height:8.5px;
  border:solid var(--on-accent); border-width:0 2px 2px 0; transform:rotate(45deg);
}
input[type=checkbox]:indeterminate{ background:var(--accent); border-color:var(--accent); }
input[type=checkbox]:indeterminate::after{ content:""; position:absolute; left:2.5px; top:5.4px; width:8px; height:2px; background:var(--on-accent); border-radius:1px; }
input[type=checkbox]:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

/* table area */
.tablearea{ display:flex; flex-direction:column; min-width:0; min-height:0; }
.toolbar{ display:flex; align-items:center; gap:12px; padding:10px 16px; border-bottom:1px solid var(--line); }
.search{ position:relative; flex:1; max-width:460px; }
.search .search-ico{ position:absolute; left:11px; top:50%; transform:translateY(-50%); color:var(--faint); pointer-events:none; }
.search .input{ padding-left:34px; padding-right:32px; }
.search input[type=search]::-webkit-search-cancel-button{ display:none; }
.search-clear{ position:absolute; right:8px; top:50%; transform:translateY(-50%); background:none; border:0; color:var(--faint); cursor:pointer; padding:3px; border-radius:4px; display:flex; }
.search-clear:hover{ color:var(--text); background:var(--panel-3); }
.toolbar-right{ margin-left:auto; display:flex; align-items:center; gap:12px; }

/* All / Selected / Unselected. A segmented control rather than three chips:
   these are mutually exclusive states of one thing, and the joined form says
   so without needing a label. */
.segmented{ display:flex; background:var(--bg-2); border:1px solid var(--line); border-radius:var(--r-sm); padding:2px; gap:2px; }
.segmented .seg{
  appearance:none; border:0; background:none; cursor:pointer;
  font:inherit; font-size:12px; font-weight:600; color:var(--faint);
  padding:4px 11px; border-radius:5px; transition:all var(--t); white-space:nowrap;
}
.segmented .seg:hover{ color:var(--text-2); background:var(--panel-2); }
.segmented .seg.is-on{ background:var(--accent); color:var(--on-accent); }
.segmented .seg.is-on:hover{ background:var(--accent-hi); }

/* The counter is now a control, so it needs to look like one. */
.counter:hover{ border-color:var(--line-2); }

.bulkbar{ display:flex; flex-wrap:wrap; gap:8px 18px; padding:10px 16px; border-bottom:1px solid var(--line); background:rgba(255,255,255,.012); }
.bulk-group{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.bulk-label{ font-size:10.5px; text-transform:uppercase; letter-spacing:.07em; color:var(--faint); margin-right:2px; }

/* virtual table */
.tbl{ display:flex; flex-direction:column; flex:1; min-height:0; }
.thead, .trow{
  display:grid;
  /* Text columns are CAPPED, and an 11th filler track soaks up the remainder.
     Uncapped 2.1fr/1.25fr meant that at 1920px the Name column stretched to
     ~700px, leaving a lake of empty space between a channel name and its group
     while the badge columns stayed pinned to the far right. */
  grid-template-columns:38px 52px 40px minmax(190px,520px) minmax(130px,300px) 92px 118px 108px 92px 104px 1fr;
  align-items:center; gap:0;
}
.thead{
  height:34px; border-bottom:1px solid var(--line-2); background:var(--bg-2);
  font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--faint); position:relative; z-index:2;
  /* .tbody carries an 11px scrollbar that .thead does not, so identical grid
     definitions resolved over different widths and every header sat a few
     pixels left of its column. box-sizing is border-box and the height is
     fixed, so this costs no vertical space. */
  padding-right:11px;
}
.th{ padding:0 10px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; user-select:none; }
.th-check{ display:grid; place-items:center; padding:0; }
.th-num{ text-align:right; padding-right:12px; }
.sortable{ cursor:pointer; transition:color var(--t); }
.sortable:hover{ color:var(--text-2); }
.sortable::after{ content:""; display:inline-block; width:0; height:0; margin-left:6px; vertical-align:middle;
  border-left:4px solid transparent; border-right:4px solid transparent; opacity:0; transition:opacity var(--t); }
.sortable.asc::after{ border-bottom:5px solid var(--accent); opacity:1; }
.sortable.desc::after{ border-top:5px solid var(--accent); opacity:1; }

/* scrollbar-gutter reserves the track even when the list is short, so the
   header/body alignment does not shift the moment the table becomes scrollable. */
.tbody{ flex:1; overflow-y:auto; overflow-x:hidden; position:relative; min-height:0; scrollbar-gutter:stable; }
.vspacer{ position:relative; width:100%; }
.vrows{ position:absolute; top:0; left:0; right:0; will-change:transform; }
/* --row-h is the SINGLE source of truth for row height: app.js reads it back
   via getComputedStyle rather than repeating the number. The virtual scroller
   positions rows arithmetically, so a CSS height that disagrees with the JS
   constant does not look slightly off -- it detaches the rows from the
   scrollbar entirely. */
.trow{
  height:var(--row-h); border-bottom:1px solid rgba(35,38,44,.6); cursor:pointer;
  transition:background 90ms linear;
}
.trow:hover{ background:rgba(255,255,255,.028); }
.trow.sel{ background:rgba(127,179,232,.09); }
.trow.sel:hover{ background:rgba(127,179,232,.14); }
.td{ padding:0 10px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px; color:var(--text-2); }
.td-check{ display:grid; place-items:center; padding:0; }
.td-num{ text-align:right; padding-right:12px; font-family:var(--mono); font-size:11.5px; color:var(--faint); }
.td-logo{ display:grid; place-items:center; padding:0; }
.logo{ width:26px; height:26px; border-radius:5px; object-fit:contain; background:var(--panel-3); }
.logo-fb{ width:26px; height:26px; border-radius:5px; display:grid; place-items:center; background:var(--panel-3); color:var(--faint); font-size:11px; font-weight:700; }
.td-name{ color:var(--text); font-weight:520; }
.td-group{ color:var(--muted); font-size:12.5px; }

/* value chips inside the table */
.vchip{ display:inline-block; padding:1.5px 7px; border-radius:5px; font-size:11px; font-weight:600; background:var(--panel-3); color:var(--text-2); letter-spacing:.02em; max-width:100%; overflow:hidden; text-overflow:ellipsis; }
.vchip.dim{ background:transparent; color:var(--faint); font-weight:500; }
.q-4K{ background:rgba(167,110,255,.16); color:#c39bff; }
.q-FHD{ background:rgba(127,179,232,.16); color:var(--accent-hi); }
.q-HD{ background:rgba(78,201,138,.15); color:var(--ok); }
.q-SD{ background:rgba(154,163,173,.15); color:var(--muted); }
.badge{ display:inline-block; padding:1px 6px; border-radius:4px; font-size:10px; font-weight:700; letter-spacing:.05em; margin-right:4px; }
.badge-dup{ background:var(--warn-a); color:var(--warn); border:1px solid rgba(232,176,85,.30); }
.badge-evt{ background:rgba(167,110,255,.14); color:#c39bff; border:1px solid rgba(167,110,255,.28); }
.badge-adult{ background:var(--danger-a); color:var(--danger); border:1px solid rgba(204,51,102,.30); }
/* Deliberately the quietest badge on the row: "no guide id" is information,
   not a defect -- most sports and event feeds legitimately have none. */
.badge-noepg{ background:rgba(154,163,173,.12); color:var(--faint); border:1px solid rgba(154,163,173,.26); }
/* Marks a channel carrying user overrides -- otherwise a renamed channel is
   indistinguishable from one the provider named that way. */
.badge-ovr{ background:var(--accent-a); color:var(--accent-hi); border:1px solid rgba(127,179,232,.30); }

/* Details affordance. Hidden until the row is hovered/focused so it does not
   add noise to 12,000 rows, but always reachable. */
.td-flags{ display:flex; align-items:center; gap:5px; }
.row-info{
  margin-left:auto; flex:none; display:grid; place-items:center;
  width:22px; height:22px; padding:0; border:0; border-radius:5px;
  background:none; color:var(--faint); cursor:pointer; opacity:0;
  transition:opacity var(--t), color var(--t), background var(--t);
}
.trow:hover .row-info, .row-info:focus-visible{ opacity:1; }
.row-info:hover{ background:var(--panel-3); color:var(--accent); }

.empty{ position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:7px; color:var(--faint); pointer-events:none; }
.empty-ico{ width:34px; height:34px; fill:none; stroke:currentColor; stroke-width:1.6; opacity:.5; }
.empty b{ color:var(--text-2); font-size:14px; }
.empty span{ font-size:12.5px; }

/* ============================= build step ============================== */
.build-grid{ display:grid; grid-template-columns:minmax(0,1.5fr) minmax(300px,.85fr); gap:20px; align-items:start; }
.kv{ margin:0; display:grid; grid-template-columns:1fr auto; gap:9px 14px; font-size:13px; }
.kv dt{ color:var(--muted); }
.kv dd{ margin:0; text-align:right; font-weight:600; color:var(--text); }
.kv dd.warn{ color:var(--warn); } .kv dd.bad{ color:var(--danger); } .kv dd.ok{ color:var(--ok); }

.estimate{ margin-top:16px; padding:14px 16px; border-radius:var(--r); border:1px solid var(--line-2); background:var(--panel-2); }
.estimate-main{ display:flex; align-items:baseline; gap:10px; }
.estimate-lbl{ font-size:12px; color:var(--muted); }
.estimate-main b{ font-size:19px; font-weight:660; margin-left:auto; color:var(--accent-hi); letter-spacing:-.01em; }
.estimate-sub{ font-size:11.5px; color:var(--faint); margin-top:5px; }

/* Quota risk. app.js has always toggled `.risky` on this block, but no rule
   ever matched it -- so the app computed the correct answer to its single most
   consequential question ("will this build get your IP firewalled?") and then
   rendered it identically to a safe estimate. Two grades, reusing the warn and
   danger ramps so it speaks the same alarm language as .callout-warn/-err. */
.estimate.risky{
  border-color:rgba(232,176,85,.42); background:var(--warn-a);
}
.estimate.risky .estimate-main b{ color:var(--warn); }
.estimate.risky .estimate-sub{ color:#d9c095; }

.estimate.risky-high{
  border-color:rgba(204,51,102,.45); background:var(--danger-a);
}
.estimate.risky-high .estimate-main b{ color:var(--danger-hi); }
.estimate.risky-high .estimate-sub{ color:#e0aeb3; }

.progress-block{ margin-top:16px; display:flex; flex-direction:column; gap:9px; }
.progress-row{ display:flex; justify-content:space-between; font-size:12px; color:var(--muted); gap:10px; }
.log{
  margin:0; max-height:190px; overflow:auto; font-family:var(--mono); font-size:11px; line-height:1.65;
  background:#050607; border:1px solid var(--line); border-radius:var(--r-sm); padding:10px 12px;
  color:var(--muted); white-space:pre-wrap; word-break:break-word;
}
.files{ display:flex; flex-direction:column; gap:9px; margin-top:14px; }
.file{
  display:flex; align-items:center; gap:11px; padding:11px 13px; border-radius:var(--r);
  border:1px solid var(--line-2); background:var(--panel-2); text-decoration:none; color:var(--text);
  transition:border-color var(--t), background var(--t), transform var(--t);
}
.file:hover{ border-color:var(--accent); background:rgba(127,179,232,.08); transform:translateY(-1px); }
.file .fi{ width:30px; height:30px; border-radius:7px; display:grid; place-items:center; background:var(--panel-3); color:var(--accent); flex:none; }
.file-txt{ display:flex; flex-direction:column; min-width:0; }
.file-txt b{ font-size:13px; }
.file-txt span{ font-size:11px; color:var(--faint); font-family:var(--mono); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.file .dl{ margin-left:auto; color:var(--faint); }
.pathbox{ display:flex; align-items:center; gap:8px; margin-top:10px; font-family:var(--mono); font-size:11px; color:var(--faint); background:#050607; border:1px solid var(--line); border-radius:var(--r-sm); padding:8px 10px; }
.pathbox span{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ============================== footer ================================= */
.footbar{
  position:fixed; inset:auto 0 0 0; height:var(--foot-h); z-index:60;
  display:flex; align-items:center; gap:14px; padding:0 18px;
  background:rgba(11,12,14,.88); backdrop-filter:blur(14px) saturate(150%);
  border-top:1px solid var(--line);
}
.footbar-spacer{ flex:1; }
.foot-msg{ font-size:12.5px; color:var(--muted); max-width:44ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

.counter{ display:flex; flex-direction:column; gap:6px; min-width:250px; }
.counter-main{ display:flex; align-items:center; gap:5px; font-size:14px; }
.counter-main b{ font-size:19px; font-weight:680; letter-spacing:-.02em; transition:color var(--t); }
.counter-slash{ color:var(--faint); font-size:16px; }
.counter-word{ color:var(--muted); font-size:12.5px; margin-left:4px; }
.cap-input{
  width:52px; font:inherit; font-size:14px; font-weight:600; text-align:center; color:var(--text-2);
  background:var(--panel-2); border:1px solid var(--line-2); border-radius:6px; padding:2px 4px;
}
.cap-input:focus{ border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-a); }
.counter-bar{ height:4px; border-radius:999px; background:var(--panel-3); overflow:hidden; width:100%; }
.counter-fill{ height:100%; width:0%; border-radius:999px; background:var(--accent); transition:width .22s ease, background var(--t); }
.counter.near b{ color:var(--warn); } .counter.near .counter-fill{ background:var(--warn); }
.counter.over b{ color:var(--danger); } .counter.over .counter-fill{ background:var(--danger); }
.counter.over .cap-input{ border-color:rgba(204,51,102,.55); }

/* tooltip */
.info{ position:relative; display:inline-flex; color:var(--faint); cursor:help; margin-left:3px; }
.info:hover{ color:var(--accent); }
.tip{
  position:absolute; bottom:calc(100% + 10px); left:50%; transform:translateX(-50%) translateY(4px);
  width:320px; padding:12px 14px; border-radius:var(--r); background:#1b1e23; border:1px solid var(--line-2);
  box-shadow:var(--sh-3); font-size:12px; line-height:1.55; color:var(--text-2);
  opacity:0; visibility:hidden; transition:all var(--t); z-index:80; pointer-events:none;
}
.tip b{ display:block; color:var(--text); margin-bottom:4px; font-size:12.5px; }
.tip::after{ content:""; position:absolute; top:100%; left:50%; margin-left:-6px; border:6px solid transparent; border-top-color:#1b1e23; }
.info:hover .tip, .info:focus .tip, .info:focus-visible .tip{ opacity:1; visibility:visible; transform:translateX(-50%); }

/* ============================== pills ================================== */
.pill{ display:inline-flex; align-items:center; gap:7px; padding:5px 11px; border-radius:999px; font-size:12px; font-weight:560; border:1px solid var(--line-2); background:var(--panel-2); color:var(--text-2); }
.dot{ width:7px; height:7px; border-radius:50%; background:var(--faint); flex:none; }
.dot-ok{ background:var(--ok); box-shadow:0 0 0 3px var(--ok-a); }
.dot-bad{ background:var(--danger); box-shadow:0 0 0 3px var(--danger-a); }

/* =============================== menu ================================== */
.menu-wrap{ position:relative; }
.menu{
  position:absolute; right:0; top:calc(100% + 8px); width:300px; z-index:70;
  background:#15171b; border:1px solid var(--line-2); border-radius:var(--r); box-shadow:var(--sh-3);
  animation:pop .13s ease both; overflow:hidden;
}
@keyframes pop{ from{ opacity:0; transform:translateY(-5px) scale(.985);} to{ opacity:1; transform:none;} }
.menu-head{ display:flex; align-items:center; padding:10px 13px; border-bottom:1px solid var(--line); font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--faint); }
.menu-head .btn{ margin-left:auto; }
.menu-body{ max-height:260px; overflow-y:auto; padding:5px; }
.menu-empty{ padding:16px 12px; text-align:center; color:var(--faint); font-size:12.5px; }
.menu-item{ display:flex; align-items:center; gap:9px; padding:8px 10px; border-radius:7px; cursor:pointer; transition:background var(--t); }
.menu-item:hover{ background:var(--panel-2); }
.menu-item .mi-txt{ display:flex; flex-direction:column; min-width:0; flex:1; }
.menu-item b{ font-size:13px; font-weight:560; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.menu-item span{ font-size:11px; color:var(--faint); }
.menu-item .mi-del{ opacity:0; background:none; border:0; color:var(--faint); cursor:pointer; padding:3px; border-radius:5px; display:flex; transition:all var(--t); }
.menu-item:hover .mi-del{ opacity:1; }
.menu-item .mi-del:hover{ color:var(--danger); background:var(--danger-a); }
.menu-foot{ display:flex; gap:7px; padding:10px; border-top:1px solid var(--line); background:rgba(255,255,255,.012); }
.menu-foot .input{ flex:1; }

/* ============================== toasts ================================= */
.toasts{ position:fixed; bottom:calc(var(--foot-h) + 16px); right:18px; z-index:200; display:flex; flex-direction:column; gap:9px; align-items:flex-end; pointer-events:none; }
.toast{
  display:flex; align-items:center; gap:10px; padding:11px 15px; border-radius:var(--r);
  background:#15171b; border:1px solid var(--line-2); box-shadow:var(--sh-3);
  font-size:13px; color:var(--text-2); max-width:420px; pointer-events:auto;
  animation:toastIn .2s cubic-bezier(.2,.9,.3,1.2) both;
}
.toast.out{ animation:toastOut .18s ease forwards; }
@keyframes toastIn{ from{ opacity:0; transform:translateX(24px) scale(.97);} to{ opacity:1; transform:none;} }
@keyframes toastOut{ to{ opacity:0; transform:translateX(24px) scale(.97);} }
.toast .ico{ flex:none; }
.toast.ok .ico{ color:var(--ok); } .toast.err .ico{ color:var(--danger); } .toast.warn .ico{ color:var(--warn); } .toast.info .ico{ color:var(--accent); }

/* ============================== modal ================================== */
.backdrop{ position:fixed; inset:0; background:rgba(3,6,10,.7); backdrop-filter:blur(3px); z-index:150; display:grid; place-items:center; padding:24px; animation:fade .15s ease both; }
.modal{ width:min(560px,100%); background:linear-gradient(180deg,var(--panel),var(--bg-2)); border:1px solid var(--line-2); border-radius:var(--r-lg); box-shadow:var(--sh-3); overflow:hidden; animation:pop .18s ease both; }
.modal-head{ display:flex; align-items:center; padding:16px 20px; border-bottom:1px solid var(--line); }
.modal-head h2{ font-size:15px; }
.modal-head .x{ margin-left:auto; background:none; border:0; color:var(--faint); cursor:pointer; display:flex; padding:4px; border-radius:6px; }
.modal-head .x:hover{ color:var(--text); background:var(--panel-3); }
.modal-body{ padding:20px; max-height:60vh; overflow-y:auto; }
.modal-foot{ display:flex; justify-content:flex-end; gap:9px; padding:14px 20px; border-top:1px solid var(--line); }
.keys{ display:grid; grid-template-columns:auto 1fr; gap:10px 16px; font-size:13px; align-items:center; }
.keys kbd{ font-family:var(--mono); font-size:11.5px; background:var(--panel-3); border:1px solid var(--line-2); border-bottom-width:2px; border-radius:5px; padding:2px 7px; color:var(--text); white-space:nowrap; }
.keys span{ color:var(--muted); }

/* Preset preview: a before → after list, with only the CHANGED lines
   emphasised so "what does this actually do to my setup" is answerable at a
   glance rather than by reading every row. */
.preview{ display:flex; flex-direction:column; gap:1px; background:var(--line); border:1px solid var(--line); border-radius:var(--r); overflow:hidden; }
.pv-row{ display:flex; align-items:baseline; gap:14px; justify-content:space-between; padding:9px 13px; background:var(--panel); font-size:12.5px; }
.pv-row span{ color:var(--faint); flex:none; }
.pv-row b{ color:var(--text-2); font-weight:600; text-align:right; word-break:break-all; }
.pv-row s{ color:var(--faint); text-decoration-color:var(--faint); font-weight:400; }
.pv-changed{ background:var(--panel-2); }
.pv-changed b{ color:var(--accent-hi); }

.preset-pw{ display:flex; gap:8px; align-items:flex-start; margin-top:10px; cursor:pointer; }
.preset-pw span{ font-size:12px; color:var(--text-2); line-height:1.4; }
.preset-pw i{ display:block; font-style:normal; font-size:11px; color:var(--faint); margin-top:2px; }

/* ============================= responsive ============================== */

/* Short screens (1366x768 laptops, and any window that isn't full height).
   The fixed chrome — 58px header + 62px rail + 64px footer + toolbars — eats
   so much of a 768px viewport that under 10 channel rows are visible, which
   makes curating 12,000 channels feel like looking through a letterbox.
   ROW_H is read from --row-h at runtime, so shrinking the row here cannot
   desynchronise the virtual scroller. */
@media (max-height:820px){
  :root{ --hdr-h:50px; --rail-h:48px; --foot-h:56px; --row-h:38px; }
  .rail-txt i{ display:none; }
  .toolbar{ padding-block:7px; }
  .bulkbar{ padding-block:7px; }
}
/* 11 bulk chips wrap to a second row long before 860px. Scroll them instead --
   a silently two-row toolbar shifts the whole table down. */
@media (max-width:1500px){
  .bulkbar{ flex-wrap:nowrap; overflow-x:auto; scrollbar-width:thin; }
}
/* Hidden columns keep their grid slot (a 0px track) — using display:none would
   re-flow the remaining cells into the wrong tracks. */
.th.col-off, .td.col-off{ padding:0; overflow:hidden; visibility:hidden; }

@media (max-width:1100px){
  .build-grid{ grid-template-columns:1fr; }
  .panel-sticky{ position:static; }
  .thead, .trow{ grid-template-columns:38px 44px 40px minmax(150px,2fr) minmax(105px,1fr) 78px 100px 0 84px 88px 1fr; }
  .th:nth-child(8), .td:nth-child(8){ padding:0; overflow:hidden; visibility:hidden; }
}
@media (max-width:860px){
  .rail-txt i{ display:none; }
  .brand-text em{ display:none; }
  .curate{ grid-template-columns:1fr; }
  .side{ position:absolute; z-index:40; width:252px; height:100%; background:var(--bg-2); box-shadow:var(--sh-3); }
  .curate.side-hidden .side{ display:none; }
  .thead, .trow{ grid-template-columns:36px 0 38px minmax(110px,2fr) 0 0 0 0 66px 78px 1fr; }
  .th:nth-child(2),.td:nth-child(2),.th:nth-child(5),.td:nth-child(5),
  .th:nth-child(6),.td:nth-child(6),.th:nth-child(7),.td:nth-child(7),
  .th:nth-child(8),.td:nth-child(8){ padding:0; overflow:hidden; visibility:hidden; }
  .foot-msg{ display:none; }
  .counter{ min-width:150px; }
  .bulkbar{ overflow-x:auto; flex-wrap:nowrap; }
  /* Narrow the shared gutter rather than overriding .wrap alone, so the rail
     keeps tracking it instead of drifting 10px out at this breakpoint. */
  :root{ --gutter:16px; }
  .wrap{ padding:22px var(--gutter) 44px; }
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ animation-duration:.01ms !important; transition-duration:.01ms !important; }
}
