/* ----- Top bar ------------------------------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  gap: var(--t-space-5);
  /* Bar spans the viewport (for the border + blur), but its children
     align to .page's content edge (inside its --t-space-6 padding) so
     the brand sits over the body's left content edge and the toggle
     over its right. Tracks the page width responsively. */
  padding-block: var(--t-space-3);
  padding-inline: max(var(--t-space-6), calc(50% - var(--t-page-max) / 2 + var(--t-space-6)));
  background: color-mix(in oklab, var(--t-surface) 80%, transparent);
  border-bottom: 1px solid color-mix(in oklab, var(--t-border) 70%, transparent);
  position: sticky;
  top: 0;
  /* High z-index so the mobile drawer (nested inside this stacking
     context) paints above everything in main, including popovers and
     flash notifications. Lightbox (1000) is the only thing on top. */
  z-index: 500;
  /* NOTE: backdrop-filter lives on ::before, NOT on .topbar itself.
     Putting it on .topbar makes the bar a containing block for
     fixed-positioned descendants, which traps the mobile drawer
     inside the topbar's ~64px box. */
  isolation: isolate;
}
.topbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  pointer-events: none;
}
/* Solid fallback when the browser can't blur so the topbar never reads
   as washed-out. */
@supports not (backdrop-filter: blur(1px)) {
  .topbar { background: var(--t-surface); }
  .topbar::before { display: none; }
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  text-decoration: none;
  color: var(--t-fg);
  font-weight: 700;
  letter-spacing: -0.015em;
  font-size: var(--t-size-lg);
  transition: color var(--t-trans-fast);
}
.brand:hover { color: var(--t-accent); }
.brand:hover .brand-mark { transform: rotate(8deg) scale(1.06); }

.brand-mark {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--t-accent), var(--t-accent-hover));
  color: var(--t-accent-fg);
  font-size: 16px;
  position: relative;
  /* Soft glow + inset highlight so the mark feels lit, not pasted on. */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.35),
    0 1px 3px color-mix(in oklab, var(--t-accent) 35%, transparent),
    0 6px 16px color-mix(in oklab, var(--t-accent) 18%, transparent);
  transition: transform 220ms var(--t-ease-spring);
}
/* Tessellated dot pattern, pure CSS, no image. */
.brand-mark::before {
  content: "";
  position: absolute;
  inset: 6px;
  background:
    conic-gradient(from 0deg at 50% 50%, currentColor 0 25%, transparent 0 50%,
                   currentColor 0 75%, transparent 0 100%);
  opacity: 0.85;
  border-radius: 3px;
}

.brand-name { font-size: var(--t-size-lg); }

.topnav {
  display: flex;
  gap: var(--t-space-1);
  margin-left: var(--t-space-3);
  flex-wrap: wrap;
}

.topnav a,
.topnav .nav-dropdown summary {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  color: var(--t-fg-soft);
  text-decoration: none;
  font-weight: 500;
  font-size: var(--t-size-md);
  padding: var(--t-space-2) var(--t-space-3);
  border-radius: var(--t-radius-sm);
  transition: background var(--t-trans-fast), color var(--t-trans-fast);
}
.topnav a .ph,
.topnav .nav-dropdown summary .ph {
  font-size: 16px;
  opacity: 0.9;
}

.topnav a:hover,
.topnav .nav-dropdown summary:hover {
  color: var(--t-fg);
  background: color-mix(in oklab, var(--t-fg) 5%, transparent);
}

/* Active state, accent soft fill + thin underline anchor so the
   active page reads at a glance without overpowering the rest of
   the nav. */
.topnav a.is-active,
.topnav .nav-dropdown.is-active summary,
.topnav .nav-dropdown[open] summary {
  color: var(--t-accent);
  background: var(--t-accent-soft);
  position: relative;
}
.topnav a.is-active::after,
.topnav .nav-dropdown.is-active summary::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 1px;
  height: 2px;
  border-radius: 1px;
  background: var(--t-accent);
  opacity: 0.85;
}

