/* VICIswitch portal.
   Served from the Go binary on the carrier's own domain, so it carries no
   VICIfast branding at all — this is the carrier's product to their customers.

   The shape of the thing: a fixed sidebar, a sticky page header, and a content
   column that scrolls on its own. That is not a style choice. The pages here
   are long tables, and a reader who scrolls to row 400 must not have to scroll
   back up to reach the menu or to see which column they are in. */

/* ---------------------------------------------------------------- tokens -- */

:root {
  /* Neutrals with a slight cool bias toward the teal accent, so a grey beside
     the accent reads as chosen rather than as the browser default. */
  --paper:#fbfbfa; --sunk:#f3f4f2; --raise:#ffffff;
  --ink:#17191a; --muted:#5c6360; --faint:#8d9491;
  --rule:#e4e5e1; --rule-firm:#c8cac5;
  --accent:#155e63; --accent-soft:#e3eeef; --accent-ink:#0e4a4e;
  --good:#1a6b47; --warn:#8a5600; --bad:#9b2f30;
  --shadow: 0 1px 2px rgba(16,24,26,.05), 0 4px 14px rgba(16,24,26,.06);
  --shadow-lift: 0 8px 30px rgba(16,24,26,.14);

  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* One scale, used everywhere. Spacing that comes from a scale is why a
     redesign looks composed rather than assembled. */
  --s1:.25rem; --s2:.5rem; --s3:.75rem; --s4:1rem; --s5:1.5rem; --s6:2rem; --s7:3rem;
  --side-w:15rem;   /* the sidebar, open */
  --rail-w:3.25rem; /* the sidebar, collapsed to icons */
  --bar-h:3.5rem;
}

/* Dark, in three states rather than two.
   The switcher has a "system" setting that stamps nothing on the root element,
   so most people see the un-stamped document and only prefers-color-scheme
   separates light from dark. An explicit choice stamps data-theme, and has to
   win in BOTH directions — a light choice on a dark OS matters as much as the
   other way round, which is why the media query is guarded rather than plain.
   Every value is redefined as a token; nothing is styled inside these blocks,
   or a component would work in one state and not the others. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:#0e1112; --sunk:#161a1b; --raise:#181d1e;
    --ink:#e7eae9; --muted:#98a19e; --faint:#6d7573;
    --rule:#242a2b; --rule-firm:#3a4243;
    --accent:#5bb5bc; --accent-soft:#122427; --accent-ink:#8fd3d8;
    --good:#4eb187; --warn:#d3a353; --bad:#dd8183;
    --shadow: 0 1px 2px rgba(0,0,0,.4), 0 4px 14px rgba(0,0,0,.3);
    --shadow-lift: 0 8px 30px rgba(0,0,0,.55);
  }
}
:root[data-theme="dark"] {
  --paper:#0e1112; --sunk:#161a1b; --raise:#181d1e;
  --ink:#e7eae9; --muted:#98a19e; --faint:#6d7573;
  --rule:#242a2b; --rule-firm:#3a4243;
  --accent:#5bb5bc; --accent-soft:#122427; --accent-ink:#8fd3d8;
  --good:#4eb187; --warn:#d3a353; --bad:#dd8183;
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 4px 14px rgba(0,0,0,.3);
  --shadow-lift: 0 8px 30px rgba(0,0,0,.55);
}

* { box-sizing:border-box; }
/* The hidden attribute must win.
   [hidden] sets display:none from the UA stylesheet, at the specificity of an
   attribute selector — so ANY class rule that sets display beats it, silently.
   The command palette is .palette { display:flex }, and it therefore rendered
   open on every page for every user until a screenshot showed it. This is the
   one place !important is the correct tool: the attribute means hidden, and no
   layout rule is entitled to disagree. */
[hidden] { display:none !important; }
html, body { height:100%; }
body { margin:0; background:var(--paper); color:var(--ink); font-family:var(--sans);
       font-size:15px; line-height:1.55; -webkit-font-smoothing:antialiased; }
a { color:var(--accent); text-decoration:none; }
a:hover { text-decoration:underline; }
:focus-visible { outline:2px solid var(--accent); outline-offset:2px; border-radius:3px; }

/* A keyboard user's first Tab should reach the content, not the twenty-odd
   links in the sidebar. */
