/* SSAI Portal — shared stylesheet.
   Palette and component names deliberately track the xstream portal so the two
   feel like one product to the same engineers. */

:root{
  --bg:#0d1117; --panel:#161b22; --panel2:#1c2128; --line:#30363d;
  --fg:#c9d1d9; --muted:#8b949e; --bright:#e6edf3;
  --ok:#3fb950; --warn:#d29922; --crit:#f85149; --info:#58a6ff;
  --focus:#1f6feb;
  --nav-h:52px;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--fg);
     font:14px/1.5 -apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
a{color:var(--info);text-decoration:none}
a:hover{text-decoration:underline}
code,.mono{font-family:ui-monospace,SFMono-Regular,Consolas,monospace}

/* ── Nav ───────────────────────────────────────────────── */
.main-nav{background:var(--panel);border-bottom:1px solid var(--line);
          height:var(--nav-h);position:sticky;top:0;z-index:50}
.nav-row{display:flex;align-items:center;height:var(--nav-h);padding:0 16px;gap:4px}
.nav-brand{margin-right:26px;line-height:1.15}
.nav-brand .b1{font-weight:700;color:#fff;font-size:16px}
.nav-brand .b2{font-size:10.5px;color:var(--info);text-transform:uppercase;letter-spacing:1px}
.nav-left{display:flex;gap:2px}
.nav-btn{padding:6px 13px;border-radius:6px;color:var(--fg);font-size:13.5px;
         border:1px solid transparent}
.nav-btn:hover{background:var(--panel2);text-decoration:none}
.nav-btn.is-active{background:var(--panel2);border-color:var(--line);color:#fff;font-weight:600}
.nav-spacer{flex:1}

/* engine state pill in the nav */
.engine-pill{display:flex;align-items:center;gap:7px;padding:4px 11px;border-radius:14px;
             background:var(--panel2);border:1px solid var(--line);font-size:12px;margin-right:12px}
.engine-pill .dot{width:8px;height:8px;border-radius:50%;background:#444}
.engine-pill.is-ok   .dot{background:var(--ok);box-shadow:0 0 6px var(--ok)}
.engine-pill.is-crit .dot{background:var(--crit);box-shadow:0 0 6px var(--crit)}
.engine-pill.is-warn .dot{background:var(--warn)}

/* avatar + user menu */
.userbox{position:relative}
.avatar{width:32px;height:32px;border-radius:50%;border:none;cursor:pointer;
        font-weight:700;font-size:12px;letter-spacing:.5px}
.user-menu{position:absolute;right:0;top:40px;min-width:210px;background:var(--panel2);
           border:1px solid var(--line);border-radius:8px;padding:6px;display:none;
           box-shadow:0 8px 24px rgba(0,0,0,.5);z-index:100}
.user-menu.show{display:block}
.user-menu .um-hd{padding:8px 10px;border-bottom:1px solid var(--line);margin-bottom:6px}
.um-name{font-weight:600;color:var(--bright)}
.um-roles{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.6px}
.user-menu a,.user-menu button{display:flex;align-items:center;width:100%;gap:8px;
  padding:8px 10px;border-radius:6px;background:none;border:none;color:var(--fg);
  font-size:13px;cursor:pointer;text-align:left;font-family:inherit}
.user-menu a:hover,.user-menu button:hover{background:var(--panel);text-decoration:none}
.user-menu button.danger{color:var(--crit)}

/* ── Alert bell (left of the avatar) ───────────────────── */
.alertbox{position:relative;margin-right:4px}
.bell{width:32px;height:32px;border-radius:50%;border:1px solid var(--line);
      background:var(--panel2);color:var(--muted);cursor:pointer;font-size:14px;
      line-height:1;position:relative;padding:0}
.bell:hover{color:var(--bright)}
/* The badge is the whole point of putting this here — it has to read at a
   glance from across a desk, so colour by worst severity, not by count. */
.bell-badge{position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;
            padding:0 4px;border-radius:8px;background:var(--warn);color:#111;
            font-size:10px;font-weight:700;line-height:16px;text-align:center}
.bell.is-crit{border-color:var(--crit);color:var(--crit)}
.bell.is-crit .bell-badge{background:var(--crit);color:#fff}
.bell.is-warn{border-color:var(--warn);color:var(--warn)}
.alert-menu{position:absolute;right:0;top:40px;width:390px;max-width:88vw;
            background:var(--panel2);border:1px solid var(--line);border-radius:8px;
            display:none;box-shadow:0 8px 24px rgba(0,0,0,.5);z-index:100}
.alert-menu.show{display:block}
.am-hd{display:flex;justify-content:space-between;align-items:center;
       padding:9px 11px;border-bottom:1px solid var(--line);
       font-weight:600;color:var(--bright);font-size:13px}
.am-ack-all{background:none;border:1px solid var(--line);border-radius:5px;
            color:var(--muted);font-size:11px;padding:3px 8px;cursor:pointer;
            font-family:inherit}
.am-ack-all:hover{color:var(--bright)}
.am-list{max-height:min(52vh,420px);overflow-y:auto}
.am-item{padding:9px 11px;border-bottom:1px solid var(--line);font-size:12px}
.am-item:last-child{border-bottom:none}
.am-item .t{font-weight:600;color:var(--bright);display:flex;gap:6px;align-items:baseline}
.am-item .d{color:var(--muted);margin-top:3px;line-height:1.45}
.am-item .m{color:var(--muted);font-size:10.5px;margin-top:4px;
            display:flex;gap:8px;align-items:center}
.am-item.sev-crit{border-left:3px solid var(--crit)}
.am-item.sev-warn{border-left:3px solid var(--warn)}
.am-item.sev-ok{border-left:3px solid var(--ok)}
/* Unknown is its own colour and deliberately NOT green: a capacity that could
   not be read must never look like a capacity that is fine. */
.am-item.sev-unknown{border-left:3px solid var(--muted)}

/* ── Chips (selectable pills) ──────────────────────────────
   SHARED, because they were not. These lived only inside creatives.php and
   viewers.php as page-local <style>, so onboard.php — which uses the same
   markup for picking renditions — rendered them as bare unstyled spans: no
   pill, no border, no selected state, no pointer.

   The cost of that was not cosmetic. An operator onboarding a channel saw what
   looked like a caption reading "360p 504p 540p 720p 1080p", could not tell it
   was a control, and shipped the default {720,1080} onto a single-bitrate
   source — which then ran a surgeon against a rendition that would never
   arrive. A control nobody can see is worse than one that is missing, because
   it still carries a default. */
.chips{display:flex;gap:5px;flex-wrap:wrap;margin:4px 0 2px}
.chip{padding:2px 9px;border-radius:11px;font-size:11px;border:1px solid var(--line);
      background:var(--panel2);color:var(--muted);cursor:pointer;user-select:none;
      white-space:nowrap}
.chip:hover{color:var(--fg);border-color:var(--muted)}
.chip.on{background:rgba(88,166,255,.16);border-color:var(--info);color:#cfe4ff;font-weight:600}
.chip.ex.on{background:rgba(248,81,73,.16);border-color:var(--crit);color:#ffc9c5}
.chip.ro{cursor:default}
.chip.ro:hover{color:var(--muted);border-color:var(--line)}
.chip.ro.dim{opacity:.65}

/* ── Storage pill ──────────────────────────────────────────
   Reads as a value, not a button, because it is on screen permanently and
   most of the time the answer is "fine". It earns attention by changing
   colour, which is why "unknown" is grey rather than absent. */
.storagebox{position:relative;margin-right:6px}
.storpill{display:flex;align-items:center;gap:5px;height:32px;padding:0 10px;
          border-radius:16px;border:1px solid var(--line);background:var(--panel2);
          color:var(--muted);font:inherit;font-size:12px;cursor:pointer;
          font-variant-numeric:tabular-nums}
.storpill:hover{color:var(--bright)}
.storpill-ico{font-size:12px;line-height:1;filter:grayscale(1) opacity(.75)}
.storpill.is-ok{border-color:var(--line)}
.storpill.is-warn{border-color:var(--warn);color:var(--warn)}
.storpill.is-crit{border-color:var(--crit);color:var(--crit);font-weight:600}
.storpill.is-unknown{border-style:dashed}
.stor-menu{position:absolute;right:0;top:40px;width:390px;max-width:88vw;
           background:var(--panel2);border:1px solid var(--line);border-radius:8px;
           display:none;box-shadow:0 8px 24px rgba(0,0,0,.5);z-index:100}
.stor-menu.show{display:block}
.am-item.is-resolved{opacity:.55;border-left-color:var(--ok)}
.am-item.is-acked .t{font-weight:500}
.am-ack{background:none;border:1px solid var(--line);border-radius:5px;
        color:var(--muted);font-size:10px;padding:2px 6px;cursor:pointer;
        font-family:inherit;margin-left:auto}
.am-ack:hover{color:var(--bright)}
.am-empty{padding:16px 11px;color:var(--muted);font-size:12px;text-align:center}
.am-ft{display:block;padding:8px 11px;border-top:1px solid var(--line);
       font-size:12px;color:var(--muted)}
.am-ft:hover{color:var(--bright);text-decoration:none}

/* ── Layout ────────────────────────────────────────────── */
.container{max-width:1500px;margin:0 auto;padding:14px 16px 40px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:13px 15px}
.card + .card{margin-top:12px}
.card h3{font-size:12.5px;color:var(--muted);text-transform:uppercase;
         letter-spacing:.9px;font-weight:600;margin-bottom:10px}
.flex-between{display:flex;align-items:center;justify-content:space-between}
.muted{color:var(--muted)}
.right{margin-left:auto}

/* KPI cards — the house dashboard idiom, shared with xstream's .dash-card so
   the two portals feel like one product.

   CENTRED, and the track is capped. auto-fit alone still stretches: three cards
   on a wide screen become billboards, four sit flush left against the container
   edge. A max track width plus justify-content puts the group in the middle at
   a readable size, whatever the count.

   ONE definition. A second copy of this block was added on 2026-08-16 for the
   ad-library overview and quietly won the cascade for dashboard.php as well —
   changing a page nobody had touched. */
.dash-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(175px,255px));
           justify-content:center;gap:11px}
.dash-card{background:var(--panel);border:1px solid var(--line);border-radius:8px;
           padding:15px;text-align:center}
.dash-card .count{font-size:29px;font-weight:700;line-height:1.1;
                  font-variant-numeric:tabular-nums}
.dash-card .label{font-size:11.5px;color:var(--muted);text-transform:uppercase;
                  letter-spacing:.8px;margin-top:5px}
.dash-card .sub{font-size:11px;color:var(--muted);margin-top:6px}

/* ── Tables ────────────────────────────────────────────── */
.table-wrap{overflow:auto;border-radius:6px}
table{width:100%;border-collapse:collapse;font-size:13px}
th{position:sticky;top:0;background:var(--panel2);color:var(--muted);text-align:left;
   font-weight:600;font-size:11.5px;text-transform:uppercase;letter-spacing:.6px;
   padding:8px 10px;border-bottom:1px solid var(--line);white-space:nowrap}
td{padding:7px 10px;border-bottom:1px solid #21262d;vertical-align:middle}
tbody tr:hover{background:rgba(255,255,255,.02)}
tbody tr:last-child td{border-bottom:none}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
.sub-row td{background:rgba(255,255,255,.015)}
.sub-row td:first-child{padding-left:26px}

/* ── Status colours (paired bg-/text- families, as in xstream) ── */
.text-ok{color:var(--ok)} .text-warn{color:var(--warn)}
.text-err{color:var(--crit)} .text-info{color:var(--info)}
.dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:7px}
.bg-ok{background:var(--ok)} .bg-warn{background:var(--warn)}
.bg-crit{background:var(--crit)} .bg-none{background:#484f58}
.badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600}
.badge-ok{background:rgba(63,185,80,.15);color:var(--ok)}
.badge-warn{background:rgba(210,153,34,.15);color:var(--warn)}
.badge-crit{background:rgba(248,81,73,.15);color:var(--crit)}
.badge-none{background:rgba(139,148,158,.15);color:var(--muted)}

/* age cells */
.age-ok{color:var(--ok)} .age-warn{color:var(--warn)}
.age-bad{color:var(--crit)} .age-miss{color:var(--muted);font-style:italic}

/* ── Controls ──────────────────────────────────────────── */
button,.btn{background:var(--panel2);border:1px solid var(--line);color:var(--fg);
  padding:6px 13px;border-radius:6px;cursor:pointer;font-size:13px;font-family:inherit}
button:hover:not(:disabled),.btn:hover{border-color:var(--info)}
button:disabled{opacity:.45;cursor:not-allowed}
button.primary{background:#1f6feb22;border-color:var(--focus);color:#cfe3ff}
button.danger{background:#f8514922;border-color:#8a3434;color:#ffb4ae}
button.mini{padding:2px 8px;font-size:11.5px;border-radius:5px}
input[type=text],input[type=password],input[type=email],select,textarea{
  width:100%;background:#0b0f14;border:1px solid var(--line);color:var(--fg);
  padding:7px 10px;border-radius:6px;font-size:13px;font-family:inherit}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--focus)}
label{display:block;font-size:11.5px;color:var(--muted);margin:9px 0 4px;
      text-transform:uppercase;letter-spacing:.5px}
.row{display:flex;gap:12px;flex-wrap:wrap}
.row > *{flex:1;min-width:180px}
.chk{display:flex;align-items:center;gap:7px;text-transform:none;letter-spacing:0;
     font-size:13px;color:var(--fg);margin:0}
.chk input{width:auto}

/* ── Tabs ──────────────────────────────────────────────── */
.tabs{display:flex;gap:3px;border-bottom:1px solid var(--line);margin-bottom:13px}
.tab{padding:8px 15px;cursor:pointer;color:var(--muted);font-size:13.5px;
     border-bottom:2px solid transparent;margin-bottom:-1px}
.tab:hover{color:var(--fg)}
.tab.active{color:#fff;border-bottom-color:var(--focus);font-weight:600}
.tabpane{display:none} .tabpane.active{display:block}

/* ── Side rail ─────────────────────────────────────────────
   The Monitor page's shape — sections down the left, content on the right —
   generalised so any page can use it. Nine horizontal tabs on Administration
   wrapped on a laptop and read as a list of unrelated words; down the side
   they read as a menu and the section names have room to be sentences.

   Same .tab / .tabpane contract as the horizontal form, so a page switches
   layout by changing its WRAPPER only — no pane markup, no JS changes. */
.rail{display:flex;gap:12px;align-items:flex-start}
/* Sticky, and SCROLLS INSIDE ITSELF once it is taller than the viewport.
   Without the max-height a sticky element taller than the screen cannot hold
   position: the browser scrolls it until its bottom edge is reached, so the
   rail drifts up and down with the main column — which looks exactly like
   sticky being broken. The rail grew past a laptop viewport when the API
   section was added, which is when this started. */
.rail-nav{width:196px;flex-shrink:0;position:sticky;
          /* Below the nav, which is itself sticky at top:0 and therefore always
             occupying the first --nav-h of the viewport. Sticking at a bare
             12px tucked the rail's first item underneath it. */
          /* The rail's NATURAL position is nav-h + 28px from the top of the
             scrollport: 14px of body padding plus 14px of container padding.
             Sticking any higher than that means it travels those pixels before
             it locks, which reads as a small jitter at the start of every
             scroll. Matching the natural offset makes it lock immediately. */
          top:calc(var(--nav-h) + 28px);
          /* And SCROLL INSIDE ITSELF beyond that. A sticky element taller than
             the space it has cannot hold position — the browser scrolls it until
             its bottom edge appears, so the rail drifts up and down with the
             main column and looks exactly like sticky being broken. The rail
             passed that height when the API section was added. The subtraction
             has to account for the nav AND the body padding, because
             body{overflow:auto} makes the body the scrollport, so 100vh alone
             overstates the room available. */
          max-height:calc(100vh - var(--nav-h) - 56px);
          overflow-y:auto;overscroll-behavior:contain}
/* min-width:0 is load-bearing: without it a wide table inside the pane sets
   the flex item's minimum to its own content width and pushes the rail off
   screen instead of scrolling inside. */
.rail-main{flex:1;min-width:0}

/* The heading card at the top of a rail page: what the page IS, and the notes
   that qualify everything under it. Those notes are the context for the content
   below — an endpoint reference, a spec browser, a tool list — so scrolling them
   away is precisely backwards: the further you read, the more you need them.
   Pinned at the same offset as the rail, so the two line up and only the
   content between them moves.
   z-index sits below the nav's 50: this never needs to cover the nav, because
   it stops exactly where the nav ends. */
.rail-head{position:sticky;top:calc(var(--nav-h) + 28px);z-index:20;
           background:var(--panel)}

/* THE GAP ABOVE A PINNED HEADER HAS TO BE OPAQUE.
   The card pins 28px below the nav, which is the right amount of air when the
   page is at the top. But the content behind it keeps scrolling THROUGH that
   28px, so rows appear above the card and it reads as floating in mid-air over
   its own page. Painting the gap in the page background closes it: content
   disappears at the nav's edge, as if it were scrolling under a solid header.
   -1px on each side covers the card's own border, so the band and the card
   line up exactly rather than showing a hairline of content down the edges.
   Sticky is a positioned value, so this anchors to the card without needing
   position:relative. */
.rail-head::before{content:"";position:absolute;left:-1px;right:-1px;
                   top:-29px;height:29px;background:var(--bg);pointer-events:none}

/* Long pages put a lot under a pinned header; give the content a little air so
   the first row is not flush against it the moment it locks. */
.rail-head + *{scroll-margin-top:calc(var(--nav-h) + 96px)}
.rail-nav .tabs{display:block;border-bottom:0;margin:0}
.rail-nav .tab{display:block;padding:7px 10px;border-bottom:0;margin:0 0 1px;
               border-radius:6px;border-left:2px solid transparent;font-size:13px}
.rail-nav .tab:hover{background:var(--panel2);color:var(--fg)}
.rail-nav .tab.active{background:var(--panel2);border-left-color:var(--focus);
                      color:#fff;font-weight:600}
/* A GROUP CAPTION MUST NOT LOOK LIKE A ROW THAT DOES NOTHING.
   It used to sit at padding-left:10px — the SAME column as .tab — in the same
   muted grey this UI uses for disabled things. In a stack of clickable rows
   that reads as three broken menu entries, which is exactly how Streams /
   Advertising / Platform were reported.

   Given its own filled band, per Rajesh. The tone is --bg, DARKER than the
   card it sits on, so it reads as INSET. That matters: hover and .active are
   --panel2, lighter than the card and therefore raised, so a heading can never
   be mistaken for a selected item — the two now differ in direction, not just
   in shade. Caption typography on top of it (smaller than a row, bold, widely
   tracked) and cursor:default, so nothing about it suggests a target. */
.rail-sec{font-size:9.5px;font-weight:700;color:var(--muted);
          text-transform:uppercase;letter-spacing:1.4px;
          background:var(--bg);border:1px solid var(--line);border-radius:5px;
          margin:14px 0 6px;padding:5px 9px;
          cursor:default;user-select:none}
.rail-sec:first-child{margin-top:0}
@media (max-width:820px){
  .rail{display:block}
  .rail-nav{width:auto;position:static;margin-bottom:11px}
  .rail-nav .tabs{display:flex;flex-wrap:wrap;gap:3px}
  .rail-nav .tab{border-left:0}
  .rail-sec{margin-top:11px}
}

/* ── Modal + toast ─────────────────────────────────────── */
.modal{position:fixed;inset:0;background:rgba(0,0,0,.66);display:none;
       align-items:center;justify-content:center;z-index:200}
.modal.show{display:flex}
.modal .box{background:var(--panel);border:1px solid var(--line);border-radius:10px;
            width:min(720px,94vw);max-height:86vh;display:flex;flex-direction:column}
.modal header{display:flex;align-items:center;gap:10px;padding:11px 15px;
              border-bottom:1px solid var(--line);font-weight:600;color:var(--bright)}
.modal .body{padding:14px 15px;overflow:auto}
.modal footer{display:flex;gap:8px;justify-content:flex-end;padding:11px 15px;
              border-top:1px solid var(--line)}
.modal pre{font:12px/1.55 ui-monospace,Consolas,monospace;color:#b9c4d0;white-space:pre-wrap}
#toast{position:fixed;right:18px;bottom:18px;max-width:460px;background:var(--panel2);
       border:1px solid var(--focus);border-radius:8px;padding:11px 15px;display:none;
       white-space:pre-wrap;font-size:12.5px;z-index:300;box-shadow:0 8px 24px rgba(0,0,0,.5)}
#toast.err{border-color:var(--crit)}

/* ── Login ─────────────────────────────────────────────── */
.login-wrap{min-height:100vh;display:flex;align-items:center;justify-content:center}
.login-box{background:var(--panel);border:1px solid var(--line);border-radius:12px;
           padding:34px;width:370px}
.login-box h1{font-size:21px;color:#fff;margin-bottom:3px}
.login-box .sub{font-size:11.5px;color:var(--info);text-transform:uppercase;
                letter-spacing:1.4px;margin-bottom:22px}
.login-err{background:rgba(248,81,73,.12);border:1px solid #8a3434;color:#ffb4ae;
           padding:9px 11px;border-radius:6px;font-size:13px;margin-bottom:13px;display:none}

/* Row state tints — control page (xstream row-green/orange/red convention,
   toned for this dark palette). Applied per-td so they read over .sub-row. */
tr.row-ok   > td { background: rgba(63,185,80,.10); }
tr.row-warn > td { background: rgba(210,153,34,.10); }
tr.row-crit > td { background: rgba(248,81,73,.10); }

/* Channel lock toggle (xstream lock-btn convention) */
.lock-btn { width:34px; height:26px; border-radius:6px; cursor:pointer;
            border:1px solid var(--line); background:transparent;
            display:inline-flex; align-items:center; justify-content:center; font-size:14px; }
.lock-btn.is-locked   { background:rgba(248,81,73,.15); border-color:rgba(248,81,73,.45); }
.lock-btn.is-unlocked { background:rgba(63,185,80,.10); }

/* ── Folder picker (Ad ingest) ─────────────────────────── */
.pathrow{display:flex;gap:5px;align-items:center}
.pathrow input{flex:1;min-width:0}
.pick-here{display:flex;gap:8px;align-items:center;justify-content:space-between;
           padding:7px 9px;background:var(--panel2);border-radius:6px;margin-bottom:7px}
/* A real listbox: it scrolls, it keyboard-navigates, and it holds a selection
   between clicks — three things a div list has to reimplement badly. */
.pick-box{width:100%;font-family:ui-monospace,Consolas,monospace;font-size:12px;
          background:var(--panel2);color:var(--fg);border:1px solid var(--line);
          border-radius:6px;padding:4px}
.pick-box option{padding:3px 6px}
.pick-new{display:flex;gap:6px;margin-top:9px}
.pick-new input{flex:1}
.reschips{display:flex;gap:5px;flex-wrap:wrap;padding:5px 0}

/* ── "How this works" guide (Ad ingest) ────────────────── */
.howto{border:1px solid var(--info);border-left-width:3px;border-radius:7px;
       background:rgba(88,166,255,.06);padding:10px 13px;margin:6px 0 13px;
       font-size:12px;line-height:1.55}
.howto-t{font-weight:600;color:var(--bright);margin-bottom:6px;font-size:12.5px}
.howto ol{margin:0 0 0 17px;padding:0}
.howto li{margin-bottom:5px}
.howto-eg{margin-top:8px;padding:8px 10px;background:var(--panel2);border-radius:6px}
.howto-eg .mono{white-space:pre;overflow-x:auto;display:block;margin:5px 0;
                font-size:11px;color:var(--fg)}
.rescheck{display:inline-flex;align-items:center;gap:4px;font-size:12px;
          padding:2px 7px;border:1px solid var(--line);border-radius:5px;
          cursor:pointer;user-select:none}
.rescheck.is-disabled{opacity:.35;cursor:not-allowed}
.nopoint{border:1px solid var(--warn);border-left-width:3px;border-radius:7px;
         background:rgba(210,153,34,.08);padding:9px 12px;margin:8px 0;
         font-size:12px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.nopoint button{margin-left:auto}

/* ── "Understood as" readback (Destination) ────────────── */
.destread{margin-top:8px;padding:9px 11px;border:1px solid var(--line);
          border-radius:7px;background:var(--panel2);font-size:12px}
.destread .dr-t{color:var(--muted);font-size:11px;text-transform:uppercase;
                letter-spacing:.7px;margin-bottom:5px}
.destread .dr-k{display:inline-block;width:86px;color:var(--muted)}
.destread .dr-v{color:var(--bright);font-family:ui-monospace,Consolas,monospace}
.destread .dr-ok{margin-top:7px;color:var(--ok)}
.destread .dr-warn{margin-top:7px;color:var(--warn);line-height:1.5}

/* ── Ingest pipeline (Ads › Ingest) ────────────────────── */
.ing-kpi{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:8px;margin-top:9px}
.ing-card{background:var(--panel2);border:1px solid var(--line);border-radius:7px;
          padding:9px 6px;text-align:center}
.ing-card .n{font-size:21px;font-weight:700;line-height:1.1}
.ing-card .l{font-size:10.5px;color:var(--muted);text-transform:uppercase;
             letter-spacing:.6px;margin-top:3px}
.ing-scan{margin-top:9px;padding:8px 11px;background:var(--panel2);
          border-radius:6px;font-size:12px}
.upwarn{margin-top:9px;padding:9px 12px;border:1px solid var(--crit);border-left-width:3px;
        border-radius:7px;background:rgba(248,81,73,.08);font-size:12px;line-height:1.5}
/* A held pipeline is a deliberate stop, not a crash — loud, but it reads as a
   decision the system made and can explain. */
.upheld{padding:10px 12px;border:1px solid var(--crit);border-left-width:3px;
        border-radius:7px;background:rgba(248,81,73,.10);font-size:12.5px;line-height:1.5}
.upunknown{padding:10px 12px;border:1px solid var(--line);border-left:3px solid var(--muted);
           border-radius:7px;background:var(--panel2);font-size:12.5px;line-height:1.5}
.uplog{font:11.5px ui-monospace,Consolas,monospace;max-height:44vh;overflow:auto;
       background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:8px 10px}
.uplog div{padding:1px 0}
.uplog div.bad{color:var(--crit)}

/* ── Status bar ───────────────────────────────────────────────────────────
   Always at the foot of every page, injected by app.js (see renderStatusBar).
   Fixed rather than in flow: an alert that scrolls off the bottom of a long
   inventory table is not a surface, and this exists because a 31-hour outage
   went unnoticed behind a bell nobody had reason to open. */
.ssb{position:fixed;left:0;right:0;bottom:0;z-index:60;
     display:flex;align-items:center;gap:7px;flex-wrap:nowrap;overflow-x:auto;
     padding:5px 11px;height:34px;box-sizing:border-box;
     background:var(--panel2,#161b22);border-top:1px solid var(--line,#30363d);
     font-size:11.5px;line-height:1}
.ssb.hidden{display:none}
/* The left edge carries the worst severity, so it reads without being read. */
.ssb.bar-ok{border-top-color:var(--ok,#3fb950)}
.ssb.bar-warn{border-top-color:var(--warn,#d29922);box-shadow:0 -1px 0 var(--warn,#d29922)}
.ssb.bar-crit{border-top-color:var(--crit,#f85149);box-shadow:0 -1px 0 var(--crit,#f85149)}

.ssb .ssb-chk{padding:1px 7px;border-radius:3px;white-space:nowrap;
              color:var(--muted,#8b949e);border:1px solid var(--line,#30363d)}
.ssb .ssb-chk.is-ok{color:var(--ok,#3fb950);border-color:rgba(63,185,80,.4)}
.ssb .ssb-chk.is-warn{color:var(--warn,#d29922);border-color:rgba(210,153,34,.5)}
.ssb .ssb-chk.is-crit,
.ssb .ssb-chk.is-unknown{color:var(--crit,#f85149);border-color:rgba(248,81,73,.55)}
.ssb .ssb-ok{color:var(--muted,#8b949e)}

.ssb .ssb-pill{display:inline-flex;align-items:center;gap:5px;white-space:nowrap;
               padding:1px 8px;border-radius:3px;text-decoration:none;
               border:1px solid transparent}
.ssb .ssb-pill.sev-crit{color:var(--crit,#f85149);background:rgba(248,81,73,.13);
                        border-color:rgba(248,81,73,.45)}
.ssb .ssb-pill.sev-warn{color:var(--warn,#d29922);background:rgba(210,153,34,.13);
                        border-color:rgba(210,153,34,.45)}
/* Acked stays VISIBLE, just quieter: "I know" is not "it stopped", and three
   acked alerts firing for two weeks is what taught us that. */
.ssb .ssb-pill.is-acked{opacity:.5}
.ssb .ssb-pill:hover{filter:brightness(1.25)}
.ssb .ssb-since{color:var(--muted,#8b949e);font-weight:400}
.ssb .ssb-flap{color:var(--bright,#e6edf3);font-size:10.5px}
.ssb .ssb-more{color:var(--muted,#8b949e);text-decoration:none;padding:1px 6px}
.ssb .ssb-more:hover{color:var(--bright,#e6edf3)}
