/* ==========================================================================
   Esagames Hosting Shield — app.css
   Every component class the panel uses. Documented in docs/DESIGN.md.
   Requires theme.css (tokens) to be loaded first.
   Namespace: .esa-*   State classes: .is-*   Modifiers: --*
   ========================================================================== */

/* ==========================================================================
   1. APP SHELL
   ========================================================================== */
.esa-shell{
  display:grid;
  grid-template-columns:minmax(0,1fr);
  grid-template-rows:auto minmax(0,1fr);
  min-height:100vh;
  min-height:100dvh;
}
.esa-shell--admin{ grid-template-columns:var(--sidebar-w) minmax(0,1fr); }

.esa-main{ min-width:0; display:flex; flex-direction:column; }

.esa-page{
  width:100%;
  max-width:var(--shell-max);
  margin:0 auto;
  /* top pad is a SECTION step: the topbar is sticky and translucent, so
     content that starts closer than one section reads as stuck to it. */
  padding:var(--sp-section) var(--sp-6) var(--sp-9);
  flex:1 1 auto;
}
.esa-page--wide{ max-width:1560px; }
.esa-page--narrow{ max-width:820px; }

/* ---- THE SECTION RHYTHM -------------------------------------------------
   Every direct child of the page well IS a section — a card, a banner, a
   stat row, a grid — so the rhythm lives here once instead of in a `u-mt-*`
   at each call site. Before this rule adjacent cards had NO margin at all
   and touched border-to-border, while the few places that did remember a
   margin used 16px, which is less than the 22px a single card pads itself
   with. Sibling margins collapse in this block flow, so .esa-page-head's own
   margin-bottom does not stack on top of the next section's margin-top.
   A page that is itself a flex stack is excluded: its `gap` does the job,
   and applying both would double the step. */
.esa-page:not(.esa-stack) > * + *{ margin-top:var(--sp-section); }
/* ...with the boxes that are not in the flow taken back out. `.esa-modal` is
   `position:fixed; inset:0`: with top AND bottom pinned, a margin-top does not
   move it, it SHRINKS it — the backdrop would stop covering the top 36px of
   the screen. A `script` element has no box to space at all — and keeping
   angle brackets out of a comment matters: app.css is inlined verbatim into
   docs/preview-design.html, whose re-sync check counts style/script blocks. */
/* NB the `:not()` is repeated on purpose: it is what makes these selectors
   match the rhythm rule's (0,2,0) specificity. Without it `.esa-page > script`
   is (0,1,1) and loses, and the exception silently does nothing. */
.esa-page:not(.esa-stack) > .esa-modal,
.esa-page:not(.esa-stack) > dialog,
.esa-page:not(.esa-stack) > template,
.esa-page:not(.esa-stack) > script{ margin-top:0; }

/* The client portal wraps its own body in a second .esa-page inside the
   layout's one. A nested well must not re-apply the PADDING — every client
   page was paying it twice (72px above the title, 56px on each side) which is
   why the portal read looser than the admin pages at the top and tighter
   everywhere else. The max-width is deliberately left alone so a nested
   `--narrow` still narrows; a nested `--wide` cannot widen past the outer
   well, which is a layout limit, not a spacing one (see DESIGN.md §3). */
.esa-page .esa-page{ padding:0; }
.esa-page.esa-stack{ gap:var(--sp-section); }

/* Inside a stack, `gap` owns the spacing: a leftover margin-bottom on these
   three would be ADDED to it (flex margins do not collapse) and the page head
   would sit 58px from the section under it while sections sit 36px apart. */
.esa-stack > .esa-page-head,
.esa-stack > .esa-servicebar,
.esa-stack > .esa-flash{ margin-bottom:0; }

/* page header: title block left, actions right */
.esa-page-head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:var(--sp-4); flex-wrap:wrap;
  margin-bottom:var(--sp-section);
  padding-bottom:var(--sp-group);
  border-bottom:1px solid var(--line);
}
.esa-page-head__title{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap; }
.esa-page-head h1{ margin:0; }
.esa-page-head__sub{ margin:var(--sp-2) 0 0; color:var(--ink-dim); font-size:14.5px; max-width:70ch; }
.esa-page-head__actions{ display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap; }

/* centered marketing-style section head (eyebrow + h2 + subtitle) */
.esa-section-head{ text-align:center; max-width:680px; margin:var(--sp-8) auto var(--sp-6); }
.esa-section-head h2{ margin:var(--sp-3) 0; line-height:1.15; }
.esa-section-head p{ font-size:15.5px; color:var(--ink-mute); }
.esa-section-head--left{ text-align:left; margin-left:0; max-width:none; }

.esa-eyebrow{
  display:inline-flex; align-items:center; gap:var(--sp-2);
  font-size:11px; font-weight:700; letter-spacing:.18em; text-transform:uppercase;
  color:var(--brand); padding:6px 12px; border-radius:var(--radius-pill);
  background:var(--brand-soft); border:1px solid var(--brand-line);
  line-height:1.4;
}
.esa-eyebrow--ok{ color:var(--ok); background:var(--ok-soft); border-color:var(--ok-line); }
.esa-eyebrow--warn{ color:var(--warn); background:var(--warn-soft); border-color:var(--warn-line); }
.esa-eyebrow--crit{ color:var(--crit); background:var(--crit-soft); border-color:var(--crit-line); }
.esa-eyebrow--neutral{ color:var(--ink-dim); background:var(--idle-soft); border-color:var(--idle-line); }

/* generic grids. The gap is the GROUP step — cards in one grid are peers,
   and peers sit closer than sections do. */
.esa-grid{ display:grid; gap:var(--sp-group); grid-template-columns:1fr; }
.esa-grid--2{ grid-template-columns:repeat(2,minmax(0,1fr)); }
.esa-grid--3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
.esa-grid--4{ grid-template-columns:repeat(4,minmax(0,1fr)); }
.esa-grid--sidecar{ grid-template-columns:minmax(0,1.6fr) minmax(0,1fr); }
.esa-grid--tight{ gap:var(--sp-tight); }

.esa-row{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap; }
.esa-row--tight{ gap:var(--sp-2); }
.esa-spread{ display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3); flex-wrap:wrap; }
.esa-stack{ display:flex; flex-direction:column; gap:var(--sp-group); }
.esa-stack--tight{ gap:var(--sp-tight); }
.esa-divider{ height:1px; background:var(--line); border:0; margin:var(--sp-group) 0; }