/* Plugin dropdown, anchored panel, not inline block. */
.nav-dropdown { position: relative; }
.nav-dropdown summary {
  list-style: none;
  cursor: pointer;
}
.nav-dropdown summary::-webkit-details-marker { display: none; }
.nav-dropdown-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 220px;
  display: grid;
  gap: 2px;
  padding: var(--t-space-2);
  background: var(--t-surface);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-md);
  box-shadow: var(--t-shadow-lg);
  z-index: 50;
}
.nav-dropdown-link {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  padding: var(--t-space-2) var(--t-space-3);
  color: var(--t-fg-soft);
  text-decoration: none;
  border-radius: var(--t-radius-sm);
  font-size: var(--t-size-md);
  white-space: nowrap;
}
.nav-dropdown-link:hover { background: var(--t-surface-sunk); color: var(--t-accent); }
.nav-dropdown-sep {
  padding: var(--t-space-2) var(--t-space-3) 0;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--t-muted);
}
.nav-dropdown-hint {
  padding: var(--t-space-2) var(--t-space-3);
  font-size: 12px;
  color: var(--t-muted);
}

/* Theme toggle, rendered twice. The .theme-toggle--topbar variant
   is a compact icon square pinned to the far right of the topbar on
   desktop. The .theme-toggle--drawer variant is a full-width labelled
   row inside the responsive drawer. CSS hides whichever isn't
   appropriate for the current viewport. */
.theme-toggle {
  display: inline-grid;
  place-items: center;
  width: 36px;
  height: 36px;
  background: transparent;
  border: 0;
  border-radius: var(--t-radius-sm);
  cursor: pointer;
  color: var(--t-fg-soft);
  padding: 0;
}
.theme-toggle:hover { color: var(--t-accent); background: var(--t-surface-sunk); }
.theme-toggle .ph { font-size: 18px; }
.theme-toggle-label { display: none; }

.theme-toggle--topbar { margin-left: auto; }
.theme-toggle--drawer { display: none; }

/* Show/hide based on the current theme. Dark icon is visible only when
   data-theme=dark, light icon is visible only when it's absent. */
:root[data-theme="dark"] .is-light-only { display: none; }
:root:not([data-theme="dark"]) .is-dark-only { display: none; }

/* Topbar battery indicator. Rendered only when at least one
   registered device reports a battery_pct (nav_batteries non-empty);
   the trigger paints in the tone of the WORST battery so a single
   critical device catches the eye even when others are fine. Native
   <details> handles hover-on-desktop + click-on-mobile without JS. */
/* Update-available badge in the topnav. Accent-tinted so it reads as "new"
   against the plain nav links, with a small dot on the icon. Only rendered
   when the background version check found a newer release. */
