/* ==========================================================
   VERYQUERY · components
   Tool-shaped component vocabulary loaded by application
   surfaces (dashboard.veryquery.com, admin.veryquery.com).
   Loaded AFTER design-system.css so cascade order is:

     1. fonts.css         (font manifest)
     2. design-system.css (tokens, resets, typography, .btn, .field,
                            .eyebrow, .prose, utilities)
     3. components.css    (THIS FILE — modals, dropdowns, panels,
                            badges, tables, intelligence map,
                            progress bars, summary rows, toasts)
     4. <surface>.css     (dashboard.css or admin's local layer)

   Marketing site / help / docs / stores do NOT load this file.
   Stores carry their own brand palette and component library.
========================================================== */

/* ==========================================================
   APP SHELL: header + brand mark + alert banner
   The body grid (sidebar + main) is per-surface and stays
   in <surface>.css; this layer covers the bits that look
   identical on every tool page.
========================================================== */
.app {
  min-height: 100vh;
  display: grid;
  /* header · optional alert banner · body. Each `auto` row collapses
     to its content height when the banner isn't rendered. */
  grid-template-rows: auto auto 1fr;
  /* Approximate sticky-header height. The sidebar uses this to offset
     its `top:` so it doesn't slide under the header. Doesn't have to
     be pixel-exact; a few px slop here just shows/hides a sliver of
     border between the two. */
  --app-header-h: 4rem;
}

/* Sidebar + main column grid. Sidebar is fixed-width; main fills the
   rest. Below 51.25em the sidebar is hidden and main goes full-width
   (mobile sidebars belong to a future drawer pattern). */
.app__body {
  display: grid;
  grid-template-columns: 15rem 1fr;
}
@media (max-width: 51.25em) {
  .app__body { grid-template-columns: 1fr; }
  .app__body > .app__sidebar { display: none; }
}

/* Main column inside .app__body. Page-level scroll, not internal —
   the sidebar and main scroll together on the body. Children stack
   in a flex column with consistent vertical rhythm via gap. */
.app__main {
  padding: var(--s-6) var(--gutter);
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
  min-width: 0;
}

.app__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--s-4) var(--gutter);
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
  position: sticky;
  top: 0;
  z-index: 10;
}
.app__mark {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--font-wordmark);
  font-size: var(--t-xl);
  letter-spacing: -0.005em;
  /* Drop the wordmark's line-height to its em-square so vertical
     centering matches the cap height, not the larger leading box. */
  line-height: 1;
  /* Brand mark, not running prose — never underline, regardless of
     the surrounding surface's `a` baseline. */
  text-decoration: none;
  color: inherit;
}
.app__mark-sigil { color: var(--accent); font-size: 1.25em; position: relative; top: 0.05em; }
.app__mark-logo {
  height: 1.1em;
  width: auto;
  display: block;
}
/* Right side of the app header: holds the property/org switcher and
   the user menu. They sit on the same baseline; gap matches the rest
   of the header rhythm. */
.app__header-end {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
}

/* Left cluster used on user/account header layouts (logo + back link). */
.app__header-start {
  display: inline-flex;
  align-items: center;
  gap: var(--s-5);
}

/* Secondary nav element next to the logo (e.g. "← Back to dashboard"). */
.app__back {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mid);
  text-decoration: none;
  transition: color var(--dur-1);
}
.app__back:hover { color: var(--accent); }

/* Slim shell for User/Org settings: no sidebar, full-width main
   column, narrower max measure so long-form forms don't sprawl. */
.app--user {
  min-height: 100vh;
  display: grid;
  grid-template-rows: auto 1fr;
}
.app__main--user {
  max-width: 56rem;
  margin: 0 auto;
  padding: var(--s-6) var(--gutter) var(--s-8);
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}

/* ==========================================================
   APP SIDEBAR
   Vertical nav rail for tool surfaces (dashboard, admin). The
   parent container (a flex row or grid) sizes the sidebar's
   width — .app__sidebar itself only owns its rule, padding, and
   the link styling. Active state uses the same disc-pin pattern
   as the .map-pin "you are here" cue.
========================================================== */
.app__sidebar {
  padding: var(--s-5);
  overflow-y: auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Column rule lives on .app__body's background gradient (below) so
     it spans the full body height — the sticky sidebar is bounded by
     the viewport, so its own border-right can't reach the bottom of
     long main columns. */
}
/* Desktop: sticky-to-viewport sidebar bounded to viewport height
   (minus the sticky header). Fixed height (not max-height) is what
   lets `margin-top: auto` on the Report button actually push it to
   the bottom of the column — max-height collapses to content and
   leaves no free space. Mobile rules override these back to static. */
@media (min-width: 51.3125em) {
  .app__sidebar {
    position: sticky;
    top: var(--app-header-h);
    align-self: start;
    height: calc(100vh - var(--app-header-h));
  }
  .app__body {
    background-image: linear-gradient(
      to right,
      transparent calc(15rem - 1px),
      var(--rule) calc(15rem - 1px),
      var(--rule) 15rem,
      transparent 15rem
    );
  }
}
.app__sidebar ul { display: flex; flex-direction: column; gap: var(--s-1); }
/* Issue-reporter button. margin-top:auto pushes it to the bottom of
   the sidebar when nav doesn't fill the viewport; when nav overflows,
   the auto-margin collapses and the button sits at the natural end of
   the scroll. Styled as a quieter affordance than the nav links —
   smaller, ink-mid, with a top rule for separation. */
.app__sidebar-report {
  margin-top: auto;
  padding: var(--s-3) var(--s-3);
  border: 0;
  background: transparent;
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mid);
  cursor: pointer;
  transition: color var(--dur-1), background var(--dur-1);
}
.app__sidebar-report:hover { color: var(--ink); background: rgba(23,20,14,0.04); }
.app__sidebar a {
  display: flex;
  align-items: center;
  padding: var(--s-2) var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mid);
  position: relative;
  transition: color var(--dur-1), background var(--dur-1);
}
.app__sidebar a:hover { color: var(--ink); background: rgba(23,20,14,0.04); }
.app__sidebar a.is-active {
  color: var(--ink);
  background: rgba(178,71,24,0.05);
}
/* "You are here" — filled accent disc straddling the active link's
   left edge. Map-pin convention; not the corner-crosshair pattern
   (crosshairs read as "add" in nav rails). */
.app__sidebar a.is-active::before {
  content: "";
  position: absolute;
  left: -3.5px;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -3.5px;
  background: var(--accent);
  border-radius: 50%;
}

/* Sticky alert banner shown above the body when there's a property/
   tenant-scope notice (no plan, entitlement issue, payment failed).
   Sets expectations before any metered op 402s. */
.app__alert {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--gutter);
  background: var(--ink);
  color: var(--paper);
  border-bottom: 1px solid var(--ink);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
}
.app__alert-body { flex: 1; line-height: 1.4; }
.app__alert .btn { flex-shrink: 0; }
/* Warning variant — subtle "heads-up" treatment for nudge states
   where nothing is broken (e.g. over-commit usage still billing
   cleanly). Paper background keeps the page calm; a thin accent
   stripe along the left edge carries the signal. Loud enough to
   notice, quiet enough not to overwhelm. */
.app__alert--warn {
  background: var(--paper);
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
  box-shadow: inset 0.25rem 0 0 0 var(--accent);
}
.app__alert--warn strong { color: var(--accent); }
/* When there's more than one axis in the same zone, the banner shows
   the worst one and a small dot indicates additional issues. Hover
   reveals which other axes are affected. */