@media (max-width:1180px){
  .esa-grid--4{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .esa-grid--sidecar{ grid-template-columns:1fr; }
}
@media (max-width:980px){
  .esa-grid--3{ grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (max-width:680px){
  .esa-grid--2,.esa-grid--3,.esa-grid--4{ grid-template-columns:1fr; }
  /* Only the horizontal padding shrinks by hand. The vertical steps follow
     --sp-section, which theme.css already stepped down to 28px here, so the
     rhythm cannot invert on a phone the way it used to. */
  .esa-page{ padding:var(--sp-section) var(--sp-4) var(--sp-8); }
}

/* ==========================================================================
   2. TOPBAR
   ========================================================================== */
.esa-topbar{
  position:sticky; top:0; z-index:60;
  grid-column:1/-1;
  background:
    radial-gradient(1200px 220px at 50% -10%, rgba(var(--brand-rgb),.08), transparent 70%),
    rgba(13,17,33,.82);
  -webkit-backdrop-filter:blur(16px) saturate(140%);
  backdrop-filter:blur(16px) saturate(140%);
  border-bottom:1px solid var(--line);
  box-shadow:0 4px 18px rgba(0,0,0,.25);
}
.esa-topbar__inner{
  max-width:1560px; margin:0 auto;
  min-height:var(--topbar-h);
  padding:0 var(--sp-6);
  display:flex; align-items:center; gap:var(--sp-4);
}
.esa-topbar__nav{ display:flex; align-items:center; gap:2px; flex:1 1 auto; min-width:0; }
.esa-topbar__right{ display:flex; align-items:center; gap:var(--sp-2); margin-left:auto; }

/* brand lockup (left) */
.esa-brand{
  display:inline-flex; align-items:center; gap:10px;
  color:#fff; font-family:var(--font-head); font-weight:800;
  font-size:15.5px; letter-spacing:-.01em; white-space:nowrap;
  padding:6px 4px; border-radius:var(--radius-sm);
}
.esa-brand:hover{ color:#fff; text-decoration:none; }

/* THE WORDMARK. public/assets/img/esagames-logo.png is 204x37 with alpha:
   "ESA" in #fff, "GAMES" in a violet gradient (#4d36f9 -> #8a5dff) that sits
   inside the brand family already.

   Two rules matter here and both are about not degrading it.

   1. Only ever scale DOWN. The <img> carries width="204" height="37" so the
      aspect ratio is known before the file loads (no layout shift); this caps
      the rendered height at 26px and lets `width:auto` derive 143px from the
      attributes. A wordmark drawn larger than it was authored goes soft on the
      stems, and there is no larger source to grow into.

   2. Lift the dark end of the gradient off the bar. The topbar paints
      rgba(13,17,33,.82) over --bg, and the darkest stop of "GAMES" (#4d36f9)
      is only ~2.9:1 against it -- legible, but the G reads dimmer than the
      white "ESA" beside it. A drop-shadow in the brand colour haloes the
      LETTERFORMS (drop-shadow follows the alpha channel, box-shadow would draw
      a rectangle) and puts light behind exactly the glyphs that need it. This
      replaces the glow the old .esa-brand__mark square carried, so the left of
      the bar keeps the same weight. No palette change, no edit to the file. */
.esa-brand__logo{
  display:block;
  height:26px; width:auto; flex:0 0 auto;
  filter:drop-shadow(0 1px 10px rgba(var(--brand-rgb),.45));
}
.esa-brand__sub{
  color:var(--ink-dim); font-family:var(--font-ui); font-weight:600;
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  padding-left:10px; margin-left:2px; border-left:1px solid var(--line-2);
}

/* nav links */
.esa-nav{ display:flex; align-items:center; gap:2px; flex-wrap:wrap; list-style:none; margin:0; padding:0; }
.esa-nav li{ margin:0; }
.esa-nav__link{
  display:inline-flex; align-items:center; gap:8px;
  padding:9px 14px; border-radius:10px;
  font-size:14.5px; font-weight:600; color:var(--ink);
  transition:color .18s var(--ease), background .18s var(--ease);
  white-space:nowrap;
}
.esa-nav__link svg{ width:16px; height:16px; color:var(--brand); flex:0 0 auto; }
.esa-nav__link:hover{ color:var(--brand); background:var(--brand-soft); text-decoration:none; }
.esa-nav__link.is-active{ color:var(--brand); background:var(--brand-soft); }
.esa-nav__link[aria-current="page"]{ color:var(--brand); background:var(--brand-soft); }

/* quiet text link used in the topbar right cluster ("Sign out") */
.esa-ghostlink{
  display:inline-flex; align-items:center; gap:6px;
  padding:9px 14px; border-radius:var(--radius-pill);
  color:var(--ink); font-weight:700; font-size:14px;
  transition:color .18s var(--ease);
}
.esa-ghostlink:hover{ color:var(--brand); text-decoration:none; }
.esa-ghostlink svg{ width:15px; height:15px; color:var(--brand); }

/* the pill-shaped brand button on the right */
.esa-userpill{
  display:inline-flex; align-items:center; gap:9px;
  padding:8px 16px 8px 8px; border-radius:var(--radius-pill);
  background:linear-gradient(135deg,var(--brand) 0,var(--brand2) 100%);
  color:var(--brand-ink); font-weight:800; font-size:13.5px;
  border:0; box-shadow:0 3px 12px rgba(var(--brand-rgb),.30);
  transition:box-shadow .18s var(--ease), transform .18s var(--ease);
  max-width:260px;
}
.esa-userpill:hover{
  color:var(--brand-ink); text-decoration:none;
  transform:translateY(-1px);
  box-shadow:0 8px 22px rgba(var(--brand-rgb),.42);
}
.esa-userpill__avatar{
  width:24px; height:24px; flex:0 0 24px; border-radius:var(--radius-pill);
  display:grid; place-items:center;
  background:rgba(255,255,255,.22); color:var(--brand-ink);
  font-size:11px; font-weight:800; letter-spacing:.02em;
  text-transform:uppercase;
}
.esa-userpill__text{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.esa-userpill__role{
  font-size:10px; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
  padding:2px 7px; border-radius:var(--radius-pill);
  background:rgba(0,0,0,.22); color:rgba(255,255,255,.92);
}

/* hamburger — only shown when the sidebar collapses */
.esa-navtoggle{
  display:none; width:38px; height:38px; border-radius:10px;
  place-items:center; color:var(--ink);
  border:1px solid var(--line-2); background:var(--surface);
}
.esa-navtoggle:hover{ color:var(--brand); border-color:var(--brand-line); }
.esa-navtoggle svg{ width:18px; height:18px; }

@media (max-width:1080px){
  .esa-topbar__inner{ padding:0 var(--sp-4); }
  .esa-brand__sub{ display:none; }
}
@media (max-width:860px){
  .esa-navtoggle{ display:grid; }
  .esa-topbar__nav{ display:none; }
  .esa-userpill__text{ display:none; }
  .esa-userpill{ padding:8px; }
  .esa-userpill__role{ display:none; }
}

/* ==========================================================================
   3. SIDEBAR (admin only)
   ========================================================================== */
.esa-sidebar{
  position:sticky; top:var(--topbar-h);
  align-self:start;
  height:calc(100vh - var(--topbar-h));
  height:calc(100dvh - var(--topbar-h));
  overflow-y:auto;
  padding:var(--sp-5) var(--sp-3);
  background:linear-gradient(180deg,var(--bg-2),rgba(21,26,45,.55));
  border-right:1px solid var(--line);
  display:flex; flex-direction:column; gap:var(--sp-5);
}
.esa-sidebar__group{ display:flex; flex-direction:column; gap:2px; }
.esa-sidebar__title{
  padding:0 12px var(--sp-2);
  font-size:10.5px; font-weight:800; letter-spacing:.16em; text-transform:uppercase;
  color:var(--ink-dim);
}
.esa-sidebar__link{
  display:flex; align-items:center; gap:10px;
  padding:9px 12px; border-radius:10px;
  color:var(--ink-mute); font-size:14px; font-weight:600;
  border:1px solid transparent;
  transition:color .18s var(--ease), background .18s var(--ease), border-color .18s var(--ease);
}
.esa-sidebar__link svg{ width:16px; height:16px; flex:0 0 16px; color:var(--ink-dim);
  transition:color .18s var(--ease); }
.esa-sidebar__link:hover{
  color:var(--ink); background:rgba(148,163,184,.06);
  border-color:var(--line); text-decoration:none;
}
.esa-sidebar__link:hover svg{ color:var(--brand); }
.esa-sidebar__link.is-active,
.esa-sidebar__link[aria-current="page"]{
  color:#fff; background:var(--brand-soft); border-color:var(--brand-line);
  box-shadow:inset 2px 0 0 var(--brand);
}
.esa-sidebar__link.is-active svg,
.esa-sidebar__link[aria-current="page"] svg{ color:var(--brand); }
.esa-sidebar__badge{
  margin-left:auto; min-width:20px; padding:1px 7px; border-radius:var(--radius-pill);
  background:var(--crit-soft); color:var(--crit);
  border:1px solid var(--crit-line);
  font-size:11px; font-weight:800; text-align:center;
  font-variant-numeric:tabular-nums;
}
.esa-sidebar__foot{ margin-top:auto; padding:var(--sp-3) 12px 0; border-top:1px solid var(--line);
  color:var(--ink-dim); font-size:11.5px; line-height:1.5; }

.esa-sidebar-backdrop{ display:none; }

@media (max-width:860px){
  .esa-shell--admin{ grid-template-columns:1fr; }
  .esa-sidebar{
    position:fixed; top:var(--topbar-h); left:0; z-index:70;
    width:min(86vw,var(--sidebar-w));
    transform:translateX(-102%);
    transition:transform .24s var(--ease);
    box-shadow:var(--shadow);
    background:var(--bg-2);
  }
  .esa-sidebar.is-open{ transform:none; }
  .esa-sidebar-backdrop{
    display:block; position:fixed; inset:var(--topbar-h) 0 0; z-index:65;
    background:rgba(5,8,18,.62); opacity:0; pointer-events:none;
    transition:opacity .24s var(--ease);
  }
  .esa-sidebar-backdrop.is-open{ opacity:1; pointer-events:auto; }
}

/* ==========================================================================
   4. CARDS
   ========================================================================== */
.esa-card{
  position:relative;
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius);
  overflow:hidden;
  transition:border-color .25s var(--ease), box-shadow .25s var(--ease),
             transform .25s var(--ease);
}
.esa-card--hover:hover{
  transform:translateY(-3px);
  border-color:var(--line-2);
  box-shadow:var(--shadow-sm);
}
.esa-card--glow{ border-color:rgba(var(--brand-rgb),.40); box-shadow:var(--glow); }
.esa-card--glow::before{
  content:""; position:absolute; inset:0 0 auto; height:3px; z-index:2;
  background:linear-gradient(90deg,transparent,var(--brand),transparent);
}
.esa-card--muted{ background:var(--bg-2); }
.esa-card--sunken{ background:rgba(13,17,33,.55); }
.esa-card--ok{ border-color:var(--ok-line); }
.esa-card--warn{ border-color:var(--warn-line); }
.esa-card--crit{ border-color:var(--crit-line); box-shadow:0 0 0 1px rgba(239,68,68,.16); }

.esa-card__head{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-4); flex-wrap:wrap;
  padding:var(--sp-5) var(--sp-5) var(--sp-4);
  border-bottom:1px solid var(--line);
  background:linear-gradient(180deg,rgba(var(--brand-rgb),.05),transparent);
}
.esa-card__titlewrap{ min-width:0; display:flex; flex-direction:column; gap:4px; }
.esa-card__title{
  margin:0; font-size:16px; font-weight:700; color:#fff;
  display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap;
}
.esa-card__title svg{ width:17px; height:17px; color:var(--brand); }
.esa-card__sub{ margin:0; font-size:13px; color:var(--ink-dim); }
.esa-card__actions{ display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap; }
.esa-card__body{ padding:var(--sp-5); }
.esa-card__body--flush{ padding:0; }
.esa-card__foot{
  padding:var(--sp-4) var(--sp-5);
  border-top:1px solid var(--line);
  background:rgba(13,17,33,.35);
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-3); flex-wrap:wrap;
  font-size:13px; color:var(--ink-dim);
}
.esa-card__ic{
  width:44px; height:44px; flex:0 0 44px; border-radius:13px;
  display:grid; place-items:center;
  background:var(--brand-soft); border:1px solid var(--brand-line);
  color:var(--brand);
}
.esa-card__ic svg{ width:21px; height:21px; }

@media (max-width:680px){
  .esa-card__head,.esa-card__body{ padding:var(--sp-4); }
  .esa-card__foot{ padding:var(--sp-3) var(--sp-4); }
}

/* ==========================================================================
   5. STAT TILES
   ========================================================================== */
.esa-stats{ display:grid; gap:var(--sp-group); grid-template-columns:repeat(4,minmax(0,1fr)); }
.esa-stats--3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
.esa-stats--2{ grid-template-columns:repeat(2,minmax(0,1fr)); }

.esa-stat{
  position:relative; overflow:hidden;
  padding:var(--sp-5);
  border-radius:var(--radius);
  background:var(--surface);
  border:1px solid var(--line);
  display:flex; flex-direction:column; gap:var(--sp-2);
  transition:border-color .25s var(--ease), transform .25s var(--ease);
}
.esa-stat:hover{ border-color:var(--line-2); transform:translateY(-2px); }
.esa-stat::after{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--brand); opacity:.55;
}
.esa-stat__label{
  font-size:11px; font-weight:800; letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink-dim);
}
.esa-stat__figure{ display:flex; align-items:baseline; gap:6px; flex-wrap:wrap; }
.esa-stat__value{
  font-family:var(--font-head); font-size:30px; font-weight:800; line-height:1;
  color:#fff; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
}
.esa-stat__unit{ font-size:13.5px; font-weight:700; color:var(--ink-mute); }
.esa-stat__trend{
  display:inline-flex; align-items:center; gap:5px;
  font-size:12.5px; font-weight:700; color:var(--ink-dim);
}
.esa-stat__trend svg{ width:13px; height:13px; }
.esa-stat__trend.is-up{ color:var(--ok); }
.esa-stat__trend.is-down{ color:var(--crit); }
.esa-stat__trend.is-flat{ color:var(--ink-dim); }
.esa-stat__foot{ font-size:12.5px; color:var(--ink-dim); }

.esa-stat--brand::after{ background:var(--brand); }
.esa-stat--ok::after{ background:var(--ok); }
.esa-stat--ok .esa-stat__value{ color:var(--ok); }
.esa-stat--warn::after{ background:var(--warn); }
.esa-stat--warn .esa-stat__value{ color:var(--warn); }
.esa-stat--crit::after{ background:var(--crit); }
.esa-stat--crit .esa-stat__value{ color:var(--crit); }
.esa-stat--info::after{ background:var(--info); }
.esa-stat--info .esa-stat__value{ color:var(--info); }
.esa-stat--neutral::after{ background:var(--idle); opacity:.35; }

/* ---- "no data yet" is not "zero" ----------------------------------------
   A tile whose value is the em-dash placeholder used to render it at the full
   30px display weight, so an unmeasured node looked exactly as authoritative
   as a measured one and a reader could take the dash for a real reading.
   partials/stat.php marks those tiles `.is-empty` (§8). Quieter, not hidden:
   the label still tells you WHAT is missing, and the tile keeps its box so
   the grid does not reflow when the first sample lands. No new colour — it
   is --ink-dim, the same token every other secondary line uses. */