.topbar-update { position: relative; color: var(--t-accent, #2563eb) !important; font-weight: 600; }
.topbar-update > .ph, .topbar-update > .ph-bold { font-size: 18px; }
.topbar-update .topbar-update-dot {
  position: absolute;
  top: 5px;
  left: 20px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--t-accent, #2563eb);
  box-shadow: 0 0 0 2px var(--t-surface, #fff);
  pointer-events: none;
}
.topbar-batteries { position: relative; }
.topbar-batteries--topbar { margin-left: auto; }
/* Push the theme-toggle's auto-margin off when the indicator is
   present, so they sit as a tight pair at the far right. */
.topbar-batteries--topbar + .theme-toggle--topbar { margin-left: 4px; }
.topbar-batteries--topbar > summary {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 36px;
  padding: 0 8px;
  border-radius: var(--t-radius-sm);
  cursor: pointer;
  color: var(--t-fg-soft);
  list-style: none;
  font-size: 0.85rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* Hide the disclosure triangle the browser puts on <summary>. */
.topbar-batteries--topbar > summary::-webkit-details-marker { display: none; }
.topbar-batteries--topbar > summary::marker { content: ""; }
.topbar-batteries--topbar > summary:hover { background: var(--t-surface-sunk); }
.topbar-batteries--topbar > summary > .ph { font-size: 18px; }
.topbar-batteries--topbar.is-tone-critical > summary { color: var(--t-danger); }
.topbar-batteries--topbar.is-tone-low      > summary { color: var(--t-warn); }
.topbar-batteries--topbar.is-tone-ok       > summary { color: var(--t-fg-soft); }
.topbar-batteries-count {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  padding: 0 4px;
  height: 18px;
  background: var(--t-surface-sunk);
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
}
.topbar-batteries--topbar.is-tone-critical .topbar-batteries-count {
  background: color-mix(in oklab, var(--t-danger) 18%, transparent);
  color: var(--t-danger);
}
.topbar-batteries--topbar.is-tone-low .topbar-batteries-count {
  background: color-mix(in oklab, var(--t-warn) 18%, transparent);
  color: var(--t-warn);
}
.topbar-batteries-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 600;
  min-width: 240px;
  max-width: 320px;
  padding: 8px 0;
  background: var(--t-surface);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-md);
  box-shadow: var(--t-shadow-md);
}
.topbar-batteries-head {
  padding: 4px 14px 8px;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--t-muted);
}
.topbar-batteries-popover ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}
.topbar-batteries-popover li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 14px;
  font-size: 0.88rem;
}
.topbar-batteries-popover .topbar-batteries-pct {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.topbar-batteries-popover li.is-tone-critical .topbar-batteries-pct { color: var(--t-danger); }
.topbar-batteries-popover li.is-tone-low      .topbar-batteries-pct { color: var(--t-warn); }
.topbar-batteries-popover li.is-tone-ok       .topbar-batteries-pct { color: var(--t-fg-soft); }
/* "View charts" footer link inside the popover. Separated from the
   list by a faint divider; hover-tinted so the affordance reads. */
.topbar-batteries-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px;
  margin-top: 4px;
  border-top: 1px solid var(--t-border);
  color: var(--t-accent);
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  letter-spacing: 0.01em;
}
.topbar-batteries-link:hover { background: var(--t-accent-soft); }
.topbar-batteries-link .ph { font-size: 14px; }
/* Single-device case: the trigger already shows pct, so the popover
   is hidden — but we still want the icon to navigate to the page. */
.topbar-batteries--topbar.is-single .topbar-batteries-popover { display: none; }
.topbar-batteries--topbar.is-single > summary { cursor: pointer; }
.topbar-batteries--topbar.is-single > summary { cursor: default; }
/* Hover open + focus open on desktop so it behaves like a tooltip;
   click-to-toggle still works on touch via the native <details>. */
@media (hover: hover) {
  .topbar-batteries--topbar:not(.is-single):hover[open],
  .topbar-batteries--topbar:not(.is-single):hover {
    /* no-op; intent is documented in the next rule */
  }
  .topbar-batteries--topbar:not(.is-single):hover > .topbar-batteries-popover,
  .topbar-batteries--topbar:not(.is-single):focus-within > .topbar-batteries-popover {
    display: block;
  }
}
/* The drawer variant collapses into a labelled list, no popover
   nesting inside the mobile sidebar. Scoped to .topnav so this rule's
   specificity matches the generic .topnav a rule above — without that
   the drawer batteries (now an <a> for the link entry-point) leak
   into the desktop top nav. */
.topnav .topbar-batteries--drawer { display: none; }

/* Hamburger + sidebar (mobile/tablet) ------------------------------ */

.nav-toggle {
  display: none;
  margin-left: auto;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  background: transparent;
  color: var(--t-fg);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-sm);
  cursor: pointer;
}
.nav-toggle:hover { background: var(--t-surface-sunk); }
.nav-scrim {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 32, 0.45);
  /* Just below the topbar (500) so the drawer sits above it. */
  z-index: 499;
}