.app__alert-more {
  display: inline-block;
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
  margin-inline: 0.4em;
  vertical-align: middle;
}

/* ==========================================================
   EMPTY STATE: tool-page hero used when a surface has no
   content yet (no properties, no items, no queries).
========================================================== */
.empty-state {
  text-align: center;
  padding: var(--s-8) 0;
}
.empty-state__title {
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  letter-spacing: -0.01em;
  margin: 0 0 var(--s-3);
  color: var(--ink);
}
.empty-state__body {
  font-size: var(--t-md);
  color: var(--ink-mid);
  max-width: 36rem;
  margin: 0 auto var(--s-5);
  line-height: 1.5;
}
.empty-state__actions {
  display: flex;
  justify-content: center;
  gap: var(--s-3);
}

/* ==========================================================
   CORNER CROSSHAIRS (surveyor's-mark ornament)
   Framed surfaces (.panel, .card, .summary-cell) get four 7px
   + marks centered on their corners. Reads as a surveyed area,
   not just a bordered box. The marks live on ::before and use
   SVG masks so a single pattern works on any background
   (including .panel--accent's accent-tint surface).

   Modal isn't in this list — its `overflow-y: auto` clips the
   pseudo and the modal's own ink border + drop-shadow already
   carries enough framing.

   Implementation: the pseudo extends 3.5px outside its parent
   (inset: -3.5px) so each 7px cross sits centered on the
   parent's corner (-3.5 to +3.5 around each vertex). Background-
   color governs the ink shade; the mask carves four crosses
   from the four corners of the pseudo box.
========================================================== */
.panel,
.card,
.summary-cell,
.intel-card {
  position: relative;
}
.panel::before,
.card::before,
.summary-cell::before,
.intel-card::before {
  content: "";
  position: absolute;
  inset: -3.5px;
  pointer-events: none;
  background-color: rgba(23, 20, 14, 0.45);
  --vq-cross: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='7'%3E%3Cpath d='M0 3.5h7M3.5 0v7' stroke='black' stroke-width='1'/%3E%3C/svg%3E");
  -webkit-mask: var(--vq-cross) top left no-repeat,
                var(--vq-cross) top right no-repeat,
                var(--vq-cross) bottom left no-repeat,
                var(--vq-cross) bottom right no-repeat;
  mask: var(--vq-cross) top left no-repeat,
        var(--vq-cross) top right no-repeat,
        var(--vq-cross) bottom left no-repeat,
        var(--vq-cross) bottom right no-repeat;
  -webkit-mask-size: 7px 7px;
  mask-size: 7px 7px;
}

/* ==========================================================
   HEADER DROPDOWN
   Two-line trigger tile (eyebrow + value + chevron) that opens
   a panel anchored beneath. Used for property/org switchers
   and user menus. The panel alignment flips for right-edge
   menus via .hd-menu--right.
========================================================== */
.hd-dropdown {
  position: relative;
  display: inline-flex;
}
/* Header picker trigger. Same visual treatment as a `.btn--small`
   (1px ink border, paper bg, ink text, ink-fill hover) but with an
   eyebrow + value + chevron flex layout instead of a single label,
   plus open-state inversion. Not composed on top of `.btn`; tuning
   the picker shouldn't require reading two stylesheets. */
.hd-dropdown__trigger {
  appearance: none;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 0;
  padding: 0.35rem 0.7rem;
  font: inherit;
  font-size: var(--t-sm);
  line-height: 1.2;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  max-width: 18rem;
  transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1);
}
.hd-dropdown__trigger:hover {
  background: var(--ink);
  color: var(--paper);
}
.hd-dropdown__trigger:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.hd-dropdown__trigger[disabled],
.hd-dropdown__trigger:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* Eyebrow has its own muted color by default; flip to paper alongside
   the rest of the trigger on hover/open so it stays legible against
   the inverted ink fill. */
.hd-dropdown__trigger:hover .hd-dropdown__eyebrow { color: var(--paper); }
.hd-dropdown.is-open .hd-dropdown__trigger {
  background: var(--ink);
  color: var(--paper);
}
.hd-dropdown.is-open .hd-dropdown__eyebrow { color: var(--paper); }
.hd-dropdown__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--t-3xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mid);
  display: block;
  line-height: 1.1;
}
.hd-dropdown__value {
  font-size: var(--t-sm);
  /* Inherit from the trigger so it flips to --paper on hover/open
     alongside the rest of the tile. */
  color: inherit;
  display: block;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 14rem;
}
.hd-dropdown__chevron {
  margin-left: auto;
  flex-shrink: 0;
  color: currentColor;
  transition: transform var(--dur-1);
}
.hd-dropdown.is-open .hd-dropdown__chevron { transform: rotate(180deg); }

/* The drop panel itself. Hidden by default; .is-open on the wrapper
   reveals it. Sits flush under the trigger, anchored left for the
   property switcher and right for the user menu. */
.hd-menu {
  position: absolute;
  top: calc(100% + 0.4rem);
  left: 0;
  min-width: 14rem;
  max-width: 22rem;
  background: var(--paper);
  border: 1px solid var(--ink);
  box-shadow: 0 12px 28px -16px rgba(0, 0, 0, 0.25), 0 2px 6px -3px rgba(0, 0, 0, 0.1);
  padding: var(--s-1) 0;
  z-index: 50;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity 120ms ease, transform 120ms ease;
}
.hd-menu--right { left: auto; right: 0; }
.hd-dropdown.is-open .hd-menu {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.hd-menu__head {
  padding: var(--s-2) var(--s-3) var(--s-1);
}
.hd-menu__head-name {
  font-size: var(--t-sm);
  color: var(--ink);
  font-weight: 500;
}
.hd-menu__head-email {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  color: var(--ink-mid);
  word-break: break-all;
  margin-top: 0.1em;
}

.hd-menu__group { display: flex; flex-direction: column; }
.hd-menu__divider {
  border-top: 1px solid var(--rule);
  margin: var(--s-1) 0;
}

.hd-menu__item {
  appearance: none;
  background: transparent;
  border: 0;
  width: 100%;
  text-align: left;
  padding: 0.45rem var(--s-3);
  cursor: pointer;
  font: inherit;
  color: var(--ink);
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  transition: background var(--dur-1), color var(--dur-1);
}
.hd-menu__item:hover,
.hd-menu__item:focus-visible {
  background: var(--paper-tint, rgba(0, 0, 0, 0.04));
  outline: none;
}
.hd-menu__item.is-current { color: var(--ink); font-weight: 500; }
.hd-menu__item-label {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--t-sm);
}
.hd-menu__check, .hd-menu__plus {
  color: var(--accent);
  font-size: var(--t-sm);
  line-height: 1;
}
.hd-menu__item--add { color: var(--accent); }
.hd-menu__item--add:hover { color: var(--accent); }

.hd-menu__hint {
  padding: 0.45rem var(--s-3);
  font-size: var(--t-xs);
  line-height: 1.45;
  color: var(--ink-mid);
}

/* Wide variant for the combined org/property picker. The combined menu
   groups properties under their owning Org. Each `.hd-menu__org` is
   one section: head row with name + role + Settings button, then
   property rows, then Add. */
.hd-dropdown--wide .hd-menu { min-width: 22rem; max-width: 30rem; }