.esa-stat.is-empty .esa-stat__value{
  font-size:22px; color:var(--ink-dim); letter-spacing:.02em;
}
.esa-stat.is-empty::after{ opacity:.22; }
.esa-stat.is-empty .esa-stat__label{ color:var(--ink-dim); }

@media (max-width:1180px){ .esa-stats{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (max-width:980px){ .esa-stats--3{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (max-width:560px){
  .esa-stats,.esa-stats--3,.esa-stats--2{ grid-template-columns:1fr; }
}

/* ==========================================================================
   6. SIGNALS — the three-part status readout (Routed / Forwarding / Origin)
   Deliberately NOT a single binary light: every signal carries its own
   label, a worded state, and a hint sentence. See CONTRACT.md §three-signal.
   ========================================================================== */
.esa-signals{
  /* the SUB-group step: three signals are one readout, not three sections */
  display:grid; gap:var(--sp-tight);
  grid-template-columns:repeat(3,minmax(0,1fr));
  align-items:stretch;
}
.esa-signals--stack{ grid-template-columns:1fr; }

.esa-signal{
  position:relative; overflow:hidden;
  display:grid;
  grid-template-columns:auto minmax(0,1fr);
  grid-template-rows:auto auto;
  column-gap:var(--sp-3); row-gap:2px;
  padding:var(--sp-4) var(--sp-4) var(--sp-4) var(--sp-4);
  border-radius:var(--radius-sm);
  background:var(--surface-2);
  border:1px solid var(--line-2);
  transition:border-color .25s var(--ease), background .25s var(--ease);
}
/* the vertical state stripe — reinforces state without relying on hue alone */
.esa-signal::before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--_sig,var(--idle));
}
.esa-signal__dot{
  grid-row:1/3; align-self:center;
  position:relative;
  width:14px; height:14px; flex:0 0 14px; border-radius:var(--radius-pill);
  background:var(--_sig,var(--idle));
  box-shadow:0 0 0 4px color-mix(in srgb, var(--_sig,var(--idle)) 18%, transparent);
}
/* animated halo — only on live states, never on idle */
.esa-signal__dot::after{
  content:""; position:absolute; inset:-4px; border-radius:inherit;
  border:1px solid var(--_sig,var(--idle));
  opacity:0;
}
.esa-signal.is-ok .esa-signal__dot::after,
.esa-signal.is-warn .esa-signal__dot::after{
  animation:esa-signal-pulse 2.6s ease-out infinite;
}
.esa-signal.is-crit .esa-signal__dot::after{
  animation:esa-signal-pulse 1.2s ease-out infinite;
}
@keyframes esa-signal-pulse{
  0%{transform:scale(.7);opacity:.9}
  70%{transform:scale(1.9);opacity:0}
  100%{transform:scale(1.9);opacity:0}
}

.esa-signal__label{
  grid-column:2; grid-row:1;
  display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap;
  font-size:11px; font-weight:800; letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink-dim);
}
.esa-signal__state{
  font-size:10px; font-weight:800; letter-spacing:.10em; text-transform:uppercase;
  padding:2px 8px; border-radius:var(--radius-pill);
  color:var(--_sig,var(--idle));
  background:color-mix(in srgb, var(--_sig,var(--idle)) 14%, transparent);
  border:1px solid color-mix(in srgb, var(--_sig,var(--idle)) 34%, transparent);
  letter-spacing:.08em;
}
.esa-signal__hint{
  grid-column:2; grid-row:2;
  margin:0; font-size:13px; line-height:1.45; color:var(--ink-mute);
}
.esa-signal__hint strong{ color:var(--ink); }

.esa-signal.is-ok{ --_sig:var(--ok); border-color:var(--ok-line); background:linear-gradient(180deg,var(--ok-soft),transparent 70%),var(--surface-2); }
.esa-signal.is-warn{ --_sig:var(--warn); border-color:var(--warn-line); background:linear-gradient(180deg,var(--warn-soft),transparent 70%),var(--surface-2); }
.esa-signal.is-crit{ --_sig:var(--crit); border-color:var(--crit-line); background:linear-gradient(180deg,var(--crit-soft),transparent 70%),var(--surface-2); }
.esa-signal.is-idle{ --_sig:var(--idle); border-color:var(--line); background:var(--bg-2); }
.esa-signal.is-idle .esa-signal__dot{
  background:transparent;
  border:2px dashed var(--idle);
  box-shadow:none;
}

/* fallback for engines without color-mix() */
@supports not (color: color-mix(in srgb, red 10%, blue)){
  .esa-signal__dot{ box-shadow:0 0 0 4px rgba(148,163,184,.14); }
  .esa-signal__state{ background:rgba(148,163,184,.12); border-color:var(--line-2); }
}

@media (max-width:900px){ .esa-signals{ grid-template-columns:1fr; } }

/* ==========================================================================
   7. BANNERS
   ========================================================================== */
.esa-banner{
  display:flex; align-items:flex-start; gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-5);
  border-radius:var(--radius);
  background:var(--surface);
  border:1px solid var(--line-2);
  border-left:3px solid var(--idle);
}
.esa-banner__icon{
  flex:0 0 36px; width:36px; height:36px; border-radius:11px;
  display:grid; place-items:center;
  background:var(--idle-soft); color:var(--ink-mute);
}
.esa-banner__icon svg{ width:19px; height:19px; }
.esa-banner__body{ flex:1 1 auto; min-width:0; }
.esa-banner__title{ margin:0 0 4px; font-size:15px; font-weight:700; color:#fff;
  font-family:var(--font-head); }
.esa-banner__text{ margin:0; font-size:13.5px; line-height:1.55; color:var(--ink-mute); }
.esa-banner__cta{ flex:0 0 auto; align-self:center; }

.esa-banner--ok{ border-left-color:var(--ok); background:linear-gradient(90deg,var(--ok-soft),transparent 55%),var(--surface); }
.esa-banner--ok .esa-banner__icon{ background:var(--ok-soft); color:var(--ok); }
.esa-banner--warn{ border-left-color:var(--warn); background:linear-gradient(90deg,var(--warn-soft),transparent 55%),var(--surface); }
.esa-banner--warn .esa-banner__icon{ background:var(--warn-soft); color:var(--warn); }
.esa-banner--crit{ border-left-color:var(--crit); background:linear-gradient(90deg,var(--crit-soft),transparent 55%),var(--surface); }
.esa-banner--crit .esa-banner__icon{ background:var(--crit-soft); color:var(--crit); }
.esa-banner--info{ border-left-color:var(--info); background:linear-gradient(90deg,var(--info-soft),transparent 55%),var(--surface); }
.esa-banner--info .esa-banner__icon{ background:var(--info-soft); color:var(--info); }
.esa-banner--brand{ border-left-color:var(--brand); background:linear-gradient(90deg,var(--brand-soft),transparent 55%),var(--surface); }
.esa-banner--brand .esa-banner__icon{ background:var(--brand-soft); color:var(--brand); }

/* big centered call-to-action band (marketing rhythm, used on empty dashboards) */
.esa-cta{
  position:relative; overflow:hidden; text-align:center;
  margin:var(--sp-9) 0 var(--sp-5);
  padding:var(--sp-8) var(--sp-7);
  border-radius:var(--radius-lg);
  background:
    radial-gradient(700px 300px at 50% -30%, rgba(var(--brand-rgb),.20), transparent 60%),
    linear-gradient(160deg,var(--surface),var(--surface-2));
  border:1px solid var(--line-2);
}
.esa-cta h2{ margin-bottom:var(--sp-3); }
.esa-cta p{ max-width:560px; margin:0 auto var(--sp-5); font-size:15.5px; }
.esa-cta__actions{ display:flex; gap:var(--sp-3); justify-content:center; flex-wrap:wrap; }

@media (max-width:680px){
  .esa-banner{ flex-wrap:wrap; padding:var(--sp-4); }
  .esa-banner__cta{ width:100%; }
  .esa-cta{ padding:var(--sp-6) var(--sp-4); }
}

/* ==========================================================================
   8. FLASH STACK
   ========================================================================== */
.esa-flash{ display:flex; flex-direction:column; gap:var(--sp-tight); margin-bottom:var(--sp-group); }

/* ==========================================================================
   9. BUTTONS
   ========================================================================== */
.esa-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:9px;
  padding:11px 20px;
  border-radius:11px;
  border:1px solid transparent;
  font-family:var(--font-ui);
  font-size:14.5px; font-weight:700; line-height:1.2;
  white-space:nowrap; cursor:pointer;
  transition:transform .18s var(--ease), box-shadow .18s var(--ease),
             background .18s var(--ease), border-color .18s var(--ease),
             color .18s var(--ease);
}
.esa-btn:hover{ text-decoration:none; }
.esa-btn svg{ width:16px; height:16px; flex:0 0 auto; }
.esa-btn:active{ transform:translateY(0) scale(.985); }
.esa-btn:disabled,.esa-btn.is-disabled,.esa-btn[aria-disabled="true"]{
  opacity:.5; pointer-events:none; transform:none; box-shadow:none;
}

.esa-btn--primary{
  background:var(--brand); color:var(--brand-ink);
  border-color:var(--brand);
  box-shadow:0 10px 26px rgba(var(--brand-rgb),.28);
}
.esa-btn--primary:hover{
  background:var(--brand2); color:var(--brand-ink);
  transform:translateY(-2px);
  box-shadow:0 16px 36px rgba(var(--brand-rgb),.40);
}
.esa-btn--ghost{
  background:transparent; color:var(--ink);
  border-color:var(--line-2);
}
.esa-btn--ghost:hover{
  color:var(--brand); border-color:var(--brand);
  background:rgba(var(--brand-rgb),.06);
  transform:translateY(-2px);
}
.esa-btn--soft{
  background:var(--brand-soft); color:var(--brand);
  border-color:var(--brand-line);
}
.esa-btn--soft:hover{ background:rgba(var(--brand-rgb),.20); color:var(--brand); transform:translateY(-2px); }
.esa-btn--danger{
  background:var(--crit); color:#fff; border-color:var(--crit);
  box-shadow:0 10px 26px rgba(239,68,68,.24);
}
.esa-btn--danger:hover{ background:#dc2626; color:#fff; transform:translateY(-2px);
  box-shadow:0 16px 34px rgba(239,68,68,.34); }
.esa-btn--danger-ghost{ background:transparent; color:var(--crit); border-color:var(--crit-line); }
.esa-btn--danger-ghost:hover{ background:var(--crit-soft); color:var(--crit); }
.esa-btn--quiet{ background:transparent; color:var(--ink-mute); border-color:transparent; padding:9px 12px; }
.esa-btn--quiet:hover{ color:var(--ink); background:rgba(148,163,184,.08); }

