/*
 * app.css — minxi-admin-specific LAYOUT only.
 *
 * Tokens, reset, theme, and the shared utility classes come from the CDN
 * (static.minxi.ai/css/{tokens,reset,theme-dark,app-base}.css, loaded before
 * this file). This file carries ONLY what is specific to this app's shell
 * layout: the body grid, content spacing, and a couple of Home page-scoped
 * classes. Selectors prefer STRUCTURE + STATE ATTRIBUTES over classes
 * (SEMANTIC-HTML CSS-hook priority); no banned structural classes
 * (`.sidebar`, `.page-header`, `.main-content`, …).
 */

/* The sidebar width — provided by app-base.css on the CDN, but defined here as
   a resilient fallback so the grid still lays out if the CDN var is absent. */
:root {
  --sidebar-width: 260px;
}

/* ── Shell foundation ──────────────────────────────────────────────────
 * The shell layout matches minxi-platform's paradigm: <body> is the grid
 * container, driven PRE-PAINT by `<html data-authed>` (stamped synchronously
 * by the head boot script from the persisted JWT). No `:has()` re-evaluation
 * race, no JS-driven reveal, no layout shift on load. Pre-auth the body is a
 * single column and the sidebar is `display:none` — the login <dialog> owns
 * the viewport via .showModal(). */
body {
  font-family: var(--font-family);
  background: linear-gradient(180deg, var(--background-start), var(--background-end));
  background-attachment: fixed;
  color: var(--text-primary);
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: minmax(0, 1fr);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

html[data-authed] body {
  grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
}

/* ── Sidebar ───────────────────────────────────────────────────────────
 * `display:none` until authed; the data-authed flag (not a [hidden] attr or a
 * JS class swap) reveals it. Spans the viewport height and scrolls on its own. */
body > aside {
  grid-row: 1;
  grid-column: 1;
  background: var(--side-nav-bg, transparent);
  border-right: 1px solid var(--border-soft, var(--border-color));
  display: none;        /* default: pre-auth, sidebar hidden */
  flex-direction: column;
  overflow-y: auto;
  padding: var(--space-md) 0;
}
html[data-authed] body > aside {
  display: flex;
}

/* Brand block. */
body > aside > header {
  padding: 0 var(--space-md) var(--space-md);
  border-bottom: 1px solid var(--border-soft, var(--border-color));
  margin-bottom: var(--space-md);
}
body > aside > header > strong {
  display: block;
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
}
body > aside > header > p {
  margin: var(--space-xs) 0 0;
  font-size: var(--font-size-xs);
  letter-spacing: var(--letter-spacing-caps);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* Global search — full-width input that matches the form-control treatment.
   Admin-only element (platform has no search slot); the bottom margin keeps
   the same --space-md rhythm the aside's removed flex `gap` used to supply, so
   the nav below isn't cramped against it. */
body > aside > [data-global-search] {
  padding: 0 var(--space-md);
  margin-bottom: var(--space-md);
}
body > aside > [data-global-search] input[type="search"] {
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-sm) var(--space-md);
  background: var(--background-start);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--font-size-sm);
  outline: none;
  /* Long (e.g. localized) placeholders degrade to an ellipsis instead of being
     hard-clipped mid-word in the narrow sidebar column. */
  text-overflow: ellipsis;
  transition: border-color var(--transition-fast);
}
body > aside > [data-global-search] input[type="search"]:focus {
  border-color: var(--accent-primary);
}

/* Pillar nav — the SHARED CDN navigation engine (navigation.js) renders
   <nav data-nav-menu> as a direct child of <aside>, with a top-level Home <a>
   followed by one <details> category group per pillar. Structure is flat:
   nav > a (Home/Dashboard) and nav > details > a (category items) — no <ul>,
   no <li>. These rules mirror minxi-platform's admin.css so the engine's output
   reads identically in both consoles (admin keeps its own theme tokens). The
   nav grows to fill the column so the footer sinks to the bottom. */
body > aside > nav {
  flex: 1 1 auto;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

/* Every nav link (Home + category items) — a padded flex row with an accent
   left-border in the active state. The shared engine emits the icon as the
   FIRST child span (.nav-icon) and the label as the SECOND (a plain <span>);
   admin's old element added a `.nav-label` class — keep matching both so the
   ellipsis rule below applies to the engine's bare label span too. */
body > aside > nav a {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1.5rem;
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--font-size-sm);
  border-left: 3px solid transparent;
  transition: background var(--transition-fast), color var(--transition-fast);
}
body > aside > nav a:hover {
  background: var(--secondary-surface, var(--surface-hover));
  color: var(--text-primary);
}
/* Active link — the platform-native aria-current="page" the engine sets. */
body > aside > nav a[aria-current="page"] {
  color: var(--accent-primary);
  background: var(--secondary-surface, var(--surface-hover));
  border-left-color: var(--accent-primary);
}
/* Top-level Home/Dashboard link — direct child of <nav>, slightly heavier to
   set it apart from the categorised items below. */
body > aside > nav > a {
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
  margin-bottom: 0.25rem;
}

/* Category groups — native <details>/<summary> (audit-23 P-13d). The summary
   is an uppercase tracked label with a chevron that rotates on [open]; the
   default disclosure triangle is hidden. */