.hd-menu__org { display: flex; flex-direction: column; }
.hd-menu__org-head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3) 0.35rem;
}
.hd-menu__org-name {
  font-size: var(--t-base);
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 14rem;
}
.hd-menu__org-role {
  font-size: var(--t-xs);
  font-family: var(--font-mono);
  letter-spacing: 0.04em;
  color: var(--ink-mid);
}
.hd-menu__org-spacer { flex: 1; }

.hd-menu__item--property,
.hd-menu__org .hd-menu__item--add { padding-left: var(--s-4); }

/* ==========================================================
   VQ-RADIO + VQ-FORM: in-modal radio stack and form spacing.
   Custom-drawn 1px ink square with an ink fill on check —
   keeps modal forms in the same editorial-cartography vocabulary
   as the rest of the design system (no native chrome, no rounded
   corners, no platform-themed blue).
========================================================== */
.vq-radio {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--s-3);
  align-items: start;
  padding: var(--s-2) 0;
  cursor: pointer;
  font-family: var(--font-body);
  border-top: 1px solid var(--rule-soft);
}
.vq-radio:first-of-type { border-top: none; }

/* Hide native control; we draw the indicator with ::before on the
   first label span. The native input still receives focus + keyboard
   events; our :focus-visible style mirrors the design-system accent. */
.vq-radio input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

.vq-radio__label {
  position: relative;
  padding-left: calc(var(--s-4) + var(--s-1));
  font-size: var(--t-md);
  color: var(--ink);
  line-height: 1.3;
}
.vq-radio__label::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.18em;
  width: 0.85em;
  height: 0.85em;
  border: 1px solid var(--ink);
  background: transparent;
  transition: background var(--dur-1) ease;
}
.vq-radio input[type="radio"]:checked + .vq-radio__label::before {
  background: var(--ink);
}
.vq-radio input[type="radio"]:focus-visible + .vq-radio__label::before {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.vq-radio__hint {
  grid-column: 2;
  padding-left: calc(var(--s-4) + var(--s-1));
  font-size: var(--t-sm);
  color: var(--ink-mid);
  line-height: 1.4;
  margin-top: var(--s-1);
}
.vq-form > * + * { margin-top: var(--s-4); }

/* ==========================================================
   PAGE HEADERS
   The bottom rule animates in left-to-right on first paint
   (rule-draw, ~360ms). Single orchestrated reveal per
   navigation; reduced-motion media query in design-system
   collapses it to instant.

   Optional .page__datum kicker line above the title gives the
   page a coordinate-shaped legend ("§ 02 · CATALOG · 41°N"),
   reinforcing the chart-on-a-desk metaphor. Pages opt in by
   adding a <div class="page__datum">…</div> as the first child
   of .page__head; it claims a full row via flex-basis: 100%.
========================================================== */
.page__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s-4);
  padding-bottom: var(--s-4);
  position: relative;
}
.page__head::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--rule);
  transform-origin: left center;
  animation: rule-draw 360ms var(--ease-out) both;
}
.page__datum {
  flex: 1 0 100%;
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mid);
  margin-bottom: var(--s-1);
}
.page__datum::before {
  content: "";
  width: var(--s-3);
  height: 1px;
  background: var(--rule);
}
.page__title {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 3vw, var(--t-2xl));
  font-weight: 420;
  font-variation-settings: "opsz" 72, "SOFT" 80;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.page__lede {
  font-size: var(--t-sm);
  color: var(--ink-mid);
  max-width: 60ch;
  margin-top: var(--s-2);
}
.page__lede code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  padding: 0.05em 0.35em;
  background: var(--paper-darker);
  border-radius: var(--radius-sm);
  color: var(--ink);
}
.page__actions { display: inline-flex; gap: var(--s-3); flex-wrap: wrap; }

/* Small mono-caps back link that claims the top row of .page__head
   (flex-basis: 100% mirrors .page__datum). Used on detail / drill-in
   pages where we want the back affordance at the visual top-left
   rather than tucked into .page__actions on the right. */
.page__breadcrumb {
  flex: 1 0 100%;
  width: fit-content;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--ink-mid);
  text-decoration: none;
  margin-bottom: var(--s-1);
}
.page__breadcrumb:hover { color: var(--accent-deep); }

/* Interior section heading used between `.page__head` and a block of
   content. Use whenever a page has more than one logical section
   under its title. */
.section-title {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  font-weight: 440;
  font-variation-settings: "opsz" 36;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: var(--s-5) 0 var(--s-3);
}
.section-title:first-child { margin-top: 0; }

/* ==========================================================
   PANELS
========================================================== */
/* Panels are flex columns: direct children stack with `gap` rather
   than per-child margins. Picking --s-4 preserves the established
   head→body rhythm (panel__head used to set margin-bottom: --s-4
   manually). Pages that need a different rhythm inside a panel can
   either set `gap: 0` on that specific panel or wrap mixed-rhythm
   sections in their own flex containers. See readme/ARCHITECTURE.md
   if added there in future. */
.panel {
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: var(--s-5) var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}
.panel--compact {
  padding: var(--s-3) var(--s-4);
  gap: var(--s-3);
}
.panel--accent {
  border-color: var(--accent);
  background: var(--accent-tint);
}
.panel--accent .panel__head { border-color: var(--accent); }
.panel--accent .panel__title { color: var(--accent-deep); }
/* State-accent variants — 4px left rule in status palette tones,
   used wherever a panel's content reflects health/severity state. */
.panel--ok    { border-left: 0.25rem solid var(--status-success); }
.panel--warn  { border-left: 0.25rem solid var(--status-warning); }
.panel--error { border-left: 0.25rem solid var(--status-error); }
.panel__body { font-size: var(--t-sm); color: var(--ink); }
.panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule);
  /* No margin-bottom — panel's gap supplies the head→body rhythm. */
}
.panel__title {
    font-family: var(--font-display);
    font-size: var(--t-lg);
    color: var(--ink);
}

/* ==========================================================
   CARDS
========================================================== */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--s-4);
}
.card-grid--tiers {
  grid-template-columns: repeat(auto-fit, minmax(17.5rem, 1fr));
}
.card {
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  padding: var(--s-4);
}
.card--current {
  border-color: var(--accent);
  border-width: 2px;
}
.card--disabled { opacity: 0.6; }
/* State-accent variants: 4px left rule in status palette tones, used
   when a card's value reflects a system state that should read at a
   glance (machine health, alert severity, etc.). */
.card--ok    { border-left: 0.25rem solid var(--status-success); }
.card--warn  { border-left: 0.25rem solid var(--status-warning); }
.card--error { border-left: 0.25rem solid var(--status-error); }

.card__value--md  { font-size: var(--t-xl); }
.card__value--sm  { font-size: var(--t-lg); font-weight: 500; }
.card__value--err { color: var(--status-error); }
.card__label {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
  display: block;
  margin-bottom: var(--s-2);
}
.card__value {
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  font-variation-settings: "opsz" 72, "SOFT" 80;
  font-weight: 420;
  color: var(--ink);
  letter-spacing: -0.015em;
}
.card__sub {
  font-size: var(--t-sm);
  color: var(--ink-mid);
  margin-top: var(--s-1);
}