@media (max-width: 900px) {
  .nav-toggle { display: inline-flex; }
  .topnav {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(320px, 86vw);
    flex-direction: column;
    align-items: stretch;
    gap: var(--t-space-1);
    margin: 0;
    padding: var(--t-space-5) var(--t-space-4);
    background: var(--t-surface);
    border-left: 1px solid var(--t-border);
    box-shadow: var(--t-shadow-lg);
    transform: translateX(100%);
    transition: transform 200ms ease;
    overflow-y: auto;
    z-index: 60;
    flex-wrap: nowrap;
  }
  body.nav-open .topnav { transform: translateX(0); }
  .topnav a, .topnav .nav-dropdown summary {
    padding: var(--t-space-3) var(--t-space-3);
    font-size: var(--t-size-md);
    justify-content: flex-start;
  }
  /* In the drawer, the dropdown panel expands inline rather than absolute. */
  .topnav .nav-dropdown-panel {
    position: static;
    box-shadow: none;
    border: 0;
    background: transparent;
    padding: var(--t-space-1) var(--t-space-3);
    margin-left: var(--t-space-4);
    border-left: 2px solid var(--t-border);
  }
  /* Drawer-only theme toggle takes over inside the drawer. */
  .theme-toggle--topbar { display: none; }
  /* Topbar battery indicator is hidden in mobile; the drawer variant
     below renders the same data inline. Drawer rules scoped to .topnav
     so the specificity beats the generic .topnav a inline-flex rule
     (otherwise the <a>-wrapped drawer batteries inherit nav-link
     layout instead of the block list shape we want here). */
  .topbar-batteries--topbar { display: none; }
  .topnav .topbar-batteries--drawer {
    display: block;
    padding: var(--t-space-3) var(--t-space-3);
    color: var(--t-fg-soft);
    font-size: 0.85rem;
  }
  .topnav .topbar-batteries--drawer .topbar-batteries-label {
    display: inline-flex;
    align-items: center;
    gap: var(--t-space-2);
    font-weight: 600;
  }
  .topnav .topbar-batteries--drawer .ph { font-size: 18px; }
  .topnav .topbar-batteries--drawer.is-tone-critical { color: var(--t-danger); }
  .topnav .topbar-batteries--drawer.is-tone-low      { color: var(--t-warn); }
  .topbar-batteries-drawer-list {
    list-style: none;
    padding: var(--t-space-2) 0 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--t-space-1);
  }
  .topbar-batteries-drawer-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--t-space-3);
    padding: 4px var(--t-space-3);
    border-left: 2px solid var(--t-border);
    color: var(--t-fg-soft);
  }
  .topbar-batteries-drawer-list li.is-tone-critical { border-color: var(--t-danger); color: var(--t-danger); }
  .topbar-batteries-drawer-list li.is-tone-low      { border-color: var(--t-warn); color: var(--t-warn); }
  .topbar-batteries-drawer-list .topbar-batteries-pct {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .theme-toggle--drawer {
    display: inline-flex;
    width: 100%;
    height: auto;
    align-items: center;
    justify-content: flex-start;
    padding: var(--t-space-3) var(--t-space-3);
    margin-top: auto;
    border-color: transparent;
    color: var(--t-fg-soft);
    gap: var(--t-space-2);
  }
  .theme-toggle--drawer .theme-toggle-label { display: inline; }
  .theme-toggle--drawer:hover { background: var(--t-surface-sunk); }
}
@media (min-width: 901px) {
  .nav-scrim { display: none; }
}

/* ----- Layout -------------------------------------------------------- */

/* Page column width, defined as a variable so the topbar can mirror
   it (see .topbar above). editor.css bumps this at ≥ 1100px because
   the editor needs the room and admin pages followed along. */
:root { --t-page-max: 880px; }

.page {
  max-width: var(--t-page-max);
  margin: 0 auto;
  padding: var(--t-space-7) var(--t-space-6) var(--t-space-8);
}

.page.page-wide { max-width: 1440px; }

/* Phone-portrait tightening, the admin shell otherwise wastes a lot
   of the screen on edge padding + card chrome that's appropriate at
   desktop widths but not at 390px. */
@media (max-width: 600px) {
  .page {
    padding: var(--t-space-4) var(--t-space-3) var(--t-space-6);
  }
  .card {
    padding: var(--t-space-4) var(--t-space-4) var(--t-space-4);
    margin-bottom: var(--t-space-3);
    border-radius: var(--t-radius-sm);
  }
  .page-head { margin-bottom: var(--t-space-4); }
  .page-head h1 { font-size: var(--t-size-2xl); }
  /* Touch targets, make buttons + selects at least 40px tall so
     they're tappable without zooming. */
  .form button,
  .form select,
  .form input[type="text"],
  .form input[type="number"],
  .form input[type="url"],
  .form input[type="email"],
  .form input[type="password"] {
    min-height: 40px;
  }
}