.esa-btn--sm{ padding:7px 13px; font-size:13px; border-radius:9px; gap:7px; }
.esa-btn--sm svg{ width:14px; height:14px; }
.esa-btn--lg{ padding:14px 26px; font-size:15.5px; border-radius:12px; }
.esa-btn--block{ width:100%; }
.esa-btn--icon{ padding:0; width:36px; height:36px; border-radius:10px; }
.esa-btn--icon.esa-btn--sm{ width:30px; height:30px; border-radius:8px; }

/* loading state — spinner replaces the leading icon */
.esa-btn.is-loading{ pointer-events:none; position:relative; color:transparent!important; }
.esa-btn.is-loading svg{ visibility:hidden; }
.esa-btn.is-loading::after{
  content:""; position:absolute; width:16px; height:16px; border-radius:50%;
  border:2px solid currentColor; border-top-color:transparent;
  color:var(--brand-ink);
  animation:esa-spin .7s linear infinite;
}
.esa-btn--ghost.is-loading::after,.esa-btn--soft.is-loading::after{ color:var(--brand); }
@keyframes esa-spin{ to{ transform:rotate(360deg); } }

.esa-btngroup{ display:inline-flex; gap:var(--sp-2); flex-wrap:wrap; align-items:center; }

/* ==========================================================================
   10. BADGES / PILLS / CHIPS
   ========================================================================== */
.esa-badge{
  display:inline-flex; align-items:center; gap:6px;
  padding:3px 10px; border-radius:var(--radius-pill);
  font-size:11.5px; font-weight:800; letter-spacing:.04em;
  line-height:1.6; white-space:nowrap;
  background:var(--idle-soft); color:var(--ink-mute);
  border:1px solid var(--idle-line);
}
.esa-badge svg{ width:12px; height:12px; }
.esa-badge--ok{ background:var(--ok-soft); color:var(--ok); border-color:var(--ok-line); }
.esa-badge--warn{ background:var(--warn-soft); color:var(--warn); border-color:var(--warn-line); }
.esa-badge--crit{ background:var(--crit-soft); color:var(--crit); border-color:var(--crit-line); }
.esa-badge--info{ background:var(--info-soft); color:var(--info); border-color:var(--info-line); }
.esa-badge--brand{ background:var(--brand-soft); color:var(--brand); border-color:var(--brand-line); }
.esa-badge--solid{
  background:linear-gradient(135deg,var(--brand),var(--brand2));
  color:var(--brand-ink); border-color:transparent;
  box-shadow:0 6px 16px rgba(var(--brand-rgb),.24);
}
.esa-badge--dot::before{
  content:""; width:6px; height:6px; border-radius:var(--radius-pill);
  background:currentColor; flex:0 0 6px;
}

/* neutral surface chip — key/value fragments, protected IPs, port lists */
.esa-chip{
  display:inline-flex; align-items:center; gap:7px;
  padding:6px 11px; border-radius:10px;
  background:var(--surface-2); border:1px solid var(--line-2);
  color:var(--ink-mute); font-size:13px; font-weight:600;
  white-space:nowrap;
}
.esa-chip svg{ width:14px; height:14px; color:var(--brand); }
.esa-chip--mono{ font-family:var(--font-mono); font-size:12.5px; letter-spacing:-.01em; color:var(--ink); }
.esa-chip--brand{ background:var(--brand-soft); border-color:var(--brand-line); color:var(--brand); }
.esa-chip button{ color:var(--ink-dim); display:inline-grid; place-items:center; }
.esa-chip button:hover{ color:var(--crit); }
.esa-chips{ display:flex; flex-wrap:wrap; gap:var(--sp-2); }

.esa-kbd{
  display:inline-block; padding:2px 7px; border-radius:6px;
  background:var(--bg); border:1px solid var(--line-2);
  border-bottom-width:2px;
  font-family:var(--font-mono); font-size:11.5px; color:var(--ink-mute);
}

.esa-avatar{
  width:34px; height:34px; flex:0 0 34px; border-radius:var(--radius-pill);
  display:grid; place-items:center;
  background:linear-gradient(135deg,var(--brand),var(--brand2));
  color:var(--brand-ink); font-weight:800; font-size:13px; text-transform:uppercase;
}
.esa-avatar--sm{ width:26px; height:26px; flex-basis:26px; font-size:11px; }
.esa-avatar--lg{ width:48px; height:48px; flex-basis:48px; font-size:17px; }

/* ==========================================================================
   11. TABLES
   The wrapper owns the horizontal scroll so the page body never moves.
   ========================================================================== */
.esa-tablewrap{
  width:100%;
  overflow-x:auto;
  -webkit-overflow-scrolling:touch;
  overscroll-behavior-x:contain;
  border-radius:var(--radius);
}
.esa-card__body--flush > .esa-tablewrap{ border-radius:0; }

.esa-table{
  width:100%; min-width:640px;
  font-size:14px;
  border-collapse:separate; border-spacing:0;
}
.esa-table thead th{
  position:sticky; top:0; z-index:1;
  padding:11px var(--sp-4);
  background:var(--bg-2);
  color:var(--ink-dim);
  font-size:11px; font-weight:800; letter-spacing:.13em; text-transform:uppercase;
  white-space:nowrap;
  border-bottom:1px solid var(--line-2);
}
.esa-table tbody td{
  padding:13px var(--sp-4);
  border-bottom:1px solid var(--line);
  color:var(--ink-mute);
  vertical-align:middle;
}
.esa-table tbody tr:last-child td{ border-bottom:0; }
.esa-table tbody tr{ transition:background .16s var(--ease); }
.esa-table tbody tr:hover{ background:rgba(var(--brand-rgb),.05); }
.esa-table tbody tr.is-selected{ background:var(--brand-soft); }
.esa-table tbody tr.is-crit td{ box-shadow:inset 3px 0 0 var(--crit); }
.esa-table tbody tr.is-warn td{ box-shadow:inset 3px 0 0 var(--warn); }
.esa-table td strong,.esa-table th strong{ color:var(--ink); }
.esa-table a{ font-weight:600; }
.esa-table--zebra tbody tr:nth-child(even){ background:rgba(148,163,184,.035); }
.esa-table--compact thead th{ padding:8px var(--sp-3); }
.esa-table--compact tbody td{ padding:9px var(--sp-3); font-size:13.5px; }
.esa-table__num{ text-align:right; font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1; white-space:nowrap; }
.esa-table__mono{ font-family:var(--font-mono); font-size:12.5px; color:var(--ink);
  white-space:nowrap; }
.esa-table__actions{ text-align:right; white-space:nowrap; }
.esa-table__actions .esa-btn{ vertical-align:middle; }
.esa-table caption{ caption-side:top; padding:0 0 var(--sp-3);
  color:var(--ink-dim); font-size:12.5px; text-align:left; }

/* scroll affordance hint under a table that overflows */
.esa-tablehint{ margin-top:var(--sp-2); font-size:12px; color:var(--ink-dim); }
@media (min-width:761px){ .esa-tablehint{ display:none; } }

/* ==========================================================================
   12. KEY / VALUE LIST — tunnel detail readouts
   ========================================================================== */
.esa-kv{ margin:0; display:grid; grid-template-columns:minmax(140px,auto) minmax(0,1fr); }
.esa-kv > dt{
  padding:10px var(--sp-4) 10px 0;
  border-bottom:1px solid var(--line);
  font-size:12.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--ink-dim);
}
.esa-kv > dd{
  margin:0; padding:10px 0;
  border-bottom:1px solid var(--line);
  color:var(--ink); font-size:14px;
  overflow-wrap:anywhere;
}
.esa-kv > dt:last-of-type,.esa-kv > dd:last-of-type{ border-bottom:0; }
.esa-kv--tight > dt,.esa-kv--tight > dd{ padding-top:7px; padding-bottom:7px; }
@media (max-width:560px){
  .esa-kv{ grid-template-columns:1fr; }
  .esa-kv > dt{ border-bottom:0; padding-bottom:2px; }
}

/* ==========================================================================
   13. FORMS
   ========================================================================== */
.esa-form{ display:flex; flex-direction:column; gap:var(--sp-5); }
.esa-fieldset{ display:flex; flex-direction:column; gap:var(--sp-4);
  padding:var(--sp-5); border:1px solid var(--line); border-radius:var(--radius);
  background:rgba(13,17,33,.35); }
.esa-fieldset > legend{
  padding:0 var(--sp-2); margin-left:calc(-1 * var(--sp-2));
  font-size:11px; font-weight:800; letter-spacing:.14em; text-transform:uppercase;
  color:var(--brand);
}
.esa-form-row{ display:grid; gap:var(--sp-4); grid-template-columns:repeat(2,minmax(0,1fr)); }
.esa-form-row--3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
@media (max-width:680px){ .esa-form-row,.esa-form-row--3{ grid-template-columns:1fr; } }

.esa-field{ display:flex; flex-direction:column; gap:7px; min-width:0; }
.esa-label{
  font-size:12.5px; font-weight:700; color:var(--ink);
  display:flex; align-items:center; gap:6px;
}
.esa-label__req{ color:var(--crit); font-weight:800; }
.esa-label__opt{ color:var(--ink-dim); font-weight:600; font-size:11.5px; }

.esa-input,.esa-select,.esa-textarea{
  width:100%;
  padding:11px 14px;
  border-radius:11px;
  background:var(--field);
  border:1px solid rgba(148,163,184,.22);
  color:var(--ink);
  font-size:14.5px; line-height:1.5;
  transition:border-color .18s var(--ease), box-shadow .18s var(--ease),
             background .18s var(--ease);
  min-height:44px;
}
.esa-textarea{ min-height:120px; line-height:1.6; }
.esa-input:hover,.esa-select:hover,.esa-textarea:hover{ border-color:var(--line-2); }
.esa-input:focus,.esa-select:focus,.esa-textarea:focus{
  outline:none;
  border-color:var(--brand);
  box-shadow:0 0 0 4px rgba(var(--brand-rgb),.14);
}
.esa-input:disabled,.esa-select:disabled,.esa-textarea:disabled,
.esa-input[readonly]{
  background:var(--bg-2); color:var(--ink-dim); border-color:var(--line);
}
.esa-input--mono{ font-family:var(--font-mono); font-size:13.5px; letter-spacing:-.01em; }
.esa-select{
  appearance:none; -webkit-appearance:none;
  padding-right:38px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23989fb7' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 13px center;
}
.esa-select option{ background:var(--surface); color:var(--ink); }

.esa-hint{ font-size:12.5px; color:var(--ink-dim); line-height:1.5; }
.esa-error{ font-size:12.5px; color:var(--crit); font-weight:600;
  display:flex; align-items:center; gap:6px; }
.esa-error svg{ width:13px; height:13px; flex:0 0 13px; }
.esa-field.is-invalid .esa-input,
.esa-field.is-invalid .esa-select,
.esa-field.is-invalid .esa-textarea,
.esa-input[aria-invalid="true"]{
  border-color:var(--crit);
  box-shadow:0 0 0 4px rgba(239,68,68,.12);
}