/* ==========================================================
   PREVIEW: definition-list (label ↔ value rows)
========================================================== */
.preview {
  display: flex;
  flex-direction: column;
}
.preview__row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-2) 0;
  border-bottom: 1px dashed var(--rule-soft);
  font-size: var(--t-sm);
}
.preview__row:last-child { border-bottom: 0; }
.preview__k {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.preview__v {
  color: var(--ink);
  text-align: right;
}

/* ==========================================================
   METER: usage-progress bar (search ops, ingest ops, etc.)
========================================================== */
/* .meter {
  margin-bottom: var(--s-5);
} */
.meter__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-3);
  margin-bottom: var(--s-2);
}
.meter__label {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.meter__value {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 36, "SOFT" 50;
  font-size: var(--t-md);
  color: var(--ink);
}
.meter__bar {
  height: var(--s-2);
  background: var(--paper-darker);
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}
.meter__fill {
  height: 100%;
  background: var(--accent);
  transition: width var(--dur-2) var(--ease-out);
}
.meter__fill--safe    { background: var(--forest); }
.meter__fill--warning { background: var(--accent); }
.meter__fill--danger  { background: var(--accent-deep); }
.meter__foot {
  display: flex;
  justify-content: space-between;
  margin-top: var(--s-2);
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-ghost);
}
.meter--na {
  opacity: 0.55;
  font-style: italic;
  color: var(--ink-mid);
  font-family: var(--font-display);
  padding: var(--s-2) 0;
}
.meter--na__label { color: var(--ink-ghost); }
.meter--na__note  { margin-left: var(--s-3); }

/* ==========================================================
   TABLES: ledger-like, editorial
   Each row reads as a measurement entry. Header uses a 2px ink
   underline (the chart's primary datum line); body rows are
   tighter than default web-app density so more entries fit on
   screen. Hover surfaces a small accent disc in the panel-padding
   gutter as a "you-are-reading-this-row" cue — same survey-pin
   vocabulary as the sidebar's active-state indicator.
========================================================== */
.tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}
.tbl thead th {
  text-align: left;
  vertical-align: bottom;
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
  padding: var(--s-2) var(--s-3);
  border-bottom: 2px solid var(--ink);
}
.tbl tbody td {
  padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--rule-soft);
  vertical-align: top;
  color: var(--ink);
  /* Tabular nums on every cell so any numeric content auto-aligns
     vertically without needing a per-cell .num class. */
  font-variant-numeric: tabular-nums;
}
.tbl tbody tr:last-child td { border-bottom: 0; }

/* Read-position datum dot: a 5px accent disc surfaces in the panel-
   padding gutter to the left of the hovered row. Anchored on the
   first cell (position: relative) so it tracks the row's vertical
   midpoint regardless of cell content height. */
.tbl tbody tr td:first-child { position: relative; }
.tbl tbody tr td:first-child::before {
  content: "";
  position: absolute;
  left: calc(var(--s-3) * -1);
  top: 50%;
  width: 5px;
  height: 5px;
  margin-top: -2.5px;
  background: var(--accent);
  border-radius: 50%;
  opacity: 0;
  transition: opacity var(--dur-1) var(--ease-out);
  pointer-events: none;
}
.tbl tbody tr:hover td:first-child::before { opacity: 1; }

.tbl td.num, .tbl th.num { text-align: right; font-family: var(--font-mono); }
.tbl td.mono, .tbl th.mono { font-family: var(--font-mono); font-size: var(--t-xs); color: var(--ink-soft); }

/* Clickable rows (item list row → edit form). */
.tbl tr.tbl-row-clickable { cursor: pointer; }

/* Row-action cluster: right-aligned group inside a `<td>`. Lives
   inside the cell (not directly on the td) so the cell remains
   display: table-cell — its vertical-align governs the layout, and
   adjacent cells with wrapping content don't break row alignment. */
.tbl .tbl-actions {
  display: inline-flex;
  gap: var(--s-3);
  justify-content: flex-end;
  flex-wrap: nowrap;
  white-space: nowrap;
}

/* Italic em-dash for null/empty cell values. Editorial register —
   reads as "no value here" rather than "this is broken". */
.dash {
  font-family: var(--font-display);
  font-style: italic;
  font-variation-settings: "opsz" 36, "SOFT" 50;
  color: var(--ink-ghost);
}

/* ==========================================================
   BADGES / STATUS CHIPS
   Sharp-cornered tinted-fill labels (no border, no rounding).
   The filled background distinguishes them from .btn (which
   uses border + paper bg for the interactive register); the
   sharp corners keep them in the rest of the design system's
   rectilinear vocabulary.

   Background opacity stays low (~10%) so badges sit on the
   page like printed labels, not loud chips.
========================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  padding: var(--badge-padding);
  font-family: var(--font-mono);
  font-size: var(--badge-font-size);
  line-height: 1.4;
  letter-spacing: var(--badge-letter-spacing);
  text-transform: uppercase;
  color: var(--ink-mid);
  background: var(--badge-neutral-bg);
}
/* Status variants change ONLY the background; text color stays the
   base `--ink-mid` from `.badge`. The hue of the tinted background is
   the signal — coloring the text too made variants look identical to
   the base badge under the default warm-paper palette. */
.badge--success { background: var(--badge-success-bg); }
.badge--warning { background: var(--badge-warning-bg); }
.badge--danger  { background: var(--badge-danger-bg); }
.badge--info    { background: var(--badge-info-bg); }
.badge--pending { background: var(--badge-pending-bg); }
.badge--unknown { background: var(--badge-unknown-bg); }

/* Flex helper for clusters of badges (e.g. user role + status pills
   under an entity title). */
.title-badges {
    display: flex;
    gap: 0.3125rem;
    align-items: center;
    margin-top: 0.375rem;
}

/* Modality pills: independent text/image/audio flags shown alongside
   search-query rows. Each pill renders only when its flag is true;
   a row can have any combination.

   Same passive-label register as .badge — sharp corners, soft tinted
   fill, no border. SVG icons inherit currentColor so they match the
   variant ink. */
.mod-pills {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-top: 0.5rem;
}
.mod-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: var(--badge-padding);
  font-family: var(--font-mono);
  font-size: var(--badge-font-size);
  line-height: 1.4;
  letter-spacing: var(--badge-letter-spacing);
  text-transform: uppercase;
  color: var(--ink-mid);
  background: rgba(23, 20, 14, 0.06);
}
.mod-pill svg { display: block; flex: 0 0 auto; }
.mod-pill--text  { color: var(--ink-mid); background: rgba(23, 20, 14, 0.06); }
.mod-pill--image { color: var(--ink);        background: rgba(23, 20, 14, 0.08); }
.mod-pill--audio { color: var(--accent-deep); background: rgba(178, 71, 24, 0.12); }

/* ==========================================================
   BUTTONS: components additions to design-system's .btn*
========================================================== */
.btn--danger {
  background: transparent;
  color: var(--accent-deep);
  border-color: var(--accent-deep);
}
.btn--danger:hover { background: var(--accent-deep); color: var(--paper); }
.btn[disabled], .btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* ==========================================================
   MODALS: editorial sheet pulled over the page