body > aside > nav details {
  margin: 0;
  display: flex;
  flex-direction: column;
}
body > aside > nav details > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.75rem 1.5rem 0.375rem;
  font-size: 0.6875rem;
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-caps);
  text-transform: uppercase;
  color: var(--text-tertiary);
  user-select: none;
  transition: background var(--transition-fast), color var(--transition-fast);
}
body > aside > nav details > summary::-webkit-details-marker { display: none; }
body > aside > nav details > summary::after {
  content: "▸";
  margin-left: auto;
  font-size: 0.7em;
  color: var(--text-tertiary);
  transition: transform var(--transition-fast);
}
body > aside > nav details[open] > summary { color: var(--accent-primary); }
body > aside > nav details[open] > summary::after {
  transform: rotate(90deg);
  color: var(--accent-primary);
}
body > aside > nav details > summary:hover {
  background: var(--surface-hover);
  color: var(--text-secondary);
}
/* Indent category items so the disclosure grouping is visually clear. */
body > aside > nav details > a { padding-left: 2rem; }

/* Nav icons — inline emoji glyphs (minxi-platform's paradigm): instant,
   zero network. A fixed-width centred gutter so labels align; emoji carry their
   own colour (a touch dimmed at rest, full on hover/active for a subtle lift). */
body > aside > nav .nav-icon {
  flex: 0 0 auto;
  width: 1.25rem;
  text-align: center;
  font-size: var(--font-size-base);
  line-height: 1;
  opacity: 0.85;
  transition: opacity var(--transition-fast);
}
body > aside > nav a:hover .nav-icon,
body > aside > nav a[aria-current="page"] .nav-icon,
body > aside > nav details[open] > summary .nav-icon { opacity: 1; }
/* Labels fill the row so the category chevron is pushed to the right edge; long
   labels ellipsize rather than wrap. Targets the engine's bare label <span>
   (the non-icon child) — matched by position so no class is required. */
body > aside > nav a > span:not(.nav-icon) {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Sidebar footer — org switcher + identity + workspace link + sign-out. */
body > aside > footer {
  margin-top: auto;
  padding: var(--space-sm);
  border-top: 1px solid var(--border-soft, var(--border-color));
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
/* Org switcher — the sidebar's TOP slot ([data-nav-switcher]): a <details>
   rendered by the shared nav engine (nav-config.js buildOrgSwitcher) whose
   <summary> is the ACTIVE ORG (avatar + name), listing the member's orgs (each →
   its own /organizations/<id> home). Single-org members get a plain link. The
   summary must OVERRIDE the category-summary's tiny uppercase-label treatment so
   the org name reads as the org context (normal case, primary, readable). */
[data-nav-switcher] > summary {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-primary);
  padding: 0.75rem 1.5rem;
}
/* The org avatar (active org in the summary + each option) shouldn't shrink. */
[data-nav-switcher] minxi-avatar { flex: 0 0 auto; }
/* Mark the active org in the list. */
[data-nav-switcher] > a[aria-current="true"] {
  color: var(--accent-primary);
  font-weight: var(--font-weight-semibold);
}

/* Member identity chip — the <minxi-identity-chip data-user-info> that
   shell-surfaces.js paints into the footer (name + active-org secondary). All
   the visual treatment lives in the component's shadow DOM; only the
   footer-level spacing + reserved geometry (so the async paint doesn't shift
   layout) stays here. Mirrors minxi-platform's [data-user-info] rule. */
body > aside > footer [data-user-info] {
  margin-bottom: 0.75rem;
  min-height: 44px;
}

/* Quick-links icon row — ONE horizontal row of three icon-only links
   (Workspace · Control Room · Orbit), painted by shell-surfaces.js into
   <nav data-quick-links> between the identity chip and the Docs/Help button.
   FULL-WIDTH: the row spans the footer's full width and the three icon cells each
   `flex: 1`, so they collectively fill that width — matching the horizontal
   extent of the full-width Docs/Help + Sign-out buttons directly below. Icon-only
   — each cell carries title + aria-label (set in JS) for the tooltip + accessible
   name. */
body > aside > footer > .footer-quick-links {
  display: flex;
  align-items: stretch;
  gap: var(--space-xs);
  margin-bottom: 0.75rem;
}
/* The icon cells (the three <a> links). Each takes an equal third of the
   full-width row (`flex: 1`), staying square-ish via a fixed height; rounded,
   transparent at rest with a tonal hover + a focus-visible ring. The glyph is
   centred and lifts slightly on hover/focus. (The `> button` arm is defensive —
   no button lives in this row today; Docs/Help is the separate full-width row
   below.) */
body > aside > footer > .footer-quick-links > a,
body > aside > footer > .footer-quick-links > button {
  display: inline-flex;
  flex: 1;
  align-items: center;
  justify-content: center;
  min-width: 0;
  height: 2.375rem;
  padding: 0;
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border-soft, var(--border-color));
  border-radius: var(--radius-md, 8px);
  font-family: inherit;
  font-size: var(--font-size-base);
  line-height: 1;
  cursor: pointer;
  transition: color var(--transition-fast),
              border-color var(--transition-fast),
              background var(--transition-fast);
}
body > aside > footer > .footer-quick-links > a:hover,
body > aside > footer > .footer-quick-links > button:hover {
  color: var(--text-primary);
  border-color: var(--border-medium, var(--text-tertiary));
  background: var(--surface-hover, var(--secondary-surface));
}
body > aside > footer > .footer-quick-links > a:focus-visible,
body > aside > footer > .footer-quick-links > button:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}
/* The icon glyph (aria-hidden decorative span) — dimmed at rest, full on
   hover/focus, mirroring the nav-icon lift. */
body > aside > footer > .footer-quick-links > a > span,
body > aside > footer > .footer-quick-links > button > span {
  opacity: 0.85;
  transition: opacity var(--transition-fast);
}
body > aside > footer > .footer-quick-links > a:hover > span,
body > aside > footer > .footer-quick-links > a:focus-visible > span,
body > aside > footer > .footer-quick-links > button:hover > span,
body > aside > footer > .footer-quick-links > button:focus-visible > span {
  opacity: 1;
}

