/* ══════════════════════════════════════════════════════════════
   PROPOSED panel component layer — NOT LOADED.
   ══════════════════════════════════════════════════════════════
   This file is a sketch, not the shipping style layer. Both panels
   inject their own complete stylesheet at runtime — panel-client.js
   builds #cc-style, panel-admin.js builds #ad-style — in a private
   cc-/ad- vocabulary, so none of the ~110 pn-* classes below were
   ever reachable. While it WAS linked it did exactly two things,
   both harmful: `.field textarea` (an element-descendant rule on an
   os.css component, specificity 0,1,1) silently beat the Control
   Center's own `.cc-ta`, and `.cc-id` collided with the client
   panel's class of the same name and leaked `flex-wrap`.

   Adopt it by deleting the two injected blocks and re-linking this
   file, or delete it. Do not link it as it stands.
   ══════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════
   hostna OS — panel layer (Control Center + Admin)
   Loaded AFTER os.css, so every token and every component defined
   there is already in scope. Nothing here restates os.css: this
   file is only the parts a control panel needs and a storefront
   does not — dense tables, status pills, counters, machines.

   AUTHORING CONTRACT (panel-client.js / panel-admin.js)
   ──────────────────────────────────────────────────────
   surfaces   a panel section is `class="tile pn-flat"` — the tile
              already carries the glass, the padding and the radius;
              pn-flat only stops it lifting under the pointer, which
              a catalogue card should do and a data surface must not.
   frame      .pn-frame > (.pn-head · .pn-mid · .pn-foot); .pn-frame
              is a direct child of the `.scroll` host (#ccHost /
              #adHost) and fills it exactly, so the host never
              scrolls and the footer never leaves the screen.
   tables     every <td> carries data-label="Column name". Above
              1100px it is unused; below, it becomes the label of
              the card row. A missing one degrades to no label,
              not to a broken layout.
   gauges     --v is a plain number 0–100 set on the element itself
              (style="--v:62"). Both the bar and the ring read it.
   buttons    row-sized buttons are `ghost-btn pn-mini`, destructive
              ones add `pn-danger`. There is no third button style.
   Reused as-is from os.css: .tile .tile-h .tile-lead .prime-btn
   .ghost-btn .lg .full .chip .chips .eyebrow .lead .tiny .field
   .field-grid .field-err .spec-table .spark .prompt-xl .path
   .tick-list .pn-gate .pn-demo .pn-blank, @keyframes rise / pip.
   ══════════════════════════════════════════════════════════════ */

/* ── 1. panel tokens ────────────────────────────────────────────
   Status colour is derived, never picked. --ok / --warn / --bad /
   --accent are the families; the *-ink variants are those families
   pushed to a value that survives a LIGHT surface, because the raw
   tokens are tuned for dark glass (#48e08b on white is decoration,
   not text). The dark block lets the raw token back through.
   ────────────────────────────────────────────────────────────── */