.page-head {
  margin-bottom: var(--t-space-6);
}

.page-head h1 {
  font-size: var(--t-size-3xl);
  font-weight: 700;
  margin: 0 0 var(--t-space-2);
  letter-spacing: -0.02em;
  line-height: 1.15;
  display: flex;
  align-items: center;
  gap: var(--t-space-3);
}

.page-head h1 .ph { font-size: 0.85em; color: var(--t-accent); }

.lede {
  color: var(--t-muted);
  margin: 0 0 var(--t-space-2);
  font-size: 13.5px;
  line-height: 1.55;
}

/* ----- Card ---------------------------------------------------------- */

.card {
  background: var(--t-surface);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-md);
  padding: var(--t-space-5) var(--t-space-6) var(--t-space-6);
  margin: 0 0 var(--t-space-4);
  scroll-margin-top: 80px;
  /* Soft elevation, small ambient shadow + an inset highlight on the
     top edge so the card reads as paper lifted off the page bg rather
     than a flat rectangle drawn on top of it. */
  box-shadow: var(--t-shadow-sm), var(--t-shadow-inset);
  transition:
    border-color var(--t-trans-fast),
    box-shadow var(--t-trans),
    transform var(--t-trans);
}
.card:hover {
  /* Whisper-soft lift on hover so the cursor feels acknowledged
     without anything actually moving in a distracting way. */
  box-shadow: var(--t-shadow), var(--t-shadow-inset);
  border-color: color-mix(in oklab, var(--t-border-strong) 80%, var(--t-border));
}

.card.narrow {
  max-width: 440px;
  margin: var(--t-space-8) auto;
  padding: var(--t-space-6);
  box-shadow: var(--t-shadow-md), var(--t-shadow-inset);
}

.card-head {
  margin-bottom: var(--t-space-4);
  display: flex;
  flex-direction: column;
  gap: var(--t-space-1);
}

.card-head h1 {
  font-size: var(--t-size-2xl);
  font-weight: 700;
  margin: 0;
  letter-spacing: -0.018em;
  display: flex;
  align-items: center;
  gap: var(--t-space-2);
}

.card-head h2 {
  font-size: var(--t-size-lg);
  font-weight: 600;
  margin: 0;
  letter-spacing: -0.008em;
  display: flex;
  align-items: center;
  gap: var(--t-space-2);
}
.card-head h2 .icon-box { margin-right: var(--t-space-1); }

.card-head .lede { margin-top: var(--t-space-1); }

.card-head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--t-space-3);
}

.card-head-action {
  flex-shrink: 0;
}

/* ----- Empty states --------------------------------------------------
   Flat icon tile + helper copy. No glow, no pulse ring. */

.empty-state {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--t-space-2);
  padding: var(--t-space-8) var(--t-space-4);
  color: var(--t-fg-soft);
}

.empty-state-icon {
  width: 72px;
  height: 72px;
  border-radius: var(--t-radius-xl);
  background: var(--t-surface-sunk);
  color: var(--t-accent);
  display: grid;
  place-items: center;
  font-size: 32px;
  margin-bottom: var(--t-space-3);
}

.empty-state h3 {
  margin: 0;
  font-size: var(--t-size-xl);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--t-fg);
}

.empty-state .lede {
  max-width: 380px;
  margin: 0;
  font-size: var(--t-size-md);
  line-height: 1.55;
}

.empty-state .btn,
.empty-state button {
  margin-top: var(--t-space-4);
}

.meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--t-space-1) var(--t-space-5);
  margin: var(--t-space-2) 0 0;
  padding: 0;
}

.meta div { display: contents; }
.meta dt {
  font-size: var(--t-size-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--t-muted);
  margin-right: var(--t-space-1);
}
.meta dd {
  margin: 0 var(--t-space-3) 0 0;
  font-size: var(--t-size-sm);
  color: var(--t-fg);
}

/* ----- Tabs --------------------------------------------------------- */