========================================================== */
.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(23, 20, 14, 0.55);
  backdrop-filter: blur(2px);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-4);
  animation: fadeIn var(--dur-1) var(--ease-out);
}
.modal {
  background: var(--paper);
  border: 1px solid var(--ink);
  max-width: 35rem;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
  padding: var(--s-6);
  box-shadow: 0 1.5rem 3rem rgba(23,20,14,0.18);
}
.modal__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-4);
  margin-bottom: var(--s-5);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
}
.modal__title {
  font-family: var(--font-display);
  font-size: var(--t-xl);
  font-variation-settings: "opsz" 72, "SOFT" 80;
  font-weight: 420;
  color: var(--ink);
}
.modal__close {
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--t-md);
  color: var(--ink-mid);
  width: var(--s-7);
  height: var(--s-7);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  flex-shrink: 0;
  transition: color var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out);
}
.modal__close:hover {
  color: var(--accent);
  background: var(--paper-darker);
}
.modal__close:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.modal__body { margin-bottom: var(--s-5); }
.modal__body .field + .field { margin-top: var(--s-4); }
.modal__foot {
  display: flex;
  gap: var(--s-3);
  justify-content: flex-end;
  flex-wrap: wrap;
  padding-top: var(--s-4);
  border-top: 1px solid var(--rule);
}

/* Lede paragraph inside a confirm dialog body. Quieter than the
   default modal body — these are short prose sentences explaining
   the consequences of a confirmation, not interactive content. */
.confirm-message {
    color: var(--ink-mid);
    font-size: var(--t-sm);
    line-height: 1.5;
}

/* ==========================================================
   TOASTS (ephemeral status messages)
========================================================== */
.toast-stack {
  position: fixed;
  top: var(--s-4);
  right: var(--s-4);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  pointer-events: none;
}
.toast {
  min-width: 16.25rem;
  max-width: 26.25rem;
  background: var(--paper);
  border: 1px solid var(--ink);
  padding: var(--s-3) var(--s-4);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.08em;
  color: var(--ink);
  box-shadow: 0 0.5rem 1.5rem rgba(23,20,14,0.16);
  pointer-events: auto;
  animation: toastIn var(--dur-1) var(--ease-out);
}
.toast--success { border-color: var(--forest); }
.toast--warning { border-color: var(--accent); }
.toast--error   { border-color: var(--accent-deep); background: var(--accent-tint); }
.toast__action {
  display: inline-block;
  margin-left: var(--s-2);
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  font-weight: 600;
}
.toast__action:hover { color: var(--accent-deep); }

/* ==========================================================
   LOADING
========================================================== */
.loading-overlay {
  position: fixed; inset: 0;
  background: rgba(238, 229, 206, 0.72);
  backdrop-filter: blur(2px);
  z-index: 150;
  display: none;
  align-items: center;
  justify-content: center;
}
.loading-overlay.is-shown { display: flex; }
.spinner {
  width: var(--s-6); height: var(--s-6);
  border: 2px solid var(--rule);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

/* ==========================================================
   FORM LAYOUT PRIMITIVES
   Composed layouts on top of design-system's .field atoms.
   Three patterns cover every form in the app:

     .form-grid     two-column grid (collapses to 1col below 40em)
     .form-stack    single-column with consistent vertical rhythm
     .form-actions  footer button bar (split layout via __detach)

   Form atoms (.field, .field__k, .field__hint, .field__error,
   .checkbox) live in design-system.css. .vq-radio + .vq-form
   live above in this file (modal-internal radio stack). The
   .switch toggle lives below in this file too — promoted from
   dashboard so admin can use it for entitlement toggles.
========================================================== */
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-4);
}
@media (max-width: 40em) {
  .form-grid { grid-template-columns: 1fr; }
}

/* Single-column form with consistent vertical rhythm between
   children, via flex gap. Default spacing is var(--s-4); --lg
   variant uses var(--s-5) for forms with section-like blocks
   instead of field-tight rows; --tight uses var(--s-3) for dense
   inline-row stacks. */
.form-stack {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.form-stack--lg    { gap: var(--s-5); }
.form-stack--tight { gap: var(--s-3); }

/* Labeled grouping. A `<section class="form-section">` with a
   heading + optional muted lede, then a `.form-stack` body. Gives
   form rows a clear "what is this group" without inventing boxed
   chrome — separation comes from whitespace, not borders. */
.form-section {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.form-section__head {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}
.form-section__title {
  font-family: var(--font-body);
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--ink);
  margin: 0;
}
.form-section__lede {
  margin: 0;
  color: var(--ink-mid);
}

/* Footer button bar. Default: all buttons right-aligned. The
   __detach modifier on a child (typically a delete or cancel)
   pushes everything after it to the right via margin-right: auto.

   Markup:
     <div class="form-actions">
       <button class="btn btn--danger form-actions__detach">Delete</button>
       <button class="btn">Cancel</button>
       <button class="btn btn--primary">Save</button>
     </div>
*/
.form-actions {
  display: flex;
  gap: var(--s-3);
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  padding-top: var(--s-4);
  margin-top: var(--s-5);
  border-top: 1px solid var(--rule);
}
.form-actions__detach { margin-right: auto; }

/* ==========================================================
   FILTER INPUT
   Free-standing input used in filter rows (search-queries,
   items list filter, etc.). Sits outside .field so it can
   participate in flex-row layouts; visual treatment mirrors
   .field input so the system reads consistently.
========================================================== */
.filter-input {
  flex: 1 1 18rem;
  max-width: 28rem;
  padding: 0.55em 0.75em;
  font-family: var(--font-body);
  font-size: var(--t-sm);
  background: var(--paper-light);
  border: 1px solid var(--rule);
  color: var(--ink);
  outline: none;
  transition: border-color var(--dur-1);
}
.filter-input::placeholder { color: var(--ink-ghost); }
.filter-input:hover { border-color: var(--ink-mid); }
.filter-input:focus { border-color: var(--ink); }

/* ==========================================================
   SWITCH (toggle)
   Used for boolean settings where on/off matters more than
   exact value (vs. checkbox which implies "tick this item").
   Promoted from dashboard so admin can use it for entitlement
   toggles, feature flags, etc.

   Markup:
     <label class="switch">
       <input type="checkbox">
       <span class="switch__track" aria-hidden="true"></span>
     </label>
========================================================== */
.switch {
  position: relative;
  display: inline-block;
  width: 2.6rem;
  height: 1.4rem;
  vertical-align: middle;
  cursor: pointer;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.switch__track {
  position: absolute;
  inset: 0;
  background: var(--rule-soft);
  border: 1px solid var(--rule);
  transition: background var(--dur-1), border-color var(--dur-1);
  border-radius: 999px;
}
.switch__track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: calc(1.4rem - 6px);
  height: calc(1.4rem - 6px);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 999px;
  transition: transform var(--dur-1), border-color var(--dur-1);
}
.switch input:checked + .switch__track {
  background: var(--accent);
  border-color: var(--accent);
}
.switch input:checked + .switch__track::after {
  transform: translateX(1.2rem);
  border-color: var(--accent-deep);
}
.switch input:focus-visible + .switch__track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.switch input:disabled + .switch__track {
  opacity: 0.55;
  cursor: not-allowed;
}

/* ==========================================================
   INLINE TEXT PRIMITIVES
========================================================== */
/* Muted mono micro-caption (letter-spaced "ticker" text). */
.hint {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.08em;
  color: var(--ink-mid);
}
.hint--tight { letter-spacing: 0.05em; }

.inline-code {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  background: var(--paper-darker);
  padding: 0.1em 0.4em;
  letter-spacing: 0.02em;
  color: var(--ink);
}

/* "Nothing here yet" block for tables/lists. */
.empty {
  padding: var(--s-6);
  text-align: center;
  color: var(--ink-mid);
  font-style: italic;
  background: var(--paper);
  border: 1px dashed var(--rule);
}
.empty--error { color: var(--accent-deep); }
.empty a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  font-weight: 600;
}
.empty a:hover { color: var(--accent-deep); }
.empty--inline {
  padding: var(--s-3);
  background: transparent;
  border: 0;
  text-align: left;
  font-style: normal;
}