/* input with a leading unit/prefix or a trailing button */
.esa-inputgroup{ display:flex; align-items:stretch; }
.esa-inputgroup > .esa-input{ border-radius:11px 0 0 11px; position:relative; z-index:1; }
.esa-inputgroup > .esa-inputgroup__addon{
  display:inline-flex; align-items:center; gap:6px;
  padding:0 14px;
  background:var(--surface-2); border:1px solid rgba(148,163,184,.22);
  border-left:0; border-radius:0 11px 11px 0;
  color:var(--ink-dim); font-size:13px; font-weight:600; white-space:nowrap;
}
.esa-inputgroup > .esa-inputgroup__addon:first-child{
  border-left:1px solid rgba(148,163,184,.22); border-right:0;
  border-radius:11px 0 0 11px;
}
.esa-inputgroup > .esa-inputgroup__addon:first-child + .esa-input{ border-radius:0 11px 11px 0; }
.esa-inputgroup .esa-btn{ border-radius:0 11px 11px 0; }

/* checkbox / radio */
.esa-check{
  display:flex; align-items:flex-start; gap:10px;
  padding:11px 13px; border-radius:11px;
  background:rgba(148,163,184,.04); border:1px solid var(--line);
  cursor:pointer;
  transition:border-color .18s var(--ease), background .18s var(--ease);
}
.esa-check:hover{ border-color:var(--line-2); background:rgba(148,163,184,.07); }
.esa-check input{
  appearance:none; -webkit-appearance:none;
  width:18px; height:18px; flex:0 0 18px; margin-top:2px;
  border:1.5px solid var(--line-2); border-radius:6px;
  background:var(--bg);
  cursor:pointer;
  transition:background .16s var(--ease), border-color .16s var(--ease);
}
.esa-check input[type="radio"]{ border-radius:var(--radius-pill); }
.esa-check input:checked{
  background:var(--brand) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/12px no-repeat;
  border-color:var(--brand);
}
.esa-check input[type="radio"]:checked{
  background-image:none;
  box-shadow:inset 0 0 0 4px var(--bg);
}
.esa-check__text{ min-width:0; }
.esa-check__label{ display:block; font-size:14px; font-weight:600; color:var(--ink); }
.esa-check__hint{ display:block; font-size:12.5px; color:var(--ink-dim); margin-top:2px; }
.esa-check.is-checked{ border-color:var(--brand-line); background:var(--brand-soft); }

/* toggle switch */
.esa-switch{ display:inline-flex; align-items:center; gap:10px; cursor:pointer; }
.esa-switch input{ position:absolute; opacity:0; width:0; height:0; }
.esa-switch__track{
  width:42px; height:24px; flex:0 0 42px; border-radius:var(--radius-pill);
  background:var(--surface-2); border:1px solid var(--line-2);
  position:relative;
  transition:background .2s var(--ease), border-color .2s var(--ease);
}
.esa-switch__track::after{
  content:""; position:absolute; top:2px; left:2px;
  width:18px; height:18px; border-radius:var(--radius-pill);
  background:var(--ink-dim);
  transition:transform .2s var(--ease), background .2s var(--ease);
}
.esa-switch input:checked + .esa-switch__track{
  background:var(--brand); border-color:var(--brand);
}
.esa-switch input:checked + .esa-switch__track::after{
  transform:translateX(18px); background:#fff;
}
.esa-switch input:focus-visible + .esa-switch__track{
  outline:2px solid var(--brand); outline-offset:2px;
}
.esa-switch__label{ font-size:14px; font-weight:600; color:var(--ink); }

.esa-form-actions{
  display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  padding-top:var(--sp-4); border-top:1px solid var(--line);
}
.esa-form-actions--end{ justify-content:flex-end; }

/* ==========================================================================
   14. CODE BLOCK + COPY BUTTON  (the origin setup script)
   ========================================================================== */
.esa-code{
  border-radius:var(--radius-sm);
  border:1px solid var(--line-2);
  background:#0a0e1c;
  overflow:hidden;
}
.esa-code__head{
  display:flex; align-items:center; gap:var(--sp-3);
  padding:9px var(--sp-4);
  background:var(--bg-2);
  border-bottom:1px solid var(--line);
}
.esa-code__title{
  font-family:var(--font-mono); font-size:12px; font-weight:600;
  color:var(--ink-dim); letter-spacing:.02em;
  display:flex; align-items:center; gap:7px; min-width:0;
}
.esa-code__title svg{ width:14px; height:14px; color:var(--brand); flex:0 0 14px; }
.esa-code__head .esa-copy{ margin-left:auto; }
.esa-code__body{
  max-height:min(60vh,520px);
  overflow:auto;
  padding:var(--sp-4);
}
.esa-code pre{
  margin:0;
  font-family:var(--font-mono);
  font-size:12.8px; line-height:1.65;
  color:#c9d2e6;
  tab-size:2;
  white-space:pre;
}
.esa-code--wrap pre{ white-space:pre-wrap; overflow-wrap:anywhere; }
.esa-code__body::selection,.esa-code pre::selection{ background:rgba(var(--brand-rgb),.4); }

/* one-line command strip with an inline copy button */
.esa-cmd{
  display:flex; align-items:center; gap:var(--sp-3);
  padding:10px var(--sp-3) 10px var(--sp-4);
  border-radius:11px;
  background:#0a0e1c; border:1px solid var(--line-2);
}
.esa-cmd code{
  flex:1 1 auto; min-width:0;
  font-family:var(--font-mono); font-size:13px; color:#c9d2e6;
  overflow-x:auto; white-space:nowrap;
}
.esa-cmd code::before{ content:"$ "; color:var(--brand); font-weight:700; }
.esa-cmd--nodollar code::before{ content:none; }

/* inline code inside prose */
.esa-inlinecode{
  padding:1px 6px; border-radius:6px;
  background:var(--surface-2); border:1px solid var(--line);
  font-family:var(--font-mono); font-size:.88em; color:var(--ink);
}

.esa-copy{
  display:inline-flex; align-items:center; gap:6px;
  padding:5px 11px; border-radius:8px;
  background:var(--surface-2); border:1px solid var(--line-2);
  color:var(--ink-mute);
  font-size:12px; font-weight:700; white-space:nowrap;
  transition:color .18s var(--ease), border-color .18s var(--ease),
             background .18s var(--ease);
}
.esa-copy svg{ width:13px; height:13px; }
.esa-copy:hover{ color:var(--brand); border-color:var(--brand-line); background:var(--brand-soft); }
.esa-copy .esa-copy__done{ display:none; }
.esa-copy.is-done{ color:var(--ok); border-color:var(--ok-line); background:var(--ok-soft); }
.esa-copy.is-done .esa-copy__idle{ display:none; }
.esa-copy.is-done .esa-copy__done{ display:inline-flex; align-items:center; gap:6px; }

/* ==========================================================================
   15. TABS
   ========================================================================== */
.esa-tabs{ display:flex; flex-direction:column; gap:var(--sp-5); }
.esa-tabs__list{
  display:flex; gap:4px; flex-wrap:wrap;
  padding:5px;
  border-radius:13px;
  background:var(--bg-2);
  border:1px solid var(--line);
}
.esa-tab{
  display:inline-flex; align-items:center; gap:8px;
  padding:9px 16px; border-radius:9px;
  font-size:14px; font-weight:700; color:var(--ink-mute);
  border:1px solid transparent;
  transition:color .18s var(--ease), background .18s var(--ease),
             border-color .18s var(--ease);
}
.esa-tab svg{ width:15px; height:15px; }
.esa-tab:hover{ color:var(--ink); background:rgba(148,163,184,.07); text-decoration:none; }
.esa-tab.is-active,.esa-tab[aria-selected="true"]{
  color:#fff; background:var(--surface);
  border-color:var(--line-2);
  box-shadow:var(--shadow-sm);
}
.esa-tab.is-active svg,.esa-tab[aria-selected="true"] svg{ color:var(--brand); }
.esa-tab__count{
  padding:0 7px; border-radius:var(--radius-pill);
  background:rgba(148,163,184,.14); color:var(--ink-dim);
  font-size:11px; font-weight:800;
}
/* .esa-tabs__panel[hidden] is handled by the global [hidden] reset in theme.css */

/* underline variant, for in-card sub-navigation */
.esa-tabs--line .esa-tabs__list{ background:none; border:0; border-bottom:1px solid var(--line);
  border-radius:0; padding:0; gap:var(--sp-2); }
.esa-tabs--line .esa-tab{ border-radius:0; border:0; border-bottom:2px solid transparent;
  padding:10px 4px; margin-bottom:-1px; box-shadow:none; background:none; }
.esa-tabs--line .esa-tab.is-active,
.esa-tabs--line .esa-tab[aria-selected="true"]{
  background:none; box-shadow:none; color:var(--brand); border-bottom-color:var(--brand);
}

/* ==========================================================================
   16. ACCORDION (native <details>)
   ========================================================================== */