.tabs {
  display: flex;
  gap: var(--t-space-1);
  margin: 0 0 var(--t-space-4);
  border-bottom: 1px solid var(--t-border);
  /* Horizontal scroll fallback when the tab strip is wider than the
     container (e.g. settings on a phone), the underline acts as a
     visible-area cue and the user can swipe to reach hidden tabs. */
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
  padding: var(--t-space-3) var(--t-space-4);
  color: var(--t-fg-soft);
  text-decoration: none;
  font-weight: 500;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--t-trans-fast), border-color var(--t-trans),
              background var(--t-trans-fast);
  border-top-left-radius: var(--t-radius-sm);
  border-top-right-radius: var(--t-radius-sm);
  white-space: nowrap;
  flex-shrink: 0;
}
@media (max-width: 600px) {
  .tab { padding: var(--t-space-2) var(--t-space-3); font-size: var(--t-size-md); gap: var(--t-space-1); }
  .tab .ph { font-size: 14px; }
}
.tab .ph { font-size: 15px; opacity: 0.75; transition: opacity var(--t-trans-fast); }
.tab .muted {
  font-size: var(--t-size-xs);
  padding: 1px 6px;
  border-radius: var(--t-radius-pill);
  background: var(--t-surface-sunk);
  margin-left: var(--t-space-1);
  font-variant-numeric: tabular-nums;
}
.tab.is-active {
  color: var(--t-accent);
  border-bottom-color: var(--t-accent);
  font-weight: 600;
}
.tab.is-active .ph { opacity: 1; }
.tab:hover {
  color: var(--t-fg);
  background: color-mix(in oklab, var(--t-fg) 4%, transparent);
}
.tab:hover .ph { opacity: 1; }

.page-name {
  display: inline-flex;
  align-items: center;
  gap: var(--t-space-2);
}
.page-icon {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--t-radius-sm);
  background: var(--t-accent-soft);
  color: var(--t-accent);
  font-size: 16px;
}


/* ----- Lightbox ---------------------------------------------------- */
/* Click any <a data-lightbox> to view the image in-page. Backdrop
   dims everything else; image scales to fit the viewport. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.85);
  display: grid;
  place-items: center;
  padding: var(--t-space-6);
  animation: lightbox-fade 120ms ease-out;
}
.lightbox-img {
  max-width: 100%;
  max-height: 100%;
  display: block;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5);
  background: #000;
}
.lightbox-close {
  position: absolute;
  top: var(--t-space-3);
  right: var(--t-space-3);
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  border: none;
  cursor: pointer;
  font-size: 24px;
  width: 40px;
  height: 40px;
  border-radius: 4px;
  display: grid;
  place-items: center;
  line-height: 1;
  transition: background 120ms ease;
}
.lightbox-close:hover { background: rgba(255, 255, 255, 0.22); }
body.lightbox-open { overflow: hidden; }
@keyframes lightbox-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}


/* ----- Modal ------------------------------------------------------- */
/* Generic centred modal for one-shot reveals (e.g. freshly-minted
   webhook tokens that won't ever be shown again). Driven by a small
   inline script, open by inclusion in the rendered HTML, close via
   the [data-modal-close] buttons, ESC, or backdrop click. All colours
   reference the theme tokens in base.css so dark mode flips with the
   rest of the UI. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.55);
  display: grid;
  place-items: center;
  padding: var(--t-space-6);
  animation: lightbox-fade 120ms ease-out;
}
.modal-card {
  background: var(--t-surface);
  color: var(--t-fg);
  border: 1px solid var(--t-border);
  border-radius: var(--t-radius-md);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.45);
  width: min(560px, 100%);
  max-height: calc(100vh - var(--t-space-8));
  overflow: auto;
  display: flex;
  flex-direction: column;
}
.modal-head,
.modal-foot {
  padding: var(--t-space-4) var(--t-space-5);
  display: flex;
  align-items: center;
  gap: var(--t-space-3);
}
.modal-head { border-bottom: 1px solid var(--t-border); }
.modal-foot {
  border-top: 1px solid var(--t-border);
  justify-content: flex-end;
}
.modal-head h2 {
  margin: 0;
  font-size: var(--t-size-lg);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: var(--t-space-2);
  flex: 1;
  color: var(--t-fg);
}
.modal-close {
  background: transparent;
  border: none;
  color: var(--t-fg-soft);
  cursor: pointer;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: var(--t-radius-sm);
  font-size: var(--t-size-xl);
  transition: background 120ms ease, color 120ms ease;
}
.modal-close:hover {
  background: var(--t-surface-soft);
  color: var(--t-fg);
}
.modal-body { padding: var(--t-space-5); color: var(--t-fg); }
.modal-body .lede { margin-top: 0; color: var(--t-fg-soft); }
.token-row {
  display: flex;
  gap: var(--t-space-2);
  align-items: stretch;
}
.token-row .token-input {
  flex: 1;
  font-family: var(--t-font-mono);
  font-size: var(--t-size-md);
  padding: var(--t-space-3);
  border: 1px solid var(--t-border-strong);
  border-radius: var(--t-radius-sm);
  background: var(--t-surface-soft);
  color: var(--t-fg);
  min-width: 0;
}
.token-row .token-input:focus {
  outline: 2px solid var(--t-accent);
  outline-offset: 2px;
  border-color: var(--t-accent);
}
body.modal-open { overflow: hidden; }

/* ============================================================================
   Tesserae-themed scrollbar
   --------------------------------------------------------------------------
   Subtle but on-theme. Uses the admin shell tokens so the scrollbar
   adapts to the active admin theme (--t-fg / --t-surface-soft) without
   pinning a specific colour. Firefox + Webkit covered; falls back to OS
   default for anything older.
   ============================================================================ */