/* Just-issued credential flash (API key, invitation token, etc.). */
.key-flash {
  background: var(--accent-tint);
  border: 1px solid var(--accent);
  padding: var(--s-3);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  word-break: break-all;
  color: var(--ink);
  letter-spacing: 0.02em;
  text-transform: none;
  margin: var(--s-3) 0;
}

/* ==========================================================
   DASH SUBHEAD: sub-section headers inside a panel (splits
   a panel into labelled zones without a full second panel).
   Group multiple zones inside their own .dash-section flex
   container so the panel's gap separates zones, the section's
   gap separates within-zone children. No per-element margins.
========================================================== */
.dash-section {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.dash-subhead {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.dash-subhead__note {
  font-size: var(--t-xs);
  color: var(--ink-mid);
  font-family: var(--font-body);
  letter-spacing: 0;
  text-transform: none;
}

/* ==========================================================
   SUMMARY ROW: counter cells used at the top of list pages
   (catalog health, items, queries). Each cell is a compact
   numeric tile that can be a button (filter), a link (drill),
   or plain (read-only).
========================================================== */
.summary-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--s-3);
}
.summary-cell {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-5);
  background: var(--paper);
  border: 1px solid var(--rule);
  color: inherit;
  text-align: left;
  font: inherit;
  text-decoration: none;
}
button.summary-cell,
a.summary-cell {
  cursor: pointer;
  text-decoration: none;
  transition: border-color var(--dur-1) var(--ease-out),
              background var(--dur-1) var(--ease-out);
}
button.summary-cell:hover,
a.summary-cell:hover {
  border-color: var(--ink);
  background: var(--paper-deep);
}
.summary-cell--active {
  border-color: var(--accent);
  background: var(--accent-tint);
}
.summary-cell__count {
  font-family: var(--font-display);
  /* Push opsz toward 96+ so big numerals get true display-cut
     proportions (taller stems, finer joins) instead of body-cut
     numerals scaled up. Lighter weight at large sizes feels like
     an instrument reading. */
  font-size: clamp(2rem, 3vw, var(--t-3xl));
  font-weight: 380;
  font-variation-settings: "opsz" 96, "SOFT" 80;
  letter-spacing: -0.025em;
  line-height: 0.95;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.summary-cell__label {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mid);
}
.summary-cell__badge {
  margin-top: auto;
}

/* ==========================================================
   FILTER ROW: inline chip + free-text filter cluster used
   under a summary row.
========================================================== */
.filter-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
}
.filter-row__label {
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
  margin-right: var(--s-2);
}

/* .filter-input rules live in the FORM LAYOUT PRIMITIVES section
   above so they sit alongside the .field/.checkbox/.switch
   vocabulary that share the same visual treatment. */

.chip {
  display: inline-flex;
  align-items: center;
  padding: 0.35em 0.9em;
  font-family: inherit;
  font-size: var(--t-xs);
  background: var(--paper);
  border: 1px solid var(--rule);
  color: var(--ink-mid);
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease-out),
              background var(--dur-1) var(--ease-out),
              color var(--dur-1) var(--ease-out);
}
.chip:hover { border-color: var(--ink); color: var(--ink); }
.chip--active {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--accent-deep);
}

/* ==========================================================
   LIST ROWS: item + query row micro-layout used inside .tbl
========================================================== */
.item-title, .query-text {
  font-size: var(--t-md);
  color: var(--ink);
  line-height: 1;
}
.item-sub, .query-sub {
  font-size: var(--t-2xs);
  font-weight: 600;
  color: var(--ink-soft);
  margin-top: 0.5rem;
  line-height: 1;
}
.query-sub.muted { color: var(--ink-mid); }

.item-error-row td {
  background: var(--paper-deep);
  padding-top: 0;
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--rule);
}

.load-more {
  display: flex;
  justify-content: center;
  margin-top: var(--s-5);
}

/* ==========================================================
   PROGRESS BAR (tracked operations: imports, large reprocess)
========================================================== */
.progress {
  margin-top: var(--s-3);
  height: 0.625rem;
  background: var(--paper-deep);
  border-radius: 4px;
  overflow: hidden;
}
.progress__fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  transition: width 120ms;
}
.progress__text {
  margin-top: var(--s-2);
  font-size: var(--t-xs);
}
.progress__errors {
  font-size: var(--t-xs);
  margin-top: var(--s-2);
  color: var(--ink-mid);
}

/* ==========================================================
   INTELLIGENCE CARDS: feature-card grid + drill-in surfaces
   The same Intelligence vocabulary is reused on the public
   /intelligence/<slug> URL, the property dashboard, and (in
   future) admin per-property views.
========================================================== */
.intel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: var(--s-4);
}

.intel-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  padding: var(--s-5);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--dur-1) var(--ease-out);
  min-height: 11rem;
}
/* Cartographic hover, not the web-app card lift: border darkens
   to ink-mid (subtle "I am selectable") and the surveyor's corner
   crosshairs flash accent (sharp "you're hovering me"). No
   translate, no shadow — flat surfaces, just precision marks. */
.intel-card:hover {
  border-color: var(--ink-mid);
}
.intel-card:hover::before {
  background-color: var(--accent);
}