.esa-acc{
  border:1px solid var(--line);
  border-radius:var(--radius);
  background:var(--surface);
  overflow:hidden;
}
.esa-acc__item + .esa-acc__item{ border-top:1px solid var(--line); }
.esa-acc__sum{
  display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);
  cursor:pointer; list-style:none;
  font-size:15px; font-weight:700; color:var(--ink);
  transition:background .18s var(--ease), color .18s var(--ease);
}
.esa-acc__sum::-webkit-details-marker{ display:none; }
.esa-acc__sum:hover{ background:rgba(var(--brand-rgb),.05); color:#fff; }
.esa-acc__sum::after{
  content:""; margin-left:auto; flex:0 0 16px;
  width:16px; height:16px;
  background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center/16px no-repeat;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center/16px no-repeat;
  color:var(--ink-dim);
  transition:transform .22s var(--ease);
}
.esa-acc__item[open] > .esa-acc__sum{ color:var(--brand); }
.esa-acc__item[open] > .esa-acc__sum::after{ transform:rotate(180deg); }
.esa-acc__body{
  padding:0 var(--sp-5) var(--sp-5);
  font-size:14px; color:var(--ink-mute); line-height:1.65;
}
.esa-acc__sum svg{ width:16px; height:16px; color:var(--brand); flex:0 0 16px; }

/* ==========================================================================
   17. MODAL (native <dialog> or .is-open on a div)
   ========================================================================== */
.esa-modal{
  position:fixed; inset:0; z-index:120;
  display:none;
  align-items:center; justify-content:center;
  padding:var(--sp-4);
  background:rgba(5,8,18,.70);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
}
.esa-modal.is-open{ display:flex; }
.esa-modal[open]{ display:flex; }
dialog.esa-modal{ border:0; max-width:none; max-height:none; width:100%; height:100%; }
dialog.esa-modal::backdrop{ background:rgba(5,8,18,.70); backdrop-filter:blur(6px); }

.esa-modal__panel{
  width:min(560px,100%);
  max-height:calc(100dvh - 2 * var(--sp-6));
  display:flex; flex-direction:column;
  background:var(--surface);
  border:1px solid var(--line-2);
  border-radius:var(--radius);
  box-shadow:var(--shadow);
  animation:esa-modal-in .22s var(--ease);
}
.esa-modal__panel--wide{ width:min(860px,100%); }
@keyframes esa-modal-in{ from{ opacity:0; transform:translateY(14px) scale(.98); } }
.esa-modal__head{
  display:flex; align-items:flex-start; gap:var(--sp-3);
  padding:var(--sp-5) var(--sp-5) var(--sp-4);
  border-bottom:1px solid var(--line);
}
.esa-modal__title{ margin:0; font-size:17px; font-weight:800; color:#fff; }
.esa-modal__sub{ margin:4px 0 0; font-size:13px; color:var(--ink-dim); }
.esa-modal__close{
  margin-left:auto; flex:0 0 32px;
  width:32px; height:32px; border-radius:9px;
  display:grid; place-items:center;
  color:var(--ink-dim); border:1px solid transparent;
}
.esa-modal__close:hover{ color:var(--ink); background:rgba(148,163,184,.10); border-color:var(--line); }
.esa-modal__close svg{ width:16px; height:16px; }
.esa-modal__body{ padding:var(--sp-5); overflow-y:auto; flex:1 1 auto; }
.esa-modal__foot{
  display:flex; align-items:center; justify-content:flex-end; gap:var(--sp-2);
  flex-wrap:wrap;
  padding:var(--sp-4) var(--sp-5);
  border-top:1px solid var(--line);
  background:rgba(13,17,33,.35);
}
.esa-modal--danger .esa-modal__panel{ border-color:var(--crit-line); }
.esa-modal--danger .esa-modal__title{ color:var(--crit); }

/* ==========================================================================
   18. EMPTY STATE
   ========================================================================== */
.esa-empty{
  display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:var(--sp-3);
  padding:var(--sp-8) var(--sp-5);
}
.esa-empty__icon{
  width:60px; height:60px; border-radius:18px;
  display:grid; place-items:center;
  background:var(--brand-soft); border:1px solid var(--brand-line);
  color:var(--brand);
  margin-bottom:var(--sp-1);
}
.esa-empty__icon svg{ width:27px; height:27px; }
.esa-empty__title{ margin:0; font-size:17px; font-weight:800; color:#fff; }
.esa-empty__text{ margin:0; max-width:46ch; font-size:14px; color:var(--ink-dim); line-height:1.6; }
.esa-empty__actions{ display:flex; gap:var(--sp-2); flex-wrap:wrap; justify-content:center;
  margin-top:var(--sp-2); }
.esa-empty--sm{ padding:var(--sp-6) var(--sp-4); }
.esa-empty--sm .esa-empty__icon{ width:46px; height:46px; border-radius:14px; }
.esa-empty--sm .esa-empty__icon svg{ width:21px; height:21px; }

/* ==========================================================================
   19. SKELETON LOADERS
   ========================================================================== */
.esa-skel{
  display:block;
  border-radius:8px;
  background:linear-gradient(90deg,
    rgba(148,163,184,.07) 25%,
    rgba(148,163,184,.16) 37%,
    rgba(148,163,184,.07) 63%);
  background-size:400% 100%;
  animation:esa-shimmer 1.5s ease-in-out infinite;
}
@keyframes esa-shimmer{ 0%{background-position:100% 50%} 100%{background-position:0 50%} }
.esa-skel--text{ height:12px; margin:7px 0; }
.esa-skel--title{ height:20px; width:45%; border-radius:7px; margin:0 0 var(--sp-3); }
.esa-skel--line{ height:12px; width:100%; }
.esa-skel--line-60{ height:12px; width:60%; }
.esa-skel--line-80{ height:12px; width:80%; }
.esa-skel--block{ height:96px; border-radius:var(--radius-sm); }
.esa-skel--tile{ height:132px; border-radius:var(--radius); }
.esa-skel--circle{ width:34px; height:34px; border-radius:var(--radius-pill); flex:0 0 34px; }
.esa-skel--btn{ height:38px; width:118px; border-radius:11px; }
.esa-skel-row{ display:flex; align-items:center; gap:var(--sp-3); padding:13px 0;
  border-bottom:1px solid var(--line); }
.esa-skel-row:last-child{ border-bottom:0; }
/* mark the whole region for assistive tech */
[data-loading="true"]{ pointer-events:none; }

/* ==========================================================================
   20. TOASTS
   ========================================================================== */
.esa-toast-region{
  position:fixed; z-index:140;
  right:var(--sp-4); bottom:var(--sp-4);
  display:flex; flex-direction:column-reverse; gap:var(--sp-2);
  width:min(380px,calc(100vw - 2 * var(--sp-4)));
  pointer-events:none;
}
.esa-toast{
  pointer-events:auto;
  display:flex; align-items:flex-start; gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4);
  border-radius:var(--radius-sm);
  background:var(--surface);
  border:1px solid var(--line-2);
  border-left:3px solid var(--idle);
  box-shadow:var(--shadow);
  animation:esa-toast-in .24s var(--ease);
}
@keyframes esa-toast-in{ from{ opacity:0; transform:translateX(18px); } }
.esa-toast.is-leaving{ animation:esa-toast-out .2s var(--ease) forwards; }
@keyframes esa-toast-out{ to{ opacity:0; transform:translateX(18px); } }
.esa-toast__icon{ flex:0 0 18px; width:18px; height:18px; margin-top:2px; color:var(--ink-mute); }
.esa-toast__body{ flex:1 1 auto; min-width:0; }
.esa-toast__title{ margin:0; font-size:14px; font-weight:700; color:#fff; }
.esa-toast__text{ margin:2px 0 0; font-size:13px; color:var(--ink-mute); line-height:1.5; }
.esa-toast__close{ flex:0 0 20px; width:20px; height:20px; border-radius:6px;
  display:grid; place-items:center; color:var(--ink-dim); }
.esa-toast__close:hover{ color:var(--ink); background:rgba(148,163,184,.12); }
.esa-toast__close svg{ width:13px; height:13px; }
.esa-toast--ok{ border-left-color:var(--ok); }   .esa-toast--ok .esa-toast__icon{ color:var(--ok); }
.esa-toast--warn{ border-left-color:var(--warn); } .esa-toast--warn .esa-toast__icon{ color:var(--warn); }
.esa-toast--crit{ border-left-color:var(--crit); } .esa-toast--crit .esa-toast__icon{ color:var(--crit); }
.esa-toast--info{ border-left-color:var(--info); } .esa-toast--info .esa-toast__icon{ color:var(--info); }
@media (max-width:560px){
  .esa-toast-region{ left:var(--sp-3); right:var(--sp-3); bottom:var(--sp-3); width:auto; }
}

/* ==========================================================================
   21. METER / PROGRESS
   ========================================================================== */
.esa-meter{
  height:8px; width:100%; border-radius:var(--radius-pill);
  background:rgba(148,163,184,.12);
  overflow:hidden;
}
.esa-meter__fill{
  display:block; height:100%; border-radius:inherit;
  background:linear-gradient(90deg,var(--brand),var(--brand2));
  transition:width .5s var(--ease);
}
.esa-meter--ok .esa-meter__fill{ background:var(--ok); }
.esa-meter--warn .esa-meter__fill{ background:var(--warn); }
.esa-meter--crit .esa-meter__fill{ background:var(--crit); }
.esa-meter__legend{ display:flex; justify-content:space-between; gap:var(--sp-2);
  margin-top:6px; font-size:12px; color:var(--ink-dim); font-variant-numeric:tabular-nums; }

/* ==========================================================================
   22. LOGIN
   ========================================================================== */
.esa-authpage{
  min-height:100vh; min-height:100dvh;
  display:flex; align-items:center; justify-content:center;
  padding:var(--sp-6) var(--sp-4);
}
.esa-auth{ width:min(430px,100%); }
.esa-auth__brand{
  display:flex; flex-direction:column; align-items:center; gap:var(--sp-3);
  margin-bottom:var(--sp-5); text-align:center;
}
/* .esa-auth__mark / .esa-brand__mark -- the improvised gradient-square marks
   that stood in for a logo before the real wordmark existed -- are RETIRED.
   Nothing renders them any more (see .esa-auth__logo / .esa-brand__logo and
   docs/DESIGN.md §5a). Do not resurrect them: two brand marks on one product
   is how a panel ends up looking like two products. */
/* The sign-in wordmark. Bigger than the topbar's because this page has nothing
   else on it, but still under the file's 37px natural height -- see
   .esa-brand__logo for why that ceiling is not negotiable. */
.esa-auth__logo{
  display:block; height:34px; width:auto;
  filter:drop-shadow(0 2px 14px rgba(var(--brand-rgb),.45));
}
.esa-auth__name{
  margin:0; font-size:20px; font-weight:800; color:#fff; letter-spacing:-.01em;
  display:flex; flex-direction:column; align-items:center; gap:var(--sp-2);
}
.esa-auth__product{
  font-family:var(--font-ui); font-weight:600; font-size:12px;
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-dim);
}
.esa-auth__tag{ margin:0; font-size:13px; color:var(--ink-dim); }
.esa-auth__card{
  background:var(--surface);
  border:1px solid var(--line-2);
  border-radius:var(--radius);
  box-shadow:var(--glow);
  padding:var(--sp-6);
  position:relative; overflow:hidden;
}
.esa-auth__card::before{
  content:""; position:absolute; inset:0 0 auto; height:3px;
  background:linear-gradient(90deg,transparent,var(--brand),transparent);
}
.esa-auth__foot{
  margin-top:var(--sp-5); text-align:center;
  font-size:12.5px; color:var(--ink-dim); line-height:1.6;
}
.esa-auth__foot a{ font-weight:600; }
@media (max-width:480px){ .esa-auth__card{ padding:var(--sp-5) var(--sp-4); } }

/* ==========================================================================
   23. MISC HELPERS USED BY PAGE TEMPLATES
   ========================================================================== */
/* small labelled group of chips/badges under a heading */
.esa-metaline{ display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap;
  font-size:13px; color:var(--ink-dim); }
.esa-metaline > span{ display:inline-flex; align-items:center; gap:6px; }
.esa-metaline svg{ width:14px; height:14px; color:var(--ink-dim); }

/* an inline "live" ticker dot for auto-refreshing views */
.esa-live{ display:inline-flex; align-items:center; gap:7px;
  font-size:12px; font-weight:700; color:var(--ink-dim);
  text-transform:uppercase; letter-spacing:.1em; }
.esa-live::before{
  content:""; width:7px; height:7px; border-radius:var(--radius-pill);
  background:var(--ok); box-shadow:0 0 0 0 rgba(34,197,94,.5);
  animation:esa-live 2s ease-out infinite;
}
@keyframes esa-live{ 70%{ box-shadow:0 0 0 6px rgba(34,197,94,0); } 100%{ box-shadow:0 0 0 0 rgba(34,197,94,0); } }
.esa-live.is-stale::before{ background:var(--warn); animation:none; }
.esa-live.is-off::before{ background:var(--idle); animation:none; }

/* subtle bordered box for notes inside a card body */
.esa-note{
  padding:var(--sp-3) var(--sp-4);
  border-radius:var(--radius-sm);
  background:rgba(148,163,184,.05);
  border:1px solid var(--line);
  font-size:13.5px; color:var(--ink-mute); line-height:1.6;
}
.esa-note--brand{ background:var(--brand-soft); border-color:var(--brand-line); }
.esa-note--warn{ background:var(--warn-soft); border-color:var(--warn-line); }
.esa-note--crit{ background:var(--crit-soft); border-color:var(--crit-line); }
.esa-note strong{ color:var(--ink); }

/* numbered step list, for the setup walkthrough */
.esa-steps{ list-style:none; margin:0; padding:0; counter-reset:esa-step;
  display:flex; flex-direction:column; gap:var(--sp-4); }
.esa-steps > li{ margin:0; position:relative; padding-left:44px; }
.esa-steps > li::before{
  counter-increment:esa-step; content:counter(esa-step);
  position:absolute; left:0; top:0;
  width:30px; height:30px; border-radius:10px;
  display:grid; place-items:center;
  background:var(--brand-soft); border:1px solid var(--brand-line);
  color:var(--brand); font-size:13px; font-weight:800;
}
.esa-steps__title{ display:block; font-size:14.5px; font-weight:700; color:#fff;
  margin-bottom:4px; padding-top:5px; }
.esa-steps__text{ font-size:13.5px; color:var(--ink-mute); }

/* ==========================================================================
   24. SIZING TOUCH-UPS FOR INLINE SVG INSIDE SMALL CHROME
   partials/icons.php emits 24x24 SVGs; these boxes need them smaller.
   ========================================================================== */
.esa-userpill__avatar svg{ width:14px; height:14px; }
.esa-navtoggle svg,.esa-modal__close svg,.esa-toast__close svg{ display:block; }
.esa-sidebar__foot .esa-live{ margin-bottom:4px; }
.esa-empty__icon svg,.esa-card__ic svg{ display:block; }
.esa-banner__title + .esa-banner__text{ margin-top:2px; }
.esa-steps__title:first-child{ padding-top:5px; }

/* a bare page (login) still paints the body background — no shell needed */
.esa-body{ min-height:100vh; min-height:100dvh; }

/* ==========================================================================
   25. PAGE-TEMPLATE EXTENSIONS
   Components the client/admin page templates (agents 6-8) reach for that are
   not part of the seven contract partials. They live here so every page stays
   inside one design system. Documented in docs/DESIGN.md §28.
   ========================================================================== */

/* ---- 25.1 charts (agent 8 draws into these) ---------------------------- */
.esa-chart{
  position:relative; margin:0; display:block;
  min-height:220px;
  padding:var(--sp-3);
  border-radius:var(--radius-sm);
  background:rgba(13,17,33,.45);
  border:1px solid var(--line);
  overflow:hidden;
}
.esa-chart > svg,.esa-chart > canvas{ display:block; width:100%; height:auto; }
.esa-chart--spark{ min-height:64px; padding:0; background:none; border:0; }
.esa-chart__caption{
  margin:var(--sp-2) 0 0; font-size:12.5px; color:var(--ink-dim); line-height:1.5;
}
/* the embedded JSON payload must never render as text */
.esa-chart > script[type="application/json"]{ display:none; }

/* A chart with nothing in it gets a sentence, never an empty grid, so the
   frame does not have to hold 220px of nothing open around it. */
.esa-chart:has(> .esa-empty){ min-height:0; }

/* The direction key: a 8px dot that ties a table column, a stat tile or a
   caption back to the line of the same colour. Decoration only -- every place
   it appears also names the direction in words, because our pair sits at
   tritan dE 7.5 and colour alone is never allowed to carry identity here. */
.esa-vizkey{
  display:inline-block; width:8px; height:8px; border-radius:var(--radius-pill);
  margin-right:6px; vertical-align:baseline; flex:0 0 8px;
}
.esa-vizkey--out{ background:var(--viz-out); }
.esa-vizkey--in{ background:var(--viz-in); }

/* The dashboard's per-direction sparkline: it lives under a stat tile's own
   figure, so it is unframed and sits tight to the number above it. */
.esa-spark{ margin:var(--sp-2) 0 0; display:block; min-height:0; }
.esa-spark__note{ margin:var(--sp-1) 0 0; font-size:11.5px; color:var(--ink-dim); }
/* charts.js sizes the canvas in fixed px (a sparkline has no axes to reflow).
   On a narrow tile that would push the page sideways, which DESIGN.md §0 rule 2
   forbids outright -- so it is allowed to shrink instead. The bitmap is already
   drawn at devicePixelRatio, so scaling down costs nothing visible. */
.esa-spark canvas{ max-width:100%; }

/* ---- 25.2 code block variants used by the setup page ------------------- */
.esa-code__pre{
  margin:0; padding:var(--sp-4);
  overflow:auto;
  font-family:var(--font-mono); font-size:12.8px; line-height:1.65;
  color:#c9d2e6; tab-size:2; white-space:pre;
  max-height:min(60vh,420px);
}
.esa-code__pre--tall{ max-height:min(72vh,620px); }
.esa-code__pre code{ font:inherit; color:inherit; background:none; padding:0; border:0; }
.esa-code__caption{
  margin:0; padding:9px var(--sp-4);
  background:var(--bg-2); border-bottom:1px solid var(--line);
  font-size:12.5px; color:var(--ink-dim);
}
/* one-line command with a Copy button on the right */
.esa-code--inline{
  display:flex; align-items:center; gap:var(--sp-2);
  flex-wrap:wrap;
  padding-right:var(--sp-2);
}
.esa-code--inline .esa-code__caption{
  flex:1 0 100%; border-bottom:0; background:none;
  padding:var(--sp-2) var(--sp-4) 0;
}
.esa-code--inline .esa-code__pre{
  flex:1 1 auto; min-width:0; white-space:pre-wrap; overflow-wrap:anywhere;
  padding:10px var(--sp-4);
}
.esa-code__copy{ flex:0 0 auto; }

/* inline value + reveal-on-JS copy button (partials in views/client/_helpers) */
.esa-copy__btn{ flex:0 0 auto; margin-left:6px; }
.esa-copy > code{ vertical-align:middle; }
/* .esa-copy is a button in the design system and a wrapper span here; keep the
   wrapper form transparent so it does not inherit the button chrome. */
span.esa-copy{
  display:inline-flex; align-items:center; gap:6px;
  padding:0; background:none; border:0; color:inherit; font-weight:inherit;
}
span.esa-copy:hover{ background:none; border:0; color:inherit; }

/* ---- 25.3 FAQ (support page) ------------------------------------------ */
.esa-faq{
  border:1px solid var(--line);
  border-radius:var(--radius-sm);
  background:var(--surface-2);
  overflow:hidden;
  transition:border-color .2s var(--ease);
}
.esa-faq + .esa-faq{ margin-top:var(--sp-2); }
.esa-faq[open],.esa-faq--open{ border-color:var(--brand-line); }
.esa-faq__q{
  display:flex; align-items:center; gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4);
  cursor:pointer; list-style:none;
  font-size:14.5px; font-weight:700; color:var(--ink);
  transition:background .18s var(--ease), color .18s var(--ease);
}
.esa-faq__q::-webkit-details-marker{ display:none; }
.esa-faq__q:hover{ background:rgba(var(--brand-rgb),.06); color:#fff; }
.esa-faq__q::after{
  content:""; margin-left:auto; flex:0 0 15px; width:15px; height:15px;
  background:var(--ink-dim);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center/15px no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center/15px no-repeat;
  transition:transform .22s var(--ease);
}
.esa-faq[open] > .esa-faq__q{ color:var(--brand); }
.esa-faq[open] > .esa-faq__q::after{ transform:rotate(180deg); background:var(--brand); }
.esa-faq__a{
  padding:0 var(--sp-4) var(--sp-4);
  font-size:13.5px; color:var(--ink-mute); line-height:1.65;
}
.esa-faq__a p{ margin:0; }

/* ---- 25.4 health findings --------------------------------------------- */
.esa-findings{ list-style:none; margin:0; padding:0; }
.esa-findings > li{ margin:0; }
.esa-finding{
  position:relative; overflow:hidden;
  padding:var(--sp-4) var(--sp-4) var(--sp-4) var(--sp-5);
  border-radius:var(--radius-sm);
  background:var(--surface-2);
  border:1px solid var(--line);
}
.esa-finding::before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--idle);
}
.esa-finding > p{ margin:0 0 var(--sp-2); font-size:13.5px; line-height:1.6; }
.esa-finding > p:last-child{ margin-bottom:0; }
.esa-finding > .esa-eyebrow{ margin-bottom:var(--sp-3); }
.esa-finding strong{ color:#fff; }
.esa-finding--ok{ border-color:var(--ok-line); }
.esa-finding--ok::before{ background:var(--ok); }
.esa-finding--info{ border-color:var(--info-line); }
.esa-finding--info::before{ background:var(--info); }
.esa-finding--warn,.esa-finding--warning{ border-color:var(--warn-line); }
.esa-finding--warn::before,.esa-finding--warning::before{ background:var(--warn); }
.esa-finding--crit,.esa-finding--critical,.esa-finding--error{
  border-color:var(--crit-line);
  background:linear-gradient(180deg,var(--crit-soft),transparent 60%),var(--surface-2);
}
.esa-finding--crit::before,.esa-finding--critical::before,.esa-finding--error::before{
  background:var(--crit);
}

/* ---- 25.5 key/value rendered as a <table> ------------------------------ */
.esa-kvtable{ min-width:0; width:100%; }
.esa-kvtable th[scope="row"],.esa-kvtable tbody th{
  width:1%; white-space:nowrap;
  padding:11px var(--sp-4) 11px 0;
  border-bottom:1px solid var(--line);
  vertical-align:top;
  font-size:12.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  color:var(--ink-dim);
  background:none;
}
.esa-kvtable tbody td{
  padding:11px 0; border-bottom:1px solid var(--line);
  color:var(--ink); vertical-align:top;
}
.esa-kvtable tbody tr:last-child th,.esa-kvtable tbody tr:last-child td{ border-bottom:0; }
.esa-kvtable tbody tr:hover{ background:none; }
.esa-kv__note{
  margin:5px 0 0; font-size:12px; color:var(--ink-dim); line-height:1.5;
}

/* ---- 25.6 service switcher & table row states -------------------------- */
.esa-servicebar{
  margin-bottom:var(--sp-group);
  padding-bottom:var(--sp-4);
  border-bottom:1px solid var(--line);
}
.esa-servicebar .esa-chip{ text-decoration:none; }
.esa-servicebar .esa-chip[aria-current="page"],
.esa-servicebar .esa-chip.is-active{
  background:var(--brand-soft); border-color:var(--brand-line); color:var(--brand);
}
.esa-table tbody tr.esa-row--current{ background:var(--brand-soft); }
.esa-table tbody tr.esa-row--current td{ box-shadow:inset 3px 0 0 var(--brand); }

/* semantic table variants — same skin, different natural widths */
.esa-table--bind{ min-width:520px; }
.esa-table--checks{ min-width:480px; }
.esa-table--events{ min-width:600px; }
.esa-table--sessions{ min-width:560px; }
.esa-table--bind.esa-kvtable,.esa-table--checks.esa-kvtable{ min-width:0; }

/* ---- 25.7 field sub-elements used by page forms ------------------------ */
.esa-field__label{
  display:flex; align-items:center; gap:6px;
  font-size:12.5px; font-weight:700; color:var(--ink);
}
.esa-field__hint{ font-size:12.5px; color:var(--ink-dim); line-height:1.5; }

/* ---- 25.8 signal modifier aliases -------------------------------------
   partials/signal.php emits .is-ok/.is-warn/.is-crit/.is-idle. Some templates
   spell the state as a BEM modifier instead; both must look identical.       */
.esa-signal--ok{ --_sig:var(--ok); border-color:var(--ok-line);
  background:linear-gradient(180deg,var(--ok-soft),transparent 70%),var(--surface-2); }
.esa-signal--warn{ --_sig:var(--warn); border-color:var(--warn-line);
  background:linear-gradient(180deg,var(--warn-soft),transparent 70%),var(--surface-2); }
.esa-signal--crit{ --_sig:var(--crit); border-color:var(--crit-line);
  background:linear-gradient(180deg,var(--crit-soft),transparent 70%),var(--surface-2); }
.esa-signal--idle{ --_sig:var(--idle); border-color:var(--line); background:var(--bg-2); }
.esa-signal--idle .esa-signal__dot,
.esa-signal__dot--idle{ background:transparent; border:2px dashed var(--idle); box-shadow:none; }
.esa-signal__dot--ok{ --_sig:var(--ok); }
.esa-signal__dot--warn{ --_sig:var(--warn); }
.esa-signal__dot--crit{ --_sig:var(--crit); }

/* ==========================================================================
   30. HIERARCHY — three levels, and the density that makes them read
   ==========================================================================
   The panel's pages are long because the subject is unforgiving: a customer
   who mis-binds a socket puts their real IP in public server lists, and an
   operator who clicks the wrong button drops a live game server. So the
   content stays. What was wrong was that ALL of it arrived at the same visual
   weight — a supporting aside and "do this or your service stays down" were
   the same 15px paragraph in the same card — and a page where everything is
   emphasised has no emphasis at all.

   Three levels, and only three:

     PRIMARY    the thing to do, and the state you are in. Card titles, code
                blocks, tables, controls, .esa-banner. Full ink.
     SECONDARY  a caution that must be seen NOW, in context: .esa-note,
                .esa-hint. Boxed or dimmed, never both.
     TERTIARY   reasoning a reader needs ONCE: .esa-explainer below. Collapsed
                by default, one line of it always visible.

   The test for tertiary is not "is this important?" — the encapsulation
   essay is very important. It is "does the reader need it EVERY time they
   open this page?" A rule you must not forget belongs in the summary line; the
   derivation behind it belongs behind the disclosure.
   -------------------------------------------------------------------------- */

/* ---- 30.1 the explainer ------------------------------------------------
   Native <details>, so it works with JavaScript off, is reachable by
   find-in-page on engines that support it, and prints open when the browser
   says so. Deliberately QUIETER than .esa-faq (a support-page Q&A list, which
   is primary content there) and than .esa-acc (a grouped accordion): this is
   a footnote that happens to be long, and it must not compete with the card
   it sits inside.                                                          */
.esa-explainer{
  border:1px solid var(--line);
  border-left:3px solid var(--line-2);
  border-radius:var(--radius-sm);
  background:rgba(148,163,184,.04);
}
.esa-explainer + .esa-explainer{ margin-top:var(--sp-2); }
.esa-explainer__sum{
  display:flex; align-items:flex-start; gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-4);
  cursor:pointer; list-style:none;
  font-size:13.5px; font-weight:600; color:var(--ink-mute); line-height:1.5;
  transition:color .18s var(--ease), background .18s var(--ease);
}
.esa-explainer__sum::-webkit-details-marker{ display:none; }
.esa-explainer__sum:hover{ color:var(--ink); background:rgba(var(--brand-rgb),.05); }
/* The chevron is the only affordance, so it must never be the thing that
   wraps: it is pinned to the right and cannot shrink. */