/* Docs/Help + Sign out — the full-width footer rows below the icon row. The
   Docs/Help button is the <minxi-docs> built-in trigger (a display:contents host,
   so the trigger participates directly in this flex column; styled full-width by
   the component's own shadow CSS off the inherited theme tokens). Sign out is the
   real <button data-logout>. The identity chip is a custom element styled above;
   the three quick-links moved into the icon row (not direct children). Brutalist:
   rectangular, thin border, tonal hover via tokens. */
body > aside > footer > a,
body > aside > footer > button {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  padding: 0.5rem 0.75rem;
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border-soft, var(--border-color));
  border-radius: 0;
  font-family: inherit;
  font-size: var(--font-size-xs);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: color var(--transition-fast),
              border-color var(--transition-fast),
              background var(--transition-fast);
}
body > aside > footer > a:hover,
body > aside > footer > button:hover {
  color: var(--text-primary);
  border-color: var(--border-medium, var(--text-tertiary));
  background: var(--surface-hover, var(--secondary-surface));
}
body > aside > footer > a:focus-visible,
body > aside > footer > button:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}
/* Sign Out is slightly de-emphasised (it's the least-used footer action): a
   softer label colour + a hush border at rest, lifting on hover into the
   destructive treatment (matches platform). */
body > aside > footer > button[data-logout] {
  color: var(--text-tertiary);
  border-color: var(--border-soft, var(--border-color));
  justify-content: center;
}
body > aside > footer > button[data-logout]:hover {
  color: var(--accent-danger, #f87171);
  border-color: var(--accent-danger, #f87171);
  background: transparent;
}

/* ── Main content ──────────────────────────────────────────────────────
 * Sits in grid column 2 when authed, the only column otherwise. */
body > main {
  grid-row: 1;
  grid-column: 1;
  padding: var(--space-lg);
  min-width: 0;
  overflow-y: auto;
}
html[data-authed] body > main {
  grid-column: 2;
}

/* The shell-owned page header (the single <h1> lives here; setPageHeader
   updates textContent without replacing the element so LCP stays pinned). */
body > main > header {
  margin: 0 0 1.5rem;
  display: flex;
  gap: var(--space-sm);
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
}
body > main > header > h1 {
  margin: 0;
  font-size: var(--font-size-xl, 1.5rem);
  line-height: 1.2;
  color: var(--text-primary);
}
body > main > header > p {
  margin: 0.25rem 0 0;
  flex-basis: 100%;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  line-height: 1.5;
}
body > main > header > p[hidden] {
  display: none;
}
body > main > header > [data-page-actions]:empty {
  display: none;
}

/* Page content target — every page's top-level content stacks here with a
   consistent rhythm (setPageContent owns this element). */
body > main > section {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/* ── Pre-auth login dialog ─────────────────────────────────────────────
 * Native top-layer <dialog> (focus trap + ESC from the platform); centred
 * both axes; content pre-rendered in HTML so it opens before any module
 * loads (no flash of the body behind the modal). */
dialog[data-login] {
  inset: 0;
  margin: auto;
  width: min(440px, calc(100% - 2rem));
  max-height: calc(100% - 2rem);
  padding: 0;
  background: var(--surface-card, var(--primary-surface));
  border: 1px solid var(--border-medium, var(--border-color));
  border-radius: var(--radius-lg, var(--radius-md));
  color: var(--text-primary);
}
dialog[data-login]::backdrop {
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(12px);
}
dialog[data-login] > form {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 3rem 3rem 2rem;
}
dialog[data-login] > form > header {
  text-align: center;
  margin-bottom: 2.5rem;
}
dialog[data-login] > form > header > img {
  display: block;
  margin: 0 auto 1.25rem;
}
dialog[data-login] > form > header > strong {
  display: block;
  margin: 0 0 0.5rem;
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-semibold);
  color: var(--accent-primary);
  letter-spacing: -0.02em;
}
dialog[data-login] > form > header > p {
  margin: 0;
  font-size: var(--font-size-xs);
  letter-spacing: var(--letter-spacing-caps);
  text-transform: uppercase;
  color: var(--text-tertiary);
}
dialog[data-login] > form > section {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
dialog[data-login] > form > section > p {
  margin: 0;
  font-size: var(--font-size-sm);
  line-height: 1.6;
  color: var(--text-secondary);
  text-align: center;
}
dialog[data-login] > form > section > p > strong {
  color: var(--text-primary);
  font-weight: var(--font-weight-medium);
}
/* Passkey CTA — styled <button> (works with or without the JS upgrade). */
dialog[data-login] [data-passkey] {
  display: block;
  width: 100%;
  padding: 0.875rem 1.5rem;
  background: var(--accent-primary);
  color: #fff;
  text-align: center;
  border: none;
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-caps);
  text-transform: uppercase;
  cursor: pointer;
  transition: filter var(--transition-fast);
}
dialog[data-login] [data-passkey]:hover,
dialog[data-login] [data-passkey]:focus-visible {
  filter: brightness(1.1);
}
dialog[data-login] [data-passkey]:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}
/* Error slot — hidden until populated (uses :empty). */
dialog[data-login] output {
  display: none;
}
dialog[data-login] output:not(:empty) {
  display: block;
  padding: 0.75rem 1rem;
  background: var(--surface-muted, var(--secondary-surface));
  border-left: 3px solid var(--danger, #f87171);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
  text-align: left;
  color: var(--text-primary);
}

/* .skip-link comes from the shared app-base.css (token-based) — no local override
   (audit-32: removed the divergent local copy that hardcoded #fff + diverged on
   radius/z-index, so the skip-link matches every other surface). */

/* ── Home page (page-scoped layout; no inline styles — AP-02) ── */
.home-page {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.home-action-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
}

.home-quick-actions > h2,
.home-control-room > h2,
.home-onboarding-banner > h2 {
  margin-top: 0;
}

/* ── Onboarding (page-scoped; layout only — AP-02) ──
   The Architect-led first-run: a welcome lede, the primary Architect CTA card,
   an optional blueprint-picker grid, and a quiet skip link. Colours/spacing
   from CDN tokens. */
.onboarding {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  max-width: 56rem;
}

.onboarding [data-skip] {
  margin: 0;
  text-align: center;
  color: var(--text-secondary);
}

.onboarding-blueprint-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-md);
  list-style: none;
  margin: var(--space-md) 0 0;
  padding: 0;
}