:root{
  --pn-gap:11px;
  --pn-r:14px;
  --pn-row:38px;                       /* data-table row height */

  --pn-hot:color-mix(in srgb,var(--warn) 42%,var(--bad));   /* suspended: warn, but louder */
  --pn-ok-ink:color-mix(in srgb,var(--ok) 46%,#06301c);
  --pn-warn-ink:color-mix(in srgb,var(--warn) 50%,#3a2100);
  --pn-hot-ink:color-mix(in srgb,var(--pn-hot) 74%,#2c0805);
  --pn-bad-ink:var(--bad);
  --pn-info-ink:color-mix(in srgb,var(--accent) 84%,#08243f);
  --pn-mute-ink:var(--ink-2);

  /* a sticky table header scrolls its own rows underneath itself, so it
     needs a backing that is nearly opaque — glass alone shows the text
     through. Blur is the second layer, not the only one. */
  --pn-solid:color-mix(in srgb,var(--bg-base) 52%,#fff);
}
html[data-theme="dark"]{
  --pn-ok-ink:var(--ok);
  --pn-warn-ink:var(--warn);
  --pn-hot-ink:var(--pn-hot);
  --pn-bad-ink:var(--bad);
  --pn-info-ink:var(--accent);
  --pn-mute-ink:var(--ink-2);
  --pn-solid:color-mix(in srgb,var(--bg-base) 80%,#aeb7c6);
}

/* --v has to be a *registered* number or the ring cannot animate: a conic
   gradient is not interpolatable, but a registered custom property is, and
   the gradient is recomputed each frame from it. Unregistered it still
   works, it just snaps. inherits:true because the ring paints in ::before. */
@property --v{syntax:"<number>";inherits:true;initial-value:0}

/* ── 2. the panel frame ─────────────────────────────────────────
   header / scrolling middle / pinned footer, the same shape the
   POWER builder uses (.rig-head / .rig-mid / .rig-total). It is
   not the same class: .rig-mid drops to overflow:visible under
   1100px because the rig column restacks there, and a panel that
   did the same would scroll its own pinned total off the screen.
   ────────────────────────────────────────────────────────────── */
.pn-frame{display:flex;flex-direction:column;gap:var(--pn-gap);height:100%;min-height:0;min-width:0}
/* flex-shrink:1 is the default, and a flex column whose content is taller than
   its box SHRINKS the items rather than overflowing — so a panel that should
   have scrolled instead squeezed every card to a fraction of its height and
   spilled its text over the next one. Everything in a panel column keeps its
   own height; only the one item marked as the scroller gives any back. */
.pn-frame > *{flex:0 0 auto;min-width:0}

.pn-head{flex:0 0 auto;display:flex;align-items:flex-end;justify-content:space-between;gap:12px 16px;flex-wrap:wrap}
.pn-head-copy{min-width:0}
.pn-head-copy h2{font-size:clamp(19px,1.9vw,25px);line-height:1.14}
.pn-head-copy .lead{font-size:12.5px;max-width:64ch;margin-top:4px}
.pn-head-act{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex:0 0 auto}

.pn-mid{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain;padding-right:4px;
  display:flex;flex-direction:column;gap:var(--pn-gap)}
.pn-mid > *{flex:0 0 auto;min-width:0}

/* one scrollbar treatment for every scroller this file adds */
.pn-mid,.pn-tbl-wrap,.pn-thread-wrap{scrollbar-width:thin;scrollbar-color:var(--stroke) transparent}
.pn-mid::-webkit-scrollbar,.pn-tbl-wrap::-webkit-scrollbar,.pn-thread-wrap::-webkit-scrollbar{width:9px;height:9px}
.pn-mid::-webkit-scrollbar-thumb,.pn-tbl-wrap::-webkit-scrollbar-thumb,.pn-thread-wrap::-webkit-scrollbar-thumb{
  background:var(--stroke);border-radius:99px;border:3px solid transparent;background-clip:padding-box}
.pn-mid::-webkit-scrollbar-thumb:hover,.pn-tbl-wrap::-webkit-scrollbar-thumb:hover{background:var(--ink-3);background-clip:padding-box}

/* the pinned footer: a total, a primary action, a status — never scrolled */
.pn-foot{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:10px 14px;flex-wrap:wrap;
  padding-top:10px;border-top:1px solid var(--stroke-2)}
.pn-foot-k{min-width:0;font-size:11.5px;color:var(--ink-3);line-height:1.45}
.pn-foot-k b{color:var(--ink-2);font-weight:560}
.pn-foot-v{display:flex;align-items:baseline;gap:5px;letter-spacing:-.035em}
.pn-foot-v b{font-size:24px;font-weight:650;font-variant-numeric:tabular-nums}
.pn-foot-v em{font-style:normal;font-size:11.5px;color:var(--ink-3);letter-spacing:0}
.pn-foot-act{display:flex;gap:8px;flex-wrap:wrap;margin-left:auto}

/* main + rail. The rail is where a total, a machine or a filter list lives. */
.pn-cols{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:var(--pn-gap);align-items:start;min-height:0}
.pn-cols.wide{grid-template-columns:minmax(0,1fr) 344px}
.pn-cols.half{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.pn-cols > *{min-width:0}
.pn-side{display:flex;flex-direction:column;gap:var(--pn-gap);min-width:0}
.pn-side > *{flex:0 0 auto;min-width:0}
.pn-grid{display:grid;gap:var(--pn-gap);grid-template-columns:repeat(auto-fit,minmax(252px,1fr))}
.pn-grid > *{min-width:0}

/* the one child of a panel column that takes the slack — a table that should
   fill the frame, a thread that should fill its card. It has to be declared
   AFTER every `> *` rule above and carry the compound selector, or those
   would out-order it and pin the thing that was meant to stretch. */
.pn-fill,.pn-frame > .pn-fill,.pn-side > .pn-fill{flex:1 1 auto;min-height:0}

/* a data surface does not lift: it is a table, not an offer */
.tile.pn-flat:hover{transform:none;background:var(--tile);
  box-shadow:inset 0 1px 0 var(--spec),0 10px 24px -20px rgba(0,0,0,.7)}
.tile.pn-flat.pn-fill{min-height:0}
/* the mock-data notice from os.css, pinned in the frame rather than scrolled */
.pn-frame > .pn-demo{flex:0 0 auto}

/* ── 3. notices ─────────────────────────────────────────────────
   One strip does four jobs: the demo caveat with a figure attached,
   an amount that is due, a failure, and the "are you sure" that a
   destructive action has to ask before it runs. A modal would move
   the shell; this does not.
   ────────────────────────────────────────────────────────────── */
.pn-note{--pn-tone:var(--pn-info-ink);display:flex;align-items:center;gap:10px 12px;flex-wrap:wrap;
  padding:10px 13px;border-radius:var(--pn-r);font-size:12px;line-height:1.5;color:var(--ink-2);
  background:color-mix(in srgb,var(--pn-tone) 10%,var(--sunk));
  border:1px solid color-mix(in srgb,var(--pn-tone) 26%,transparent)}
.pn-note::before{content:"";flex:0 0 auto;align-self:center;width:7px;height:7px;border-radius:50%;background:var(--pn-tone)}
.pn-note b{color:var(--ink);font-weight:560}
.pn-note-b{flex:1 1 200px;min-width:0}
.pn-note-v{flex:0 0 auto;font-size:19px;font-weight:650;letter-spacing:-.03em;color:var(--ink);font-variant-numeric:tabular-nums}
.pn-note-act{display:flex;gap:7px;flex-wrap:wrap;margin-left:auto}
.pn-note--ok{--pn-tone:var(--pn-ok-ink)}
.pn-note--warn{--pn-tone:var(--pn-warn-ink)}
.pn-note--hot{--pn-tone:var(--pn-hot-ink)}
.pn-note--bad{--pn-tone:var(--pn-bad-ink)}
.pn-note--mute{--pn-tone:var(--pn-mute-ink)}

/* ── 4. status pills ────────────────────────────────────────────
   One shape, one tone variable. The state classes are aliases onto
   five tones, so markup can print the API status straight through:
   class="pn-pill pn-pill--${order.status}".
   ────────────────────────────────────────────────────────────── */
.pn-pill{--pn-tone:var(--pn-mute-ink);display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;
  height:21px;padding:0 9px 0 8px;border-radius:99px;white-space:nowrap;
  font-size:11px;font-weight:560;letter-spacing:.005em;color:var(--pn-tone);
  background:color-mix(in srgb,var(--pn-tone) 14%,transparent);
  border:1px solid color-mix(in srgb,var(--pn-tone) 26%,transparent)}
.pn-pill::before{content:"";flex:0 0 auto;width:6px;height:6px;border-radius:50%;background:currentColor}
.pn-pill.sm{height:18px;font-size:10px;padding:0 8px 0 7px;gap:5px}
.pn-pill.bare::before{display:none;content:none}
.pn-pill.strong{background:color-mix(in srgb,var(--pn-tone) 24%,transparent);
  border-color:color-mix(in srgb,var(--pn-tone) 46%,transparent)}

.pn-pill--ok,.pn-pill--active,.pn-pill--running,.pn-pill--paid,.pn-pill--online,.pn-pill--resolved{--pn-tone:var(--pn-ok-ink)}
.pn-pill--warn,.pn-pill--pending,.pn-pill--unpaid,.pn-pill--open,.pn-pill--due{--pn-tone:var(--pn-warn-ink)}
.pn-pill--suspended,.pn-pill--on_hold,.pn-pill--overdue{--pn-tone:var(--pn-hot-ink)}
.pn-pill--failed,.pn-pill--error,.pn-pill--canceled,.pn-pill--cancelled{--pn-tone:var(--pn-bad-ink)}
.pn-pill--refunded,.pn-pill--closed,.pn-pill--stopped,.pn-pill--neutral{--pn-tone:var(--pn-mute-ink)}
.pn-pill--info,.pn-pill--new{--pn-tone:var(--pn-info-ink)}
/* a machine that is actually running says so the way the menubar does */
.pn-pill--running::before{box-shadow:0 0 0 0 color-mix(in srgb,var(--ok) 55%,transparent);animation:pip 2.4s infinite}

/* where the state needs no word — a VM in a list, a cell in a dense row —
   the answer is os.css's .dot, not an empty pill. It ships ok/warn/edge;
   these are the three a panel also has to say. */
.dot.bad{background:var(--pn-bad-ink);box-shadow:0 0 8px color-mix(in srgb,var(--bad) 55%,transparent)}
.dot.hot{background:var(--pn-hot-ink);box-shadow:0 0 8px color-mix(in srgb,var(--pn-hot) 55%,transparent)}
.dot.mute{background:var(--ink-3);box-shadow:none}

/* ── 5. the data table ──────────────────────────────────────────
   Dense rows, a sticky header, a hairline instead of zebra stripes,
   numerals tabular and right-aligned. The container is the scroller
   in both axes — the page cannot move, so a wide table has to carry
   its own overflow.
   ────────────────────────────────────────────────────────────── */
.pn-tbl-wrap{min-height:0;min-width:0;overflow:auto;overscroll-behavior:contain;
  border-radius:var(--pn-r);border:1px solid var(--stroke-2);background:var(--sunk)}
.pn-tbl{width:100%;min-width:560px;border-collapse:separate;border-spacing:0;font-size:12.5px}
.pn-tbl.narrow{min-width:420px}
.pn-tbl.roomy{min-width:720px}

.pn-tbl thead th{position:sticky;top:0;z-index:2;height:32px;padding:0 12px;text-align:left;white-space:nowrap;
  font-size:10.5px;font-weight:560;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);
  background:color-mix(in srgb,var(--pn-solid) 88%,transparent);
  -webkit-backdrop-filter:blur(12px) saturate(160%);backdrop-filter:blur(12px) saturate(160%);
  border-bottom:1px solid var(--stroke)}
.pn-tbl th.sort{cursor:pointer;user-select:none}
.pn-tbl th.sort:hover{color:var(--ink-2)}
.pn-tbl th[aria-sort]{color:var(--ink)}
.pn-tbl th[aria-sort]::after{content:"";display:inline-block;margin-left:6px;border:3.5px solid transparent}
.pn-tbl th[aria-sort="ascending"]::after{border-bottom-color:currentColor;margin-bottom:3px}
.pn-tbl th[aria-sort="descending"]::after{border-top-color:currentColor;margin-top:3px}

.pn-tbl td{height:var(--pn-row);padding:0 12px;vertical-align:middle;color:var(--ink-2);
  border-bottom:1px solid var(--stroke-2)}
.pn-tbl tbody tr:last-child td{border-bottom:0}
.pn-tbl tbody tr{transition:background .16s}
.pn-tbl tbody tr:hover{background:var(--tile)}
.pn-tbl tbody tr[tabindex]{cursor:pointer}
/* inset, because an outward outline is clipped by the scroller it sits in */
.pn-tbl tbody tr:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;background:var(--tile)}
.pn-tbl tbody tr.is-sel{background:color-mix(in srgb,var(--accent) 13%,transparent);
  box-shadow:inset 3px 0 0 var(--accent)}
.pn-tbl tbody tr.is-muted td{opacity:.55}

.pn-tbl .k{color:var(--ink);font-weight:560}
.pn-tbl .k small{display:block;font-size:10.5px;font-weight:400;color:var(--ink-3);line-height:1.35}
.pn-tbl .dim{color:var(--ink-3)}
.pn-tbl .num,.pn-tbl th.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.pn-tbl .mono{font-family:"SF Mono",ui-monospace,Menlo,Consolas,monospace;font-size:11px;color:var(--ink-2)}
.pn-tbl .act,.pn-tbl th.act{width:1%;text-align:right;white-space:nowrap}
.pn-tbl .wrap{white-space:normal;line-height:1.4;padding-top:7px;padding-bottom:7px}

/* row actions stay quiet until the row is pointed at or focused into */
.pn-rowact{display:inline-flex;gap:6px;justify-content:flex-end;opacity:.4;transition:opacity .18s}
tr:hover .pn-rowact,tr:focus-visible .pn-rowact,tr:focus-within .pn-rowact{opacity:1}

/* row-sized buttons: the same two buttons, one size down — the way .lg works */
.ghost-btn.pn-mini,.prime-btn.pn-mini{height:25px;padding:0 10px;font-size:11.5px;border-radius:8px;gap:6px}
.ghost-btn.pn-mini svg,.prime-btn.pn-mini svg{width:13px;height:13px;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.ghost-btn.pn-danger{color:var(--pn-bad-ink);border-color:color-mix(in srgb,var(--bad) 34%,var(--stroke))}
.ghost-btn.pn-danger:hover{background:color-mix(in srgb,var(--bad) 14%,var(--tile-2))}
.prime-btn.pn-danger{background:linear-gradient(180deg,color-mix(in srgb,var(--bad) 88%,#fff 12%),color-mix(in srgb,var(--bad) 80%,#000 20%));
  box-shadow:0 8px 20px -10px color-mix(in srgb,var(--bad) 80%,transparent),inset 0 1px 0 rgba(255,255,255,.4)}
/* os.css gives the two buttons no disabled state — nothing in the storefront
   disables one. A panel does it constantly (power cooldown, a pager at the
   last page, a submit in flight), so the state is added HERE and scoped to
   the two panel hosts rather than to the buttons themselves. The id lifts it
   over .prime-btn:hover, which would otherwise light a dead control. */
#ccHost .prime-btn:disabled,#ccHost .ghost-btn:disabled,
#adHost .prime-btn:disabled,#adHost .ghost-btn:disabled{opacity:.42;cursor:default;transform:none;filter:none;box-shadow:none}

/* ── 6. toolbar: search · filter chips · pagination ─────────── */
.pn-bar{flex:0 0 auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}
.pn-search{display:flex;align-items:center;gap:8px;height:32px;padding:0 11px;border-radius:11px;
  flex:1 1 220px;min-width:0;max-width:360px;
  background:var(--sunk);border:1px solid var(--stroke-2);transition:border-color .2s,box-shadow .2s}
.pn-search:focus-within{border-color:color-mix(in srgb,var(--accent) 55%,transparent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 14%,transparent)}
.pn-search svg{width:15px;height:15px;flex:0 0 auto;fill:none;stroke:var(--ink-3);stroke-width:1.8;stroke-linecap:round}
.pn-search input{flex:1 1 auto;min-width:0;font-size:13px;background:none}
.pn-search input::placeholder{color:var(--ink-3)}
.pn-search .pn-x{flex:0 0 auto;width:18px;height:18px;border-radius:6px;font-size:12px;line-height:1;color:var(--ink-3)}
.pn-search .pn-x:hover{background:var(--tile-2);color:var(--ink)}
.pn-bar .chip em{font-style:normal;font-size:10.5px;color:var(--ink-3);margin-left:5px;font-variant-numeric:tabular-nums}
.pn-bar .chip.on em{color:var(--ink-2)}

.pn-pager{display:flex;align-items:center;gap:5px;flex:0 0 auto;margin-left:auto}
.pn-pager .n{padding:0 6px;font-size:11.5px;color:var(--ink-3);font-variant-numeric:tabular-nums;white-space:nowrap}
.pn-page{width:27px;height:27px;flex:0 0 auto;display:grid;place-items:center;border-radius:9px;
  font-size:12px;font-variant-numeric:tabular-nums;color:var(--ink-2);
  background:var(--tile-2);border:1px solid var(--stroke-2);transition:.18s}
.pn-page.wide{width:auto;padding:0 10px}
.pn-page:hover:not(:disabled){background:var(--tile-hover);color:var(--ink);transform:translateY(-1px)}
.pn-page.on{color:var(--ink);background:color-mix(in srgb,var(--accent) 16%,var(--tile-2));
  border-color:color-mix(in srgb,var(--accent) 42%,transparent)}
.pn-page:disabled{opacity:.32;cursor:default;transform:none}

/* ── 7. stat tiles ──────────────────────────────────────────────
   Written as class="tile pn-stat". The :not(.tile) rule below is a
   safety net, not a second card: identical tokens, so a counter
   that forgets the tile still lands on the same surface.
   ────────────────────────────────────────────────────────────── */
.pn-stats{display:grid;gap:var(--pn-gap);grid-template-columns:repeat(auto-fit,minmax(158px,1fr))}
.pn-stats > *{min-width:0}
.pn-stat{--pn-tone:var(--pn-info-ink);gap:6px}
.pn-stat:not(.tile){display:flex;flex-direction:column;min-width:0;padding:14px 16px;border-radius:var(--r-tile);
  background:var(--tile);border:1px solid var(--stroke-2);box-shadow:inset 0 1px 0 var(--spec)}
.pn-stat-k{font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pn-stat-v{display:flex;align-items:baseline;gap:5px;letter-spacing:-.035em;min-width:0}
.pn-stat-v b{font-size:29px;line-height:1;font-weight:650;font-variant-numeric:tabular-nums}
.pn-stat-v em{font-style:normal;font-size:11.5px;color:var(--ink-3);letter-spacing:0}
.pn-stat .spark{flex:0 0 auto;height:26px;margin-top:auto}
.pn-stat .spark polyline{stroke:var(--pn-tone)}
.pn-stat--ok{--pn-tone:var(--pn-ok-ink)}
.pn-stat--warn{--pn-tone:var(--pn-warn-ink)}
.pn-stat--bad{--pn-tone:var(--pn-bad-ink)}
.pn-stat--mute{--pn-tone:var(--pn-mute-ink)}

.pn-delta{display:inline-flex;align-items:center;gap:4px;font-size:11px;color:var(--ink-3);font-variant-numeric:tabular-nums}
.pn-delta::before{font-size:8px;line-height:1}
.pn-delta.up{color:var(--pn-ok-ink)}
.pn-delta.up::before{content:"▲"}
.pn-delta.down{color:var(--pn-bad-ink)}
.pn-delta.down::before{content:"▼"}
.pn-delta.flat::before{content:"—";font-size:10px}

/* ── 8. gauge and meter, both driven by --v (a number, 0–100) ── */
.pn-meter{--v:0;--pn-tone:var(--accent);position:relative;display:block;width:100%;min-width:0;
  height:5px;border-radius:99px;background:var(--sunk);overflow:hidden}
.pn-meter::after{content:"";position:absolute;left:0;top:0;bottom:0;border-radius:99px;
  width:clamp(0%,calc(var(--v) * 1%),100%);transition:width .6s var(--ease);
  background:linear-gradient(90deg,color-mix(in srgb,var(--pn-tone) 55%,var(--accent-3)),var(--pn-tone))}
.pn-meter.lg{height:7px}
.pn-meter.is-warn{--pn-tone:var(--warn)}
.pn-meter.is-bad{--pn-tone:var(--bad)}

.pn-meter-row{display:grid;grid-template-columns:minmax(44px,auto) minmax(0,1fr) 46px;align-items:center;gap:9px;
  font-size:11px;color:var(--ink-3)}
.pn-meter-row em{font-style:normal;text-align:right;color:var(--ink-2);font-variant-numeric:tabular-nums}
.pn-meters{display:flex;flex-direction:column;gap:7px;flex:1 1 200px;min-width:0}

/* the ring is a conic gradient with a hole punched in it, not an SVG: the
   mask has to live on ::before or it would eat the label in the middle */
.pn-ring{--v:0;--pn-tone:var(--accent);position:relative;flex:0 0 auto;width:58px;height:58px;
  display:grid;place-items:center;text-align:center;transition:--v .55s var(--ease)}
.pn-ring::before{content:"";position:absolute;inset:0;border-radius:50%;
  background:conic-gradient(from -90deg,var(--pn-tone) calc(var(--v) * 3.6deg),var(--sunk) 0);
  -webkit-mask:radial-gradient(closest-side,transparent 69%,#000 70%);
          mask:radial-gradient(closest-side,transparent 69%,#000 70%)}
.pn-ring b{position:relative;font-size:13px;font-weight:640;line-height:1;font-variant-numeric:tabular-nums}
.pn-ring em{position:relative;display:block;margin-top:2px;font-style:normal;font-size:9.5px;color:var(--ink-3)}
.pn-ring.sm{width:46px;height:46px}
.pn-ring.sm b{font-size:11.5px}
.pn-ring.is-warn{--pn-tone:var(--warn)}
.pn-ring.is-bad{--pn-tone:var(--bad)}
.pn-rings{display:flex;gap:12px;flex-wrap:wrap;align-items:center}

/* ── 9. machine card ────────────────────────────────────────────
   class="tile pn-flat pn-machine". Power buttons are pinned to the
   bottom of the card, so a card that grows a longer spec list does
   not push them out of reach.
   ────────────────────────────────────────────────────────────── */
.pn-machine{gap:11px}
.pn-machine > *{flex:0 0 auto;min-width:0}
.pn-machine:not(.tile){display:flex;flex-direction:column;min-width:0;padding:14px 16px;border-radius:var(--r-tile);
  background:var(--tile);border:1px solid var(--stroke-2);box-shadow:inset 0 1px 0 var(--spec)}
.pn-machine-h{display:flex;align-items:flex-start;justify-content:space-between;gap:9px;flex-wrap:wrap}
.pn-machine-id{display:flex;flex-direction:column;gap:2px;min-width:0}
.pn-machine-id b{font-size:14px;letter-spacing:-.015em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pn-machine-id span{font-size:11px;color:var(--ink-3);font-family:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pn-specs{display:flex;flex-wrap:wrap;gap:5px}
.pn-specs span{padding:3px 8px;border-radius:99px;font-size:10.5px;color:var(--ink-3);
  background:var(--sunk);border:1px solid var(--stroke-2);white-space:nowrap}
.pn-specs b{color:var(--ink-2);font-weight:560}
.pn-machine-body{display:flex;gap:14px;align-items:center;flex-wrap:wrap}

.pn-power{display:flex;gap:6px;flex-wrap:wrap;margin-top:auto;padding-top:10px;border-top:1px solid var(--stroke-2)}
.pn-power .ghost-btn{flex:1 1 auto;height:29px;padding:0 11px;font-size:11.5px;border-radius:9px}
.pn-power .ghost-btn svg{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
/* the console enforces a 30 s cooldown between power calls; the bar is that
   wait made visible. JS still owns `disabled` — this is the readout, not the
   timer, and under reduced motion it simply finishes at once. */
.pn-cool{position:relative;overflow:hidden}
.pn-cool::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;transform-origin:0 50%;
  background:color-mix(in srgb,var(--accent) 70%,transparent);animation:pn-cool 30s linear forwards}
@keyframes pn-cool{from{transform:scaleX(1)}to{transform:scaleX(0)}}

/* ── 10. ticket thread ──────────────────────────────────────── */
.pn-thread-wrap{min-height:0;overflow:auto;overscroll-behavior:contain;padding-right:4px}
.pn-thread{display:flex;flex-direction:column;gap:10px;min-width:0}
.pn-thread > *{flex:0 0 auto}
.pn-msg{display:flex;flex-direction:column;gap:5px;min-width:0;max-width:min(72ch,88%);
  padding:11px 13px;border-radius:16px;font-size:12.5px;line-height:1.55;color:var(--ink-2);
  background:var(--tile);border:1px solid var(--stroke-2);box-shadow:inset 0 1px 0 var(--spec);
  animation:rise .4s var(--ease-out) both}
.pn-msg-h{display:flex;align-items:baseline;gap:9px;font-size:10.5px;color:var(--ink-3)}
.pn-msg-h b{font-size:11.5px;font-weight:560;color:var(--ink)}
.pn-msg-h time{margin-left:auto;flex:0 0 auto;font-variant-numeric:tabular-nums}
.pn-msg p{white-space:pre-wrap;word-break:break-word}
.pn-msg--staff{align-self:flex-start;border-bottom-left-radius:6px;
  background:linear-gradient(150deg,color-mix(in srgb,var(--accent) 13%,var(--tile)),var(--tile) 62%);
  border-color:color-mix(in srgb,var(--accent) 24%,var(--stroke-2))}
.pn-msg--client{align-self:flex-end;border-bottom-right-radius:6px}
/* whoever is reading sees their own side on the right, so the admin panel
   adds .flip and the two roles swap sides without swapping class names */
.pn-thread.flip .pn-msg--staff{align-self:flex-end;border-bottom-left-radius:16px;border-bottom-right-radius:6px}
.pn-thread.flip .pn-msg--client{align-self:flex-start;border-bottom-right-radius:16px;border-bottom-left-radius:6px}
.pn-msg--note{align-self:stretch;max-width:100%;font-size:11.5px;color:var(--ink-3);
  background:var(--sunk);border-style:dashed}

.prompt-xl.pn-reply{padding:10px;border-radius:16px}
.prompt-xl.pn-reply textarea{font-size:13.5px;min-height:66px;padding:4px 4px 9px}
.pn-reply-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}

/* ── 11. form rows ──────────────────────────────────────────────
   The control itself is still a .field from os.css — this only
   adds the two-column geometry a settings form wants, plus the
   ok state and the textarea .field never had.
   ────────────────────────────────────────────────────────────── */
.pn-form{display:flex;flex-direction:column;min-width:0}
.pn-form > *{flex:0 0 auto}
.pn-row{display:grid;grid-template-columns:minmax(130px,.85fr) minmax(0,1.4fr);gap:4px 18px;align-items:start;
  padding:10px 0;border-bottom:1px solid var(--stroke-2)}
.pn-row:last-child{border-bottom:0}
.pn-row-k{display:flex;flex-direction:column;gap:2px;min-width:0;padding-top:7px}
.pn-row-k b{font-size:12.5px;font-weight:560}
.pn-row-k span{font-size:11px;color:var(--ink-3);line-height:1.45}
.pn-row-v{display:flex;flex-direction:column;gap:6px;min-width:0}
.pn-row-v.inline{flex-direction:row;align-items:center;flex-wrap:wrap;gap:8px;padding-top:5px}
.pn-req{color:var(--pn-bad-ink);font-weight:600}
.pn-help{font-size:10.5px;color:var(--ink-3);line-height:1.5}

/* the ok counterpart to the .field.is-bad already in os.css */
.field.is-ok input,.field.is-ok select,.field.is-ok textarea{border-color:color-mix(in srgb,var(--ok) 52%,transparent)}
.field-ok{font-size:10.5px;color:var(--pn-ok-ink);min-height:13px;line-height:1.2}
.field textarea{width:100%;min-height:78px;padding:9px 12px;border-radius:11px;resize:vertical;
  font-size:13.5px;line-height:1.5;background:var(--sunk);border:1px solid var(--stroke-2);color:var(--ink);
  transition:border-color .2s,box-shadow .2s}
.field textarea::placeholder{color:var(--ink-3)}
.field textarea:focus{border-color:color-mix(in srgb,var(--accent) 55%,transparent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 14%,transparent)}

/* a value that is read, copied and never typed: token, EPP code, IP */
.pn-val{display:flex;align-items:center;justify-content:space-between;gap:10px;min-width:0;
  padding:8px 11px;border-radius:11px;background:var(--sunk);border:1px solid var(--stroke-2)}
.pn-val code{min-width:0;font:11.5px/1.5 "SF Mono",ui-monospace,Menlo,Consolas,monospace;color:var(--ink-2);
  word-break:break-all}
.pn-val.blur code{filter:blur(4px);user-select:none;transition:filter .2s}
.pn-val.blur:hover code,.pn-val.blur:focus-within code{filter:none;user-select:text}

/* ── 12. empty state ────────────────────────────────────────── */
.pn-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:11px;text-align:center;
  min-height:180px;padding:36px 22px;border-radius:var(--r-tile);border:1px dashed var(--stroke);
  font-size:12.5px;line-height:1.55;color:var(--ink-3)}
.pn-empty-ico{width:44px;height:44px;opacity:.8}
.pn-empty-ico svg{width:100%;height:100%}
.pn-empty b{display:block;margin-bottom:4px;font-size:14px;letter-spacing:-.01em;color:var(--ink)}
.pn-empty p{max-width:46ch}
.pn-empty.sm{min-height:0;padding:22px 18px;gap:8px}
.pn-empty.sm .pn-empty-ico{width:32px;height:32px}

/* ── 13. skeletons ──────────────────────────────────────────── */
.pn-sk{position:relative;overflow:hidden;border-radius:8px;
  background:color-mix(in srgb,var(--ink) 8%,transparent)}
.pn-sk::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--spec) 50%,transparent),transparent);
  animation:pn-shim 1.5s var(--ease) infinite}
@keyframes pn-shim{to{transform:translateX(100%)}}
.pn-sk-line{height:11px}
.pn-sk-line.sm{height:9px;width:58%}
.pn-sk-line.lg{height:17px;width:42%}
.pn-sk-row{height:var(--pn-row);border-radius:10px;margin:6px 8px}
.pn-sk-tile{height:92px;border-radius:var(--r-tile)}
.pn-sk-ring{width:58px;height:58px;border-radius:50%;flex:0 0 auto}
.pn-sk-stack{display:flex;flex-direction:column;gap:8px}
/* the global reduced-motion rule freezes the sweep off-screen; remove it
   outright and let the flat block carry the "not here yet" on its own */
@media (prefers-reduced-motion:reduce){
  .pn-sk::after{display:none}
  .pn-sk{background:color-mix(in srgb,var(--ink) 11%,transparent)}
}

/* ══════════════════════════════════════════════════════════════
   14. Control Center — the parts only a customer sees
   ══════════════════════════════════════════════════════════════ */
.cc-id{display:flex;align-items:center;gap:13px;flex-wrap:wrap;min-width:0}
.cc-avatar{flex:0 0 auto;width:44px;height:44px;border-radius:14px;display:grid;place-items:center;
  font-size:15px;font-weight:640;letter-spacing:.02em;color:#fff;
  background:linear-gradient(160deg,color-mix(in srgb,var(--accent) 76%,#fff 24%),color-mix(in srgb,var(--accent-2) 78%,#000 22%));
  box-shadow:0 10px 20px -14px color-mix(in srgb,var(--accent) 85%,transparent),inset 0 1px 0 rgba(255,255,255,.4)}
.cc-id-body{flex:1 1 180px;min-width:0}
.cc-id-body b{display:block;font-size:14.5px;letter-spacing:-.015em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc-id-body span{font-size:11.5px;color:var(--ink-3);word-break:break-word}
.cc-wallet{margin-left:auto;text-align:right;flex:0 0 auto}
.cc-wallet b{display:block;font-size:20px;font-weight:650;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.cc-wallet em{font-style:normal;font-size:10.5px;color:var(--ink-3)}
.cc-wallet.is-neg b{color:var(--pn-bad-ink)}

/* free months earned — the rewards ledger, stated plainly */
.cc-reward{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:9px 12px;border-radius:var(--pn-r);
  font-size:11.5px;color:var(--ink-2);
  background:color-mix(in srgb,var(--accent-3) 11%,var(--sunk));
  border:1px solid color-mix(in srgb,var(--accent-3) 26%,transparent)}
.cc-reward b{font-size:17px;font-weight:650;letter-spacing:-.02em;color:var(--ink);font-variant-numeric:tabular-nums}

/* nameservers: four fields that belong together and fail together */
.cc-ns{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:9px}
.cc-ns > *{min-width:0}

/* usage over time, drawn as bars rather than a chart library */
.cc-usage{display:flex;align-items:flex-end;gap:3px;height:56px;padding-top:4px}
.cc-usage i{flex:1 1 0;min-width:2px;border-radius:3px 3px 0 0;transition:height .5s var(--ease);
  height:clamp(2px,calc(var(--v) * 1%),100%);
  background:linear-gradient(180deg,var(--accent),color-mix(in srgb,var(--accent) 40%,transparent))}
.cc-usage i.hot{background:linear-gradient(180deg,var(--warn),color-mix(in srgb,var(--warn) 35%,transparent))}

/* ══════════════════════════════════════════════════════════════
   15. Admin — the parts only staff see
   ══════════════════════════════════════════════════════════════ */
/* the provisioning queue: what is waiting, what broke, in that order */
.ad-queue{display:flex;flex-direction:column;gap:7px}
.ad-q{--pn-tone:var(--pn-warn-ink);position:relative;display:grid;
  grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;
  padding:10px 13px 10px 16px;border-radius:var(--pn-r);overflow:hidden;
  background:var(--sunk);border:1px solid var(--stroke-2)}
.ad-q::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--pn-tone)}
.ad-q b{font-size:12.5px;letter-spacing:-.01em}
.ad-q span{font-size:11px;color:var(--ink-3);line-height:1.45}
.ad-q-act{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
.ad-q--failed{--pn-tone:var(--pn-bad-ink)}
.ad-q--active{--pn-tone:var(--pn-ok-ink)}
.ad-q--pending{--pn-tone:var(--pn-warn-ink)}

/* a Proxmox node and what is running on it */
.ad-nodes{display:grid;gap:var(--pn-gap);grid-template-columns:repeat(auto-fit,minmax(268px,1fr))}
.ad-nodes > *{min-width:0}
.ad-node-h{display:flex;align-items:center;justify-content:space-between;gap:9px;flex-wrap:wrap}
.ad-node-h b{font-size:13.5px;letter-spacing:-.015em}
.ad-vms{display:flex;flex-direction:column;gap:4px;margin-top:2px}
.ad-vm{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:9px;
  padding:6px 9px;border-radius:10px;font-size:11.5px;background:var(--sunk);border:1px solid var(--stroke-2)}
.ad-vm b{font-weight:560;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ad-vm code{font:10.5px "SF Mono",ui-monospace,Menlo,Consolas,monospace;color:var(--ink-3)}

/* System: two version stamps that are required to match */
.ad-ver{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:12px;color:var(--ink-2)}
.ad-ver code{padding:3px 9px;border-radius:8px;font:11.5px "SF Mono",ui-monospace,Menlo,Consolas,monospace;
  color:var(--ink-2);background:var(--sunk);border:1px solid var(--stroke-2)}
.ad-ver.is-off code{color:var(--pn-bad-ink);border-color:color-mix(in srgb,var(--bad) 42%,transparent)}
.ad-ver.is-off{color:var(--pn-bad-ink)}

/* "signed in as" — staff borrowing a customer's session must never be subtle */
.ad-as{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding:8px 12px;border-radius:var(--pn-r);
  font-size:11.5px;color:var(--ink-2);
  background:color-mix(in srgb,var(--accent-2) 12%,var(--sunk));
  border:1px solid color-mix(in srgb,var(--accent-2) 30%,transparent)}
.ad-as b{color:var(--ink)}

/* ══════════════════════════════════════════════════════════════
   16. responsive
   1440×900 and 1280×800 are the design sizes. Below 1100 the
   tables stop being tables — a row that scrolls sideways forever
   is not readable, a card is. At 375 nothing may leave the window.
   ══════════════════════════════════════════════════════════════ */
@media (max-width:1180px){
  .pn-cols,.pn-cols.wide{grid-template-columns:1fr}
  .pn-side{flex-direction:row;flex-wrap:wrap}
  .pn-side > *{flex:1 1 260px}
}

@media (max-width:1100px){
  /* card rows. The header text is kept in the accessibility tree but taken
     out of the flow; each cell states its own column from data-label. */
  .pn-tbl{min-width:0}
  .pn-tbl.narrow,.pn-tbl.roomy{min-width:0}
  .pn-tbl thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
  .pn-tbl,.pn-tbl tbody,.pn-tbl tr,.pn-tbl td{display:block}
  .pn-tbl tbody tr{position:relative;margin:8px;padding:5px 2px;border-radius:var(--pn-r);
    background:var(--tile);border:1px solid var(--stroke-2);box-shadow:inset 0 1px 0 var(--spec)}
  .pn-tbl tbody tr:hover{background:var(--tile-hover)}
  .pn-tbl tbody tr.is-sel{box-shadow:inset 0 1px 0 var(--spec),0 0 0 2px var(--accent)}
  .pn-tbl tbody tr:focus-visible{outline-offset:2px}
  .pn-tbl td{height:auto;display:flex;align-items:baseline;justify-content:space-between;gap:8px 16px;
    padding:5px 12px;border-bottom:0;text-align:right}
  .pn-tbl td::before{content:attr(data-label);flex:0 0 auto;text-align:left;
    font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
  /* the primary cell keeps its second line on a row of its own — as flex
     items, label / name / sub-line would otherwise sit three across */
  .pn-tbl td.k{padding-top:8px;font-size:13.5px;flex-wrap:wrap}
  .pn-tbl td.k small{flex:1 0 100%;text-align:right}
  .pn-tbl td.wrap{flex-direction:column;align-items:stretch;text-align:left}
  .pn-tbl .act,.pn-tbl td.act{width:auto;justify-content:flex-end;padding-bottom:8px}
  .pn-tbl .act::before{content:none}
  .pn-rowact{opacity:1}
}

@media (max-width:900px){
  .pn-head{align-items:flex-start;flex-direction:column;gap:9px}
  .pn-head-act{width:100%}
  .pn-stats{grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}
  .pn-stat-v b{font-size:25px}
  .pn-row{grid-template-columns:1fr;gap:6px;padding:9px 0}
  .pn-row-k{padding-top:0}
  .pn-msg{max-width:94%}
  .pn-cols.half{grid-template-columns:1fr}
  .pn-side{flex-direction:column}
  .pn-side > *{flex:0 0 auto}
  .pn-foot-act{width:100%;margin-left:0}
  .pn-foot-act .prime-btn,.pn-foot-act .ghost-btn{flex:1 1 auto}
}

@media (max-width:620px){
  .pn-search{max-width:none;flex:1 1 100%}
  .pn-pager{margin-left:0;width:100%;justify-content:space-between}
  .pn-stats{grid-template-columns:repeat(auto-fit,minmax(124px,1fr))}
  .pn-stat-v b{font-size:22px}
  .pn-machine-body{gap:11px}
  .pn-rings{width:100%;justify-content:space-between;gap:8px}
  .pn-power .ghost-btn{flex:1 1 calc(50% - 3px)}
  .pn-note-act{width:100%;margin-left:0}
  .pn-grid,.ad-nodes,.cc-ns{grid-template-columns:1fr}
  .pn-msg{max-width:100%}
  .ad-q{grid-template-columns:1fr}
  .ad-q-act{justify-content:flex-start}
  .pn-tbl tbody tr{margin:7px 6px}
  .pn-tbl td{padding:5px 10px}
}

/* a short window: the panel gives up padding and headline size, not rows */
@media (max-height:820px){
  :root{--pn-row:34px;--pn-gap:9px}
  .pn-head-copy .lead{display:none}
  .pn-stat-v b{font-size:25px}
  .pn-stat .spark{height:22px}
  .pn-ring{width:50px;height:50px}
  .pn-foot-v b{font-size:21px}
  .pn-empty{min-height:140px;padding:26px 20px}
}