/* Firefox 64+ */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--t-fg-soft, #9aa0aa) var(--t-surface-sunk, transparent);
}

/* Webkit / Blink (Safari, Chrome, Edge). Wider + on a soft track so
   the themed thumb actually reads instead of vanishing into the page
   background; min-height keeps the grab target hittable on long
   scrolls. */
*::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}
*::-webkit-scrollbar-track {
  background: color-mix(in srgb, var(--t-fg-soft, #9aa0aa) 14%, transparent);
  border-radius: 999px;
}
*::-webkit-scrollbar-thumb {
  background: var(--t-fg-soft, #9aa0aa);
  border: 2px solid transparent;
  background-clip: padding-box;
  border-radius: 999px;
  min-height: 32px;
}
*::-webkit-scrollbar-thumb:hover {
  background: var(--t-fg, #444);
  background-clip: padding-box;
}
*::-webkit-scrollbar-corner {
  background: transparent;
}

/* Topbar "Restart required" button. Lit by the marketplace context
   processor when one or more widget installs/uninstalls are waiting
   on a process restart. Sits between any batteries indicator and the
   theme toggle. ``--t-warn`` tone draws the eye without being as
   loud as a danger pill. Posts to /plugins/browse/restart via
   data-restart-form so clicking it opens the spinner modal and the
   page auto-reloads when the new process is back. */
.topbar-restart-form { margin: 0; }
.topbar-restart-form { margin-left: auto; }
/* When batteries are also shown, batteries already grabbed the auto
   margin, so this row of trailing items collapses cleanly. */
.topbar-batteries--topbar ~ .topbar-restart-form { margin-left: 0; }
.topbar-restart-form + .theme-toggle--topbar { margin-left: 4px; }
.topbar-restart {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  border-radius: var(--t-radius-sm);
  border: 1px solid color-mix(in srgb, var(--t-warn, #c98a00) 50%, transparent);
  background: color-mix(in srgb, var(--t-warn, #c98a00) 14%, transparent);
  color: var(--t-fg, #1a1a1a);
  font: inherit;
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.topbar-restart:hover,
.topbar-restart:focus-visible {
  background: color-mix(in srgb, var(--t-warn, #c98a00) 24%, transparent);
  border-color: var(--t-warn, #c98a00);
}
.topbar-restart .ph,
.topbar-restart .ph-bold {
  color: var(--t-warn, #c98a00);
  font-size: 16px;
}
/* Hide the label on narrow widths so the chip stays a single icon
   on mobile without crowding the row. The icon-only form keeps a
   tooltip via the form's ``title`` attribute. */
@media (max-width: 640px) {
  .topbar-restart-label { display: none; }
  .topbar-restart { padding: 0 10px; }
}