.esa-explainer__sum::after{
  content:""; margin-left:auto; flex:0 0 14px; width:14px; height:14px;
  margin-top:3px; background:var(--ink-dim);
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center/14px no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") center/14px no-repeat;
  transition:transform .22s var(--ease);
}
.esa-explainer[open]{ border-left-color:var(--brand-line); background:rgba(var(--brand-rgb),.045); }
.esa-explainer[open] > .esa-explainer__sum{ color:var(--ink); }
.esa-explainer[open] > .esa-explainer__sum::after{ transform:rotate(180deg); background:var(--brand); }
.esa-explainer__sum svg{ width:15px; height:15px; flex:0 0 15px; margin-top:2px; color:var(--ink-dim); }
/* A summary line can carry an <code> long enough to blow out the flex row;
   min-width:0 lets it wrap instead of pushing the chevron off the edge. */
.esa-explainer__sum > span{ min-width:0; }
.esa-explainer[open] > .esa-explainer__sum svg{ color:var(--brand); }
.esa-explainer__body{
  padding:0 var(--sp-4) var(--sp-4);
  font-size:13.5px; color:var(--ink-mute); line-height:1.7;
}
/* The body is prose by definition, so it gets the reading measure without
   anyone having to remember to ask for it. */
.esa-explainer__body > p,
.esa-explainer__body > ul,
.esa-explainer__body > ol{ max-width:72ch; }
.esa-explainer__body > :first-child{ margin-top:0; }