.skip { position:absolute; left:-999px; top:0; z-index:100; background:var(--accent);
        color:#fff; padding:var(--s2) var(--s4); }
.skip:focus { left:var(--s2); top:var(--s2); }

/* ------------------------------------------------------------- app frame -- */

/* min-height, not height.
 *
 * `height:100%` capped this grid at the viewport, so a long page overflowed it
 * rather than making it taller. That leaves the sticky sidebar below with a
 * containing block exactly one screen tall — no room to travel — so it
 * scrolled away with the content instead of staying put. min-height lets the
 * grid grow to the page, which is the range the sidebar sticks within. */
.app { display:grid; grid-template-columns:var(--side-w) minmax(0,1fr); min-height:100%; }
:root[data-rail="1"] .app { grid-template-columns:var(--rail-w) minmax(0,1fr); }

/* align-self:start matters as much as the sticky.
 *
 * A grid item stretches to its row by default, and a stretched item is as tall
 * as the whole page — so `top:0` is already satisfied and it never sticks.
 * Starting it keeps the box its own 100vh. */
.side { grid-column:1; align-self:start; display:flex; flex-direction:column; min-height:0;
        background:var(--sunk); border-right:1px solid var(--rule);
        position:sticky; top:0; height:100vh; }

.side-head { display:flex; align-items:center; gap:var(--s2);
             padding:var(--s3) var(--s3) var(--s2); }
.brand { display:flex; align-items:center; gap:var(--s2); min-width:0;
         font-weight:700; font-size:.95rem; color:var(--ink); letter-spacing:-.01em; }
.brand:hover { text-decoration:none; }
.brand .mark { color:var(--accent); font-size:1.15rem; line-height:1; flex:none; }
.brand-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.rail-toggle { margin-left:auto; flex:none; background:none; border:none; cursor:pointer;
               color:var(--faint); font-size:1rem; line-height:1; padding:var(--s1) var(--s2);
               border-radius:4px; }
.rail-toggle:hover { color:var(--ink); background:var(--rule); }

/* The go-to control. A button rather than a live input, because the palette is
   a modal and an inline field that opens a modal on focus is a trap for anyone
   tabbing through. */
.cmd-open { display:flex; align-items:center; gap:var(--s2); width:calc(100% - var(--s3) * 2);
            margin:0 var(--s3) var(--s3); padding:var(--s2) var(--s2);
            background:var(--paper); border:1px solid var(--rule-firm); border-radius:5px;
            color:var(--muted); font:inherit; font-size:.82rem; cursor:pointer; text-align:left; }
.cmd-open:hover { color:var(--ink); border-color:var(--accent); }
.cmd-open .cmd-icon { font-size:.95rem; flex:none; }
.cmd-open .cmd-label { flex:1; overflow:hidden; white-space:nowrap; }
kbd { font-family:var(--mono); font-size:.7rem; padding:.05rem .3rem; border-radius:3px;
      border:1px solid var(--rule-firm); background:var(--sunk); color:var(--muted); }

.side-nav { flex:1; min-height:0; overflow-y:auto; padding:0 var(--s2) var(--s4);
            scrollbar-width:thin; }
.side-nav .group { margin-top:var(--s4); }
.side-nav h2 { font-size:.66rem; text-transform:uppercase; letter-spacing:.09em;
               color:var(--faint); font-weight:700; margin:0 0 var(--s1); padding:0 var(--s2); }
.side-nav .item { display:flex; align-items:center; gap:var(--s2); padding:.36rem var(--s2);
                  border-radius:5px; color:var(--muted); font-size:.86rem; font-weight:500;
                  line-height:1.35; }
.side-nav .item:hover { background:var(--rule); color:var(--ink); text-decoration:none; }
.side-nav .item .lbl { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* The current page. A filled chip rather than a left border, because at this
   indentation a border competes with the group headings above it. */
.side-nav .item.on { background:var(--accent); color:#fff; font-weight:600; }
:root[data-theme="dark"] .side-nav .item.on,
:root:not([data-theme="light"]) .side-nav .item.on { color:#08191b; }
.side-nav .item.solo { font-size:.88rem; font-weight:600; color:var(--ink); }
.side-nav .item.solo.on { color:#fff; }
:root[data-theme="dark"] .side-nav .item.solo.on { color:#08191b; }
.side-nav .item .badge { flex:none; min-width:1.25rem; padding:0 .32rem; border-radius:999px;
                         background:var(--bad); color:#fff; font-size:.68rem; font-weight:700;
                         text-align:center; line-height:1.25rem; }
.side-nav .item.on .badge { background:#fff; color:var(--bad); }

.side-foot { flex:none; border-top:1px solid var(--rule); padding:var(--s3); font-size:.78rem; }
.whoami { display:flex; flex-direction:column; min-width:0; }
.whoami .name { color:var(--ink); font-weight:600; overflow:hidden;
                text-overflow:ellipsis; white-space:nowrap; }
.whoami .role { color:var(--faint); font-size:.72rem; }
.side-actions { display:flex; align-items:center; gap:var(--s2); margin-top:var(--s2);
                flex-wrap:wrap; }
.side-actions .out { color:var(--muted); }

/* The collapsed rail. Labels go, the active chip and the badge stay — those are
   the two things a glance is for. Hover on a link still shows its title
   attribute, which is why every item carries one. */
:root[data-rail="1"] .brand-name,
:root[data-rail="1"] .side-nav h2,
:root[data-rail="1"] .side-nav .item .lbl,
:root[data-rail="1"] .cmd-open .cmd-label,
:root[data-rail="1"] .cmd-open kbd,
:root[data-rail="1"] .whoami,
:root[data-rail="1"] .side-actions .out,
:root[data-rail="1"] .seeded { display:none; }
:root[data-rail="1"] .side-head { justify-content:center; padding:var(--s3) 0 var(--s2); }
:root[data-rail="1"] .rail-toggle { margin:0; }
:root[data-rail="1"] .rail-toggle::before { content:"›"; }
:root[data-rail="1"] .rail-toggle { font-size:0; }
:root[data-rail="1"] .rail-toggle::before { font-size:1rem; }
:root[data-rail="1"] .cmd-open { justify-content:center; margin:0 var(--s2) var(--s3);
                                 width:calc(100% - var(--s2) * 2); }
:root[data-rail="1"] .side-nav .group { margin-top:var(--s3); padding-top:var(--s3);
                                        border-top:1px solid var(--rule); }
:root[data-rail="1"] .side-nav .item { justify-content:center; }
/* The first letter stands in for the label. Supplied by the server as one
   character rather than clipped out of the title with a width, because clipping
   depends on the font and lands mid-glyph on the wrong one. */
:root[data-rail="1"] .side-nav .item::before {
  content:attr(data-initial); font-weight:700; font-size:.82rem;
  width:1.1rem; text-align:center;
}
:root[data-rail="1"] .side-foot { padding:var(--s2); text-align:center; }

/* ------------------------------------------------------------ page header -- */

.frame { grid-column:2; min-width:0; display:flex; flex-direction:column; }

.bar { position:sticky; top:0; z-index:20; display:flex; align-items:center; gap:var(--s4);
       /* The help popover is absolutely positioned against this. */
       min-height:var(--bar-h); padding:var(--s2) var(--s5);
       background:color-mix(in srgb, var(--paper) 86%, transparent);
       backdrop-filter:blur(8px); border-bottom:1px solid var(--rule); }
@supports not (backdrop-filter: blur(8px)) { .bar { background:var(--paper); } }
.menu-open { display:none; background:none; border:1px solid var(--rule-firm); border-radius:4px;
             color:var(--ink); font-size:1rem; line-height:1; padding:.3rem .55rem; cursor:pointer; }
.bar-title { min-width:0; flex:1; }
.bar-title h1 { font-size:1.18rem; letter-spacing:-.015em; margin:0; font-weight:650;
                overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.bar-title h1 .ctx { font-size:.74rem; font-weight:400; color:var(--faint);
                     margin-left:var(--s2); letter-spacing:0; }

/* The "what is this page for" control.
 *
 * Sits on the title because that is what it explains. It is hidden until the
 * script finds an explanation to put in it, so a page without one never shows
 * a button that opens an empty box. */
.help-toggle { margin-left:calc(var(--s2) * -1); flex:none; align-self:center;
               width:1.15rem; height:1.15rem; padding:0;
               background:none; border:1px solid var(--rule-firm); border-radius:50%;
               color:var(--faint); font-size:.7rem; font-weight:700; line-height:1;
               cursor:pointer; transition:color .12s, border-color .12s, background .12s; }
.help-toggle:hover { color:var(--ink); border-color:var(--ink); }
.help-toggle.on { background:var(--accent); border-color:var(--accent); color:#fff; }

/* Anchored to the header rather than to the button: the title can be a long
   breadcrumb, and a popover that follows it around lands off-screen on the
   pages with the longest names. */
.help-pop { position:absolute; z-index:30; top:calc(var(--bar-h) - var(--s2));
            right:var(--s5); left:auto; width:min(34rem, calc(100vw - var(--s5) * 2));
            background:var(--paper); border:1px solid var(--rule-firm); border-radius:8px;
            box-shadow:var(--shadow-lift); padding:var(--s4) var(--s4) var(--s2); }
.help-pop .sub { margin:0 0 var(--s3); max-width:none; }
.help-pop .sub:last-child { margin-bottom:var(--s2); }
@media (max-width:60rem) {
  .help-pop { right:var(--s4); width:calc(100vw - var(--s4) * 2); }
}
.crumbs { display:flex; align-items:center; gap:var(--s1); font-size:.72rem; color:var(--faint);
          margin-bottom:-.1rem; }
.crumbs a { color:var(--faint); }
.crumbs a:hover { color:var(--accent); }
.crumbs .sep { opacity:.55; }

/* The alert indicator in the header, beside the title rather than in the menu.
   The sidebar already carries the count; this is for the case where the
   sidebar is collapsed or off-screen, which on a phone is always. */
.bar-alerts { display:flex; align-items:center; gap:var(--s2); flex:none; padding:.22rem .55rem;
              border:1px solid var(--rule-firm); border-radius:999px; font-size:.74rem;
              color:var(--muted); }
.bar-alerts:hover { text-decoration:none; border-color:var(--accent); color:var(--ink); }
.bar-alerts .dot { width:.5rem; height:.5rem; border-radius:50%; background:var(--good); }
.bar-alerts.lit { border-color:var(--bad); color:var(--bad); }
.bar-alerts.lit .dot { background:var(--bad); }
.bar-alerts .badge { font-weight:700; font-variant-numeric:tabular-nums; }

/* 104rem, not 88.
   At 88rem (1408px) a 1080p screen wasted 240px: 1920 minus a 17rem sidebar
   leaves 1648px of room and the content stopped short of it. The cap still
   exists — on a 1440p or ultrawide monitor a table stretched to 3000px is
   unreadable, and prose is held much tighter than this by .sub and the 66ch
   rules — but it should not bind on the commonest screen there is. */
main { min-width:0; padding:var(--s5); max-width:104rem; }
body.plain main { max-width:82rem; margin:0 auto; padding:var(--s5) var(--s4) var(--s7); }

/* ---------------------------------------------------------------- type ---- */

h2 { font-size:1rem; margin:var(--s6) 0 var(--s2); font-weight:600; letter-spacing:-.008em; }
h2:first-child { margin-top:0; }
.since { font-size:.78rem; font-weight:400; color:var(--faint); margin-left:var(--s2); }
.sub { color:var(--muted); font-size:.88rem; margin:.1rem 0 var(--s5); max-width:40rem; }
.small { font-size:.76rem; }
.dim { color:var(--faint); }
.good { color:var(--good); } .warn { color:var(--warn); } .bad { color:var(--bad); font-weight:600; }
.muted { color:var(--faint); }

/* ---------------------------------------------------------------- panels -- */

/* A titled block of content. Most pages are a sequence of these, and giving
   them a real surface is what separates a dashboard from a document. */
.panel { background:var(--raise); border:1px solid var(--rule); border-radius:7px;
         box-shadow:var(--shadow); margin:0 0 var(--s5); overflow:hidden; }
.panel > header { display:flex; align-items:baseline; gap:var(--s3); flex-wrap:wrap;
                  padding:var(--s3) var(--s4); border-bottom:1px solid var(--rule);
                  background:var(--sunk); }
.panel > header h2 { margin:0; font-size:.9rem; }
.panel > header .note-inline { font-size:.76rem; color:var(--faint); }
.panel > header .spacer { flex:1; }
.panel-body { padding:var(--s4); }
.panel .scroll { margin:0; border:none; }

/* ------------------------------------------------------------------ kpis -- */

.kpis { display:grid; grid-template-columns:repeat(auto-fit,minmax(9.5rem,1fr));
        gap:1px; background:var(--rule); border:1px solid var(--rule); border-radius:7px;
        overflow:hidden; margin:0 0 var(--s5); box-shadow:var(--shadow); }
.kpi { background:var(--raise); padding:var(--s3) var(--s4); display:flex; flex-direction:column;
       gap:.1rem; min-width:0; }
.kpi .l { font-size:.68rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); }
.kpi .v { font-family:var(--mono); font-size:1.28rem; font-weight:600;
          font-variant-numeric:tabular-nums; letter-spacing:-.02em; }
.kpi .h { font-size:.73rem; color:var(--faint); }

/* --------------------------------------------------------------- tables --- */

/* Wide tables scroll inside their own box. They do not reflow into stacked
   cards: a rate table read as cards is unreadable, and the comparison between
   two rows is the entire reason the table exists. */
/* 84vh, not 76.
   On a page whose only content is the table — calls in progress, call records —
   76vh left the table scrolling inside itself while 150px of page sat empty
   below it. 84vh leaves room for the breadcrumb, the title and the padding
   above, and nothing else, which is the right amount. The rem cap is raised to
   match so it binds only on screens tall enough for it to matter. */
.scroll { overflow:auto; margin:0 0 var(--s5); border:1px solid var(--rule); border-radius:7px;
          background:var(--raise); box-shadow:var(--shadow); max-height:min(84vh, 64rem); }
table { border-collapse:separate; border-spacing:0; width:100%; font-size:.85rem; }
/* A column that should size to its content, not share the leftover width.
   width:1% is the standard trick: the browser gives such a column its minimum
   and hands the remainder to the columns without it.
   Without this, widening the page spreads the gaps instead of closing them — a
   five-column table on a wide screen puts 370px between a number and its
   neighbour, which is worse than the narrow layout it replaced. */
th.hug, td.hug { width:1%; white-space:nowrap; }

/* ------------------------------------------------------- carrier branding -- */

/* An uploaded logo, at whatever aspect ratio it happens to be.
   Bounded by height rather than width, because a header has a fixed height and
   a logo's width is whatever the design says. object-fit:contain so a square
   mark and a long wordmark both sit correctly rather than one being squashed. */
.brand-logo { height:1.5rem; width:auto; max-width:11rem; object-fit:contain;
              display:block; flex:none; }
.brand-logo + .brand-name { margin-left:var(--s2); }
/* On the sign-in page it can be larger — it is the only thing on the screen. */
.auth-logo { height:3rem; max-width:100%; margin:0 auto var(--s4); }

/* The colour swatch on the branding page. */
.swatch { display:inline-block; width:2.25rem; height:1.5rem; border-radius:4px;
          border:1px solid var(--rule-firm); vertical-align:middle; }
.swatch-row { display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
              margin:var(--s3) 0; }
.logo-preview { display:flex; align-items:center; gap:var(--s4); flex-wrap:wrap;
                padding:var(--s4); border:1px solid var(--rule); border-radius:7px;
                background:var(--paper); margin:var(--s3) 0; }
.logo-preview img { max-height:3rem; max-width:14rem; object-fit:contain; }
/* Sticky header. On a four-hundred-row table the column names are the first
   thing you lose and the thing you need most. */
th { position:sticky; top:0; z-index:2; text-align:left; font-size:.67rem; text-transform:uppercase;
     letter-spacing:.07em; color:var(--muted); font-weight:700; padding:.5rem .6rem;
     background:var(--sunk); border-bottom:1px solid var(--rule-firm); white-space:nowrap; }
td { padding:.44rem .6rem; border-bottom:1px solid var(--rule); vertical-align:top; }
tbody tr:last-child td { border-bottom:none; }
tbody tr:hover td { background:var(--accent-soft); }
/* The row the keyboard is on. Distinct from hover, because they are different
   facts and a reader using both at once must be able to tell them apart. */
tbody tr.cursor td { background:var(--accent-soft);
                     box-shadow:inset 3px 0 0 var(--accent); }
tr.off { opacity:.55; }
.n { text-align:right; }
.m { font-family:var(--mono); font-size:.8rem; font-variant-numeric:tabular-nums; }
.nowrap { white-space:nowrap; }
.empty { text-align:center; color:var(--faint); padding:var(--s7) var(--s4); }
tfoot td { position:sticky; bottom:0; z-index:2; }
tfoot tr.total td { border-top:2px solid var(--rule-firm); background:var(--sunk); font-weight:600; }

/* ------------------------------------------------- pagination + row size -- */

.tablefoot { display:flex; align-items:center; gap:var(--s4); flex-wrap:wrap;
             margin:calc(var(--s5) * -1 + var(--s3)) 0 var(--s5); font-size:.8rem; }
.rows { display:flex; align-items:center; gap:.2rem; }
.rows-l { color:var(--faint); font-size:.72rem; text-transform:uppercase;
          letter-spacing:.07em; margin-right:.3rem; }
.rows a, .rows span.on { padding:.12rem .42rem; border-radius:4px; font-variant-numeric:tabular-nums;
                         color:var(--muted); }
.rows a:hover { background:var(--sunk); color:var(--ink); text-decoration:none; }
.rows span.on { background:var(--accent); color:#fff; font-weight:600; }
:root[data-theme="dark"] .rows span.on { color:#08191b; }

.pager { display:flex; align-items:center; gap:var(--s4); margin-left:auto; }
.pager-count { color:var(--muted); font-variant-numeric:tabular-nums; }
.pager-count .dim { margin-left:.35rem; }
.pager-nav { display:flex; gap:var(--s2); }
.pager .btn { padding:.22rem .6rem; font-size:.78rem; }
.pager .btn.off { color:var(--faint); border-color:var(--rule); cursor:default; }

/* ----------------------------------------------------------------- chrome -- */

.tag { display:inline-block; font-size:.7rem; padding:.05rem .42rem; border-radius:3px;
       background:var(--sunk); border:1px solid var(--rule-firm); color:var(--muted);
       white-space:nowrap; }
.tag.ok   { color:var(--good); border-color:currentColor; background:transparent; }
.tag.warn { color:var(--warn); border-color:currentColor; background:transparent; }
.tag.info { color:var(--accent); border-color:currentColor; background:transparent; }
.tag.bad  { color:var(--bad); border-color:currentColor; background:transparent; }

.note { padding:var(--s3) var(--s4); margin:0 0 var(--s5); font-size:.86rem;
        border:1px solid var(--rule); border-left:3px solid var(--rule-firm);
        border-radius:5px; background:var(--raise); }
.note.ok   { border-left-color:var(--good); }
.note.bad  { border-left-color:var(--bad); }
.note.warn { border-left-color:var(--warn); }

form.filters, form.inline { display:flex; gap:var(--s2); align-items:center; flex-wrap:wrap;
                            margin:0 0 var(--s4); }
label.field { display:flex; flex-direction:column; gap:.1rem; font-size:.7rem;
              text-transform:uppercase; letter-spacing:.07em; color:var(--muted); }
input, select, textarea { font:inherit; font-size:.86rem; padding:.36rem .55rem;
                border:1px solid var(--rule-firm); border-radius:5px;
                background:var(--paper); color:var(--ink); }
input:focus, select:focus, textarea:focus { outline:2px solid var(--accent); outline-offset:1px; }
button { font:inherit; font-size:.84rem; font-weight:500; padding:.36rem .85rem; border-radius:5px;
         border:1px solid var(--accent); background:var(--accent); color:#fff; cursor:pointer; }
button:hover { filter:brightness(1.08); }
button.link { background:none; border:none; color:var(--accent); padding:0; text-decoration:underline; }
button.danger { background:var(--bad); border-color:var(--bad); }
button.quiet { background:none; border:1px solid var(--rule-firm); color:var(--ink);
               font-size:.78rem; padding:.2rem .6rem; }
button.quiet:hover { background:var(--sunk); filter:none; }
.btn { display:inline-block; padding:.36rem .85rem; border:1px solid var(--rule-firm);
       border-radius:5px; font-size:.84rem; color:var(--ink); background:var(--raise); }
.btn:hover { text-decoration:none; border-color:var(--accent); color:var(--accent); }
.clear { font-size:.8rem; color:var(--muted); }

button.theme { background:none; border:1px solid var(--rule-firm); border-radius:4px;
  color:var(--muted); font:inherit; font-size:.72rem; padding:.1rem .45rem;
  cursor:pointer; line-height:1.5; white-space:nowrap; }
button.theme:hover { color:var(--ink); background:var(--rule); filter:none; }

.pills { display:flex; gap:.25rem; margin:0 0 var(--s4); flex-wrap:wrap; }
.pills a { padding:.26rem .7rem; font-size:.8rem; border:1px solid var(--rule-firm);
           border-radius:999px; color:var(--muted); background:var(--raise); }
.pills a:hover { text-decoration:none; border-color:var(--accent); color:var(--accent); }
.pills a.on { background:var(--accent); border-color:var(--accent); color:#fff; }
:root[data-theme="dark"] .pills a.on { color:#08191b; }
.pills .grow { flex:1; }

.cols { display:grid; grid-template-columns:1fr 1fr; gap:var(--s6); }
@media (max-width:66rem) { .cols { grid-template-columns:1fr; } }
.deck { margin-bottom:var(--s6); }

/* ------------------------------------------------------------------ auth -- */

.auth { max-width:22rem; margin:5rem auto; }
.auth h1 { font-size:1.5rem; margin:0 0 .2rem; letter-spacing:-.015em; }
.auth form { display:flex; flex-direction:column; gap:.3rem; margin-top:var(--s5); }
.auth label { font-size:.76rem; color:var(--muted); margin-top:var(--s2); }
.auth button { margin-top:var(--s4); padding:.5rem; }
.fine { font-size:.76rem; color:var(--faint); margin-top:var(--s4); }
.pad { padding:var(--s7) 0; text-align:center; }
.big { font-size:3rem; font-family:var(--mono); color:var(--faint); }

/* ---------------------------------------------------------------- charts -- */

.charts { display:grid; grid-template-columns:1fr 1fr; gap:var(--s5); margin:0 0 var(--s6); }
@media (max-width:76rem) { .charts { grid-template-columns:1fr; } }
.chart { width:100%; min-height:120px; margin-top:var(--s2); }
.chart .empty { padding:var(--s7) 0; }
.uplot, .u-wrap { font-family:var(--sans) !important; }
.u-legend { font-size:.78rem; color:var(--muted); }
.u-legend .u-marker { width:10px; height:10px; }
.u-select { background:rgba(21,94,99,.12); }

/* ---------------------------------------------------------------- alerts -- */

/* Cards rather than table rows: each one carries a sentence of explanation and
   an action, and a table that has to hold a paragraph is a table fighting its
   own format. The severity stripe does the scanning so the eye finds the
   critical ones without reading any of them. */
.alerts { display:flex; flex-direction:column; gap:var(--s3); margin:0 0 var(--s6); }
.alert { border:1px solid var(--rule); border-left:4px solid var(--muted);
  border-radius:7px; background:var(--raise); padding:var(--s3) var(--s4);
  box-shadow:var(--shadow); }
.alert.critical { border-left-color:var(--bad); }
.alert.warning  { border-left-color:var(--warn); }
.alert.info     { border-left-color:var(--muted); }
/* Acknowledged alerts stay on the page but stop competing for attention: the
   person who needed to see it has, and it is still true. */
.alert.acked { opacity:.6; }
.alert header { display:flex; align-items:baseline; gap:var(--s2); flex-wrap:wrap; }
.alert header h2 { font-size:.93rem; margin:0; font-weight:600; }
.alert .sev { font-size:.66rem; text-transform:uppercase; letter-spacing:.06em;
  font-weight:700; color:var(--muted); }
.alert.critical .sev { color:var(--bad); }
.alert.warning .sev  { color:var(--warn); }
.alert .age { font-size:.75rem; color:var(--muted); white-space:nowrap; margin-left:auto; }
.alert .val { font-size:.85rem; margin:.4rem 0 0; font-weight:600; }
.alert .detail { font-size:.82rem; color:var(--muted); margin:.35rem 0 var(--s2);
  line-height:1.5; max-width:66ch; }
.alert footer { display:flex; align-items:center; gap:var(--s3); font-size:.78rem; }
.alert footer form { margin:0; }

/* Period-over-period moves. Deliberately quieter than the figure they sit
   beside: the value is the fact, the change is the context, and a badge that
   shouts louder than the number it qualifies inverts that. */
.chg { font-size:.74rem; font-weight:600; letter-spacing:.01em; }
.chg.good { color:var(--good); }
.chg.bad { color:var(--bad); }
.chg.warn { color:var(--warn); }
.chg.muted, .chg.none { color:var(--muted); font-weight:500; }
.kpi .chg { display:block; margin-top:.1rem; }

/* The per-channel economics panel reads as a worked calculation rather than a
   data table: a narrow figure column with the reasoning beside it, so the
   arithmetic can be followed down the page instead of scanned across. */
table.econ td:first-child { width:16rem; }
table.econ td:nth-child(2) { width:7rem; text-align:right; font-weight:600; }
table.econ td:last-child { font-size:.8rem; line-height:1.45; color:var(--muted); }
table.econ tr.rule td { border-top:1px solid var(--rule-firm); }
table.econ tr.total td { border-top:2px solid var(--rule-firm); background:var(--sunk); }
@media (max-width:52rem) {
  table.econ td:first-child, table.econ td:nth-child(2) { width:auto; }
  table.econ td:last-child { display:none; }
}

details.upload { margin:var(--s3) var(--s4) var(--s4); }
details.upload summary { cursor:pointer; font-size:.84rem; color:var(--accent); }
details.upload textarea { width:100%; font-family:var(--mono); font-size:.78rem;
  padding:var(--s2); margin-top:var(--s2); }

/* The generated-data marker. Operator chrome only, and deliberately quiet: it
   is there so the person driving has been straight without having to remember
   to be, not to argue with every number on the page. */
.seeded { display:inline-block; font-size:.66rem; text-transform:uppercase; letter-spacing:.06em;
  font-weight:700; color:var(--warn); border:1px solid var(--warn);
  border-radius:3px; padding:.05rem .35rem; margin-bottom:var(--s2); opacity:.75; }

/* The signalling trace from a test call. Monospace and tight, because it is
   read as a sequence of events rather than as prose, and the arrows only line
   up in a fixed-width font. */
pre.trace { background:var(--sunk); border:1px solid var(--rule); border-radius:5px;
  padding:var(--s3) var(--s4); overflow-x:auto; font-family:var(--mono); font-size:.8rem;
  line-height:1.55; margin:var(--s2) 0 var(--s4); }

/* ---------------------------------------------------------- filter bar -- */

/* Two rows, not one wrapping row.
   Eleven controls on one line wrap into a ragged block where nothing lines up
   and the control you want is wherever the previous label happened to end.
   Grouped instead: when and who on the first row, what and how on the second. */
.filterbar { display:flex; flex-direction:column; gap:var(--s2);
             background:var(--raise); border:1px solid var(--rule);
             border-radius:7px; box-shadow:var(--shadow);
             padding:var(--s3) var(--s4); margin:0 0 var(--s4); }
.filter-row { display:flex; align-items:flex-end; gap:var(--s3); flex-wrap:wrap; }
.filter-row .field { min-width:0; }
.filter-go { display:flex; align-items:center; gap:var(--s3); margin-left:auto; }
@media (max-width:60rem) {
  .filter-go { margin-left:0; width:100%; }
}

.viewacts { display:flex; align-items:flex-end; gap:var(--s4); flex-wrap:wrap; }
.viewacts form { margin:0; }

/* --------------------------------------------------------------- signals -- */

.signals { display:flex; flex-direction:column; gap:1px; background:var(--rule);
           border:1px solid var(--rule); border-radius:6px; overflow:hidden; }
.signal { display:flex; align-items:baseline; gap:var(--s4); flex-wrap:wrap;
          padding:var(--s3) var(--s4); background:var(--raise);
          border-left:3px solid transparent; }
/* The stripe is the scanning affordance: the eye finds the red rows without
   reading any of them, which is the only reason a panel like this is faster
   than the report that produced it. */
.signal.bad  { border-left-color:var(--bad); }
.signal.warn { border-left-color:var(--warn); }
.signal.good { border-left-color:var(--good); }
.signal-main { flex:1; min-width:14rem; display:flex; flex-direction:column; gap:.1rem; }
.signal-label { font-size:.88rem; font-weight:600; }
.signal-note { font-size:.76rem; color:var(--muted); line-height:1.45; max-width:70ch; }
.signal-figs { display:flex; align-items:baseline; gap:var(--s3); flex:none;
               font-variant-numeric:tabular-nums; }
.signal-value { font-family:var(--mono); font-size:.92rem; font-weight:600; }
.signal-was { font-size:.76rem; color:var(--faint); }
@media (max-width:60rem) {
  .signal-figs { width:100%; }
}

/* -------------------------------------------------------- report builder -- */

.report-form { display:flex; flex-direction:column; gap:var(--s4); margin:0; }
.report-form fieldset { border:none; padding:0; margin:0; min-width:0; }
.report-form legend { font-size:.68rem; text-transform:uppercase; letter-spacing:.08em;
                      color:var(--muted); font-weight:700; padding:0; margin-bottom:var(--s2); }
/* A grid, not a row of wrapping labels: twelve measures on one line wrap into
   a ragged block where nothing lines up and the checkbox you want is wherever
   the previous label happened to end. */
.checks { display:grid; grid-template-columns:repeat(auto-fill,minmax(11rem,1fr));
          gap:.2rem var(--s3); }
.checks label { display:flex; align-items:center; gap:var(--s2); font-size:.84rem;
                padding:.12rem 0; cursor:pointer; }
.checks input { margin:0; flex:none; }
/* A hint marker rather than the text. Twelve descriptions on the page would be
   a page of prose; the one that matters is read on hover, and the two answer
   rates are named apart so neither NEEDS the hint to be safe. */
.checks .q { font-size:.62rem; color:var(--faint); border:1px solid var(--rule-firm);
             border-radius:50%; width:.95rem; height:.95rem; display:inline-flex;
             align-items:center; justify-content:center; flex:none; }
.report-go { display:flex; align-items:flex-end; gap:var(--s3); flex-wrap:wrap; }

/* --------------------------------------------------------------- exports -- */

.exports { display:flex; flex-direction:column; gap:var(--s3); margin:0 0 var(--s6); }
.job { display:flex; align-items:flex-start; gap:var(--s4); flex-wrap:wrap;
       border:1px solid var(--rule); border-left:4px solid var(--rule-firm);
       border-radius:7px; background:var(--raise); padding:var(--s3) var(--s4);
       box-shadow:var(--shadow); }
.job.running, .job.queued { border-left-color:var(--accent); }
.job.done    { border-left-color:var(--good); }
.job.failed  { border-left-color:var(--bad); }
/* An expired export is still a record of what was sent, so it stays legible —
   just quiet enough not to compete with the ones that have a file. */
.job.expired { border-left-color:var(--rule-firm); opacity:.62; }
.job-main { flex:1; min-width:16rem; }
.job-main h3 { font-size:.94rem; margin:0 0 .1rem; font-weight:600; }
.job-main h3 .range { font-weight:400; color:var(--muted); margin-left:var(--s2); }
.job-meta { font-size:.78rem; color:var(--muted); margin:.1rem 0 0; }
.job-err { font-size:.8rem; color:var(--bad); margin:.3rem 0 0; font-family:var(--mono); }
.job-act { display:flex; align-items:center; gap:var(--s2); flex:none; }
.job-act form { margin:0; }
.btn.primary { background:var(--accent); border-color:var(--accent); color:#fff; }
:root[data-theme="dark"] .btn.primary { color:#08191b; }
.btn.primary:hover { color:#fff; filter:brightness(1.08); }
:root[data-theme="dark"] .btn.primary:hover { color:#08191b; }

/* The progress bar. Against the planner's estimate, so it is honest about
   being approximate — it stops at 99% until the row that finishes the job
   actually lands, because a bar that sits at 100% for thirty seconds is worse
   than one that sits at 99%.

   Named .progress and not .bar. .bar is the sticky page header, which is on
   every page in the portal — so a five-line progress-bar rule collapsed the
   header to four pixels and painted its breadcrumbs and badge in accent green,
   everywhere, from the moment it was added. Class names in one flat stylesheet
   are one namespace; a generic one taken twice is not a specificity problem,
   it is a collision. */
.progress { height:.42rem; border-radius:999px; background:var(--rule); overflow:hidden;
            margin:var(--s2) 0 .2rem; max-width:30rem; }
.progress span { display:block; height:100%; background:var(--accent);
                 transition:width .3s ease-out; }
@media (prefers-reduced-motion: reduce) { .progress span { transition:none; } }

/* -------------------------------------------------------- command palette -- */

.scrim { position:fixed; inset:0; background:rgba(10,14,15,.45); z-index:40; }
.palette { position:fixed; inset:0; z-index:50; display:flex; justify-content:center;
           align-items:flex-start; padding:12vh var(--s4) var(--s4); pointer-events:none; }
.palette-box { pointer-events:auto; width:min(34rem,100%); background:var(--raise);
               border:1px solid var(--rule-firm); border-radius:9px; box-shadow:var(--shadow-lift);
               overflow:hidden; display:flex; flex-direction:column; max-height:70vh; }
.palette-box input { border:none; border-bottom:1px solid var(--rule); border-radius:0;
                     padding:var(--s3) var(--s4); font-size:.95rem; background:var(--raise); }
.palette-box input:focus { outline:none; }
.palette-box ul { list-style:none; margin:0; padding:var(--s1) 0; overflow-y:auto; flex:1; }
.palette-box li { display:grid; grid-template-columns:auto 1fr; gap:0 var(--s2);
                  padding:.34rem var(--s4); cursor:pointer; align-items:baseline; }
.palette-box li[hidden] { display:none; }
.palette-box li.on { background:var(--accent-soft); }
.p-label { font-size:.88rem; font-weight:600; }
.p-group { font-size:.68rem; text-transform:uppercase; letter-spacing:.07em; color:var(--faint);
           justify-self:start; }
.p-hint { grid-column:1 / -1; font-size:.76rem; color:var(--muted); }
.palette-none { padding:var(--s4); margin:0; color:var(--faint); font-size:.84rem;
                text-align:center; }

/* ------------------------------------------------------------------ phone -- */

/* The sidebar becomes a drawer. Live, Alerts and Business are meant to be
   READ on a phone — a carrier checks whether anything is on fire from a train
   — so those pages have to be usable at this width. Nothing here is meant to
   be edited on a phone, and no attempt is made to pretend otherwise. */
@media (max-width:60rem) {
  .app { grid-template-columns:minmax(0,1fr); }
  :root[data-rail="1"] .app { grid-template-columns:minmax(0,1fr); }
  .side { position:fixed; z-index:45; left:0; top:0; width:min(17rem,84vw);
          transform:translateX(-101%); transition:transform .16s ease-out;
          box-shadow:var(--shadow-lift); }
  body.drawer .side { transform:none; }
  /* The rail collapse is a desktop affordance; on a phone the drawer is
     already the collapsed state. */
  :root[data-rail="1"] .brand-name,
  :root[data-rail="1"] .side-nav h2,
  :root[data-rail="1"] .side-nav .item .lbl,
  :root[data-rail="1"] .cmd-open .cmd-label,
  :root[data-rail="1"] .whoami,
  :root[data-rail="1"] .side-actions .out { display:revert; }
  :root[data-rail="1"] .side-nav .item::before { content:none; }
  :root[data-rail="1"] .side-nav .item,
  :root[data-rail="1"] .cmd-open { justify-content:flex-start; }
  .rail-toggle { display:none; }
  .menu-open { display:block; }
  .bar { padding:var(--s2) var(--s4); gap:var(--s3); }
  .bar-title h1 { font-size:1.05rem; }
  main { padding:var(--s4); }
  .kpis { grid-template-columns:repeat(auto-fit,minmax(7.5rem,1fr)); }
  .kpi { padding:var(--s2) var(--s3); }
  .kpi .v { font-size:1.1rem; }
  .scroll { max-height:none; }
  .palette { padding:6vh var(--s3) var(--s3); }
}

@media (prefers-reduced-motion: reduce) {
  .side { transition:none; }
}

/* ------------------------------------------------------------------ print -- */

/* Mostly for statements, which are the one thing here that leaves the screen:
   a wholesale customer queries an invoice by printing it and marking it up, and
   a page that prints its navigation and drops its table borders is a page that
   gets retyped into a spreadsheet instead.

   Everything interactive goes, because none of it means anything on paper. The
   type goes to a serif at a print size, tables get their rules back, and rows
   are told not to break across a page — a destination whose figures land on two
   sheets is the row that gets disputed. */
@media print {
  .side, .bar, .pills, .palette, .scrim, .skip, form, .chart, .charts,
  button, .seeded, .tablefoot, footer .link { display:none !important; }
  .app { display:block; }
  body { background:#fff; color:#000; font-family:Georgia, "Times New Roman", serif;
         font-size:10.5pt; }
  main { max-width:none; padding:0; }
  a { color:#000; text-decoration:none; }
  h1 { font-size:16pt; } h2 { font-size:12pt; margin-top:1.2em; }
  .sub, .note { color:#333; font-size:9pt; }
  .note { background:none; border:none; border-left:2px solid #999; }
  .panel { border:none; box-shadow:none; border-radius:0; }
  .panel > header { background:none; border-bottom:1px solid #000; padding:0 0 .2em; }
  .panel-body { padding:0; }
  .scroll { overflow:visible; border:none; box-shadow:none; max-height:none; border-radius:0; }
  table { border-top:1.5pt solid #000; border-bottom:1.5pt solid #000; font-size:9.5pt; }
  th { position:static; background:none; border-bottom:.5pt solid #000; color:#000; }
  td { border-bottom:.25pt solid #ccc; }
  tfoot td { position:static; }
  tr { page-break-inside:avoid; }
  thead { display:table-header-group; }
  tfoot tr.total td { border-top:1pt solid #000; background:none; }
  /* KPI tiles become a single line of figures rather than a grid of boxes. */
  .kpis { display:block; border:none; background:none; margin:.6em 0 1em;
          box-shadow:none; border-radius:0; }
  .kpi { display:inline-block; border:none; padding:0 1.6em 0 0; background:none; }
  .kpi .v { font-family:inherit; font-size:12pt; }
  .m { font-family:inherit; }
}

/* ------------------------------------------------- number manipulation -- */
/*
   Scoped under .testresult rather than borrowing .big, which is already the
   3rem numeral on empty-state panels. A shared class that means two sizes is
   how the page header ended up fighting the chart bars.
*/

.testresult { margin-top:var(--s4); padding-top:var(--s4); border-top:1px solid var(--rule); }
.testline { display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap; }
.testline code { font-size:1.15rem; letter-spacing:.01em; }
.testline .arrow { color:var(--faint); font-size:1.1rem; }
/* The result is the answer to the question, so it is the one thing that
   carries colour — and only when it actually differs from what went in. */
.testline code.changed { color:var(--accent-ink); background:var(--accent-soft);
  padding:.1rem .4rem; border-radius:3px; }

table.steps { margin-top:var(--s4); width:100%; }
table.steps td, table.steps th { padding:.3rem .6rem; font-size:.82rem; }

/* A rule that is switched off, or a step that did not fire, still has to be
   readable — it is usually the row someone is looking for. Dimmed, not hidden,
   and not so dim it cannot be read. */
tr.dimrow td { opacity:.55; }

table.tight { width:100%; margin:var(--s3) 0; }
table.tight td, table.tight th { padding:.3rem .6rem; }

label.check { display:flex; align-items:center; gap:.4rem; font-size:.8rem;
  color:var(--muted); white-space:nowrap; }
label.check input { margin:0; }

.unwatched { margin-top:var(--s2); font-size:.82rem; line-height:1.45; }
.unwatched b { color:var(--ink); }

/* One letter, quiet. An attestation is context on a call, not a headline —
   and A is the common case on a configured box, so it must not shout. */
.attest { display:inline-block; min-width:1.1em; text-align:center;
  font-size:.68rem; font-weight:600; padding:.05rem .25rem; border-radius:2px;
  background:var(--sunk); color:var(--muted); vertical-align:.08em; }

/* A paste area inside a filter row. The row is flex, so a textarea needs to be
   told to take the width it wants rather than the width of a text input. */
.field.wide { flex: 1 1 22rem; min-width: 18rem; }
.field.wide textarea { width: 100%; font-family: var(--mono); font-size: .8rem;
  padding: .35rem .5rem; border: 1px solid var(--rule-firm); border-radius: 3px;
  background: var(--raise); color: var(--ink); resize: vertical; }

/* ------------------------------------------------------- the SIP ladder -- */
/*
   A sequence diagram as a table, because that is what it is: ordered events
   with a direction. A canvas drawing would look more like the textbook picture
   and would not be selectable, searchable, or readable on a phone.
*/
table.ladder td { vertical-align: top; padding: .3rem .6rem; }
.arrowcell { color: var(--faint); font-size: 1.1rem; text-align: center; width: 2rem; }

/* The raw message, collapsed. It is the thing you eventually need and never
   the thing you need first. */
.rawmsg { margin-top: .25rem; }
.rawmsg summary { cursor: pointer; }
.rawmsg pre { margin: .35rem 0 0; padding: .5rem .6rem; background: var(--sunk);
  border-radius: 3px; font-size: .74rem; line-height: 1.45; overflow-x: auto;
  white-space: pre-wrap; word-break: break-word; max-height: 22rem; }

/* ------------------------------------------------- connection details -- */

/* Config blocks a customer pastes into their own switch. Collapsed by default:
   three of them open at once would push everything else on the page below the
   fold, and most visits to this page are not an onboarding. */
.cfg { border:1px solid var(--rule); border-radius:6px; margin:var(--s2) 0; background:var(--sunk); }
.cfg > summary { padding:var(--s2) var(--s3); cursor:pointer; font-size:.85rem;
                 font-weight:600; color:var(--ink); list-style:none; }
.cfg > summary::-webkit-details-marker { display:none; }
.cfg > summary::before { content:"▸"; display:inline-block; width:1rem; color:var(--faint); }
.cfg[open] > summary::before { content:"▾"; }
.cfg > summary:hover { background:var(--rule); }
.cfgblock { margin:0; padding:var(--s3); border-top:1px solid var(--rule);
            background:var(--paper); overflow-x:auto;
            font-family:var(--mono); font-size:.76rem; line-height:1.5;
            white-space:pre; -moz-tab-size:2; tab-size:2; }

/* ------------------------------------------------------- fraud presets -- */

/* Named patterns rather than a form of statistics. Cards, because the choice
   being made is "which of these shapes do I care about" and that is a read
   before it is an input. */
.presets { display:grid; gap:var(--s3); margin:var(--s3) 0 var(--s5);
           grid-template-columns:repeat(auto-fill, minmax(21rem, 1fr)); }
.preset { display:flex; flex-direction:column; gap:var(--s2);
          border:1px solid var(--rule); border-radius:8px; padding:var(--s4);
          background:var(--sunk); }
.preset h3 { margin:0; font-size:.95rem; font-weight:650; letter-spacing:-.01em; }
.preset .catches { margin:0; font-size:.84rem; color:var(--muted); flex:1; }
.preset .why > summary { cursor:pointer; font-size:.76rem; color:var(--faint); }
.preset .why > summary:hover { color:var(--ink); }
.preset .why p { margin:var(--s2) 0 0; }
.preset-do { display:flex; flex-wrap:wrap; align-items:flex-end; gap:var(--s2);
             padding-top:var(--s2); border-top:1px solid var(--rule); }
.preset-do .field { margin:0; }
.preset-do button { margin-left:auto; }

/* The manual form, kept but folded away. */
.tune { border:1px solid var(--rule); border-radius:8px; margin:var(--s4) 0; }
.tune > summary { padding:var(--s3) var(--s4); cursor:pointer; font-weight:600;
                  font-size:.88rem; }
.tune > summary:hover { background:var(--sunk); }

/* -------------------------------------------------------------- gauges -- */

.gauges { display:grid; gap:var(--s3); margin:var(--s3) 0 var(--s5);
          grid-template-columns:repeat(auto-fit, minmax(11rem, 1fr)); }
.gauge { position:relative; display:flex; flex-direction:column; align-items:center;
         border:1px solid var(--rule); border-radius:8px; padding:var(--s3) var(--s2) var(--s2);
         background:var(--sunk); }
.gauge-svg { width:100%; max-width:9rem; height:auto; overflow:visible; }
.g-track { fill:none; stroke:var(--rule-firm); stroke-width:9; stroke-linecap:round; }
.g-fill  { fill:none; stroke:var(--accent); stroke-width:9; stroke-linecap:round;
           transition:d .4s ease, stroke .2s; }
.gauge.warn .g-fill { stroke:var(--warn); }
.gauge.over .g-fill { stroke:var(--bad); }
.gauge.none .g-fill { stroke:var(--faint); }

/* The reading sits inside the dial's opening. */
.g-read { position:absolute; top:54%; left:0; right:0; text-align:center;
          font-variant-numeric:tabular-nums; }
.g-read b { font-size:1.5rem; font-weight:650; letter-spacing:-.02em; }
.g-of { font-size:.78rem; color:var(--faint); }
.g-label { margin-top:var(--s2); font-size:.78rem; color:var(--muted);
           text-transform:uppercase; letter-spacing:.07em; }

/* An unsold ceiling reads as no ceiling rather than as zero. */
.gauge.none .g-of::after { content:" unsold"; }

/* ---------------------------------------------------------------- tabs -- */

.tabstrip { display:flex; gap:var(--s1); flex-wrap:wrap;
            border-bottom:1px solid var(--rule); margin:var(--s5) 0 var(--s4); }
.tabstrip button { background:none; border:none; cursor:pointer;
                   padding:var(--s2) var(--s3); margin-bottom:-1px;
                   font:inherit; font-size:.88rem; font-weight:600;
                   color:var(--faint); border-bottom:2px solid transparent; }
.tabstrip button:hover { color:var(--ink); }
.tabstrip button.on { color:var(--ink); border-bottom-color:var(--accent); }
.tabs > [data-panel] > h2:first-child { margin-top:0; }

/* ------------------------------------------------ hour-of-day histogram -- */

/* When an account calls, in its own timezone. Bars rather than a line chart:
   24 discrete buckets are a histogram, and a line between them implies a
   continuity that hours of the day do not have. */
.hours { display:grid; grid-template-columns:repeat(24, 1fr); gap:2px;
         align-items:end; height:7rem; margin:var(--s3) 0 var(--s2); }
.hourbar { display:flex; flex-direction:column; justify-content:flex-end;
           height:100%; min-width:0; }
.hb-fill { background:var(--accent); border-radius:2px 2px 0 0; min-height:0; }
.hourbar:hover .hb-fill { background:var(--good); }
.hb-lab { font-size:.6rem; color:var(--faint); text-align:center;
          font-variant-numeric:tabular-nums; padding-top:2px; }

/* A count on a tab, for a panel that has something waiting in it. */
.pip { display:inline-block; min-width:1.1rem; padding:0 .28rem; border-radius:999px;
       background:var(--rule-firm); color:var(--ink);
       font-size:.66rem; font-weight:700; line-height:1.25rem; text-align:center;
       vertical-align:middle; }
.tabstrip button.on .pip { background:var(--accent); color:#fff; }

/* ------------------------------------------------------ read by a model -- */

/* Visually distinct from everything the switch measured itself. This is a
   reading of evidence, not evidence, and the page should not let the two be
   confused at a glance. */
.panel.ai { border-left:3px solid var(--accent); }
.explain { margin:0 0 var(--s3); font-size:.95rem; line-height:1.6; white-space:pre-wrap; }
.explain-ask { display:flex; align-items:center; gap:var(--s3); flex-wrap:wrap;
               margin:var(--s3) 0; }
.explain-ask .small { max-width:44rem; }

/* ----------------------------------------------------- setup checklist -- */

.panel.setup { border-left:3px solid var(--warn); }
.progress { height:5px; border-radius:3px; background:var(--rule-firm);
            overflow:hidden; margin:0 0 var(--s4); }
.progress-fill { height:100%; background:var(--accent); transition:width .3s ease; }

.checklist { list-style:none; margin:0; padding:0; display:flex;
             flex-direction:column; gap:var(--s2); }
.checklist li { display:flex; gap:var(--s2); align-items:flex-start; }
.checklist .mark { flex:none; width:1.1rem; text-align:center; color:var(--faint);
                   font-weight:700; line-height:1.5; }
.checklist li.done .mark { color:var(--good); }
/* A satisfied step recedes. The list is read for what is still outstanding,
   and anything already done competing for attention makes that harder. */
.checklist li.done > div { color:var(--faint); }
.checklist .small { max-width:52rem; margin-top:.1rem; }