.intel-card__title {
  font-family: var(--font-display);
  font-size: var(--t-xl);
  font-variation-settings: "opsz" 48, "SOFT" 60;
  font-weight: 420;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.intel-card__lede {
  font-size: var(--t-sm);
  color: var(--ink-mid);
  margin: 0;
  line-height: 1.45;
}
.intel-card__signal {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.intel-card__count {
  font-family: var(--font-mono);
  font-size: var(--t-sm);
  color: var(--ink);
}
.intel-card__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

.intel-chip {
  display: inline-block;
  padding: 0.2em 0.65em;
  background: rgba(0,0,0,0.04);
  border: 1px solid var(--rule-soft);
  font-size: var(--t-xs);
  color: var(--ink-mid);
  white-space: nowrap;
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
.intel-chip--gap {
  border-color: var(--accent);
  color: var(--accent-deep);
}

.intel-card__when {
  font-size: var(--t-xs);
  color: var(--ink-mid);
}

/* Empty / standing-by state on a card's signal slot. Italic display
   font with a small accent dot reads as intentional, like an editorial
   pull quote, instead of the plain "no data" sans line. */
.intel-card__empty {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  font-family: var(--font-display);
  font-style: italic;
  font-variation-settings: "opsz" 48, "SOFT" 60;
  font-weight: 380;
  font-size: var(--t-md);
  line-height: 1.35;
  color: var(--ink-mid);
  letter-spacing: -0.005em;
}
.intel-card__empty::before {
  content: "";
  flex-shrink: 0;
  width: 0.35rem;
  height: 0.35rem;
  border-radius: 50%;
  background: var(--accent);
  transform: translateY(-0.15em);
}
.intel-card__empty--error { color: var(--accent-deep); }
.intel-card__empty--error::before { background: var(--accent-deep); }
.intel-card__arrow {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  font-size: var(--t-lg);
  color: var(--ink-mid);
}
.intel-card:hover .intel-card__arrow { color: var(--accent); }

/* Static card variant: per-item Intelligence cards are content blocks
   on a single page, not links. Suppress the hover affordance entirely
   so they read as content, not interactive surfaces. */
.intel-card--static { cursor: default; }
.intel-card--static:hover { border-color: var(--rule-soft); }
.intel-card--static:hover::before { background-color: rgba(23, 20, 14, 0.45); }

/* Per-item Intelligence layout: top row of three tight cards, bottom
   row of two wider list cards. */
.item-intel-row {
  display: grid;
  gap: var(--s-4);
}
.item-intel-row--3up { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.item-intel-row--2up { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 56em) {
  .item-intel-row--3up,
  .item-intel-row--2up { grid-template-columns: 1fr; }
}

/* Scrollable list region inside an intel-card. Caps the height so a
   long duplicates list doesn't push the card past its neighbors. */
.intel-card__scroll {
  margin-top: var(--s-2);
  max-height: 14rem;
  overflow-y: auto;
}
.intel-list-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--rule-soft);
  text-decoration: none;
  color: inherit;
}
.intel-list-row:last-child { border-bottom: 0; }
a.intel-list-row:hover .intel-list-row__title { color: var(--accent); }
.intel-list-row__main {
  flex: 1 1 auto;
  min-width: 0;
}
.intel-list-row__title {
  font-size: var(--t-sm);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.intel-list-row__meta {
  font-size: var(--t-xs);
  color: var(--ink-mid);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Relative-strength bar. Width comes from a discrete bucket class
   (--w0 through --w100) so the markup stays free of inline styles
   while still rendering a visible signal. */
.intel-bar {
  flex: 0 0 auto;
  width: 4.5rem;
  height: 0.4rem;
  background: rgba(0,0,0,0.04);
  border: 1px solid var(--rule-soft);
  position: relative;
  overflow: hidden;
}
.intel-bar__fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--accent);
}
.intel-bar__fill--w0   { width: 0; }
.intel-bar__fill--w10  { width: 10%; }
.intel-bar__fill--w20  { width: 20%; }
.intel-bar__fill--w30  { width: 30%; }
.intel-bar__fill--w40  { width: 40%; }
.intel-bar__fill--w50  { width: 50%; }
.intel-bar__fill--w60  { width: 60%; }
.intel-bar__fill--w70  { width: 70%; }
.intel-bar__fill--w80  { width: 80%; }
.intel-bar__fill--w90  { width: 90%; }
.intel-bar__fill--w100 { width: 100%; }

/* Trend / status pills. Reuses the muted-chip aesthetic; color cue is
   subtle so the pills work as flow markers, not alarm bells. */
.intel-pill {
  display: inline-block;
  padding: 0.15em 0.55em;
  font-size: var(--t-xs);
  font-family: var(--font-mono);
  border: 1px solid var(--rule-soft);
  background: rgba(0,0,0,0.04);
  color: var(--ink-mid);
}
.intel-pill--rising,
.intel-pill--undersupplied {
  border-color: var(--accent);
  color: var(--accent-deep);
}
.intel-pill--cooling,
.intel-pill--oversupplied,
.intel-pill--steady,
.intel-pill--balanced {
  border-color: var(--rule-soft);
  color: var(--ink-mid);
}

/* Cluster list rows: shared across demand and catalog views. */
.intel-cluster {
  padding: var(--s-4);
  border-bottom: 1px solid var(--rule-soft);
}
.intel-cluster:last-child { border-bottom: 0; }
.intel-cluster__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
}
.intel-cluster__name {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  font-weight: 460;
}
.intel-cluster__meta {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  color: var(--ink-mid);
}
.intel-cluster__desc {
  margin-top: var(--s-2);
  font-size: var(--t-sm);
  color: var(--ink-mid);
  line-height: 1.5;
}
.intel-cluster__archetypes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
}

/* Lens row: one entry in a per-lens list (demand-vs-supply table,
   placement-detail candidates). Block + bordered separator. */
.intel-lens-row {
  padding: var(--s-4);
  border-bottom: 1px solid var(--rule-soft);
}
.intel-lens-row:last-child { border-bottom: 0; }
.intel-lens-row__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
}
.intel-lens-row__name-wrap {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  flex-wrap: wrap;
  min-width: 0;
}
.intel-lens-row__name {
  font-family: var(--font-display);
  font-size: var(--t-lg);
  font-weight: 460;
}
.intel-lens-row__score {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  color: var(--ink-mid);
}
.intel-lens-row__narrative {
  font-size: var(--t-sm);
  color: var(--ink);
  line-height: 1.55;
}
.intel-lens-row__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  font-size: var(--t-xs);
  color: var(--ink-mid);
}

.intel-trend {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.04em;
}
.intel-trend--rising  { color: var(--accent-deep); }
.intel-trend--cooling { color: var(--ink-mid); }

/* ==========================================================
   INTELLIGENCE MAP: heatmap canvas + anchor overlay
   Pairs with cdn/js/heatmap.js. Same treatment on the public
   /intelligence URL and inside the property dashboard so the
   shareable view and the in-product view read as the same thing.
========================================================== */
.intel-map-frame {
  position: relative;
  /* 35rem matches the CANVAS_H = 560 constant in dashboard/intelligence/intelligence.js,
     which the page's mouse-position math relies on. Keep the two in sync. */
  height: 35rem;
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  overflow: hidden;
}
.intel-map-frame canvas {
  display: block;
  width: 100%;
  cursor: default;
}

/* Stacked heatmap (canvas) + anchor overlay (svg). SVG is
   pointer-events: none so mouse events fall through to the canvas
   for hover hit-testing; tooltip positioning uses canvas-relative
   coords. */
.intel-map__canvas,
.intel-map__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.intel-map__canvas { z-index: 1; }
.intel-map__svg    { z-index: 2; pointer-events: none; }

.intel-map__node-dot {
  fill: var(--ink);
  stroke: var(--paper);
  stroke-width: 1.5;
  transition: r 120ms ease-out;
}
.intel-map__node-dot.is-hovered {
  r: 8;
  fill: var(--accent);
}
.intel-map__node-label {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 500;
  fill: var(--ink);
  paint-order: stroke;
  stroke: var(--paper);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.intel-map-tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  padding: var(--s-3);
  max-width: 20rem;
  box-shadow: 0 4px 14px rgba(0,0,0,0.08);
  z-index: 2;
}
/* Sample-size legend pinned to the bottom-right of the map frame.
   Anchors interpretation of heatmap density (warm region from 1.2k
   searches reads very differently from one drawn from 12). The
   Period selector above the map already names the window, so this
   only carries the count. */
.intel-map__legend {
  position: absolute;
  bottom: var(--s-3);
  right: var(--s-3);
  z-index: 2;
  pointer-events: none;
  background: var(--paper);
  border: 1px solid var(--rule-soft);
  padding: 0.35rem 0.65rem;
  font-family: var(--font-mono);
  font-size: var(--t-2xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mid);
  font-variant-numeric: tabular-nums;
}
.intel-map__legend strong {
  font-weight: 500;
  color: var(--ink);
}
.intel-map-tooltip__name {
  font-family: var(--font-display);
  font-size: var(--t-md);
  font-weight: 500;
  color: var(--ink);
}
.intel-map-tooltip__meta {
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  color: var(--ink-mid);
  margin-top: 0.15rem;
}
.intel-map-tooltip__desc {
  margin-top: var(--s-2);
  font-size: var(--t-xs);
  color: var(--ink-mid);
  line-height: 1.45;
}