/* A rule that must be stated even when its derivation is folded away: the
   summary carries a state colour, the body stays quiet. Used by the
   "there is no apply button" rule on /admin/node. */
.esa-explainer--crit{ border-left-color:var(--crit-line); }
.esa-explainer--crit > .esa-explainer__sum{ color:var(--ink); font-weight:700; }
.esa-explainer--crit > .esa-explainer__sum svg{ color:var(--crit); }
.esa-explainer--crit[open]{ border-left-color:var(--crit); background:var(--crit-soft); }
.esa-explainer--warn{ border-left-color:var(--warn-line); }
.esa-explainer--warn > .esa-explainer__sum svg{ color:var(--warn); }
.esa-explainer--warn[open]{ border-left-color:var(--warn); background:var(--warn-soft); }

/* An explainer standing alone as a page section rather than inside a card. */
.esa-explainer--section{ background:var(--surface); border-color:var(--line); }
.esa-explainer--section > .esa-explainer__sum{ padding:var(--sp-4) var(--sp-5); font-size:14.5px; }
.esa-explainer--section > .esa-explainer__body{ padding:0 var(--sp-5) var(--sp-5); font-size:14px; }

/* ---- 30.2 measure ------------------------------------------------------
   A card body in the 1320px well is ~1250px wide. At 15px Inter that is
   ~160 characters a line — roughly twice the distance an eye can track back
   to the start of the next line, which is the mechanical reason these pages
   read as a wall even when each sentence is short.

   Capped on DIRECT children only, and only on running text. Tables, code
   blocks, grids, forms, banners and the signal row keep the full width they
   were designed for; a table that stopped at 72ch would be a worse bug than
   the one being fixed here.                                                */
.esa-card__body > p,
.esa-card__body > ul,
.esa-card__body > ol,
.esa-card__body > .esa-note,
.esa-banner__body > p,
.esa-banner__text{ max-width:72ch; }

/* ---- 30.3 the gap between a heading and its body -----------------------
   theme.css gives every heading `margin:0` and every paragraph a BOTTOM
   margin only, so a heading inside a card body sat flush on the sentence
   under it while carrying 12px of air above the next paragraph — the
   spacing said the heading belonged to the text ABOVE it. Headings own the
   space beneath them, and take a group step above when they follow content. */
.esa-card__body :is(h2,h3,h4,h5,h6){ margin-bottom:var(--sp-2); }
.esa-card__body > * + :is(h2,h3,h4,h5,h6){ margin-top:var(--sp-group); }
.esa-card__body > :first-child{ margin-top:0; }

/* ---- 30.4 action rows --------------------------------------------------
   The per-client action list on /admin/clients/{name}. Every row used to be
   the same <strong> + two hint paragraphs + a button, so "toggle the ICMP
   responder" (which cannot disturb anybody) and "delete and recreate the
   link" (which drops the customer's players) were the same object with a
   different badge. The cost of the action is now carried by the ROW, not by
   a word inside it.                                                        */
.esa-actionrow{
  display:flex; align-items:flex-start; justify-content:space-between;
  gap:var(--sp-4); flex-wrap:wrap;
  padding:var(--sp-4);
  border:1px solid var(--line);
  border-left:3px solid var(--line-2);
  border-radius:var(--radius-sm);
  background:rgba(148,163,184,.03);
}
.esa-actionrow__body{ min-width:min(100%,240px); flex:1 1 340px; }
.esa-actionrow__title{
  display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap;
  font-size:14.5px; font-weight:700; color:var(--ink); margin-bottom:var(--sp-1);
}
.esa-actionrow__body > p{ max-width:68ch; }
.esa-actionrow__do{ flex:0 0 auto; display:flex; align-items:center; gap:var(--sp-2); }
/* Safe. Nothing on the wire moves. */
.esa-actionrow--safe{ border-left-color:var(--ok-line); }
/* Disruptive: this customer's traffic stops. The red edge is the row's, so it
   is visible before the reader has parsed a single word of the hint. */
.esa-actionrow--danger{
  border-left-color:var(--crit); border-color:var(--crit-line);
  background:var(--crit-soft);
}
.esa-actionrow--danger .esa-actionrow__title{ color:#fff; }
.esa-actionrow--warn{ border-left-color:var(--warn); border-color:var(--warn-line);
  background:var(--warn-soft); }

/* The label that separates the two groups of actions. */
.esa-actiongroup{ display:flex; flex-direction:column; gap:var(--sp-3); }
.esa-actiongroup__label{
  display:flex; align-items:center; gap:var(--sp-2);
  font-size:11px; font-weight:800; letter-spacing:.12em; text-transform:uppercase;
  color:var(--ink-dim);
}
.esa-actiongroup__label svg{ width:14px; height:14px; }
.esa-actiongroup--danger .esa-actiongroup__label{ color:var(--crit); }
.esa-actiongroup--safe .esa-actiongroup__label{ color:var(--ok); }