.onboarding-blueprint-grid > li {
  display: flex;
}

.onboarding-blueprint-grid > li > minxi-card {
  width: 100%;
}

/* ── Settings → Organization (page-scoped layout; no inline styles — AP-02) ──
   The org-identity section (icon uploader) stacked above the timezone/language
   singleton form. Layout/spacing only — colours from CDN tokens. */
.settings-org {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.settings-org-identity {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.settings-org-identity > h2 {
  margin: 0;
}

.settings-org-identity > p {
  margin: 0;
  color: var(--text-secondary);
}

/* ── Settings groups (IA restructure — Profile / System PATH-SEGMENT tabs) ──
   Page-scoped layout only (AP-02 — colours from CDN tokens). The group shell
   (`_group.js`) stacks the tab-bar above the active tab panel; each tab body
   stacks its cards/sections. */
.settings-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-md, var(--space-lg));
}

.settings-branding,
.settings-policies,
.settings-keys {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Muted note for scaffolded / gated surfaces (Policies guardrails, Keys history,
   Agency-pack gate). */
.settings-scaffold-note {
  margin: 0;
  color: var(--text-tertiary);
  font-size: 0.9em;
}

/* Inline drift warning from the AUTOMATIC About fact-check — sits directly beside
   the field it flags (name / description / what-we-do), not in a review panel. */
.settings-field-warning {
  margin: var(--space-xs) 0 0;
  padding: var(--space-xs) var(--space-sm);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  border-left: 3px solid var(--warning, #d97706);
  background: var(--surface-raised);
  border-radius: var(--radius-sm);
}

/* Native colour swatches for the Branding tab. */
.settings-color-input {
  width: 3rem;
  height: 2rem;
  padding: 0;
  border: 1px solid var(--border-soft, var(--border-color));
  border-radius: var(--radius-sm);
  background: transparent;
  cursor: pointer;
}

/* Brand images (icon / logo / wordmark) on the Brand tab — ONE set. Each slot's
   preview box is shaped to the kind's recommended aspect ratio, and its backdrop
   reflects the org's chosen theme base (data-theme-base on the .settings-org
   root) so the logo is checked on the surface it will actually appear on. */
.brand-asset-set {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg);
  align-items: flex-start;
}
.brand-asset-slot {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.brand-asset-slot-label {
  font-size: var(--font-size-md);
  font-weight: 600;
}
.brand-asset-rec {
  font-size: var(--font-size-sm);
  color: var(--text-tertiary);
}
.brand-asset-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: 4.5rem;            /* shared height so the Upload buttons line up */
  padding: var(--space-sm);
  border: 1px solid var(--border-soft, var(--border-color));
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}
/* Widths encode each kind's recommended aspect ratio at the shared height
   (icon 1:1 · logo 3:1 · wordmark 4:1) — the boxes read as the right shape AND,
   being the same height, keep their Upload buttons aligned on a shared row. */
.brand-asset-slot[data-asset-type="icon"] .brand-asset-preview {
  width: 4.5rem;
}
.brand-asset-slot[data-asset-type="logo"] .brand-asset-preview {
  width: 13.5rem;
}
.brand-asset-slot[data-asset-type="wordmark"] .brand-asset-preview {
  width: 18rem;
}
/* Backdrop reflects the chosen theme base (independent of the admin theme). */
.settings-org[data-theme-base="light"] .brand-asset-preview {
  background: #ffffff;
}
.settings-org[data-theme-base="dark"] .brand-asset-preview {
  background: #0d001c;
}
.brand-asset-preview img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.brand-asset-empty {
  font-size: var(--font-size-sm);
  color: var(--text-tertiary);
}
.brand-asset-actions {
  display: flex;
  gap: var(--space-xs);
  flex-wrap: wrap;
}

/* All five colour tokens on one wrapping row (each swatch is small); wraps on
   narrow screens. */
.settings-color-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-md);
  align-items: flex-end;
}

/* Typography slots — the three font pickers (Heading · Body · Display) on one
   line when there's room, collapsing to one column on narrow screens. */
.settings-font-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-sm) var(--space-md);
}
@media (max-width: 40rem) {
  .settings-font-grid {
    grid-template-columns: 1fr;
  }
}

/* ── Brand live-preview card. Colours + fonts come entirely from --pv-* custom
   properties set live by the tab (dynamic theming — AP-02 ok). Fallbacks here
   keep the card legible before the first update / when a slot is unset. */