/* ==========================================================
   INTELLIGENCE: density bar (relative-strength visualization
   used in positioning/catalog).
========================================================== */
.intel-density {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-2);
  font-size: var(--t-sm);
}
.intel-density__bar {
  flex: 1;
  height: 6px;
  background: var(--rule-soft);
  overflow: hidden;
}
.intel-density__fill {
  height: 100%;
  background: var(--accent);
}

/* Refresh-in-progress banner on Intelligence drill-in pages. */
.intel-refreshing {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: rgba(178,71,24,0.06);
  border: 1px solid var(--accent);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.05em;
  color: var(--accent-deep);
}
.intel-spinner {
  width: 0.75rem;
  height: 0.75rem;
  border: 1.5px solid var(--rule-soft);
  border-top-color: var(--accent-deep);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
  flex-shrink: 0;
}

/* Informational callout shared across Intelligence pages. */
.intel-callout {
  padding: var(--s-3) var(--s-4);
  border-left: 2px solid var(--accent);
  background: rgba(178,71,24,0.03);
  font-size: var(--t-sm);
  color: var(--ink-mid);
}

/* ==========================================================
   MOBILE SAFETY NET (components-shaped)
   Per-surface app-shell mobile rules (header, sidebar,
   main-column gutter) live in <surface>.css. This media query
   only covers components that are in components: panel padding,
   table scroll containment.
========================================================== */
@media (max-width: 51.25em) {
  .panel { padding: var(--s-4); }
  .panel:has(.tbl) {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .panel:has(.tbl) .tbl { min-width: 36rem; }
  .panel__head {
    flex-wrap: wrap;
    row-gap: var(--s-2);
  }
}

/* ==========================================================
   PAGINATION
   Numbered page navigation used at the bottom of paginated
   list pages (admin, future dashboard pages). Renders as:
     [< Previous] [1] [2] ... [N] [Next >]   Page [_3_] of N
========================================================== */
.pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 5px;
    margin-top: var(--s-5);
    padding: var(--s-4);
}

.page-btn {
    padding: 0.4rem 0.75rem;
    background: var(--paper);
    border: 1px solid var(--rule);
    cursor: pointer;
    color: var(--ink-mid);
    font-family: var(--font-mono);
    font-size: var(--t-2xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    transition: color var(--dur-1), border-color var(--dur-1);
}
.page-btn:hover {
    border-color: var(--ink);
    color: var(--ink);
}

.page-number {
    padding: 0.4rem 0.75rem;
    color: var(--ink-mid);
    font-family: var(--font-mono);
    font-size: var(--t-2xs);
    letter-spacing: 0.08em;
}
.page-number.active {
    background: var(--accent);
    color: var(--paper);
    font-weight: 600;
}

.page-ellipsis { padding: 8px; color: var(--ink-mid); }

.page-goto {
    margin-left: var(--s-4);
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}
.page-goto__label {
    font-family: var(--font-mono);
    font-size: var(--t-2xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-mid);
}
.page-goto-input {
    width: 4rem;
    padding: 0.25rem 0.5rem;
    border: 1px solid var(--rule);
    background: var(--paper-light);
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: var(--t-sm);
    text-align: center;
}

/* ==========================================================
   BADGE: small variant — tighter footprint for in-row tags.
========================================================== */
.badge--sm {
  padding: 0.125rem 0.5rem;
  font-size: var(--t-3xs);
}

/* ==========================================================
   BUTTON: warning variant
   Amber treatment for non-destructive but consequential actions
   (release lock, retry, purge cache). Distinct from .btn--danger
   so "this is reversible but you should know what you're doing"
   reads differently from "this destroys data."
========================================================== */
.btn--warning {
  background: var(--status-warning);
  color: var(--paper);
  border-color: var(--status-warning);
}
.btn--warning:hover {
  background: #946530;
  border-color: #946530;
  color: var(--paper);
}

/* ==========================================================
   COMPACT TABLE — .tbl + .tbl--mini modifier
   Compact variant for "top N", row-detail sub-tables, and other
   in-card tables where dashboard's editorial padding is too loose.
   Smaller font, tighter cells, no row-stripe.
========================================================== */
.tbl--mini th,
.tbl--mini td {
  padding: var(--s-1) var(--s-2);
  font-size: var(--t-xs);
}
.tbl--mini thead th {
  font-size: var(--t-2xs);
  letter-spacing: 0.06em;
  font-weight: 500;
  color: var(--ink-mid);
}
.tbl--mini td.mono { font-family: var(--font-mono); }

/* ==========================================================
   LIST + DETAIL LAYOUT
   Two-column layout used wherever the page is a scrollable list
   on the left and a detail/edit pane on the right. Tiers, properties,
   admins, etc. all use this shape.
========================================================== */
.list-detail {
  display: grid;
  grid-template-columns: 22rem 1fr;
  gap: var(--s-6);
  min-height: 32rem;
}
.list-detail > .panel { overflow-y: auto; }
@media (max-width: 51.25em) {
  .list-detail { grid-template-columns: 1fr; }
}

/* Vertical stack of selectable rows. Used inside the list column
   of .list-detail and anywhere a click-to-select list is needed. */
.card-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

/* Selectable list-row card. Sharp corners, ink-tint accent on
   hover and selected, dim on disabled. */
.selectable-card {
  padding: var(--s-4);
  border: 1px solid var(--rule);
  cursor: pointer;
  background: var(--paper);
  transition: border-color var(--dur-1), background var(--dur-1);
}
.selectable-card:hover {
  border-color: var(--accent);
  background: rgba(178, 71, 24, 0.05);
}
.selectable-card.is-selected {
  border-color: var(--accent);
  background: rgba(178, 71, 24, 0.10);
}
.selectable-card.is-disabled { opacity: 0.6; }
.selectable-card__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-3);
}
.selectable-card__head h4 { margin: 0; }
.selectable-card__meta {
  margin-top: var(--s-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  font-size: var(--t-xs);
  color: var(--ink-mid);
}

/* ==========================================================
   FILTER BANNER — soft accent strip shown above a filtered
   table to reassure the operator that a filter is active and
   provide a one-click clear.
========================================================== */
.filter-banner {
  background: rgba(178, 71, 24, 0.06);
  border: 1px solid rgba(178, 71, 24, 0.25);
  border-radius: var(--radius-sm);
  padding: var(--s-2) var(--s-4);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  font-size: var(--t-sm);
  color: var(--ink-soft);
}

/* ==========================================================
   ERROR MESSAGE — small centered error string below a form,
   above a button, etc. Status-error tone.
========================================================== */
.error-message {
  color: var(--status-error);
  text-align: center;
  margin-top: var(--s-2);
  font-size: var(--t-sm);
}

/* ==========================================================
   EMPTY STATE ROW — single full-row centered empty message
   inside a .tbl tbody, e.g. "No results match your filters."
========================================================== */
.empty-state-row td {
  text-align: center;
  padding: var(--s-7);
  color: var(--ink-mid);
}

/* ==========================================================
   ANIMATIONS
========================================================== */
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes toastIn {
  from { opacity: 0; transform: translateX(0.5rem); }
  to   { opacity: 1; transform: translateX(0); }
}
/* Page-head bottom rule "drawing" reveal — single orchestrated
   moment per page navigation. Origin is left so the line extends
   right, like a survey transit being plotted. */
@keyframes rule-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
