/* ----- 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); }

/* The server mark (templates/_brand_mark.html), the same inline SVG in
   both designs; its colours are the --p-logo-* tokens in base.css. */
.brand-mark {
  display: inline-block;
  flex: none;
  width: 28px;
  height: 28px;
  transition: transform 220ms var(--t-ease-spring);
}
.brand-mark svg { width: 100%; height: 100%; display: block; }

/* The self-hosted lockup: "Tesserae" in Inter 700 at -0.02em, then the
   SERVER tag, uppercase Inter 700 at 0.12em in an ink pill with paper
   text (light pill, ink text in the dark theme). The brand sheet sets the
   tag at ~0.35x the wordmark; at nav sizes that would be ~6px, so it
   holds a 9px floor and keeps the sheet's pill proportions. */
.brand-name { display: inline-flex; align-items: center; font-size: var(--t-size-lg); }
.brand-word {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: "Inter", var(--t-font);
  font-weight: 700;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.brand-tag {
  display: inline-block;
  padding: 3px 5px 3px 6px;
  border-radius: 3px;
  background: #1C1B19;
  color: #FFFDF8;
  font-family: "Inter", var(--t-font);
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
:root[data-theme="dark"] .brand-tag { background: #EDE9DF; color: #1C1B19; }

.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. */
/* Theme toggle icon per mode: half-circle while following the device,
   sun/moon once a choice is saved. Before the bootstrap runs the mode
   attribute is absent, which reads as "system". */
:root[data-theme-mode="light"] .is-mode-system,
:root[data-theme-mode="dark"] .is-mode-system { display: none; }
:root:not([data-theme-mode="light"]) .is-mode-light { display: none; }
:root:not([data-theme-mode="dark"]) .is-mode-dark { display: none; }
: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;
}
/* "New devices" tag: heard-but-unregistered devices, a .tg tg--teal
   tg--lg (chips.css) with the count as .tg-count. Sits first in the
   right-hand cluster, so it takes the auto margin and hands the tight
   4px spacing to whatever follows it (batteries, restart, theme). */
.topbar-discovered--topbar { margin-left: auto; }
.topbar-discovered--topbar > .ph-bold { font-size: 15px; }
.topbar-discovered-count { font-variant-numeric: tabular-nums; }
.topbar-discovered--topbar ~ .topbar-batteries--topbar,
.topbar-discovered--topbar ~ .theme-toggle--topbar,
.topbar-discovered--topbar ~ .topbar-optin { margin-left: 4px; }
.topbar-discovered--topbar ~ .topbar-restart-form { margin-left: 0; }
.topnav .topbar-discovered--drawer { display: 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 { font-variant-numeric: tabular-nums; opacity: 1; }
.topbar-batteries--topbar.is-tone-critical .topbar-batteries-count { color: var(--t-danger); }
.topbar-batteries--topbar.is-tone-low .topbar-batteries-count { 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-discovered--topbar { display: none; }
  .topnav .topbar-discovered--drawer {
    display: block;
    padding: var(--t-space-3) var(--t-space-3);
    color: var(--t-accent);
    font-size: 0.85rem;
  }
  .topnav .topbar-discovered--drawer .topbar-discovered-label {
    display: inline-flex;
    align-items: center;
    gap: var(--t-space-2);
    font-weight: 600;
  }
  .topnav .topbar-discovered--drawer .ph-bold { font-size: 18px; }
  .topnav .topbar-discovered-drawer-list {
    list-style: none;
    margin: var(--t-space-1) 0 0;
    padding: 0 0 0 26px;
    color: var(--t-fg-soft);
    font-size: 0.8rem;
  }
  .topnav .topbar-discovered-drawer-list li { display: flex; gap: var(--t-space-2); }
  .topnav .topbar-discovered-id { font-family: var(--t-font-mono); }
  .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-family: var(--t-font-mono);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  opacity: 0.75;
  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; }
/* Opt-in prompt for installs that never answered the online-features question. */
.topbar-optin { display: inline-flex; align-items: center; gap: 4px; margin-left: auto; }
.topbar-optin .inline { margin: 0; }
.topbar-optin-yes {
  border-color: color-mix(in srgb, var(--t-accent, #2563eb) 50%, transparent);
  background: color-mix(in srgb, var(--t-accent, #2563eb) 12%, transparent);
}
.topbar-optin-yes:hover,
.topbar-optin-yes:focus-visible {
  background: color-mix(in srgb, var(--t-accent, #2563eb) 22%, transparent);
  border-color: var(--t-accent, #2563eb);
}
.topbar-optin-no {
  height: 36px;
  padding: 0 8px;
  border: 0;
  background: transparent;
  color: var(--t-fg-muted, #666);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.topbar-optin-no:hover { color: var(--t-fg, #1a1a1a); text-decoration: underline; }
/* The x only shows on Paper's folded icon rail, where the words don't fit. */
.topbar-optin-no > .ph { display: none; }
.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; }
}