.settings-brand-preview {
  --pv-bg: var(--background-end);
  --pv-surface: var(--surface-raised);
  --pv-text: var(--text-primary);
  --pv-primary: var(--accent-primary, #6d28d9);
  --pv-accent: var(--accent-secondary, #22d3ee);
  --pv-font-heading: var(--font-sans, sans-serif);
  --pv-font-body: var(--font-sans, sans-serif);
  --pv-font-display: var(--font-sans, sans-serif);
  --pv-font-mono: var(--font-mono, monospace);
  display: block;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-soft, var(--border-color));
  overflow: hidden;
}
/* The preview's page frame reflects the chosen Theme base (light/dark) — stamped
   on the .settings-org root by the Theme base select — so the brand card is shown
   on the surface it will actually sit on, updating live when the base changes. */
.settings-org[data-theme-base="light"] .settings-brand-preview {
  background: #f2f2f6;
  padding: var(--space-md);
}
.settings-org[data-theme-base="dark"] .settings-brand-preview {
  background: #0d001c;
  padding: var(--space-md);
}
.settings-org[data-theme-base="light"] .settings-brand-preview .bp-canvas,
.settings-org[data-theme-base="dark"] .settings-brand-preview .bp-canvas {
  border-radius: var(--radius-md);
}
.settings-brand-preview .bp-canvas {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-md);
  background: var(--pv-bg);
  color: var(--pv-text);
}
.settings-brand-preview .bp-head {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-xs);
}
.settings-brand-preview .bp-logo {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  object-fit: cover;
}
.settings-brand-preview .bp-display {
  font-family: var(--pv-font-display);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
}
.settings-brand-preview .bp-heading {
  font-family: var(--pv-font-heading);
  font-size: 1.15rem;
  font-weight: 600;
  margin: 0;
}
.settings-brand-preview .bp-body {
  font-family: var(--pv-font-body);
  font-size: 0.95rem;
  margin: 0;
  opacity: 0.85;
}
.settings-brand-preview .bp-chips {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-xs);
}
.settings-brand-preview .bp-chip {
  padding: 0.2rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  font-family: var(--pv-font-body);
}
.settings-brand-preview .bp-chip-primary {
  background: var(--pv-primary);
  color: var(--pv-bg);
}
.settings-brand-preview .bp-chip-accent {
  background: var(--pv-accent);
  color: var(--pv-bg);
}
.settings-brand-preview .bp-mono {
  font-family: var(--pv-font-mono);
  font-size: 0.8rem;
  margin-top: var(--space-xs);
  padding: 0.2rem 0.5rem;
  border-radius: var(--radius-sm);
  background: var(--pv-surface);
  align-self: flex-start;
}

/* ── About › Website ownership — an input flanked by a status chip + verify
   button, with a proof panel that drops below on "Verify ownership". */
.settings-website-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
}
.settings-website-row > input {
  flex: 1 1 14rem;
  min-width: 0;
}
.settings-verify-panel {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border-soft, var(--border-color));
  border-radius: var(--radius-sm);
}
.settings-verify-methods {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-xs) var(--space-sm);
  margin: 0;
  width: 100%;
}
.settings-verify-methods dt {
  color: var(--text-secondary);
  font-size: 0.9em;
}
.settings-verify-methods dd {
  margin: 0;
  min-width: 0;
}
.settings-verify-methods dd code {
  word-break: break-all;
}

/* ── About › Website auto-fill: the trigger + hint sit under the Website field;
   the review panel drops below on completion with one group per Character
   section (each has its own Apply). */
.autofill-controls {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
}
.autofill-review {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  width: 100%;
  margin-top: var(--space-sm);
  padding: var(--space-md);
  border: 1px solid var(--border-soft, var(--border-color));
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}
/* The panel sets `display:flex`, which beats the UA `[hidden]{display:none}`
   (author origin wins over UA at equal specificity) — so restore hidden here,
   else the empty review box shows as an orphan before any run. */
.autofill-review[hidden] {
  display: none;
}
.autofill-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--border-soft, var(--border-color));
}
.autofill-group:last-child {
  border-bottom: none;
  padding-bottom: 0;
}
.autofill-group h4 {
  margin: 0;
}
.autofill-group-actions {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
}
.autofill-preview-line {
  margin: 0;
  color: var(--text-secondary, var(--text-primary));
}
.autofill-preview-list {
  margin: 0;
  padding-left: 1.2rem;
  color: var(--text-secondary, var(--text-primary));
}
.autofill-swatches {
  display: flex;
  gap: var(--space-xs);
  flex-wrap: wrap;
}
.autofill-swatch {
  width: 1.6rem;
  height: 1.6rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft, var(--border-color));
  background: var(--sw, transparent);
}

/* ── Ethos › Voice — a 1–5 range slider flanked by its two end labels. */
.settings-ethos {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
/* The four voice sliders lay out in a tight 2-column grid (compact — the block
   reads as one control cluster, not four full-width rows). */
.settings-voice-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-sm) var(--space-md);
}
@media (max-width: 640px) {
  .settings-voice-grid {
    grid-template-columns: 1fr;
  }
}
.settings-voice-slider {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.settings-voice-end {
  color: var(--text-tertiary);
  font-size: 0.9em;
  white-space: nowrap;
  flex: 0 0 auto;
  min-width: 6.5rem;
}
/* Compact the end labels when the sliders are packed two-up. */
.settings-voice-grid .settings-voice-end {
  min-width: 4.25rem;
  font-size: 0.8em;
}
.settings-voice-end:last-child {
  text-align: right;
}
.settings-range {
  flex: 1 1 auto;
  min-width: 0;
  cursor: pointer;
  accent-color: var(--accent, var(--primary));
}

/* ── Accessible switch (role="switch" on a checkbox) — Settings boolean toggles.
   `appearance: none` lets us paint a track + thumb; `.checked` still carries the
   value so the read-back is unchanged (AP-02 — styled by class, not inline). */
.settings-switch {
  appearance: none;
  -webkit-appearance: none;
  width: 2.5rem;
  height: 1.4rem;
  flex: 0 0 auto;
  position: relative;
  border-radius: 999px;
  border: 1px solid var(--border-soft, var(--border-color));
  background: var(--border-medium, var(--border-color));
  cursor: pointer;
  vertical-align: middle;
  transition: background var(--transition-fast), border-color var(--transition-fast);
}

.settings-switch::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0.15rem;
  width: 1rem;
  height: 1rem;
  border-radius: 50%;
  background: #fff;
  transform: translateY(-50%);
  transition: left var(--transition-fast);
}

.settings-switch:checked {
  background: var(--accent-primary);
  border-color: var(--accent-primary);
}

.settings-switch:checked::before {
  left: calc(100% - 1.15rem);
}

.settings-switch:focus-visible {
  outline: 2px solid var(--accent-primary);
  outline-offset: 2px;
}

/* Forced-colors: `appearance: none` controls lose their affordance under an OS
   colour override, so keep the switch legible with system colours. */
@media (forced-colors: active) {
  .settings-switch {
    border: 1px solid CanvasText;
  }
  .settings-switch::before {
    background: CanvasText;
  }
  .settings-switch:checked {
    background: Highlight;
  }
}

/* ── Webhook event picker — grouped switches (icon/logo/wordmark style rows), one
   accessible switch per event with its label + description. */
.settings-webhook-events {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}
.webhook-event-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.webhook-event-group-label {
  margin: 0;
  font-size: var(--font-size-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
}
.webhook-event-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-xs) 0;
  cursor: pointer;
}
.webhook-event-text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.webhook-event-label {
  font-weight: 500;
}
.webhook-event-desc {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}

/* ── Member profile page (page-scoped layout; no inline styles — AP-02) ──
   The org-agnostic identity editor (avatar + name) stacked above the
   "Your organizations" memberships list. Layout only — colours from CDN tokens. */
.profile-page {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
  max-width: 48rem;
}

.profile-identity,
.profile-orgs {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.profile-identity > h2,
.profile-orgs > h2 {
  margin: 0;
}

.profile-org-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* Each membership row: avatar + name, a link that switches to that org's home. */
.profile-org-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  text-decoration: none;
  transition: background-color var(--transition-fast);
}

.profile-org-row:hover,
.profile-org-row:focus-visible {
  background: var(--surface-hover);
}

.profile-org-name {
  font-weight: var(--font-weight-medium);
}

/* ── Recipe builder (page-scoped two-pane layout; no inline styles — AP-02) ──
   The two-pane authoring UX: left = Skill + slot list, right = per-slot binding
   editor; a collapsible dry-run drawer below; a Cancel/Save footer. Layout only
   — colours/spacing come from CDN tokens (--space-*, --border-color). */
.recipe-builder {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.recipe-builder-panes {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) minmax(280px, 2fr);
  gap: var(--space-md);
  align-items: start;
}

/* Collapse to a single column on narrow viewports. */
@media (max-width: 720px) {
  .recipe-builder-panes {
    grid-template-columns: 1fr;
  }
}

.recipe-builder-slot-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.recipe-builder-slot-list > li > button {
  width: 100%;
  text-align: left;
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  font: inherit;
}

.recipe-builder-slot-list > li > button[aria-current="true"] {
  border-color: var(--primary-color, currentColor);
  background: var(--primary-surface, transparent);
}

.recipe-builder-dryrun > summary {
  cursor: pointer;
  font-weight: var(--font-weight-semibold);
}

/* ── Destructive-confirm dialog (ui/confirm.js; layout only — AP-02) ──
   A transient native <dialog>.showModal() overlay for destructive confirms
   (UI-PARADIGM §5). Structure + [data-confirm*] hooks; colours from CDN tokens. */
dialog[data-confirm] {
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  max-width: 28rem;
  background: var(--surface-color, var(--primary-surface));
  color: var(--text-primary);
}

dialog[data-confirm]::backdrop {
  background: rgba(0, 0, 0, 0.5);
}

dialog[data-confirm] > h2 {
  margin: 0 0 var(--space-sm);
}

dialog[data-confirm] > p {
  margin: 0 0 var(--space-lg);
  color: var(--text-secondary);
}

dialog[data-confirm] > [data-confirm-actions] {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

dialog[data-confirm] [data-confirm-ok][data-danger] {
  color: var(--danger-color, #c0392b);
  border-color: var(--danger-color, #c0392b);
}

/* ── Liquid App (page-scoped; layout only — AP-02) ── */
.liquid-app {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

.liquid-app-preview {
  width: 100%;
  aspect-ratio: 16 / 10;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

.liquid-version-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.liquid-version-list > li {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

.liquid-version-list > li > [data-current] {
  margin-left: auto;
}

/* ── Liquid Files media manager (page-scoped; layout only — AP-02) ── */
.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--space-md);
  list-style: none;
  margin: 0;
  padding: 0;
}

.media-grid > li {
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.media-grid a {
  display: block;
  color: inherit;
  text-decoration: none;
}

/* The single-file detail PREVIEW image (file-detail.js). The media grid itself
   now uses <minxi-thumbnail>; this full-width 1:1 preview is page-local layout
   (object-fit cover + aspect-ratio for intrinsic dims / no CLS — P-14c). */
.media-thumb {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
  background: var(--primary-surface);
}

.media-dropzone {
  border: 2px dashed var(--border-color);
  border-radius: var(--radius-sm);
  padding: var(--space-lg);
  text-align: center;
  color: var(--text-secondary);
}

.media-dropzone[data-dragover] {
  border-color: var(--primary-color, currentColor);
  background: var(--primary-surface, transparent);
}

.recipe-builder-footer {
  display: flex;
  gap: var(--space-sm);
  align-items: center;
}

/* ── Wave 3 (Projects + Services pillars) — page-scoped layout only (AP-02; no
   inline styles, no business logic — variants + state attributes only). The
   shared vocabulary (.dl-grid, .detail-actions, .payload-textarea,
   [data-form-error], [data-external]) comes from the CDN app-base.css. ── */

/* Pipeline detail — sub-collection lists (Projects / Runs / Reports tabs). */
[data-sub-list] {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

[data-sub-list] > li {
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

/* Run detail — chat thread + mandate. (The header action bar is the shared
   <minxi-entity-actions> element, which lays out its own buttons.) */
[data-chat-thread] {
  list-style: none;
  margin: 0 0 var(--space-md);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

[data-chat-message] {
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

[data-chat-meta] {
  display: flex;
  gap: var(--space-sm);
  align-items: baseline;
  color: var(--text-secondary);
}

[data-chat-composer] {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

[data-mandate-authorized] {
  color: var(--success-color, currentColor);
}

/* Services offering form — the mode selector (the teaser-gate, D-29). */
[data-mode-selector] {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

[data-mode-option] {
  display: flex;
  align-items: flex-start;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

[data-mode-option] > span {
  display: flex;
  flex-direction: column;
}

[data-mode-option][data-locked] {
  opacity: 0.7;
}

[data-mode-upsell] {
  margin-left: auto;
}

/* Services wallets — the three-card grid (ledger / withdrawals / bank-account
   lists render through the shared <minxi-data-table>). */
.services-wallets,
.services-withdrawals,
.services-kyc,
.services-banks {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.wallet-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-md);
}

/* Services offering detail — the public-link card. */
[data-public-link] {
  display: block;
  word-break: break-all;
  margin-bottom: var(--space-sm);
}

/* ── Wave 4 (Billing + Analytics + Orbit pillars) — page-scoped layout
   only (AP-02; no inline styles, no business logic — variants + state
   attributes only). The shared vocabulary (.dl-grid, .detail-actions,
   [data-form-error], [data-external]) comes from the CDN app-base.css. ── */

/* Pillar page wrappers — vertical card stacks. */
.billing-overview,
.billing-capacity,
.billing-wallet,
.billing-usage,
.analytics-activity,
.analytics-metrics,
.orbit-detail,
.marketplace-packs {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* Marketplace packs — the four explainer cards in a responsive grid. */
.pack-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-md);
}

[data-unlocks] {
  margin: var(--space-sm) 0;
  padding-left: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* Billing — catalog-item / payment / ledger lists (invoice line-items + the
   usage breakdown render through the shared <minxi-data-table>). */
[data-catalog-item-list],
[data-payment-methods],
[data-batches],
[data-budgets],
[data-ai-usage],
[data-ledger] {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

[data-catalog-item-row] {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

[data-catalog-item-row] > [data-catalog-item-action] {
  margin-left: auto;
}

/* Analytics activity — the event feed + the summary strip + the live badge. */
[data-feed] {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

[data-event-row] {
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

[data-live-row] {
  border-color: var(--accent-color, var(--border-color));
}

/* Orbit detail — review list. */
[data-review-list] {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

[data-review-row] {
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

/* ── Wave 6 (gated pillars: Developer · Agency · Enterprise) — page-scoped
   layout only (AP-02; no inline styles, no business logic — variants + state
   attributes only). The shared vocabulary (.dl-grid, .detail-actions,
   .wallet-card-grid, [data-form-error], [data-external]) comes from earlier
   waves + the CDN app-base.css. ── */

/* Pillar page wrappers — vertical card stacks. */
.developer-analytics,
.developer-payouts,
.developer-domain,
.agency-orgs,
.agency-domain,
.enterprise-domain,
.enterprise-controls,
.enterprise-support {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* Developer payouts (recent-payouts) + kits (version list) render through
   the shared <minxi-data-table>. */

/* Shared custom-domain manager (Developer · Agency · Enterprise). */
[data-domains],
[data-tickets] {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

[data-domain-row],
[data-ticket-row] {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

[data-domain-row] > [data-domain-actions],
[data-ticket-row] > [data-ticket-meta] {
  margin-left: auto;
}

[data-domain-actions] {
  display: flex;
  gap: var(--space-xs);
}

/* ── Control Room (management surface) — page-scoped layout only (AP-02; no
   inline styles, no business logic — structure + state attributes). The 3D view
   itself renders on the minxi-control service (embedded here via an iframe); this
   file owns the management chrome: the toolbar, the views table + its row
   actions, the inline preview iframe, and the create/access dialog bodies. ── */
.control-room {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.control-room-toolbar {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

.control-room-list {
  min-height: 8rem;
}

/* The views table — the listing of the org's views. */
.control-room-views {
  width: 100%;
  border-collapse: collapse;
}

.control-room-views th,
.control-room-views td {
  padding: var(--space-sm) var(--space-md);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border-color);
}

.control-room-views th {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
}

/* The per-row actions bar — text-style action buttons + a danger variant. */
.control-room-row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  align-items: center;
}

.control-room-action {
  border: none;
  background: transparent;
  padding: 0;
  color: var(--accent-color, var(--primary-color));
  font: inherit;
  cursor: pointer;
  text-decoration: none;
}

.control-room-action:hover {
  text-decoration: underline;
}

.control-room-action[data-danger] {
  color: var(--danger-color, #d9534f);
}

/* The inline embedded preview — an iframe of the live 3D view, sized to avoid
   layout shift; width scales to the row, height keeps a 16:9 box. */
.control-room-preview-frame {
  width: 100%;
  max-width: 40rem;
  aspect-ratio: 16 / 9;
  height: auto;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--surface-color, var(--primary-surface));
}

/* ── Create / edit dialog body ── */
.control-room-kinds {
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: var(--space-md);
  margin: var(--space-md) 0 0;
}

.control-room-kinds > legend {
  padding: 0 var(--space-xs);
  font-weight: var(--font-weight-semibold);
}

.control-room-clamp-note {
  margin: 0 0 var(--space-sm);
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
}

.control-room-kind-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-xs) 0;
}

.control-room-kind-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
}

.control-room-kind-tier {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
}

.control-room-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

/* ── Manage-access (share tokens) dialog body ── */
.control-room-access-intro,
.control-room-access-note {
  color: var(--text-secondary);
}

.control-room-token-list {
  list-style: none;
  margin: var(--space-md) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.control-room-token {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--border-color);
}

.control-room-token-meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.control-room-token-sub {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
}

.control-room-token-revoked {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
}

/* The one-time plaintext token reveal — a highlighted block + a copy field. */
[data-reveal] {
  margin: var(--space-md) 0;
  padding: var(--space-md);
  border: 1px solid var(--accent-color, var(--primary-color));
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent-color, var(--primary-color)) 8%, transparent);
}

[data-reveal][hidden] {
  display: none;
}

.control-room-reveal-heading {
  display: block;
}

.control-room-reveal-warning {
  margin: var(--space-xs) 0 var(--space-sm);
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
}

.control-room-reveal-row {
  display: flex;
  gap: var(--space-sm);
  align-items: center;
}

.control-room-reveal-input {
  flex: 1;
  min-width: 0;
  font-family: var(--font-family-mono, monospace);
}


/* ── Mobile shell (≤600px) — drawer pattern ─────────────────────────────
 * On phones the 260px sidebar takes too much of a small viewport; convert
 * to an off-screen drawer that slides in when the hamburger is tapped.
 * Same shape as the internal-app implementation (2026-06-12 mobile fix).
 * Hamburger toggle + backdrop are declared once in index.html; CSS gates
 * their visibility to ≤600px; main.js wires the toggle behavior.
 */

@supports (padding: env(safe-area-inset-top)) {
  html[data-authed] body > aside {
    padding-top: env(safe-area-inset-top, 0);
    padding-bottom: env(safe-area-inset-bottom, 0);
    padding-left: env(safe-area-inset-left, 0);
  }
  body > main {
    padding-top: max(var(--space-lg, 1.5rem), env(safe-area-inset-top, 0));
    padding-right: max(var(--space-lg, 1.5rem), env(safe-area-inset-right, 0));
    padding-bottom: max(var(--space-lg, 1.5rem), env(safe-area-inset-bottom, 0));
  }
}

button[data-nav-toggle] {
  display: none;
  position: fixed;
  top: max(0.5rem, env(safe-area-inset-top, 0));
  left: max(0.5rem, env(safe-area-inset-left, 0));
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  background: var(--bg-secondary, #1a1a2e);
  border: 1px solid var(--border-color, rgba(255,255,255,0.08));
  border-radius: var(--radius-md, 0.4rem);
  color: var(--text-primary, #e6edf3);
  cursor: pointer;
  z-index: 101;
  padding: 0;
  font-family: inherit;
  font-size: 1.25rem;
  line-height: 1;
}
button[data-nav-toggle]:hover { border-color: var(--accent-primary, #a855f7); }
button[data-nav-toggle]:focus-visible {
  outline: 2px solid var(--accent-primary, #a855f7);
  outline-offset: 2px;
}

[data-nav-backdrop] {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 99;
  cursor: pointer;
}

@media (max-width: 600px) {
  html[data-authed] body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
  }
  html[data-authed] body > aside {
    position: fixed;
    top: 0;
    left: 0;
    width: min(300px, 85vw);
    height: 100vh;
    max-height: none;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    z-index: 100;
    box-shadow: 2px 0 16px rgba(0, 0, 0, 0.45);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    border-right: 1px solid var(--border-color);
    border-bottom: none;
    /* The desktop aside is transparent (--side-nav-bg) because it sits on
     * the page's own gradient. As a floating drawer it overlays content,
     * so it needs an opaque background matching the page theme. */
    background: linear-gradient(180deg, var(--background-start, #0a0014), var(--background-end, #0d001c));
  }
  html[data-authed] body > aside[data-mobile-open] {
    transform: translateX(0);
  }
  html[data-authed] body > main {
    grid-row: 1;
    grid-column: 1;
    padding: 3.5rem 1rem 1rem;
  }
  button[data-nav-toggle] { display: inline-flex; }
  body > aside[data-mobile-open] ~ [data-nav-backdrop] { display: block; }
  html[data-authed] body:has(aside[data-mobile-open]) {
    overflow: hidden;
  }
  main :where(table, .data-table) {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* ── Accessibility (audit-31 — ported from the staff console) ── */

/* Honour a reduced-motion preference: collapse animations + transitions. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* High-contrast / forced-colors: our subtle backgrounds + borders vanish when
   the OS overrides colours, so force visible affordances on the chrome that
   relies on them (active nav item, nav toggle, search field). */
@media (forced-colors: active) {
  body > aside nav a[aria-current="page"] {
    border-inline-start: 3px solid Highlight;
  }
  button[data-nav-toggle],
  body > aside > [data-global-search] input[type="search"] {
    border: 1px solid currentColor;
  }
}
