/* markedin.io — public chrome + form styles
   Consolidated from pipeline/surface/mockups/FEAT-005-public-pages/.
   Tokens come from tokens.css (loaded before this file). */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  line-height: 1.6;
  color: var(--text-primary);
  background: var(--surface-bg);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Page + form-field text selection — theme-aware via --selection-bg
   (FEAT-005-R27). The source editor keeps its own (CodeMirror) selection. */
::selection { background: var(--selection-bg); }
::-moz-selection { background: var(--selection-bg); }

/* === Public chrome ============================================================ */

.public-chrome {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-8) var(--space-3);
}

.brand {
  font-size: var(--font-size-sm);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-primary);
  margin-bottom: var(--space-6);
  text-align: center;
}
.brand .dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-primary);
  vertical-align: middle;
  margin: 0 4px 2px;
}

/* === Card ===================================================================== */

.card {
  width: 100%;
  max-width: 480px;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-4);
  box-shadow: var(--shadow-md);
}

.card-title {
  font-family: var(--font-secondary);
  font-size: var(--font-size-2xl);
  font-weight: 600;
  color: var(--text-primary);
  text-align: center;
  margin-bottom: var(--space-3);
}

.card-title:has(+ .card-framing) {
  margin-bottom: var(--space-1);
}

.card-framing {
  font-family: var(--font-primary);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
  line-height: 1.5;
  margin-bottom: var(--space-3);
}

.tagline {
  font-family: var(--font-secondary);
  font-size: var(--font-size-3xl);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.2;
  margin-bottom: var(--space-2);
  text-align: center;
}

.subtitle {
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  color: var(--text-secondary);
  line-height: 1.55;
  margin-bottom: var(--space-4);
  max-width: 36ch;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

/* === Buttons ================================================================== */

.actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.625rem 1rem;
  border-radius: var(--radius-md);
  font-family: var(--font-primary);
  font-size: var(--font-size-sm);
  font-weight: 500;
  border: 1px solid transparent;
  cursor: pointer;
  min-height: 44px;
  text-decoration: none;
  transition: all var(--transition-fast);
}
.btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.btn-primary {
  background: var(--color-primary);
  color: var(--on-bright);
}
.btn-primary:hover {
  background: var(--color-primary-dark);
  color: var(--on-bright);
  box-shadow: var(--shadow-sm);
}
.btn-secondary {
  background: var(--surface-card);
  color: var(--text-primary);
  border-color: var(--border-default);
}
.btn-secondary:hover {
  background: var(--color-gray-100);
  border-color: var(--color-gray-300);
}
/* Muted while a sibling action is in flight — e.g. the delete-confirm's Cancel is
   gated alongside the destroy button so the confirm can't be dismissed mid-delete
   (the `.btn[aria-disabled]` rule already sets cursor:not-allowed). */
.btn-secondary[aria-disabled="true"],
.btn-secondary[aria-disabled="true"]:hover {
  opacity: 0.45;
  background: var(--surface-card);
  border-color: var(--border-default);
}

/* Landing CTAs use the larger 48px hit target. */
.actions .btn {
  font-size: var(--font-size-base);
  padding: 0.75rem 1.25rem;
  min-height: 48px;
}

/* === Form fields ============================================================== */

.field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-bottom: var(--space-2);
}
.field label {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--text-secondary);
}

.input {
  width: 100%;
  padding: 0.625rem 0.875rem;
  font-family: var(--font-primary);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: var(--radius-md);
  transition: all var(--transition-fast);
  min-height: 44px;
}
.input::placeholder { color: var(--text-muted); }
.input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--input-focus-ring);
}
.input-error { border-color: var(--color-error); }
.input-error:focus {
  border-color: var(--color-error);
  box-shadow: 0 0 0 3px var(--color-error-bg);
}
/* Autofilled inputs honour the theme. The browser paints autofilled fields with
   its own background that `background` can't override, so cover it with an inset
   box-shadow in --input-bg and force the text/caret colours. Layer the focus
   ring back when focused (the fill would otherwise hide it). Covers every input;
   surfaced once dark mode reached the public pages (FEAT-005-R26). */
.input:-webkit-autofill,
.input:-webkit-autofill:hover,
.input:-webkit-autofill:active {
  -webkit-text-fill-color: var(--text-primary);
  -webkit-box-shadow: 0 0 0 1000px var(--input-bg) inset;
  caret-color: var(--text-primary);
}
.input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 3px var(--input-focus-ring), 0 0 0 1000px var(--input-bg) inset;
}
.input-error:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 3px var(--color-error-bg), 0 0 0 1000px var(--input-bg) inset;
}

.field-help {
  font-size: var(--font-size-xs);
  color: var(--text-tertiary);
}
.field-error {
  font-size: var(--font-size-xs);
  color: var(--color-error);
}
.field-error code {
  font-family: var(--font-mono);
  font-size: 0.95em;
  background: var(--color-error-bg);
  padding: 1px 4px;
  border-radius: var(--radius-sm);
}

/* === Live-availability indicator (FEAT-005-R14) ============================== */
/* Mirrors pipeline/surface/patterns/live-availability-check.html v001-005      */

/* helper + availability indicator on one row, helper left, indicator right */
.helper-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.helper-row .field-help { flex: 1 1 auto; min-width: 0; }

.availability {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-size-xs);
  flex: 0 0 auto;
  min-height: 18px;
}
.availability .badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  font-size: 16px;
  flex-shrink: 0;
}
.availability .badge .ph { font-size: 16px; line-height: 1; }
.availability .badge.success { color: var(--color-success); }
.availability .badge.error   { color: var(--color-error); }
.availability .badge.warning { color: var(--color-warning); }
.availability .badge.muted   { color: var(--text-secondary); }
.availability .label-success { color: var(--color-success); font-weight: 700; }
.availability .label-error   { color: var(--color-error);   font-weight: 700; }
.availability .label-warning { color: var(--color-warning); font-weight: 700; }
.field-help.warning { color: var(--color-warning); }

/* The live-availability fragment carries data-state on #username-feedback;
   CSS reads that state and styles the surrounding field declaratively, so the
   server is the sole source of truth (pure hypermedia — no JS DOM mutation
   beyond the htmx:responseError fallback for 5xx network errors). */
.field:has(#username-feedback[data-state="taken"]) .input {
  border-color: var(--color-error);
}
/* When live-check returns available, override any stale .input-error from a
   prior submit-path render so the input's red border clears without server
   round-trip. */
.field:has(#username-feedback[data-state="available"]) .input.input-error,
.field:has(#username-feedback[data-state="available"]) .input {
  border-color: var(--input-border);
}
/* Invalid (malformed value — FEAT-005-R28, pattern v001-012): red border on
   the input, and the empty verdict slot collapses so the helper keeps the
   row's full width. No verdict renders — an invalid value isn't unavailable. */
.field:has(#username-feedback[data-state="invalid"]) .input {
  border-color: var(--color-error);
}
#username-feedback[data-state="invalid"] { flex: 0 0 0; }

/* Error state (5xx / unreachable) is non-blocking — input keeps default
   border per the pattern's `input is NOT marked input-error` obligation. */
.field:has(#username-feedback[data-state="error"]) .input.input-error,
.field:has(#username-feedback[data-state="error"]) .input {
  border-color: var(--input-border);
}

/* Base spinning glyph — the 14px indeterminate spinner. Only the create/edit
   identity form reveals it (during its delayed `checking` state — scoped rules
   live in the .space-form-pane section). Signup keeps the base availability
   behaviour and does not render a spinner. */
.spinner {
  width: 14px;
  height: 14px;
  border: 2px solid var(--color-gray-200);
  border-top-color: var(--text-secondary);
  border-radius: 50%;
  display: inline-block;
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Form-level error (login pattern) */
.form-error {
  padding: 0.625rem 0.875rem;
  border-radius: var(--radius-md);
  border-left: 3px solid var(--color-error);
  background: var(--color-error-bg);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  margin-bottom: var(--space-2);
}
.form-error strong { font-weight: 600; }

.submit { margin-top: var(--space-2); }

/* === Alt action =============================================================== */

.alt-action {
  margin-top: var(--space-3);
  text-align: center;
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.alt-action a {
  color: var(--color-primary-dark);
  text-decoration: none;
  font-weight: 500;
}
.alt-action a:hover { text-decoration: underline; }

/* === Agreement checkbox (FEAT-048; the product's first checkbox) ============= */

.field-agree { margin-top: var(--space-1); }
/* .field-agree scope: the shipped `.field label` rule (0-1-1) would beat a
   bare .agree-label (0-1-0) and flatten the flex row back to display:block —
   the checkbox then baseline-aligns and no margin can move it. */
.field-agree .agree-label {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  min-height: 44px;
  padding-top: 0.25rem;
  font-size: var(--font-size-sm);
  font-weight: 400;
  color: var(--text-secondary);
  cursor: pointer;
  line-height: 1.5;
}
.agree-label a {
  color: var(--color-primary-dark);
  text-decoration: none;
  font-weight: 500;
  padding: 1px 3px;
  margin: -1px -3px;
  border-radius: var(--radius-sm);
}
.agree-label a:hover { text-decoration: underline; }
.agree-label a:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.25);
}
.checkbox {
  appearance: none;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  flex-shrink: 0;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  position: relative;
  transition: all var(--transition-fast);
}
.checkbox:checked {
  background: var(--color-primary);
  border-color: var(--color-primary);
}
.checkbox:checked::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 4px;
  width: 8px;
  height: 4px;
  border-left: 2px solid var(--on-bright);
  border-bottom: 2px solid var(--on-bright);
  transform: rotate(-45deg);
}
.checkbox:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--input-focus-ring);
  border-color: var(--color-primary);
}
.checkbox-error { border-color: var(--color-error); }

/* === Footer =================================================================== */

.footer {
  padding: var(--space-3);
  text-align: center;
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  border-top: 1px solid var(--border-subtle);
}
.footer a {
  color: var(--text-tertiary);
  text-decoration: none;
  margin: 0 var(--space-1);
}
.footer a:hover {
  color: var(--text-secondary);
  text-decoration: underline;
}

/* === Public-doc reading layout (FEAT-048; IA public_screens[terms|privacy]) == */

.doc-header { padding: var(--space-6) var(--space-3) 0; }

.public-doc {
  flex: 1;
  width: 100%;
  max-width: 42rem;
  margin: 0 auto;
  padding: 0 var(--space-3) var(--space-8);
}

.doc-title {
  font-family: var(--font-secondary);
  font-size: var(--font-size-3xl);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.2;
  margin: var(--space-6) 0 var(--space-3);
}

.doc-body { line-height: 1.7; }
.doc-body h2 {
  font-size: var(--font-size-xl);
  font-weight: 600;
  color: var(--text-primary);
  margin: var(--space-4) 0 var(--space-1);
}
.doc-body p { margin-bottom: var(--space-2); color: var(--text-primary); }
.doc-body > p:first-of-type { font-size: var(--font-size-sm); color: var(--text-tertiary); }

.doc-contact {
  margin-top: var(--space-6);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.doc-contact p { margin: 0; }
.doc-contact a, .doc-links a {
  color: var(--color-primary-dark);
  text-decoration: none;
  font-weight: 500;
}
.doc-contact a:hover, .doc-links a:hover { text-decoration: underline; }
.doc-contact a:focus-visible, .doc-links a:focus-visible, .brand a:focus-visible, .footer a:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.25);
  border-radius: var(--radius-sm);
}

.doc-links {
  margin-top: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--text-tertiary);
}

/* The doc header's brand is a home link; the shipped .brand is plain text. */
.brand a { color: inherit; text-decoration: none; }

/* === xs reflow ================================================================ */

@media (max-width: 600px) {
  .public-chrome { padding: var(--space-4) var(--space-2); }

  .brand {
    font-size: var(--font-size-xs);
    margin-bottom: var(--space-3);
  }
  .brand .dot { width: 5px; height: 5px; }

  .card {
    max-width: none;
    padding: var(--space-3) var(--space-2);
    border-radius: var(--radius-lg);
  }

  .card-title { font-size: var(--font-size-2xl); margin-bottom: var(--space-2); }
  .card-title:has(+ .card-framing) { margin-bottom: var(--space-1); }
  .card-framing { margin-bottom: var(--space-2); }
  .tagline { font-size: var(--font-size-2xl); }
  .subtitle { font-size: var(--font-size-sm); margin-bottom: var(--space-3); }

  .input { font-size: 16px; padding: 0.75rem 0.875rem; } /* >=16px suppresses iOS zoom on focus */

  .actions .btn { padding: 0.75rem 1rem; }

  .footer { padding: var(--space-2); }
}

/* === Studio chrome ===========================================================
   FEAT-017 studio layout. Body class .studio-body activates the page-lock
   model: html + body fixed at 100vh; the studio's content regions own their
   own scroll viewports (per pipeline/surface/mockups/FEAT-017-panes-static/
   IMPLEMENTATION-NOTES.md scroll_model).

   Pass 1.1 (project scaffolding) ships the container shell + cmd-trigger.
   Pass 1.2 fills the Browse-specific grid; pass 1.3 fills the Artifact-
   specific grid. Layer 2-4 layer chrome + content + overlays on top.
*/
html:has(body.studio-body),
body.studio-body {
  height: 100vh;
  overflow: hidden;
}

body.studio-body {
  font-family: var(--font-primary);
  font-size: var(--font-size-base);
  line-height: 1.6;
  color: var(--text-primary);
  background: var(--surface-card);
  display: block;
}

.studio {
  height: 100vh;
  /* Per-screen, per-breakpoint layout (grid for lg Browse / Artifact,
     flex for xs) lands in passes 1.2 and 1.3. */
}

/* cmd-trigger — persistent command-bar trigger pill at bottom-center.
   Always rendered by the layout (per FEAT-017-architecture.mi v001-000);
   hidden when an overlay-region panel is open (overlay passes own the
   hide rule). */
.cmd-trigger {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  width: 98px;
  height: 16px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-primary);
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  background: rgba(255, 255, 255, 0.55);
  backdrop-filter: blur(6px) saturate(120%);
  -webkit-backdrop-filter: blur(6px) saturate(120%);
  border: 1px solid rgba(214, 214, 210, 0.7);
  border-radius: 3px;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.10), 0 2px 6px rgba(0, 0, 0, 0.04);
  cursor: pointer;
  z-index: 25;
  transition: color var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast);
}
.cmd-trigger i {
  position: absolute;
  left: 6px;
  font-size: 10px;
  line-height: 1;
}
.cmd-trigger:hover {
  color: var(--text-primary);
  border-color: var(--color-gray-300);
  background: rgba(255, 255, 255, 0.92);
}
.cmd-trigger:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* move-to-Trash toast (FEAT-021) — overlay-region NOTIFICATION layer. Bottom-LEFT
   (clear of the bottom-center cmd-trigger), no backdrop, non-blocking. Hidden
   until studio.js raises it via .is-open (a class toggle, not the hidden attr —
   same author-CSS-overrides-UA-default pitfall openModal avoids). Auto-dismisses
   on a timer and is dismissed by any action (navigate / open any panel). Carries
   a generic message + an inline Undo + a close. Mirrors mockups-move-to-trash-
   toast-lg.html. */
.toast {
  position: fixed;
  bottom: 16px;
  left: 16px;
  z-index: 30;
  display: none;
  align-items: center;
  gap: 10px;
  max-width: 460px;
  padding: 10px 10px 10px 16px;
  background: var(--surface-elevated);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  font-size: 13px;
  color: var(--text-primary);
}
.toast.is-open { display: inline-flex; }
/* message · Undo (inline). The message is GENERIC ("Item moved to Trash") — it
   does not name the deleted space — so the toast is a fixed, compact width. */
.toast-body { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.toast-msg { font-weight: 500; }
.toast-dot { color: var(--text-muted); }
/* The move-to-Trash toast's inline Undo action (toast-family action-link). */
.toast-undo {
  appearance: none;
  background: transparent;
  border: none;
  padding: 0;
  color: var(--color-primary-dark);
  font-family: var(--font-primary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.toast-undo:hover { text-decoration: underline; }
.toast-undo:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: 2px; }

/* === FEAT-025 session-expired notice — overlay notification (notification
   pattern), transcribed from the surface storyboard. Distinct from the move-toast:
   a longer message + up to two actions, so the body WRAPS and the actions
   right-align (the pattern's responsive contract). Persistent — hidden until
   studio.js adds .is-open; no auto-dismiss / no close. */
.notice {
  position: fixed;
  bottom: 16px;
  left: 16px;
  z-index: 30;
  display: none;
  max-width: 460px;
  grid-template-columns: 1fr auto;
  column-gap: 12px;
  row-gap: 10px;
  align-items: start;
  padding: 12px 14px 12px 16px;
  background: var(--surface-elevated);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  font-size: 13px;
  color: var(--text-primary);
}
.notice.is-open { display: grid; }
/* Grid: [ message · col1 ] [ ✕ · col2 ] on row 1; actions span row 2,
   right-aligned to the ✕'s edge. Message keeps its own column; the ✕ is seated
   top-right on the first line; the actions share the right edge with the ✕. */
.notice-msg { grid-column: 1; grid-row: 1; min-width: 0; line-height: 1.5; font-weight: 500; }
.notice-close { grid-column: 2; grid-row: 1; align-self: start;
  appearance: none; background: transparent; border: none; cursor: pointer;
  color: var(--text-muted); font-size: 14px; line-height: 1.5; padding: 0 2px; }
.notice-close:hover { color: var(--text-primary); }
.notice-actions { grid-column: 1 / -1; grid-row: 2; justify-self: end;
  display: inline-flex; align-items: center; gap: 7px; }
.notice-action {
  appearance: none;
  background: transparent;
  border: none;
  padding: 0;
  color: var(--color-primary-dark);
  font-family: var(--font-primary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.notice-action:hover { text-decoration: underline; }
.notice-action:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: 2px; }
/* neutral separator BETWEEN the actions — a REAL element (shown only with Copy). */
.notice-sep { color: var(--text-muted); font-weight: 400; }
/* transient success ✓ to the LEFT of Copy (right-anchored actions → no nudge);
   style-guide success colour, matching the shipped Synced ✓ (tone-success). */
.notice-check { color: var(--color-success); font-size: 14px; }
/* SYNCED single-line variant (no unsaved changes) — one line like the move-to-trash
   toast: message · Log in clustered left, ✕ pushed far-right. Overrides the two-row
   grid. DOM order is msg / close / actions, so `order` re-sequences to msg · actions · ✕
   and margin-auto on the ✕ pushes it to the right edge (just after Log in when the
   toast is content-width; at the far edge when full-width at xs). */
.notice.is-open.notice--inline { display: inline-flex; align-items: center; gap: 8px; }
.notice--inline .notice-msg { order: 1; }
.notice--inline .notice-actions { order: 2; }
.notice--inline .notice-close { order: 3; margin-left: auto; align-self: center; }
/* xs: full-width-minus-margins; message wraps in col 1; actions row stays
   right-aligned; the ✕ stays seated top-right on line 1; 44px action taps. */
@media (max-width: 599px) {
  /* full-width at xs → spans the center, so RAISE above the bottom-center command-bar
     trigger (bottom:16 + height:16) to bottom:44 — clears it by ~12px (never overlay it).
     Per the notification pattern's clearance mechanism + mockups-move-to-trash-toast-xs.html. */
  .notice { left: 12px; right: 12px; max-width: none; bottom: 44px; }
  /* The two-row actions inherit the lg treatment (no tap-height inflation) so the xs
     vertical rhythm — message line + actions line — matches lg exactly (storyboard
     v002-003). No `display` on the base .notice-action here, so a hidden Copy keeps
     the UA [hidden] display:none (no leak). */
  .notice-close { padding: 0 6px; }
  /* Synced single-line at xs: match the move-to-trash toast's 48px height
     (10px vertical padding + a 26px content row); drop the 44px tap inflation. */
  .notice--inline { padding-top: 10px; padding-bottom: 10px; }
  .notice--inline .notice-action { min-height: 0; }
  .notice--inline .notice-close { min-height: 26px; display: inline-flex; align-items: center; }
  /* move-to-trash toast: same clearance — goes full-width and RAISES above the
     cmd-trigger (bottom:44), message grows so the ✕ sits at the right edge. Per
     mockups-move-to-trash-toast-xs.html + the notification pattern (v001-002).
     NOTE: the `display` belongs on .is-open ONLY — the base .toast stays display:none
     (gated on is-open). Putting display on the base .toast would defeat the gate and
     show the toast unsummoned. */
  .toast { left: 12px; right: 12px; max-width: none; bottom: 44px; }
  .toast.is-open { display: flex; }
  .toast-body { flex: 1; min-width: 0; }
}
/* Failure variant — a refused move / failed undo shows the message alone (no
   reversible action), so the dot + Undo are dropped. */
.toast--bare .toast-dot,
.toast--bare .toast-undo { display: none; }
/* close — dismisses the toast (the move stays; Undo is the reversal). */
.toast-close {
  appearance: none;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-tertiary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.toast-close i { font-size: 15px; line-height: 1; }
.toast-close:hover { background: var(--color-gray-100); color: var(--text-primary); }
.toast-close:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }

/* === Studio Browse shell ====================================================
   FEAT-017 pass 1.2 — Browse shell. Layout grammar per FEAT-017-architecture
   .mi v001-000 contracts.template_hierarchy and the FEAT-016 chrome mockups.
   Three breakpoint variants:
   - xs (default, ≤599px): flex column with header + location-region +
     contents-region. nav surfaces via drawer (lands in pass 2.2).
   - sm (600-899px): flex column with header + contents-region. No
     location-region (breadcrumb fits in header per IA). nav drawer-summoned.
   - lg (≥900px): grid 192px+1fr × 56px+1fr. Header spans both columns.
     nav-region inline as the left column. contents-region right column.
*/

/* Universal chrome — brand-pane (markedin·io wordmark + amber dot). */
.brand-pane {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-primary);
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.brand-pane .dot {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-primary);
  margin: 0 4px 1px;
}

/* account-pane — text-avatar button (initial from tenant). Always present
   in the header at every breakpoint; behaviour (the account-menu overlay
   it summons) lands in pass 2.3. */
.account-pane {
  width: 32px;
  height: 32px;
  border: none;
  border-radius: var(--radius-full);
  background: var(--color-gray-500);
  color: #ffffff;
  font-family: var(--font-primary);
  font-size: 13px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--transition-fast);
}
.account-pane:hover { background: var(--color-gray-600); }
.account-pane:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* nav-drawer-toggle-pane — chrome trigger for the nav drawer at xs + sm.
   Hidden at lg (nav-region is inline). Behaviour lands in pass 2.2. */
.nav-drawer-toggle-pane {
  width: 40px;
  height: 40px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.nav-drawer-toggle-pane i { font-size: 22px; line-height: 1; }
.nav-drawer-toggle-pane:hover {
  background: var(--color-gray-100);
  color: var(--text-primary);
}
.nav-drawer-toggle-pane:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* header-region — universal chrome strip. Layout flex by default; per-
   breakpoint padding and inner spacing tuned below. */
.header-region {
  height: 56px;
  flex-shrink: 0;
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-default);
  display: flex;
  align-items: center;
  gap: 12px;
}

/* location-region — xs only. 36px strip below header carrying the
   breadcrumbs-panel (per IA dual-placement). Hidden at sm + lg via
   the Browse layout overrides below. */
.location-region {
  height: 36px;
  flex-shrink: 0;
  background: var(--surface-bg);
  border-bottom: 1px solid var(--border-default);
  display: flex;
  align-items: center;
}

/* nav-region — lg only (Browse). Left column hosting shortcuts-pane.
   Hidden at xs + sm (nav surfaces via drawer). */
.nav-region {
  background: var(--surface-bg);
  border-right: 1px solid var(--border-default);
  overflow-y: auto;
}

/* === shortcuts-pane (Layer 3.9) =============================================
   Per IA `shortcuts-pane`: curated quick-access list grouped by section
   (Pinned, System). Dual-homed: directly in nav-region at lg+xl-md inclusive;
   inside nav-drawer-panel's drawer-body at xs+sm (same partial, same content,
   different parent + reflow). Per substrate-includes-interactivity, the UI
   affordances (focus-visible, hover, aria-current, link semantics) land now;
   FEAT-007 fills in data + selection logic (the fixture groups become
   user-pinned spaces + system items derived from substrate).

   Per canonical lg Browse mockup (mockups-panes-lg-browse-contents.html)
   and xs nav-open mockup (mockups-panes-xs-browse-contents-nav-open.html).
*/
.shortcuts-pane { padding: 24px 0 0; }

.shortcuts-group + .shortcuts-group {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--border-default);
}

.shortcuts-group-label {
  padding: 0 20px 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.shortcuts-pane ul { list-style: none; }

.shortcuts-pane li a {
  padding: 8px 20px;
  font-size: 14px;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.shortcuts-pane li a i {
  font-size: 18px;
  color: var(--text-tertiary);
  flex-shrink: 0;
}
.shortcuts-pane li a:hover {
  background: var(--color-gray-100);
  color: var(--text-primary);
}
.shortcuts-pane li a[aria-current="page"] {
  background: var(--surface-card);
  color: var(--text-primary);
  font-weight: 600;
  box-shadow: inset 3px 0 0 0 var(--color-primary);
}
.shortcuts-pane li a[aria-current="page"] i {
  color: var(--color-primary);
}
.shortcuts-pane li a:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}

/* xs / sm reflow: shortcuts-pane lives inside nav-drawer-panel's drawer-body.
   Padding tightens vertically; group separator gap shrinks; row padding +
   icon size grow to 44px touch target per the xs mockup; :active replaces
   :hover for touch feedback. aria-current treatment unchanged across bps. */
@media (max-width: 899px) {
  .shortcuts-pane { padding: 16px 0 8px; }

  .shortcuts-group + .shortcuts-group {
    margin-top: 12px;
    padding-top: 16px;
  }

  .shortcuts-pane li a {
    padding: 10px 20px;
    gap: 12px;
    min-height: 44px;
  }
  .shortcuts-pane li a i { font-size: 20px; }
  .shortcuts-pane li a:active {
    background: var(--color-gray-100);
    color: var(--text-primary);
  }
}

/* contents-region — always present. Hosts title-block-panel +
   contents-list-pane (content lands in 3.3 + 3.4). Owns no scroll itself;
   the contents-list-pane <ul> inside owns the scroll viewport per the
   scroll model (IMPLEMENTATION-NOTES). */
/* title-block-group — the header stack: the title row plus, on the Agent Tokens
   view, the access-warning callout. A flex child of contents-region that never
   shrinks, so the header keeps its height while the contents list scrolls. */
.title-block-group {
  flex-shrink: 0;
  background: var(--surface-card);
}
.contents-region {
  background: var(--surface-card);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

/* title-block-panel — flex-row hosting title-block-pane + actions-pane +
   access-pane on Browse. Per IA title-block-panel: access-pane margin-
   left:auto pushes to the right edge; the three panes align flex-start
   so actions-pane and access-pane sit in the h1's row. Static placement
   above the contents-list — no sticky positioning because the list
   itself is the scroll viewport. Lifted verbatim from the canonical lg
   mockup mockups-panes-lg-browse-contents.html. */
.title-block-panel {
  background: var(--surface-card);
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: var(--space-2) var(--space-4) var(--space-1);
  flex-shrink: 0;
}

/* title-block-pane — heading container. Subtitle row deferred (no IA
   slot in scope for FEAT-017). */
.title-block-pane { min-width: 0; }
.title-block-pane h1 {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  line-height: 34px;
}

/* FEAT-021 trash surfaces — title-block LAYOUT only. The components themselves
   are canonical: the in-Trash chip is `.badge .badge-info`, the permanent-
   removal buttons (Delete / Empty Trash) are `.btn .btn-destructive
   .btn-compact` — all mirrored from STYLE-GUIDE in the components section. These
   rules add only what the trash title-block context needs on top. Per the lg/xs
   container + browse mockups. */

/* trash-strip — wraps the in-Trash chip. At lg it is display:contents
   (transparent), so the chip is a direct flex child sitting inline beside the
   title; at xs it becomes a row below the title row (narrow media query), so a
   long space name never crowds the chip — per the lg/xs container mockups. */
.title-block-panel .trash-strip {
  display: contents;
}
/* The chip vertically centers beside the title and does not shrink. */
.title-block-panel .badge {
  align-self: center;
  flex-shrink: 0;
}

/* Artifact in-Trash chip — inline in the (fixed-height) workspace toolbar at lg
   via .trash-chip-toolbar; at xs it moves to a bar below the toolbar
   (.workspace-region .trash-strip), since the toolbar can't wrap. The two are
   separate elements toggled by breakpoint. Per the lg/xs artifact mockups; the
   xs override lives in the narrow media query below. */
/* The token read-only badge (FEAT-034-R3) takes the SAME treatment for the same
   reason — one read-only condition named on the toolbar, relocated to a strip at
   xs — so it shares these rules rather than restating them. Per the lg/xs token
   mockups. */
.trash-chip-toolbar,
.readonly-chip {
  align-self: center;
  flex-shrink: 0;
}
.workspace-region .trash-strip,
.workspace-region .readonly-strip {
  display: none;
}

/* trash-banner — framing note that lives INSIDE the Trash root's title-block-
   panel as a full-width row beneath the title. Part of the static header (fixed
   by layout — the title-block-panel never scrolls; only the contents list does),
   so the banner stays put at lg + xs. The --banner modifier lets the panel wrap
   the banner onto its own row; non-trash title-blocks are untouched. */
.title-block-panel--banner {
  flex-wrap: wrap;
  /* Match the mockup's banner spacing: 10px above (title → banner) and 10px below
     (banner → list) at lg. The banner is the wrapped second row, so row-gap is the
     title→banner gap and padding-bottom is the banner→list gap. (Was space-3/24px
     below — too large vs the mockup's 10px.) */
  row-gap: 10px;
  padding-bottom: 10px;
}
.trash-banner {
  flex: 0 0 100%;
  margin: 0;
  padding: 0.875rem 1rem;
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  line-height: 1.6;
  background: var(--color-info-bg);
  border-left: 3px solid var(--color-info);
  border-radius: var(--radius-md);
}

/* access-warning-banner — the Agent Tokens view's standing callout that every token
   has full-owner reach (FEAT-034-R3). Takes the trash-banner's shape in WARN tokens:
   a left-ruled tinted block between the header and the listing, static in the
   contents-region (it never scrolls with the list). Per the lg/xs token mockups. */
.access-warning-banner {
  flex-shrink: 0;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 4px var(--space-4) 8px;
  padding: 0.875rem 1rem;
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  line-height: 1.55;
  background: var(--color-warning-bg);
  border-left: 3px solid var(--color-warning);
  border-radius: var(--radius-md);
}
.access-warning-banner i {
  font-size: 18px;
  line-height: 1.4;
  color: var(--color-warning);
  flex-shrink: 0;
}
@media (max-width: 899px) {
  .access-warning-banner {
    gap: 8px;
    margin: 4px var(--space-2) 8px;
    padding: 0.75rem 0.875rem;
  }
}

/* xs: the trashed artifact's inline toolbar chip — and the token's read-only badge
   (FEAT-034-R3) — give way to a bar below the toolbar (the fixed-height toolbar
   can't wrap). Per mockups-trash-artifact-xs + mockups-agent_tokens-token-xs. */
@media (max-width: 899px) {
  /* scoped to the toolbar (0,2,0) so it beats the later `.badge { display:inline-flex }`. */
  .workspace-toolbar-panel .trash-chip-toolbar,
  .workspace-toolbar-panel .readonly-chip {
    display: none;
  }
  .workspace-region .trash-strip,
  .workspace-region .readonly-strip {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    padding: 7px 12px;
    background: var(--surface-card);
    border-bottom: 1px solid var(--border-default);
  }
}

/* actions-pane — tight row of icon buttons (edit / + / delete on Browse).
   Buttons are chrome only in FEAT-017 — no functional handlers (deferred
   to FEAT-009/FEAT-010 and Layer 4 overlay passes). Vertically nudged
   3px to center on the h1's effective line-height (28px font × 1.2 line-
   height = 33.6px against the 28px-tall buttons). */
.actions-pane {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 3px;
}
/* The action buttons themselves — scoped by direct-child + the .add-trigger
   wrapper introduced in pass 4.1 (which holds the + button + its
   anchored .add-menu-panel). The narrower selector keeps these rules
   from cascading into menuitems nested deeper (otherwise width:28px +
   padding:0 would squash the dropdown's <button class='menu-item'>s
   to icon size). */
.actions-pane > button:not(.btn),
.actions-pane > .add-trigger > button {
  appearance: none;
  width: 28px;
  height: 28px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.actions-pane > button i,
.actions-pane > .add-trigger > button i { font-size: 16px; line-height: 1; }
.actions-pane > button:not(.btn):hover,
.actions-pane > .add-trigger > button:hover {
  background: var(--color-gray-100);
  color: var(--text-primary);
}
.actions-pane > button:not(.btn):focus-visible,
.actions-pane > .add-trigger > button:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* access-pane — combined avatar-stack + count trigger that summons
   access-popover-panel (Layer 4.3). Single button per IA "one combined
   target" rule. Padded so the hover background reads as a distinct
   button around the avatars + count cluster. margin-left:auto pushes
   it to the right edge of title-block-panel. */
.access-pane {
  appearance: none;
  margin-left: auto;
  border: none;
  background: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 4px 12px;
  border-radius: var(--radius-md);
  color: var(--text-tertiary);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.access-pane:hover {
  background: var(--color-gray-100);
  color: var(--text-primary);
}
.access-pane:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.access-pane-avatars {
  display: inline-flex;
  align-items: center;
}
.access-pane-avatar {
  width: 26px;
  height: 26px;
  border-radius: var(--radius-full);
  border: 2px solid var(--surface-card);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  color: #ffffff;
}
.access-pane-avatar + .access-pane-avatar { margin-left: -8px; }

.access-pane-count {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}
.access-pane-count i { font-size: 16px; }

/* title-block-divider and overflow-pane are xs-only elements per IA
   title-block-panel.role at xs (the access-pane drops into the
   overflow-menu-panel summoned by ⋮ at xs; lg + sm carry access-pane
   inline). Hide at lg + sm; the xs override below switches them on. */
.title-block-divider,
.overflow-pane,
.overflow-trigger,
.overflow-menu-panel {
  display: none;
}

/* xs title-block-panel — single flex-row hosting title-block-pane
   (flex 1, truncating h1) + actions-pane + title-block-divider +
   overflow-pane (⋮). access-pane is hidden at xs; the overflow ⋮
   (chrome-only here; Layer 4.2 wires the menu summoning + Layer 4.3
   the access-popover) is where the access-pane lives at xs. Compound
   shadow puts a 1px hairline + subtle elevation beneath the chrome
   stack, separating it from the scrolling list. Lifted from
   mockups-panes-xs-browse-contents.html. Breakpoint extended to 899
   so Browse uses the xs treatment through sm — per the user-directed
   simplification that drops the separate sm Browse layout. */
@media (max-width: 899px) {
  /* The divider sits on the GROUP, not the title row, so it falls below the
     access-warning callout when one is present (FEAT-034-R3). With no callout the
     group ends where the title row ends and the divider lands unchanged. */
  .title-block-group {
    box-shadow: 0 1px 0 var(--border-default), 0 4px 8px rgba(0, 0, 0, 0.04);
    position: relative;
    z-index: 1;
  }
  .title-block-panel {
    align-items: center;
    gap: 8px;
    padding: var(--space-2) var(--space-2) var(--space-1);
  }
  /* min-height matches the compact action button (32px at xs) so the title row
     is the SAME height whether or not a button is present (a descendant view has
     none) — no jump between views — and the h1 centers in that reserved height.
     Per mockups-trash-container-xs.html. */
  .title-block-pane { flex: 1; min-width: 0; min-height: 32px; display: flex; align-items: center; }
  .title-block-pane h1 {
    font-size: 24px;
    line-height: 30px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Compact action buttons (Delete / Empty Trash) take a 32px touch target at xs
     — bigger than the 28px lg/desktop compact scale. Per the xs container mockup. */
  .title-block-panel .btn-compact { min-height: 32px; }
  /* In-Trash chip drops to its own row beneath the title at xs: the panel wraps,
     and the .trash-strip becomes a full-width row ordered last so the title +
     Delete keep row 1 (the title gets the width back, no longer crowded). Per
     mockups-trash-container-xs.html. */
  /* row-gap:0 cancels the panel's flex `gap` on the wrap axis — otherwise the
     8px row-gap stacks on the strip's margin + padding and inflates the title→chip
     space and the bar height. column-gap (title↔Delete) stays 8px. */
  .title-block-panel--chip { flex-wrap: wrap; row-gap: 0; }
  /* The title-pane takes a 1px flex-basis (not flex:1's 0-basis) so the full-width
     strip always OVERFLOWS the first row and wraps below — even on a descendant /
     trashed artifact with no Delete button beside the title (a 0-basis title lets
     a 100%-basis strip fit the row EXACTLY, which does not wrap, crowding the
     title). 1px is near-zero, so it still shares row 1 with a Delete button in the
     removal view and grows to fill via flex-grow; ellipsis still applies. */
  .title-block-panel--chip .title-block-pane { flex-basis: 1px; }
  .title-block-panel--chip .trash-strip {
    display: flex;
    flex: 0 0 100%;
    order: 99;
    align-items: center;
    /* A full-width bar at the bottom of the header panel. NO negative SIDE margins:
       they would shrink the strip's flex-line footprint below 100%, letting it sit
       beside the (1px-basis) title instead of wrapping — the descendant/artifact
       crowding bug. The strip spans the content box at full 100%, so `1px title +
       100% strip` overflows row 1 and wraps reliably (with or without a Delete
       button). The negative BOTTOM margin pulls the strip to the panel's bottom
       edge so the header box-shadow is the separator to the list; space-1 top
       margin + 7px padding match the mockup's bar height; the chip aligns with the
       title's left edge. */
    margin: var(--space-1) 0 calc(-1 * var(--space-1));
    padding: 7px 0;
    background: var(--surface-card);
  }
  /* Browse actions-pane (inside title-block-panel) — scoped so the
     xs button sizing doesn't bleed onto Artifact's actions-pane (which
     lives in workspace-toolbar-panel and takes its own xs sizing
     below). */
  .title-block-panel .actions-pane { flex-shrink: 0; margin-top: 0; }
  /* Scoped the same way as the base actions-pane button rules — see
     comment there. Direct children + the .add-trigger > button only;
     menuitems nested deeper retain their .menu-item sizing. */
  .title-block-panel .actions-pane > button:not(.btn),
  .title-block-panel .actions-pane > .add-trigger > button {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
  }
  .title-block-panel .actions-pane > button i,
  .title-block-panel .actions-pane > .add-trigger > button i { font-size: 18px; }

  /* access-pane lives in the overflow menu at xs — not inline. */
  .access-pane { display: none; }

  /* xs-only chrome — divider between actions and overflow trigger. */
  .title-block-divider {
    display: block;
    width: 1px;
    height: 20px;
    background: var(--border-default);
    flex-shrink: 0;
    margin: 0 4px;
  }
  .overflow-pane {
    display: inline-flex;
    appearance: none;
    width: 32px;
    height: 32px;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    align-items: center;
    justify-content: center;
    padding: 0;
    flex-shrink: 0;
    transition: background var(--transition-fast), color var(--transition-fast);
  }
  .overflow-pane i { font-size: 18px; line-height: 1; }
  .overflow-pane:hover {
    background: var(--color-gray-100);
    color: var(--text-primary);
  }
  .overflow-pane:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
  }

  /* overflow-trigger — wrapper so overflow-menu-panel anchors to the ⋮
     button via position:absolute. Same anchored-dropdown pattern as
     .add-trigger from pass 4.1 (the lg add-menu) — at xs both menus
     share the right-aligned dropdown shape per IA. */
  .overflow-trigger {
    display: inline-flex;
    position: relative;
  }

  /* overflow-menu-panel — anchored beneath the ⋮ trigger, right-aligned
     so it stays within the viewport (the ⋮ button sits at the far right
     of the xs title-block-panel). Padding 6px 0 instead of 4px (slightly
     more breathing room since the only menuitem is taller than add-
     menu's single-line items). Hosts a single menuitem at pitch_demo —
     the access-pane content (avatars + count + label). z-index 21 same
     as add-menu-panel. */
  .overflow-menu-panel {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    width: 220px;
    background: var(--surface-card);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px 0;
    z-index: 21;
  }
  .overflow-menu-panel.is-open { display: block; }

  /* Overflow-menu access menuitem — strict mockup-faithful sizing.
     The base .menu-item rule from account-menu (pass 2.3) ships
     wider proportions (gap:16px, padding:12px 20px) and a larger
     .menu-item i font-size (22px) that cascade in and push the
     `{N} with access` count past the 220px panel width, wrapping
     the text. Scope inside .overflow-menu-panel to restore the
     canonical xs Browse overflow-open mockup's proportions —
     avatar 24x24 (vs the inline access-pane's 26x26), menuitem
     gap:12px + padding:8px 12px + min-height:44px (touch target),
     icon 16px (matching the inline access-pane's people icon),
     and the count carries `white-space: nowrap` so even slack-
     case widths don't wrap. */
  .overflow-menu-panel .access-pane-avatar {
    width: 24px;
    height: 24px;
  }
  .overflow-menu-panel .menu-item {
    gap: 12px;
    padding: 8px 12px;
    min-height: 44px;
  }
  .overflow-menu-panel .access-pane-count {
    white-space: nowrap;
  }
  .overflow-menu-panel .access-pane-count i {
    font-size: 16px;
  }
}

/* contents-list-pane — columnar list of the current container's
   children. The <ul> owns the scroll viewport so the column header
   stays put without sticky positioning. Bottom padding on the <ul>
   clears the cmd-trigger so the last row scrolls clear. Two visual
   shapes live in the same DOM: the lg+sm tabular form (col-* cells)
   and the xs stacked form (row-info wrapper with row-name + row-meta
   + chevron). CSS shows the breakpoint-appropriate elements and
   hides the others via display:none (which also hides from screen
   readers, avoiding double-read). Lifted from canonical lg + xs
   Browse mockups. */
/* contents-body — the OOB swap target wrapping the BrowseState body
   (FEAT-009-R1 container-create stay-and-focus). display:contents makes it
   layout-neutral: contents-list-pane / empty-state stay flex children of
   contents-region, and the class-based rules here are unaffected. */
.contents-body { display: contents; }

.contents-list-pane {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  padding: 0 20px;
}
.contents-list-pane ul {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: var(--space-6);
  list-style: none;
}

/* lg + sm tabular form — 5-column grid; xs-only elements (row-info,
   row-chevron) hidden. */
.contents-list-row,
.contents-list-header {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) 110px 150px 90px;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
}
.contents-list-header {
  background: var(--surface-card);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-tertiary);
  border-bottom: 1px solid var(--border-default);
  padding-top: 8px;
  padding-bottom: 8px;
  flex-shrink: 0;
}
.contents-list-header .col-size { text-align: right; }

.contents-list-row {
  font-size: 14px;
  color: var(--text-primary);
  border-radius: 6px;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--transition-fast);
}
.contents-list-row:hover { background: var(--color-gray-100); }
.contents-list-row:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}
/* Explicit focus ring for a PROGRAMMATICALLY-focused row (FEAT-021 Undo
   stay-and-focus). :focus-visible above paints only in keyboard modality, so a
   mouse-initiated restore (click Undo) focuses the row but shows no ring. The
   undo handler adds this class (removed on blur) to render the identical ring
   regardless of modality. Mirrors the :focus-visible rule exactly. */
.contents-list-row.is-focus-ring {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}
.contents-list-row .col-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.contents-list-row .col-icon i {
  font-size: 18px;
  color: var(--text-tertiary);
}
.contents-list-row .col-name {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Platform-fixture rows mute (FEAT-007-R4): the name drops to tertiary +
   regular weight (the icon is already tertiary at every breakpoint). Lifted
   from the canonical FEAT-007-R4-fixture-rows mockups. */
.contents-list-row.row-fixture .col-name {
  color: var(--text-tertiary);
  font-weight: 400;
}
.contents-list-row .col-owner,
.contents-list-row .col-date,
.contents-list-row .col-size {
  font-size: 13px;
  color: var(--text-tertiary);
}
.contents-list-row .col-size {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* xs-only elements (rendered but hidden at lg + sm). */
.contents-list-row .row-info,
.contents-list-row .row-chevron {
  display: none;
}

/* xs stacked form — flex row with stacked name + combined meta.
   Extended to cover sm (≤899px) per the user-directed simplification
   that drops the separate sm Browse layout. Container takes over the
   pane's padding (zero horizontal); rows have their own internal
   padding and a subtle separator. */
@media (max-width: 899px) {
  .contents-list-pane { padding: 0; }
  .contents-list-header { display: none; }

  .contents-list-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-subtle);
    border-radius: 0;
    grid-template-columns: none;
  }
  .contents-list-row:active { background: var(--color-gray-100); }

  /* Hide the lg-only cells; show the xs-only wrapper + chevron. */
  .contents-list-row .col-name,
  .contents-list-row .col-owner,
  .contents-list-row .col-date,
  .contents-list-row .col-size {
    display: none;
  }
  .contents-list-row .row-info {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .contents-list-row .row-chevron {
    display: inline-block;
    flex-shrink: 0;
    font-size: 16px;
    color: var(--text-muted);
  }

  /* col-icon (shared element) takes its xs-specific sizing. */
  .contents-list-row .col-icon {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
  }
  .contents-list-row .col-icon i { font-size: 20px; }

  .contents-list-row .row-name {
    font-size: 14px;
    font-weight: 500;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Platform-fixture rows mute at xs too (FEAT-007-R4). */
  .contents-list-row.row-fixture .row-name {
    color: var(--text-tertiary);
    font-weight: 400;
  }
  .contents-list-row .row-meta {
    font-size: 11px;
    color: var(--text-tertiary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* empty-state — replaces contents-list-pane in the contents-region body
   for the four Browse non-populated states (empty-container, empty-
   tenant, not-found, error). Per FEAT-007 mockups + IA v008-017
   empty_and_error_states[]. Centered icon + message/heading + optional
   body. flex:1 fills the available vertical space below title-block-
   panel; min-height:0 lets the flex column shrink. Same rule across
   all four states; the icon and copy slots differ per state, the
   layout doesn't. */
.empty-state {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--space-4);
  gap: var(--space-2);
}
.empty-icon {
  font-size: 56px;
  color: var(--text-muted);
}
.empty-message {
  font-size: var(--font-size-lg);
  color: var(--text-secondary);
  max-width: 420px;
}
/* The secondary line under an empty-state message — the recovery, sized below the
   message it follows (FEAT-034-R3 agent-tokens empty mockups). At xs it steps down
   with the narrower measure the xs mockup sets. */
.empty-sub {
  font-size: var(--font-size-base);
  color: var(--text-secondary);
  max-width: 420px;
}
@media (max-width: 899px) {
  .empty-sub {
    font-size: var(--font-size-sm);
    max-width: 280px;
  }
}
.empty-heading {
  font-size: var(--font-size-2xl);
  font-weight: 600;
  color: var(--text-primary);
}
.empty-body {
  font-size: var(--font-size-base);
  color: var(--text-secondary);
  max-width: 460px;
}

/* breadcrumbs-panel — viewport wrapper for breadcrumbs-pane. Dual-placed
   per IA: in header-region at lg + sm; in location-region at xs. Owns
   horizontal-scroll chrome for long paths. */
.breadcrumbs-panel {
  flex: 1;
  min-width: 0;
  height: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  display: flex;
  align-items: center;
}
.breadcrumbs-panel::-webkit-scrollbar { display: none; }

/* breadcrumbs-pane — crumb sequence. Content rendered by pass 3.1
   (handlers/studio.go buildBreadcrumbs + partials/studio_shell.html
   breadcrumb-chain sub-template). Per-state styling lifted from the
   canonical lg mockup (mockups-panes-lg-browse-contents.html lines 84-
   112). Leading "/" via ::before separates the chain from the sibling
   pane (brand-pane at lg). Anchors render in text-secondary; hover
   strengthens to text-primary; the .sep span between crumbs is muted;
   the [aria-current="page"] (last crumb, no link) renders primary +
   semibold. */
.breadcrumbs-pane {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  white-space: nowrap;
  flex-shrink: 0;
}
.breadcrumbs-pane::before {
  content: "/";
  color: var(--text-muted);
  font-weight: 400;
}
.breadcrumbs-pane a {
  color: var(--text-secondary);
  text-decoration: none;
  transition: color var(--transition-fast);
}
.breadcrumbs-pane a:hover { color: var(--text-primary); }
.breadcrumbs-pane a:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
.breadcrumbs-pane .sep {
  color: var(--text-muted);
}
.breadcrumbs-pane [aria-current="page"] {
  color: var(--text-primary);
  font-weight: 600;
}

/* === Browse layout — xs default (≤599px) =================================== */
.studio-body--browse .studio {
  display: flex;
  flex-direction: column;
}
.studio-body--browse .header-region {
  padding: 0 8px 0 4px;
}
.studio-body--browse .header-region .breadcrumbs-panel {
  display: none; /* breadcrumbs live in location-region at xs */
}
.studio-body--browse .brand-pane {
  margin: 0 auto; /* center brand between toggle (left) and account (right) at xs */
}
.studio-body--browse .nav-region {
  display: none; /* nav drawer-summoned at xs */
}
.studio-body--browse .location-region .breadcrumbs-panel {
  padding: 0 16px;
}
.studio-body--browse .contents-region {
  flex: 1;
}

/* === Browse layout — lg (≥900px) ============================================
   Browse uses the xs layout through xs + sm (≤899px) — the prior 600-899
   sm-only treatment was removed: matching Artifact's xs-covers-sm rule
   keeps breadcrumbs in location-region consistently across screens at sm.
   See pipeline/structure/INFORMATION-ARCHITECTURE.mi for the breakpoint
   role (xs through sm) once that revision lands. */
@media (min-width: 900px) {
  .studio-body--browse .studio {
    display: grid;
    grid-template-columns: 192px 1fr;
    grid-template-rows: 56px 1fr;
  }
  .studio-body--browse .header-region {
    grid-column: 1 / -1;
    padding: 0 16px;
  }
  .studio-body--browse .nav-drawer-toggle-pane {
    display: none; /* nav inline at lg; no drawer trigger */
  }
  .studio-body--browse .nav-region {
    display: block;
    grid-column: 1;
    grid-row: 2;
  }
  /* Restore lg breadcrumb placement: header carries the breadcrumbs,
     location-region hides. The xs default carries breadcrumbs in
     location-region and hides them in the header; lg flips that.
     Brand and account-pane shift: brand left-aligned next to the
     header breadcrumbs, account pinned right via margin-left:auto.
     (These four rules previously lived in an @media (min-width: 600px)
     block that applied to sm + lg; the sm simplification removed sm
     but inadvertently dropped lg too — restored here as lg-only.) */
  .studio-body--browse .location-region {
    display: none;
  }
  .studio-body--browse .header-region .breadcrumbs-panel {
    display: flex;
  }
  .studio-body--browse .brand-pane {
    margin: 0;
  }
  .studio-body--browse .account-pane {
    margin-left: auto;
  }
  .studio-body--browse .contents-region {
    grid-column: 2;
    grid-row: 2;
  }
}

/* === Studio Artifact shell ==================================================
   FEAT-017 pass 1.3 — Artifact shell. Per workspace-no-nav-affordance: no nav
   region and no nav-drawer-toggle at any breakpoint. workspace-region fills
   the space below the header (single column at lg; lands inspector-region as
   sibling column in 2.4 when inspector opens).

   Per IA v008-003: Artifact adopts the xs layout at sm — the lg inline-
   inspector + split-view treatment compresses too narrowly at sm widths
   (split panes ~180px at 600px viewport), so the xs design extends upward to
   cover sm. Only two breakpoint variants:
   - xs covering sm (default, ≤899px): flex column. Header (brand centered +
     account right). Location-region with breadcrumbs-panel. Workspace-region
     with 44px touch-friendly toolbar + content panel. View-toggle source /
     preview only (no split). Inspector summons via drawer overlay.
   - lg (≥900px): grid 1fr × 56px+1fr. Header spans the column. Workspace-
     region fills row 2. 40px toolbar. View-toggle adds split. Inspector
     opens inline as right column (Layer 2.4).
*/

/* Universal workspace chrome — used by Artifact at every breakpoint. */
.workspace-region {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--surface-card);
  overflow: hidden;
}

.workspace-toolbar-panel {
  flex-shrink: 0;
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-default);
  display: flex;
  align-items: center;
}

.workspace-content-panel {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Source-body / preview-body view-toggle visibility (pass 3.7).
   data-view on the .studio element drives which panel(s) render.
   At lg, "split" lays both panels side by side via grid (1fr 1px 1fr);
   "source" or "preview" collapse to a single 1fr column with the
   non-selected panel + split-divider display:none. At xs/sm the
   workspace-content-panel is flex-column (no split — view-toggle
   has no split segment at narrow widths); "source" or "preview"
   hides the non-selected panel. */
.studio[data-view="source"] .preview-panel,
.studio[data-view="source"] .split-divider {
  display: none;
}
.studio[data-view="preview"] .source-panel,
.studio[data-view="preview"] .split-divider {
  display: none;
}
/* Restore each panel's intended display in split (NOT `display: revert`, which
   rolls a <div> back to the UA default `block` — that clobbered .source-panel's
   `flex`, collapsing .source-pane to content height and leaving the page background
   showing below a short source). source-panel is the flex column; the others block. */
.studio[data-view="split"] .source-panel {
  display: flex;
}
.studio[data-view="split"] .preview-panel,
.studio[data-view="split"] .split-divider {
  display: block;
}

@media (max-width: 899px) {
  /* xs/sm has no split mode — if the cookie carries "split" (the
     split button is hidden by view-toggle at narrow widths but the
     cookie can survive a resize from lg), fall back to preview-only
     so the visible panel matches studio.js normalizeViewToggle's
     aria-pressed pick (pass 2.5: split intent at narrow → preview is
     the effective pressed segment). Showing the source-panel here
     would contradict the toggle's visual state. */
  .studio[data-view="split"] .source-panel,
  .studio[data-view="split"] .split-divider {
    display: none;
  }
}

/* === Source body (pass 3.6) =================================================
   Editor-style source render of the artifact's .mi text. source-panel
   hosts source-pane; source-pane is ONE grid whose rows each pair a
   line number (column 1) with that line's typed segments (column 2).
   Each segment is a span whose class tints the run per the syntax
   vocabulary (fm-fence, fm-key, fm-string, fm-bool, md-h, md-tpl,
   md-bold, md-block). The active line carries the editor-cursor
   indicator at the end of its segments. Lifted from canonical lg + xs
   Artifact mockups.

   FEAT-049 restructured the lane from two sibling columns to one grid.
   Two columns stay aligned only while every line is exactly one row
   tall — the assumption wrapping exists to break, and one the previous
   line-heights already broke by ~2.4px per line. Both wrappers go
   display:contents so number N and line N become cells of the same row,
   and the template stamps grid-row on each cell: column assignment
   alone does NOT pair them, because the flattened wrappers make the
   numbers and the lines one auto-placed sequence and the numbers
   consume the first N rows. grid-auto-rows:max-content is likewise
   load-bearing — under the default `auto` the implicit tracks resolve
   to 20px once the content overflows a short pane, and wrapped lines
   paint over their successors. */
.source-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  flex: 1;
}
.source-pane {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-auto-rows: max-content;
  align-content: start;
  padding: 16px 0 64px;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text-primary);
}
.editor-gutter,
.editor-content { display: contents; }
.line-num {
  grid-column: 1;
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface-bg);
  border-right: 1px solid var(--border-subtle);
  color: var(--text-muted);
  font-size: 11px;
  line-height: 20px;
  padding: 0 10px 0 16px;
  text-align: right;
  user-select: none;
  font-variant-numeric: tabular-nums;
}
.editor-line {
  grid-column: 2;
  line-height: 20px;
  min-height: 20px;
  padding: 0 24px;
}
/* Wrap state is CSS alone — the served markup is identical either way. */
.studio[data-wrap="on"] .source-pane { overflow-x: hidden; }
.studio[data-wrap="on"] .editor-line { white-space: pre-wrap; overflow-wrap: anywhere; }
.studio[data-wrap="off"] .source-pane { overflow-x: auto; }
.studio[data-wrap="off"] .editor-line { white-space: pre; }
.editor-line.is-active {
  background: var(--color-gray-100);
  box-shadow: inset 2px 0 0 0 var(--color-gray-400);
}
.editor-cursor {
  display: inline-block;
  width: 1.5px;
  height: 1.1em;
  background: var(--text-primary);
  vertical-align: text-bottom;
  margin-left: -0.5px;
  animation: editor-cursor-blink 1s steps(2) infinite;
}
@keyframes editor-cursor-blink { to { visibility: hidden; } }

/* Source syntax tinting. Muted palette per tool aesthetics; brand
   amber reserved for template expressions and active line. */
.fm-fence  { color: var(--text-muted); }
.fm-key    { color: var(--text-secondary); }
.fm-string { color: var(--text-primary); }
.fm-bool   { color: var(--color-primary-dark); }
.md-h      { color: var(--text-primary); font-weight: 700; }
.md-tpl    { color: var(--color-primary-dark); font-weight: 500; }
.md-bold   { color: var(--text-primary); font-weight: 700; }
.md-block  { color: var(--text-secondary); }

/* xs/sm Artifact source body — slightly smaller font + tighter
   gutter padding + tighter line padding per the canonical xs mockup.
   The lane's padding now sits on the pane rather than on the two
   wrappers (which no longer generate boxes), so the narrow tightening
   is re-expressed against the pane and the number cell. */
@media (max-width: 899px) {
  .source-pane { font-size: 12px; padding: 14px 0 64px; }
  .line-num { padding: 0 8px 0 12px; }
  .editor-line { padding: 0 16px; }
}

/* FEAT-010 invalid-source parse-error strip — pinned at the top of the source
   panel (above the editor), carrying the runtime parser diagnostic in plain
   sight. Error-toned; monospace (it quotes source positions). Hidden until a
   parse-gate 422; the source pane owns its own scroll below it. */
.source-error-strip {
  flex-shrink: 0;
  padding: 8px 16px;
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  line-height: 1.5;
  color: var(--color-error-text);
  background: var(--color-error-bg);
  border-bottom: 1px solid var(--border-default);
}
.source-error-strip[hidden] { display: none; }

/* === FEAT-010: editable source editor (CodeMirror 6) ========================
   On the editable Artifact view the source-pane (data-editor) is the CodeMirror
   mount host; studio.js adds .cm-mounted once the editor replaces the read-only
   gutter/content render (which stays as the pre-mount / no-JS fallback and the
   in-Trash read-only render).

   Colours for the mounted editor are owned by our CM theme + HighlightStyle in the
   bundle (tools/codemirror-vendor/src/mi-editor.mjs) — token-driven (var()s), so the
   selection wins at CM's theme layer and syntax/selection/cursor/gutter/background
   all flip with data-theme. We deliberately style no colour here. The only rules
   left are line-box GEOMETRY, because the 11px gutter and the 12.5px content must
   share one fixed line height or the line numbers drift from their source lines. */
.source-pane.cm-mounted { display: block; padding: 0; overflow: hidden; }
.source-pane .cm-editor { height: 100%; }
.source-pane .cm-editor.cm-focused { outline: none; }
.source-pane .cm-scroller {
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 20px; /* fixed px: the 11px gutter + 12.5px content share one line box */
}
.source-pane .cm-content { padding: 16px 0; }
.source-pane .cm-line { padding: 0 24px; }
.source-pane .cm-lineNumbers .cm-gutterElement { padding: 0 10px 0 16px; font-size: 11px; }

/* Save (diskette) in the Artifact toolbar actions-pane — disabled until the
   editor buffer diverges from the loaded baseline (the dirty-gate). */
.actions-pane > button[disabled] { color: var(--text-muted); cursor: not-allowed; }
.actions-pane > button[disabled]:hover { background: transparent; color: var(--text-muted); }

@media (max-width: 899px) {
  /* FEAT-017-R3: the mounted editor's focusable text (.cm-content inherits this)
     renders at 16px on mobile — at iOS's auto-zoom-on-focus threshold — so focusing
     the source does not zoom the chrome, WITHOUT pinning the viewport (pinch-zoom
     stays available). Line-box geometry (line-height 20px from the base rule, 11px
     gutter) is unchanged, so line numbers stay aligned. */
  .source-pane .cm-scroller { font-size: 16px; }
  .source-pane .cm-line { padding: 0 16px; }
  .source-pane .cm-lineNumbers .cm-gutterElement { padding: 0 8px 0 12px; }
}

/* status-subbar-panel — the edit-save coherence status strip below the toolbar.
   The single channel for the coherence headline (+ action link, follow-up); always
   open on an editable artifact, resting Synced included (FEAT-010-R2 removed the
   last vestige of a hide path); tone classes carry severity, copy stays plain
   (the pattern's tone_tokens). */
.status-subbar-panel {
  flex-shrink: 0;
  /* FEAT-010-R2 (director-settled 2026-09-05): band-split vertical rhythm.
     At lg the strip stays economical — 4px above and below the 26px pill (34px
     resting), enough air for a bordered control without costing the workspace —
     while the ≤899 stacked column below keeps its roomier 8px (--space-1). */
  min-height: 34px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 16px;
  background: var(--surface-bg);
  border-bottom: 1px solid var(--border-default);
}
/* Row 1 — version-select + the coherence status, side by side at every width
   (FEAT-010-R2: the selector's one permanent home; server-rendered first in the
   row), wrapping if narrow. */
.status-subbar-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.status-subbar-panel .status-message {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-size-xs);
  font-weight: 500;
  color: var(--text-secondary);
}
.status-subbar-panel .status-glyph { font-size: 15px; color: var(--text-tertiary); }
/* Editing's unsaved indicator — a CSS-drawn FILLED dot (the mockup's ● ph-fill
   ph-circle), since the vendored Phosphor subset has no fill weight. Inherits the
   tone colour via currentColor. */
.status-subbar-panel .status-glyph.status-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0;   /* drop the 15px icon line-box so the dot centers on the text */
  line-height: 0;
}
.status-subbar-panel .status-glyph.status-dot::before {
  content: "";
  display: block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
}
.status-subbar-panel .status-detail { font-size: var(--font-size-xs); color: var(--text-muted); }
/* tones — glyph carries the severity colour (the pattern's tone_tokens). */
.status-subbar-panel.tone-neutral .status-glyph { color: var(--text-tertiary); }
.status-subbar-panel.tone-info    .status-glyph { color: var(--color-info, var(--color-primary-dark)); }
.status-subbar-panel.tone-warn    .status-glyph { color: var(--color-primary-dark); }
.status-subbar-panel.tone-error   .status-glyph { color: var(--color-error); }
.status-subbar-panel.tone-success .status-glyph { color: var(--color-success); }

/* Saving (in-flight) — the flash-guarded subbar spinner. The busy signal is this
   spinner + the disabled Save; the Save diskette is not itself animated. */
@keyframes coherence-spin { to { transform: rotate(360deg); } }
.status-subbar-panel .status-glyph.ph-circle-notch { animation: coherence-spin 0.7s linear infinite; }

/* the subbar reconciliation action link (Update to server version / Copy my
   changes) — opens the state's decision dialog. */
.status-subbar-panel .status-link {
  appearance: none;
  border: none;
  background: transparent;
  padding: 0;
  font-family: var(--font-primary);
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--color-primary-dark);
  cursor: pointer;
  text-decoration: underline;
}
.status-subbar-panel .status-link[hidden] { display: none; }
.status-subbar-panel .status-link:hover { color: var(--text-primary); }

/* auto-sync-toggle-pane (FEAT-036) — the "Auto sync" preference switch, anchored at
   the RIGHT of the coherence status bar (margin-left:auto pushes it past the status +
   any action link). A labelled switch: text label + track + thumb; role=switch. The
   track fills --color-info when on; the thumb slides. Height 26px to sit centered in
   the 34px subbar. Presentation-only — no artifact fetch (studio.js). */
.status-subbar-panel .auto-sync-toggle-pane {
  appearance: none;
  order: 1;          /* FEAT-036: first in source (for the ≤899 float) but flex-ordered LAST at lg */
  margin-left: auto; /* lg single-row → far right */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 26px;
  /* No right padding: the switch TRACK (the visible right edge) sits flush to the
     button edge so it aligns to the header's account-avatar gutter (FEAT-036 director:
     align the visible control, not the invisible container). At lg the subbar's 16px
     right gutter matches the header's, so the track lands on the avatar's line; ≤899
     nudges 4px (header gutter 12px vs subbar 16px). */
  padding: 0 0 0 6px;
  flex-shrink: 0;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: background var(--transition-fast);
}
.status-subbar-panel .auto-sync-toggle-pane:hover { background: var(--color-gray-100); }
.auto-sync-toggle-pane .as-label { font-size: var(--font-size-xs); font-weight: 500; color: var(--text-secondary); }
.auto-sync-toggle-pane .as-track {
  position: relative;
  width: 30px;
  height: 17px;
  border-radius: var(--radius-full);
  background: var(--color-gray-300);
  transition: background var(--transition-fast);
  flex-shrink: 0;
}
.auto-sync-toggle-pane .as-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition-fast);
}
.auto-sync-toggle-pane[aria-checked="true"] .as-track { background: var(--color-info, var(--color-primary-dark)); }
.auto-sync-toggle-pane[aria-checked="true"] .as-thumb { transform: translateX(13px); }
.auto-sync-toggle-pane:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }

/* FEAT-010 reconciliation dialogs (Update / Overwrite / Salvage) — the
   container-create dialog chrome (header with title + close-X, no icon; scrolling
   body; footer). lg: a centered card over the shared modal-backdrop; xs: a
   full-screen modal (below) per the dialog-chrome convention. */
.reconcile-dialog {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 460px;
  max-width: calc(100vw - 32px);
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xl);
  flex-direction: column;
  z-index: 21;
}
.reconcile-dialog.is-open { display: flex; }
.reconcile-pane { padding: 20px 24px 0; display: flex; flex-direction: column; gap: 8px; }
.reconcile-pane .description { font-size: 14px; color: var(--text-secondary); line-height: 1.55; }
.reconcile-pane .consequence { font-size: 13px; color: var(--text-muted); line-height: 1.5; margin-top: 4px; }
.reconcile-dialog .modal-footer { justify-content: flex-end; gap: 8px; padding: 24px; border-top: none; }

@media (max-width: 899px) {
  /* xs: full-screen modal (close-X moves left via the shared modal-header reflow;
     scrolling body; footer pinned with safe-area) — the create-form treatment. */
  .reconcile-dialog {
    inset: 0;
    transform: none;
    width: auto;
    max-width: none;
    border-radius: 0;
    box-shadow: none;
  }
  .reconcile-dialog .modal-header { justify-content: flex-start; padding: 0 4px; }
  .reconcile-dialog .reconcile-pane { flex: 1; overflow-y: auto; padding: 20px 16px; }
  .reconcile-dialog .modal-footer {
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
    background: var(--surface-card);
  }
}

/* FEAT-010 version-select — Local / Server (the read-only peek). Lifted from the
   canonical editing mockup; the dropdown menu opens via openModal (.is-open). */
.version-select-pane { position: relative; display: inline-flex; align-self: center; flex-shrink: 0; }
.version-select {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 8px 0 12px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  color: var(--text-primary);
  font-family: var(--font-primary);
  font-size: var(--font-size-xs);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition-fast);
}
.version-select:hover { background: var(--color-gray-100); }
.version-select i { font-size: 14px; color: var(--text-tertiary); }
.version-select[disabled] { opacity: 0.5; cursor: not-allowed; }
.version-select[disabled]:hover { background: var(--surface-card); }
.version-select-menu {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 150px;
  flex-direction: column;
  padding: 4px;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 22;
}
.version-select-menu.is-open { display: flex; }
.version-select-menu [data-version] {
  appearance: none;
  border: none;
  background: transparent;
  text-align: left;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  font-family: var(--font-primary);
  font-size: var(--font-size-xs);
  color: var(--text-primary);
  cursor: pointer;
}
.version-select-menu [data-version]:hover { background: var(--color-gray-100); }
.version-select-menu [data-version][aria-checked="true"] { font-weight: 700; }
@media (max-width: 899px) {
  /* ≤899: the subbar becomes a left-aligned COLUMN — row 1 holds the
     version-select + status (.status-subbar-row, the pane server-rendered there
     at every width per FEAT-010-R2), the action link sits below — the IA's
     stacked coherence row (mockup: padding 8px 16px, gap 7px). */
  /* FEAT-036: at ≤899 the coherence bar uses a FLOAT-AROUND layout so the auto-sync
     toggle reserves the top-right corner only on the line it occupies — the version-
     select + status flow around it on line 1 and, when the status is long, it wraps to
     its OWN FULL-WIDTH line below the toggle (not re-wrapping as if the toggle were
     still beside it). The panel is a flow-root (block); the toggle floats right; the
     row is block with inline-flowing children; the action link sits below. */
  .status-subbar-panel {
    display: block;
    /* The stacked touch column keeps the roomier 8px vertical padding the band
       has always used (FEAT-010-R2 band-split rhythm; lg is 4px). */
    padding: 8px 16px;
  }
  .status-subbar-panel .auto-sync-toggle-pane {
    float: right;
    margin-left: 0;
    /* Header's right gutter is 12px at ≤899 (vs the subbar's 16px) — pull the track
       4px to land on the account-avatar's right edge. */
    margin-right: -4px;
  }
  .status-subbar-panel .status-subbar-row {
    display: block;
    line-height: 26px;
  }
  .status-subbar-panel .version-select {
    display: inline-flex;
    vertical-align: middle;
    margin-right: 8px;
  }
  .status-subbar-panel .status-message {
    display: inline-flex;
    vertical-align: middle;
  }
  .status-subbar-panel .status-link {
    display: block;
    clear: right;   /* always below the floated toggle, never beside it */
    margin-top: 7px;
  }
}

/* === Preview body + split divider (pass 3.7) ================================
   preview-panel hosts preview-pane (the rendered markdown HTML — pre-
   rendered by the fixture today; chroma/markedin emits this downstream
   per FEAT-008 view-mode). split-divider is a discrete 1px DOM element
   between source-panel and preview-panel inside workspace-content-panel
   when the view-toggle is "split" at lg. Future drag-to-resize attaches
   behaviour to .split-divider without restructuring markup. Lifted from
   canonical lg + xs Artifact mockups. */
.split-divider {
  background: var(--border-default);
  min-height: 0;
}

.preview-panel {
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
}

.preview-pane {
  padding: 28px 32px 64px;
  max-width: 720px;
  font-family: var(--font-primary);
  color: var(--text-primary);
}
.preview-pane h1 {
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: 16px;
  line-height: 1.25;
}
.preview-pane h2 {
  font-size: 18px;
  font-weight: 600;
  margin-top: 24px;
  margin-bottom: 8px;
  line-height: 1.3;
}
.preview-pane p {
  margin-bottom: 12px;
  color: var(--text-primary);
}
.preview-pane ul {
  margin: 8px 0 16px 24px;
}
.preview-pane li {
  margin-bottom: 4px;
}
.preview-pane code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--surface-code);
  padding: 1px 4px;
  border-radius: var(--radius-sm);
}
/* Fenced code block (FEAT-008-R1, mockup mockups-preview-code-block-lg): the
   pre is the single container; the code inside goes block/transparent so an
   inline background can't paint per-line fragments. Margin matches the pane's
   paragraph rhythm; wide blocks scroll rather than break the pane. */
.preview-pane pre {
  background: var(--surface-code);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin: 12px 0;
  overflow-x: auto;
}
.preview-pane pre code {
  display: block;
  background: transparent;
  padding: 0;
  border-radius: 0;
  line-height: 1.55;
}
.preview-pane .check {
  color: var(--color-success);
}

/* xs/sm preview-pane — adjusted padding + slightly smaller heading
   sizes + explicit paragraph/list sizing per the canonical xs mockup.
   preview-panel itself takes flex:1 at xs (single-column flex-column
   workspace-content-panel) so it fills its scroll viewport. */
@media (max-width: 899px) {
  .preview-panel { flex: 1; }
  .preview-pane {
    padding: 24px 20px 64px;
    max-width: none;
  }
  .preview-pane h1 {
    font-size: 26px;
    margin-bottom: 14px;
  }
  .preview-pane h2 {
    font-size: 17px;
    margin-top: 22px;
  }
  .preview-pane p,
  .preview-pane li {
    font-size: 15px;
    line-height: 1.55;
  }
  .preview-pane li { margin-bottom: 6px; }
}

/* lg workspace-content-panel — grid for split-view (1fr 1px 1fr).
   When data-view is "source" or "preview" the grid collapses to a
   single 1fr column (see view-toggle visibility rules above; the
   non-selected panel + split-divider are display:none, leaving the
   remaining panel to fill via the grid's auto-layout). */
@media (min-width: 900px) {
  .workspace-content-panel {
    display: grid;
    grid-template-columns: 1fr 1px 1fr;
    flex-direction: initial;
  }
  .studio[data-view="source"] .workspace-content-panel,
  .studio[data-view="preview"] .workspace-content-panel {
    grid-template-columns: 1fr;
  }
}

/* inspector-toggle-pane — trigger that opens inspector-region inline at lg
   or summons inspector-drawer-panel at xs (overlay). Sits at the right
   edge of workspace-toolbar-panel via margin-left: auto. Behaviour lands
   in pass 2.4. Sizing varies by breakpoint (touch-friendly at xs+sm). */
.inspector-toggle-pane {
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  margin-left: auto;
  flex-shrink: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.inspector-toggle-pane:hover {
  background: var(--color-gray-100);
  color: var(--text-primary);
}
.inspector-toggle-pane:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* === Artifact layout — xs default (≤599px) ================================== */
.studio-body--artifact .studio {
  display: flex;
  flex-direction: column;
}
.studio-body--artifact .header-region {
  /* Right padding 12px so the account-pane sits the same distance from the
     viewport edge as the canonical FEAT-017 xs artifact mockup
     (mockups-panes-xs-artifact-source-inspector-closed-cmd-empty.html). Left
     stays at 16px — Artifact has no nav-drawer-toggle, so the left-aligned brand
     sits 16px from the edge (more breathing room than the toggle-anchored Browse
     layout). */
  padding: 0 12px 0 16px;
}
.studio-body--artifact .header-region .breadcrumbs-panel {
  display: none; /* breadcrumbs live in location-region at xs */
}
.studio-body--artifact .brand-pane {
  /* Left-align the brand (Artifact has no nav-drawer toggle to anchor a centered
     wordmark) — per the artifact mockup; margin-right:auto pushes the account-pane
     to the right edge. lg keeps its own margin:0 + account margin-left:auto. */
  margin-right: auto;
}
.studio-body--artifact .location-region .breadcrumbs-panel {
  padding: 0 16px;
}
.studio-body--artifact .workspace-region {
  flex: 1;
}
.studio-body--artifact .workspace-toolbar-panel {
  height: 44px;
  /* Right gutter 12px so the inspector button BOX aligns to the account avatar +
     the Auto sync track at ≤899 (the header's right gutter is 12px here). Left stays
     4px; the title-pane's ≤899 padding-left brings the title onto the 16px line. */
  padding: 0 12px 0 4px;
  gap: 4px;
}
.studio-body--artifact .inspector-toggle-pane {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
}
.studio-body--artifact .inspector-toggle-pane i {
  font-size: 18px;
  line-height: 1;
}

/* No sm media query for Artifact — per IA v008-007 both screens adopt
   the xs layout at sm (Browse joined Artifact in this rule). The xs
   defaults above apply throughout the ≤899px range. */

/* === Artifact workspace-toolbar — title-pane + actions (pass 3.5) ===========
   artifact-title-pane: leftmost cell in workspace-toolbar-panel; the
   artifact's friendly_name as an h1, single-line truncating ellipsis.
   actions-pane: edit + delete only on Artifact (no add — Artifact-scoped
   per IA actions-pane.content). Buttons share the .actions-pane base
   sizing (28x28) at lg with Browse's title-block actions-pane; xs sizing
   is parent-scoped (Artifact gets 36x36 inside .workspace-toolbar-panel,
   Browse gets 32x32 inside .title-block-panel, set above). Lifted from
   the canonical lg + xs Artifact mockups. */
.artifact-title-pane {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
  padding: 0 8px;
  flex-shrink: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 240px;
}

/* toolbar-divider--actions-view is xs-only — separates actions-pane
   from view-toggle-pane on Artifact at xs (the canonical xs mockup
   uses a divider here rather than a spacer; lg uses .toolbar-spacer
   instead, switched below). */
.toolbar-divider--actions-view { display: none; }

@media (max-width: 899px) {
  .artifact-title-pane {
    flex: 1;
    min-width: 0;
    max-width: none;
    padding-left: 12px; /* + toolbar 4px = the title on the 16px chrome gutter (brand/breadcrumb/status) at ≤899 */
  }
  /* Artifact actions-pane xs sizing — 36x36 buttons per the canonical
     xs mockup. Scoped to .workspace-toolbar-panel so it doesn't fight
     Browse's xs actions-pane (32x32, scoped to .title-block-panel). */
  .workspace-toolbar-panel .actions-pane { gap: 2px; flex-shrink: 0; }
  .workspace-toolbar-panel .actions-pane button {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
  }
  .workspace-toolbar-panel .actions-pane button i { font-size: 18px; }
  /* The xs sizing rule above sets color unconditionally at the same specificity
     (0,2,1) as the base [disabled] muted rule and lands later, so it would tint
     a disabled button (Save at rest; Rename/Delete when severed) the enabled
     secondary colour — looking enabled at xs. Re-assert the muted disabled
     colour here; the [disabled] attribute raises specificity to (0,2,2) so it
     wins without touching the enabled styling. */
  .workspace-toolbar-panel .actions-pane button[disabled] { color: var(--text-muted); }

  /* At xs, the artifact-title-pane's flex:1 does the spacing; the
     toolbar-spacer is unnecessary and visually empty. Replace it with
     the explicit toolbar-divider--actions-view between actions-pane
     and view-toggle-pane per the xs mockup. */
  .workspace-toolbar-panel .toolbar-spacer { display: none; }
  .workspace-toolbar-panel .toolbar-divider--actions-view { display: block; }

  /* XR-18 artifact-toolbar overflow — sizing per the XR-18 xs mockups, SCOPED
     to the toolbar so Browse's 32px title-row trigger and its 220px panel keep
     their own proportions. The shared overflow classes carry the band gating
     (hidden above 900px via the base rule) and the anchored-dropdown shape. */
  .workspace-toolbar-panel .overflow-trigger { flex-shrink: 0; }
  .workspace-toolbar-panel .overflow-pane { width: 36px; height: 36px; }
  .workspace-toolbar-panel .overflow-menu-panel { width: 200px; padding: 4px 0; }
  .workspace-toolbar-panel .overflow-menu-panel .menu-section { padding: 2px 0; }
  .workspace-toolbar-panel .overflow-menu-panel .menu-item {
    gap: 12px;
    padding: 0 14px;
    min-height: 44px;
    font-size: 15px;
  }
  .workspace-toolbar-panel .overflow-menu-panel .menu-item i { font-size: 18px; }
  .workspace-toolbar-panel .overflow-menu-panel .menu-item svg { width: 18px; height: 18px; }

  /* XR-18 — the band flip. The canonical rename / delete buttons STAY IN THE
     DOM (their ids, dialogs, htmx bindings, and coherence writes are what the
     menu items delegate to) but are hidden across the band, so Save is the
     only visible inline action and the ⋮ carries the rest. Exactly one home
     is visible per band; the Browse access-pane is the precedent. */
  .workspace-toolbar-panel #artifact-rename-toggle,
  .workspace-toolbar-panel #artifact-delete-toggle,
  .workspace-toolbar-panel #token-delete-toggle { display: none; }
}

/* === View-toggle (Layer 2 pass 2.5) ==========================================
   Segmented control in workspace-toolbar-panel. Three segments at lg
   (source / preview / split); two at xs/sm (source / preview — split
   hidden via .view-toggle-split @media). MUI toggle-button pattern:
   container outlined; segments sit flush sharing 1px dividers; pressed
   segment is depressed (darker bg + inset shadow). State is HTMX-driven
   per FEAT-017-architecture.mi v001-001 — server reads the mi_view
   cookie, sets aria-pressed on each segment, HTMX swaps studio_shell.
   No JS — aria-pressed is the only state signal. Per the canonical
   mockup mockups-panes-lg-artifact-split-inspector-closed.html.

   toolbar-spacer + toolbar-divider land here too; Layer 3.4 will use
   the spacer to separate the left-side toolbar group (artifact-title-
   pane + actions-pane) from the right-side group (view-toggle-pane +
   inspector-toggle-pane).
*/

.toolbar-spacer {
  flex: 1;
  min-width: 0;
}

.toolbar-divider {
  width: 1px;
  height: 20px;
  background: var(--border-default);
  flex-shrink: 0;
  margin: 0 4px;
}

/* === Source wrap control (FEAT-049) =========================================
   A single toggle button sitting to the right of the artifact actions behind
   its own divider. It borrows the view-toggle segment's dimensions, icon size,
   colour, hover, and pressed treatment so the two read as one family — but it
   is a lone button rather than a segmented group, so it carries no container
   border and no border at rest; only the pressed state fills. The glyph is an
   inline SVG drawn to Phosphor's proportions (currentColor, so it inherits the
   button's colour and flips with the theme exactly as the icon font does).
   Wide viewports only — the narrow toolbar has no room for it. Per the
   canonical lg mockup. */
.wrap-toggle-pane {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.wrap-toggle-pane button {
  appearance: none;
  width: 28px;
  height: 24px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 14px;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.wrap-toggle-pane button svg { width: 1em; height: 1em; display: block; }
.wrap-toggle-pane button:hover {
  color: var(--text-primary);
  background: var(--color-gray-100);
}
/* The standalone focus offset (+2px), not the inset one: this button sits loose
   in the toolbar rather than at a container's edge. Its neighbour the view-toggle
   rings the same way, which is what makes the two read as one family. */
.wrap-toggle-pane button:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.wrap-toggle-pane button[aria-pressed="true"] {
  background: var(--color-gray-200);
  color: var(--text-tertiary);
  box-shadow: inset 0 1px 2px rgba(33, 30, 24, 0.10);
}

.view-toggle-pane {
  display: inline-flex;
  align-items: center;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  padding: 0;
  gap: 0;
  flex-shrink: 0;
}
.view-toggle-pane button {
  appearance: none;
  width: 28px;
  height: 24px;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.view-toggle-pane button:first-child {
  border-top-left-radius: calc(var(--radius-md) - 1px);
  border-bottom-left-radius: calc(var(--radius-md) - 1px);
}
.view-toggle-pane button:last-child {
  border-top-right-radius: calc(var(--radius-md) - 1px);
  border-bottom-right-radius: calc(var(--radius-md) - 1px);
}
.view-toggle-pane button + button {
  border-left: 1px solid var(--border-default);
}
.view-toggle-pane button i { font-size: 14px; line-height: 1; }
.view-toggle-pane button:hover {
  color: var(--text-primary);
  background: var(--color-gray-100);
}
.view-toggle-pane button:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.view-toggle-pane button[aria-pressed="true"] {
  background: var(--color-gray-200);
  color: var(--text-tertiary);
  box-shadow: inset 0 1px 2px rgba(33, 30, 24, 0.10);
}

@media (max-width: 899px) {
  /* xs touch-target size for the view-toggle segments — 36x32 with a 16px icon,
     per the canonical FEAT-017 + FEAT-021 xs artifact mockups. The base rule is
     the 28x24 / 14px desktop size; without this override the toggle rendered too
     small at xs. */
  .view-toggle-pane button {
    width: 36px;
    height: 32px;
  }
  .view-toggle-pane button i {
    font-size: 16px;
  }
  /* Tighter toolbar dividers at xs (2px sides vs the 4px desktop base) — the
     spacing flanking the view-toggle / inspector, per the FEAT-017 xs artifact
     mockup. */
  .toolbar-divider {
    margin: 0 2px;
  }
  /* FEAT-049: the wrap control is wide-viewport only. The narrow toolbar is
     already full at 36px touch targets, and the overflow that gives it a home
     here is separate work — so the control and its divider both drop out
     rather than crowding the actions. */
  .wrap-toggle-pane,
  .toolbar-divider--wrap {
    display: none;
  }
  /* xs/sm: split is unavailable per IA — workspace too narrow to host
     two parallel panes at usable widths. CSS hides the segment; the
     second-child (preview) becomes the visually-last button and
     restores its rounded right corners. The shared border-left between
     button:nth-child(2) and the now-hidden split is moot — preview
     keeps its standard divider with source on its left.

     Selector scoped to .view-toggle-pane button.view-toggle-split for
     specificity (0,2,1) so it beats the .view-toggle-pane button base
     rule (0,1,1) which sets display: inline-flex. .view-toggle-split
     alone (0,1,0) would lose and the segment would stay visible. */
  .view-toggle-pane button.view-toggle-split {
    display: none;
  }
  .view-toggle-pane button:nth-child(2) {
    border-top-right-radius: calc(var(--radius-md) - 1px);
    border-bottom-right-radius: calc(var(--radius-md) - 1px);
  }
}

/* === Inspector chrome (Layer 2 pass 2.4a) ====================================
   Dual-shape per IA `inspector-region` + `inspector-drawer-panel`:
   - lg: inspector-region renders inline as a 240px right column when .is-open
     (in-plane, not overlay). Grid switches from 1fr → 1fr 240px via :has().
     inspector-toggle-pane is hidden from workspace-toolbar-panel while open
     (close affordance lives in inspector-toolbar-panel instead, per IA).
   - xs/sm: inspector-drawer-panel renders as a right-side slide-in drawer
     in overlay-region (mirror of account-menu drawer; same close-X-on-left
     pattern). aria-modal=true, focus trapped via studio.js openModal.
   inspector-meta-pane is dual-homed: directly in inspector-region at lg,
   inside inspector-drawer-panel's drawer-body at xs. Empty in this pass
   (Layer 3.7 fills both placements with the same content).
*/

.inspector-region {
  display: none;
}

.inspector-toolbar-panel {
  height: 40px;
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-default);
  display: flex;
  align-items: center;
  padding: 0 8px;
  flex-shrink: 0;
}

.inspector-close {
  width: 28px;
  height: 28px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  margin-left: auto;
  flex-shrink: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.inspector-close i { font-size: 16px; line-height: 1; }
.inspector-close:hover { background: var(--color-gray-100); color: var(--text-primary); }
.inspector-close:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.inspector-meta-pane {
  flex: 1;
  overflow-y: auto;
  background: var(--surface-card);
  padding: 16px;
  min-height: 0;
}

/* inspector-title — the artifact's friendly_name at the top of
   inspector-meta-pane. Smaller and tighter than the workspace
   artifact-title-pane; word-break:break-word handles long names
   that exceed the inspector's column width. Per canonical lg
   Artifact inspector-open mockup. */
.inspector-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 12px;
  word-break: break-word;
}

/* meta-list — 80px:1fr grid; <dt> uppercase muted labels,
   <dd> primary-tone values. word-break:break-word covers the long
   Path value. */
.meta-list {
  display: grid;
  grid-template-columns: 80px 1fr;
  gap: 6px 8px;
  font-size: 12px;
}
.meta-list dt {
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10px;
  font-weight: 700;
  align-self: center;
}
.meta-list dd {
  color: var(--text-primary);
  word-break: break-word;
}

/* xs/sm inspector-meta-pane — hosted inside inspector-drawer-panel
   so it sits in a wider container. Padding + inspector-title +
   meta-list scale up slightly per the canonical xs mockup. */
@media (max-width: 899px) {
  .inspector-meta-pane {
    padding: 20px 20px 16px;
  }
  .inspector-title {
    font-size: 15px;
    margin-bottom: 16px;
  }
  .meta-list {
    grid-template-columns: 88px 1fr;
    gap: 10px 12px;
  }
}

.inspector-drawer-panel {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 280px;
  background: var(--surface-card);
  border-left: 1px solid var(--border-default);
  box-shadow: -4px 0 12px rgba(26, 22, 19, 0.08);
  flex-direction: column;
  z-index: 10;
}
.inspector-drawer-panel.is-open {
  display: flex;
}
.inspector-drawer-panel .drawer-header {
  justify-content: flex-start;
}
.inspector-drawer-panel .drawer-body {
  background: var(--surface-card);
}

@media (min-width: 900px) {
  /* lg: inspector-region inline as a 240px right column when open. The
     `.studio--inspector-open` modifier is rendered server-side on the
     .studio div (from the mi_inspector cookie); HTMX swap of .studio
     carries the new class with the new shell, so JS doesn't manage the
     class anymore (2.4b architecture). */
  .studio.studio--inspector-open .inspector-region {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--surface-card);
    border-left: 1px solid var(--border-default);
    grid-row: 2;
    grid-column: 2;
    overflow: hidden;
  }
  /* Studio grid shifts when the inspector is open at lg. minmax(0, 1fr) on the
     workspace column for the same reason as the single-column case above — the
     header spans it, so a bare `1fr` lets long breadcrumbs overflow (FEAT-017-R5). */
  .studio-body--artifact .studio.studio--inspector-open {
    grid-template-columns: minmax(0, 1fr) 240px;
  }
  /* Inspector-toggle-pane is removed from workspace-toolbar-panel when
     the inspector is open at lg per IA — the close affordance lives in
     inspector-toolbar-panel instead. CSS-hide functionally equivalent
     to non-render. The toolbar-divider that precedes the toggle also
     hides since its only job is to separate the toggle from the
     view-toggle to its left; with the toggle gone, the divider is
     orphaned. (Only one toolbar-divider exists in workspace-toolbar-
     panel at this layer; Layer 3.4 may need a tighter selector if it
     introduces additional dividers elsewhere in the toolbar.) */
  .studio-body--artifact .studio.studio--inspector-open .inspector-toggle-pane,
  .studio-body--artifact .studio.studio--inspector-open .workspace-toolbar-panel .toolbar-divider {
    display: none;
  }
  /* Inspector-drawer-panel is xs/sm-only; unconditionally hidden at lg. */
  .inspector-drawer-panel,
  .inspector-drawer-panel.is-open {
    display: none;
  }
}

/* === Artifact layout — lg (≥900px) ========================================== */
@media (min-width: 900px) {
  .studio-body--artifact .studio {
    display: grid;
    /* minmax(0, 1fr), not 1fr: the header-region spans this column and holds the
       nowrap breadcrumbs-pane, whose min-content would otherwise expand a bare
       `1fr` track past the viewport — growing the header and pushing the account
       offscreen instead of letting the breadcrumbs-panel scroll. minmax(0, …)
       caps the track at the viewport so long paths scroll, matching Browse
       (FEAT-017-R5). */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 56px 1fr;
  }
  .studio-body--artifact .header-region {
    grid-column: 1 / -1; /* span all columns at lg — including the inspector col when open */
    padding: 0 16px;
  }
  .studio-body--artifact .location-region {
    display: none; /* breadcrumbs live in header-region at lg per IA artifact.note */
  }
  .studio-body--artifact .header-region .breadcrumbs-panel {
    display: flex; /* restore at lg — Artifact carries breadcrumbs inline per IA */
  }
  .studio-body--artifact .brand-pane {
    margin: 0; /* left-aligned alongside breadcrumbs at lg, not centered */
  }
  .studio-body--artifact .account-pane {
    margin-left: auto; /* pin right; empty breadcrumbs-panel doesn't yet expand via flex:1 */
  }
  .studio-body--artifact .workspace-region {
    grid-row: 2;
  }
  .studio-body--artifact .workspace-toolbar-panel {
    height: 40px;
    /* Right gutter 16px so the inspector button BOX aligns to the header account
       avatar + the Auto sync track (the shared chrome gutter). Left stays 8px — the
       title-pane's own 8px padding lands the title on the 16px line already. */
    padding: 0 16px 0 8px;
    gap: 8px;
  }
  .studio-body--artifact .inspector-toggle-pane {
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
  }
  .studio-body--artifact .inspector-toggle-pane i {
    font-size: 16px;
  }
}

/* === Nav-drawer overlay (Layer 2 pass 2.2) ===================================
   xs/sm only on Browse — nav-region is inline at lg so the drawer disappears.
   nav-drawer-panel slides in from the left at 280px wide; drawer-backdrop dims
   the area to the right of the drawer (left: 280px). The `hidden` attribute
   on both elements is the closed state; studio.js removes it on open. At lg,
   `display: none` overrides any open-state attribute change — the toggle is
   also hidden at lg so users cannot trigger it.

   Per FEAT-016 mockups-shell-xs-browse-nav-open.html. ARIA + focus-trap
   behaviour land in src/web/static/studio.js. Per IA nav-drawer-panel
   (overlay-region, aria-modal=true, role=dialog).
*/
/* Shared drawer-backdrop: full-viewport scrim. Each open drawer / menu
   sits at z-index 10 above this z-index 8 backdrop, with its opaque
   background covering its own strip. One backdrop element (in studio.html
   layout) serves nav-drawer (Browse xs/sm), account-menu drawer (xs/sm,
   both screens), and account-menu popover (lg, both screens — lighter
   wash per IA). At lg the backdrop stays available (not hidden by the
   media query below); only the nav-drawer is hidden at lg. */
.drawer-backdrop {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(26, 22, 19, 0.40);
  z-index: 8;
}
.drawer-backdrop.is-open {
  display: block;
}

.nav-drawer-panel {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 280px;
  background: var(--surface-card);
  border-right: 1px solid var(--border-default);
  box-shadow: var(--shadow-lg);
  flex-direction: column;
  z-index: 10;
}
.nav-drawer-panel.is-open {
  display: flex;
}

.drawer-header {
  height: 56px;
  background: var(--surface-card);
  border-bottom: 1px solid var(--border-default);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0 var(--space-1);
  flex-shrink: 0;
}

.drawer-close {
  width: 40px;
  height: 40px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.drawer-close i { font-size: 20px; line-height: 1; }
.drawer-close:hover { background: var(--color-gray-100); color: var(--text-primary); }
.drawer-close:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.drawer-body {
  flex: 1;
  overflow-y: auto;
  background: var(--surface-bg);
}

@media (min-width: 900px) {
  .nav-drawer-panel,
  .drawer-backdrop {
    display: none;
  }
  /* No backdrop at lg per IA overlay-region.note menu convention.
     Outside-click dismissal for the lg account-menu popover is handled
     by a document-level listener in studio.js (engaged when openModal
     is called without a backdrop element). Per canonical lg mockup
     mockups-panes-lg-browse-contents-account-menu-open.html. */
}

/* Hide the cmd-trigger whenever any overlay-region panel is open. Per
   IA overlay-region.note: "The command-bar trigger hides while a panel
   is open." Uses :has() to read the existing .is-open semantic on
   overlay panels — no new semantics introduced for the cmd-trigger
   itself (it is a persistent fixed trigger, not an overlay panel).
   The inspector-region at lg is IN-PLANE (not overlay) so its .is-open
   does not hide the cmd-trigger — scoped to overlay-region panels via
   id-/class-specific selectors. */
.studio-body:has(.nav-drawer-panel.is-open) .cmd-trigger,
.studio-body:has(.account-menu-panel.is-open) .cmd-trigger,
.studio-body:has(.inspector-drawer-panel.is-open) .cmd-trigger,
.studio-body:has(.add-menu-panel.is-open) .cmd-trigger,
.studio-body:has(.overflow-menu-panel.is-open) .cmd-trigger,
.studio-body:has(.access-popover-panel.is-open) .cmd-trigger,
.studio-body:has(.space-form-panel.is-open) .cmd-trigger,
.studio-body:has(.delete-confirm-panel.is-open) .cmd-trigger,
.studio-body:has(.reconcile-dialog.is-open) .cmd-trigger,
.studio-body:has(.command-bar-panel.is-open) .cmd-trigger {
  display: none;
}

/* === Account-menu overlay (Layer 2 pass 2.3) =================================
   Dual-shape per IA `account-menu-panel`:
   - xs/sm: right-side slide-in drawer (280px), aria-modal=true, role=dialog,
     focus trapped. Mirrors nav-drawer's chrome on the opposite edge.
   - lg: pop-under popover anchored beneath account-pane, role=menu, no focus
     trap, arrow keys cycle items. Lighter backdrop wash (set above).

   studio.js initAccountMenu flips role + aria-modal + aria-haspopup at
   open-time per breakpoint. drawer-header (with close-X) is hidden at lg
   — outside-click and Esc dismiss the popover; no close-X needed.

   Per FEAT-016 mockups-shell-xs-browse-account-menu-open.html for the xs
   shape and IA account-menu-panel.state for the lg popover positioning.
*/
.account-menu-panel {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 280px;
  background: var(--surface-card);
  border-left: 1px solid var(--border-default);
  box-shadow: -4px 0 12px rgba(26, 22, 19, 0.08);
  flex-direction: column;
  z-index: 10;
}
.account-menu-panel.is-open {
  display: flex;
}
.account-menu-panel .drawer-header {
  justify-content: flex-start;
}

.account-menu-pane {
  flex: 1;
  overflow-y: auto;
  background: var(--surface-card);
}

/* Profile preview block at the top of the menu. Per IA account-menu-pane:
   "profile preview / recent-activity item" candidate; per FEAT-017
   canonical mockups (lg + xs) the profile block is shipped with this
   pass. */
.profile-block {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border-default);
}
.profile-avatar {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  background: var(--color-gray-500);
  color: #ffffff;
  font-size: 13px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.profile-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.profile-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.profile-handle {
  font-size: 12px;
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-section { padding: 4px 0; }
.menu-section + .menu-section { border-top: 1px solid var(--border-default); }
/* FEAT-030: the Log out menu item is a form POST (/logout); make the form
   layout-transparent so its submit button is laid out exactly like a menu-item. */
.menu-logout-form { display: contents; }

.menu-item {
  appearance: none;
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 12px 20px;
  font-family: var(--font-primary);
  font-size: 14px;
  color: var(--text-primary);
  background: transparent;
  border: none;
  text-align: left;
  cursor: pointer;
  transition: background var(--transition-fast);
}
.menu-item:hover { background: var(--color-gray-100); }
.menu-item:active { background: var(--color-gray-100); }
.menu-item:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.menu-item i {
  font-size: 22px;
  color: var(--text-tertiary);
  flex-shrink: 0;
}
.menu-item .label { flex: 1; }
/* XR-18: menu items host inline SVG glyphs (the purpose-drawn wrap icon) sized
   and coloured as the font icons are, a trailing state check (.item-state,
   toggled via `hidden`), and a disabled treatment. The muting is a property of
   the item itself — it is NOT inherited from the inline actions container the
   item's canonical control lives in. Hover guarded off when disabled, per the
   .btn-destructive convention. */
.menu-item svg {
  width: 22px;
  height: 22px;
  color: var(--text-tertiary);
  flex-shrink: 0;
  display: block;
}
.menu-item .item-state { margin-left: auto; color: var(--text-primary); }
.menu-item[disabled] { color: var(--text-muted); cursor: not-allowed; }
.menu-item[disabled] i,
.menu-item[disabled] svg { color: var(--text-muted); }
.menu-item[disabled]:hover,
.menu-item[disabled]:active { background: transparent; }

@media (min-width: 900px) {
  /* lg popover per canonical mockup mockups-panes-lg-browse-contents-
     account-menu-open.html: position fixed, anchored under the account-
     pane trigger, no drawer chrome, no backdrop. */
  .account-menu-panel {
    position: fixed;
    top: 56px;
    right: 16px;
    bottom: auto;
    width: 260px;
    height: auto;
    background: var(--surface-card);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    z-index: 21;
  }
  /* No close-X at lg — outside-click / Esc dismiss the popover. */
  .account-menu-panel .drawer-header {
    display: none;
  }
  /* Slightly tighter padding at lg per the lg mockup. */
  .profile-block { padding: 12px 14px; }
  .menu-item { padding: 8px 14px; gap: 12px; }
  .menu-item i { font-size: 18px; }
}

/* === Add-menu overlay (pass 4.1) ============================================
   The `+` add-trigger in Browse's title-block actions-pane summons the
   add-menu-panel. Per IA add-menu-panel + the canonical lg + xs Browse
   add-menu-open mockups: the panel is a small anchored dropdown beneath
   the + button at all breakpoints (NOT a bottom-sheet at xs as the IA's
   xs chrome was originally marked TBD — the canonical xs mockup landed
   the same anchored-dropdown shape, just right-aligned with larger
   touch targets). The dropdown is logically in overlay-region per IA
   but physically rendered inline inside the actions-pane via the
   .add-trigger wrapper (position: relative) so the panel anchors to
   the + button without a fixed-positioning calc. No backdrop — menus
   are transient surfaces per overlay-region.note menu convention.
   Outside-click + Esc dismiss via studio.js openModal with
   cycle:'menu'. */
.add-trigger {
  position: relative;
  display: inline-flex;
}

.add-menu-panel {
  display: none;
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  width: 180px;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 4px 0;
  z-index: 21;
}
.add-menu-panel.is-open { display: block; }

/* xs/sm: panel right-aligns to the trigger's right edge so it stays
   within the viewport (the + button sits near the right edge of the
   title-block at narrow widths with limited room to the right); wider
   (200px) for the larger touch targets the existing .menu-item rules
   already produce at xs base. */
@media (max-width: 899px) {
  .add-menu-panel {
    right: 0;
    left: auto;
    width: 200px;
  }
}

/* === Modal chrome (Layer 4.3+) ================================================
   Shared modal-* classes used by access-popover-panel and (Layer 4.4 / 4.5)
   container-create-panel + container-delete-confirm-panel. modal-backdrop is
   the scrim that dims the studio at lg (rendered at xs but hidden — full-screen
   modals cover the viewport themselves). modal-header / modal-title /
   modal-subtitle / modal-close / modal-footer are the dual-breakpoint chrome
   primitives the access-popover lifts from the canonical lg + xs access-open
   mockups; create / delete panels reuse them.

   modal-backdrop closed state is display:none; .is-open toggles to block.
   Same .is-open semantics openModal already manages.
*/
.modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(26, 22, 19, 0.40);
  z-index: 20;
}
.modal-backdrop.is-open { display: block; }

.modal-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border-default);
  flex-shrink: 0;
}
.modal-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
}
.modal-subtitle {
  font-size: 13px;
  color: var(--text-tertiary);
}
.modal-close {
  appearance: none;
  margin-left: auto;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.modal-close i { font-size: 18px; line-height: 1; }
.modal-close:hover { background: var(--color-gray-100); color: var(--text-primary); }
.modal-close:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.modal-footer {
  padding: 12px 20px;
  border-top: 1px solid var(--border-default);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.modal-footer a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--color-primary-dark);
  text-decoration: none;
  transition: color var(--transition-fast);
}
.modal-footer a i { font-size: 14px; line-height: 1; }
.modal-footer a:hover { color: var(--color-primary); }
.modal-footer a:focus-visible { outline-color: var(--color-primary); }

/* === access-popover-panel (Layer 4.3) ========================================
   Dual-shape per IA `access-popover-panel`:
   - lg / xl-md: centered popover, ~380px wide, max-height calc(100vh - 120px),
     above modal-backdrop scrim. modal-close on the RIGHT of modal-header.
   - xs:        full-viewport modal (position:absolute; inset:0). NO backdrop —
     the panel covers the studio. modal-close on the LEFT of modal-header.

   Per canonical mockups mockups-panes-lg-browse-contents-access-open.html and
   mockups-panes-xs-browse-contents-access-open.html.
*/
.access-popover-panel {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 380px;
  max-height: calc(100vh - 120px);
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  flex-direction: column;
  overflow: hidden;
  z-index: 21;
}
.access-popover-panel.is-open { display: flex; }

.access-list-pane {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.access-row {
  /* button-default resets: rows are <button> so Tab + Enter reach them and
     focus-visible renders naturally. FEAT-007 swaps the data-href='/_stub'
     navigation for the real per-user-actions handler; the universal UI
     plumbing (focus, hover, keyboard) stays as-is. */
  appearance: none;
  border: none;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 10px 20px;
  transition: background var(--transition-fast);
}
.access-row:hover { background: var(--color-gray-50); }
.access-row:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}
.access-row .avatar {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  color: #ffffff;
}
.access-row .name-and-role {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.access-row .name {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.access-row .role {
  font-size: 12px;
  color: var(--text-tertiary);
}
.access-row .last-active {
  font-size: 12px;
  color: var(--text-tertiary);
  text-align: right;
  flex-shrink: 0;
}

/* xs: full-viewport modal. The panel covers the studio (position:absolute
   inset:0); modal-backdrop is suppressed (the panel itself is opaque).
   modal-header reflows: close-X on the LEFT (mobile-modal convention) ahead
   of the title; modal-subtitle hidden at xs (title alone reads cleaner on
   narrow screens). modal-close grows to the 44px touch target; access-row
   gains border separators + the active-tap feedback per canonical xs mockup. */
@media (max-width: 899px) {
  .modal-backdrop { display: none !important; }

  .access-popover-panel {
    position: absolute;
    inset: 0;
    transform: none;
    width: auto;
    max-height: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 20;
  }

  .modal-header {
    height: 56px;
    padding: 0 4px;
    gap: 8px;
  }
  .modal-subtitle { display: none; }
  .modal-title { font-size: 16px; font-weight: 700; }

  .modal-close {
    width: 44px;
    height: 44px;
    margin-left: 0;
    order: -1;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
  }
  .modal-close i { font-size: 22px; }

  .access-row {
    grid-template-columns: 36px minmax(0, 1fr) auto;
    padding: 12px 16px;
    min-height: 60px;
    border-bottom: 1px solid var(--border-subtle);
    transition: background var(--transition-fast);
  }
  .access-row:active { background: var(--color-gray-100); }
  .access-row .avatar { width: 36px; height: 36px; font-size: 12px; }

  .modal-footer {
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
    background: var(--surface-card);
  }
  .modal-footer a {
    font-size: 14px;
    min-height: 44px;
    padding: 0 4px;
  }
  .modal-footer a i { font-size: 18px; }
}

/* === container-create-panel (Layer 4.4) ======================================
   Dual-shape per IA `container-create-panel` + `space-form-pane`:
   - lg / xl-md: centered popover, 480px wide, modal-backdrop scrim. Header
     (title + close-X on RIGHT) + form body + footer (Cancel + Create on
     RIGHT). Close-X mirrors access-popover at lg.
   - xs:        full-viewport modal (position:absolute; inset:0), no backdrop.
     Header (close-X on LEFT + title) sticky at top; form body scrolls;
     footer pinned at bottom with safe-area inset padding.

   Hosts `space-form-pane` — `<form id="space-form-pane">` with three stacked
   .field elements: editable friendly_name input, transposed slug + code_key
   displays. Submit + Cancel live in the modal-footer outside the form;
   submit uses HTML5 form-association via the form attribute.

   Per canonical mockups mockups-panes-lg-browse-contents-container-create-
   open.html and mockups-panes-xs-browse-contents-container-create-open.html.
*/
.space-form-panel {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 480px;
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xl);
  flex-direction: column;
  z-index: 21;
}
.space-form-panel.is-open { display: flex; }

/* space-form-pane — three vertically stacked fields with comfortable spacing.
   Pad inside the panel; field gap from the IA-declared "friendly_name + slug
   + code_key" trio. */
.space-form-pane {
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 0;
}

/* autogen-note (FEAT-034-R3) — the create-token form's auto-generated-value
   note, an info-tinted callout above the Name field. Rendered only when the
   panel carries AutogenNote (the token-create-panel); no-op elsewhere. Matches
   the create-token mockup. */
.autogen-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 12px;
  background: var(--color-info-bg);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}
.autogen-note i {
  font-size: 18px;
  line-height: 1.3;
  color: var(--color-info);
  flex-shrink: 0;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* .field input rules are scoped here (not bleeding into FEAT-005 forms)
   because the workshop input shape is different from the public-pages login
   /signup style. */
.space-form-pane .field input,
.space-form-pane .field label,
.space-form-pane .field .field-label {
  font-family: var(--font-primary);
}
.space-form-pane .field label,
.space-form-pane .field .field-label {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-primary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 2px;
  display: block;
}
.space-form-pane .field input {
  width: 100%;
  padding: 10px 12px;
  font-size: 14px;
  color: var(--text-muted);
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: var(--radius-sm);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.space-form-pane .field input.has-value { color: var(--text-primary); }
.space-form-pane .field input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--input-focus-ring);
}

/* Transposed value — derived (read-only) identifier; muted background +
   monospace to signal "computed." Same vertical rhythm as input. */
.transposed-value {
  padding: 10px 12px;
  font-family: var(--font-mono);
  font-size: 14px;
  color: var(--text-primary);
  background: var(--surface-code);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  word-break: break-all;
}

/* ── Create form live states (FEAT-009, built to the create-form storyboard) ──
   The space-form-pane is shared (create/edit); these add the live create
   behaviour, reusing the badge/label/field-error/spinner verdict classes
   already defined for the signup live check. */

/* Derived slug/code_key shown as a worked example before any input is typed. */
.transposed-value.is-example { color: var(--text-muted); }

/* Red input border on a blocking outcome. The TAKEN verdict (uniqueness) and the
   INVALID state (format/charset/length rule failures, which return their own
   verdict-less availability-invalid fragment) both border red. Read from the
   feedback's data-state so it generalizes across the create panels (no hardcoded
   id). The unreachable verdict (data-state="error") is non-blocking, so the input
   keeps its default border. */
.space-form-pane .field:has(.availability[data-state="taken"]) input,
.space-form-pane .field:has(.availability[data-state="invalid"]) input {
  border-color: var(--color-error);
}
.space-form-pane .field:has(.availability[data-state="taken"]) input:focus,
.space-form-pane .field:has(.availability[data-state="invalid"]) input:focus {
  box-shadow: 0 0 0 3px var(--color-error-bg);
}

/* Create button — the three functional states from the storyboard. aria-disabled
   (not the disabled attribute) keeps it in the modal's Tab cycle: openModal
   computes the cycle once at open, and a real `disabled` would drop the button
   for the whole modal session (the original regression). */
.btn[aria-disabled="true"] { cursor: not-allowed; }
.btn-primary[aria-disabled="true"],
.btn-primary[aria-disabled="true"]:hover {
  background: var(--color-gray-200);
  border-color: var(--color-gray-200);
  color: var(--text-muted);
  box-shadow: none;
}
/* Submitting / in-flight — stays primary (it was ready), shows progress, inert. */
.btn-primary[aria-busy="true"],
.btn-primary[aria-busy="true"]:hover {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--on-bright);
  cursor: progress;
}
.btn-spinner {
  width: 13px;
  height: 13px;
  margin-right: 7px;
  border: 2px solid var(--color-primary-dark);
  border-top-color: var(--on-bright);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  flex-shrink: 0;
}

.field-help {
  font-size: 12px;
  color: var(--text-tertiary);
}

.helper-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.helper-row .field-help { flex: 1 1 auto; min-width: 0; }

/* Availability indicator on friendly_name's helper-row. Fixture state at 4.4
   is decorative ("Available" badge); FEAT-009 wires the live derivation +
   uniqueness check that drives state. aria-live="polite" on the wrapper
   announces verdict updates without interrupting typing. */
.availability {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  flex: 0 0 auto;
  min-height: 18px;
}
.availability .badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  font-size: 16px;
  flex-shrink: 0;
}
.availability .badge .ph { font-size: 16px; line-height: 1; }
.availability .badge.success { color: var(--color-success); }
.availability .label-success { color: var(--color-success); font-weight: 700; }

/* === Create-form availability divergences — scoped to .space-form-pane ========
   The create/edit identity form diverges from the base live-availability-check
   visuals (signup keeps the base, unscoped). Per the FEAT-009 create-form
   storyboard + the IA uses_patterns divergences:
     - fixed-footprint verdict slot so the row never reflows as the verdict /
       spinner appears, changes, or clears;
     - the prior verdict is HELD through a ~1s low-latency window, and the spinner
       reveals only if the check outruns it (a faster check swaps the verdict
       directly — no spinner flash, the verdict never blanks).
   The spinner-reveal and the verdict-hide share one delayed opacity toggle, so
   they flip together at the 1s mark and both revert instantly if the check
   resolves first. The spinner is absolutely positioned at the slot's right edge,
   occupying the verdict's position without taking flow space. */
.space-form-pane .helper-row { position: relative; }
/* No source-field helper. An input-validation field-error renders with NO verdict
   (availability-invalid leaves the slot empty + collapsed), so it flexes to the
   FULL width of the field. The unreachable fallback is a field-help (warning) that
   DOES sit beside the ⚠ verdict, covered by the base .helper-row .field-help rule. */
.space-form-pane .helper-row .field-error { flex: 1 1 auto; min-width: 0; }
/* No reserved footprint — the status row sizes to its content (the old constant 40px
   gap read as unexplained dead space). Two channels share the row but never at once:
   an availability VERDICT (available/taken/unreachable) holds the fixed-width right
   slot; an input-validation ERROR (invalid) collapses that slot and runs the message
   full width. Both are one line in the common case, so the derived fields hold; only
   a rare wrapping message grows the form downward. Scoped to .space-form-pane so
   signup is untouched. Matches the create-form storyboard. */
.space-form-pane .availability {
  flex: 0 0 112px;
  justify-content: flex-end;
  margin-left: auto;
}
/* Malformed name: the verdict slot is empty (data-state="invalid") and collapses,
   yielding the full width of the row to the message. */
.space-form-pane .availability[data-state="invalid"] { flex: 0 0 0; }
/* Pin the status-row content's line-box to the empty idle slot's height (18px =
   the .availability min-height). Without this, a verdict label or error message
   introduces a text line-box ~1px taller than the empty idle slot, growing the
   row by 1px — and because the panel is centered (translate -50%), that 1px
   re-centers the whole modal, nudging the title + Name field up. Pinning the
   feedback to the idle line keeps the modal height constant across idle <->
   verdict <-> one-line error. (Multi-line errors still grow, by design.) */
.space-form-pane .availability,
.space-form-pane .helper-row .field-error { line-height: 18px; }
.space-form-pane .availability-spinner {
  position: absolute;
  right: 0;
  top: 0;
  display: inline-flex;
  align-items: center;
  min-height: 18px;
  opacity: 0;
}
.space-form-pane .field:has(input.htmx-request) .availability-spinner {
  opacity: 1;
  transition: opacity 0s linear 1s;
}
.space-form-pane .field:has(input.htmx-request) .availability {
  opacity: 0;
  transition: opacity 0s linear 1s;
}

/* container-create-panel scoped modal-header — overrides the access-popover-
   shared shape: justify-content swaps from access-popover's "title — subtitle
   — close (margin-left:auto)" to create-panel's "title left — close right
   (space-between)". modal-close style itself is inherited from the shared
   chrome. */
.space-form-panel .modal-header {
  justify-content: space-between;
  padding: 14px 20px;
}
.space-form-panel .modal-header .modal-close { margin-left: 0; }

/* container-create-panel scoped modal-footer — overrides the access-popover-
   shared shape: justify-content swaps from access-popover's "Add people on
   left" to create-panel's "Cancel + Create on right". */
.space-form-panel .modal-footer {
  justify-content: flex-end;
  padding: 14px 20px;
}

/* xs reflow per IA: full-viewport modal; header 56px with close-X on LEFT;
   form scrolls; footer pinned with safe-area padding. Field input sized 16px
   to avoid iOS zoom-on-focus. Buttons grow to 44px touch targets. */
@media (max-width: 899px) {
  .space-form-panel {
    position: absolute;
    inset: 0;
    transform: none;
    width: auto;
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 20;
  }

  /* Re-use the same modal-header xs reflow as access-popover (close on LEFT
     via order:-1; title font-size grows; close-X 44px touch target). The
     shared modal-header rule already runs at xs; the container-specific
     override below corrects justify-content + padding for the form panel. */
  .space-form-panel .modal-header {
    justify-content: flex-start;
    padding: 0 4px;
  }

  .space-form-pane {
    flex: 1;
    overflow-y: auto;
    padding: 20px 16px;
    gap: 18px;
  }

  .space-form-pane .field input {
    padding: 12px;
    font-size: 16px;
    min-height: 44px;
    color: var(--text-primary);
    background: var(--surface-card);
    border-color: var(--border-default);
  }
  .space-form-pane .field input:focus {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
    border-color: var(--color-primary);
    box-shadow: none;
  }

  .transposed-value {
    padding: 12px;
    background: var(--color-gray-100);
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  .availability .label-success {
    color: var(--text-primary);
    font-weight: 600;
  }

  .space-form-panel .modal-footer {
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
    background: var(--surface-card);
  }

  /* xs button sizing — grow to 44px touch target with larger padding +
     font + radius. Scoped to container-create-panel's footer so the
     access-popover footer's anchor styling isn't affected. */
  .space-form-panel .modal-footer .btn {
    padding: 12px 20px;
    font-size: 15px;
    border-radius: var(--radius-md);
    min-height: 44px;
  }
}

/* === container-delete-confirm-panel (Layer 4.5) ==============================
   Per the IA's `overlay-region.note` → "Chrome shape — dialog vs alertdialog"
   rule (INFORMATION-ARCHITECTURE.mi v008-010): role="alertdialog" panels
   carry BESPOKE ALERT CHROME — single body pane carrying title + message +
   warning context, plus a footer with the decision pair. No modal-header
   strip; the title (<h2>) flows with the message body as one continuous
   thought. No internal dividers between body and footer; the footer's top
   padding is the visual gap. NO close-X affordance — Cancel (in the
   footer) is the dismissal affordance, per platform convention for alert
   dialogs (Material 3, iOS UIAlertController, macOS NSAlert, Apple HIG,
   Windows MessageBox). Esc and backdrop tap also dismiss; both treated as
   Cancel.

   The role attribute is the single structural signal — chrome shape is
   determined by the IA's role assignment, not by per-panel design judgement.
   Sibling alert-shaped panel: `artifact-delete-confirm-panel` (inherits the
   same chrome via the same role).

   Dual sizing:
   - lg / xl-md: centered 420px popover with surface-card bg + shadow-xl.
   - xs:        centered 90% (max 340px) with surface-card bg + border +
                shadow-lg + radius-lg. Modal-footer buttons grow to equal
                flex (side-by-side 50/50) per canonical xs mockup.

   Canonical mockups: mockups-panes-lg-browse-contents-container-delete-
   confirm-open.html, mockups-panes-xs-browse-contents-container-delete-
   confirm-open.html. Both mockups omit close-X — the mockup designer's
   instinct was correct and aligned with platform convention.
*/
.delete-confirm-panel {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 420px;
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-xl);
  flex-direction: column;
  z-index: 21;
}
.delete-confirm-panel.is-open { display: flex; }

.delete-confirm-pane {
  padding: 24px 24px 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.delete-confirm-pane h2 {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.4;
}
.delete-confirm-pane .description {
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.55;
}
.delete-confirm-pane .child-count {
  font-size: 13px;
  color: var(--text-primary);
  background: var(--color-warning-bg);
  border: 1px solid rgba(245, 158, 11, 0.3);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin-top: 8px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.delete-confirm-pane .child-count i {
  font-size: 18px;
  color: var(--color-warning);
  flex-shrink: 0;
}

.delete-confirm-panel .modal-footer {
  justify-content: flex-end;
  padding: 24px;
  /* Per the IA's bespoke alert chrome rule (overlay-region.note → Chrome
     shape — dialog vs alertdialog): "No internal dividers between body
     and footer; the footer's top padding is the visual gap." The shared
     .modal-footer carries border-top from access-popover's uniform-
     dialog chrome — scope-overridden here for alertdialog flavour. */
  border-top: none;
}

/* === .btn-destructive variant (Layer 4.5) ====================================
   New variant for destructive primary actions in confirm dialogs. Error-tone
   background; same shape as .btn-primary otherwise (44px min-height, content-
   width when scoped to modal-footer at lg — see the shared modal-footer-btn
   override below). */
/* btn-destructive — reconciled to the canonical STYLE-GUIDE v001-011 definition:
   ghost-family (transparent fill, red border + red text), NOT solid. A
   destructive action is not a primary CTA; the red border/text carry the signal.
   Consumed by the trash permanent-removal affordances and the (dormant) delete-
   confirm panel. */
.btn-destructive {
  background: transparent;
  color: var(--color-error);
  border: 1px solid var(--color-error);
}
/* Hover only when interactive — a disabled Empty-Trash (empty Trash) or an
   aria-disabled in-flight toggle must not light up on rollover. :hover still
   matches a disabled element in CSS, so guard it explicitly. */
.btn-destructive:not(:disabled):not([aria-disabled="true"]):hover {
  background: var(--color-error-bg);
  color: var(--color-error);
}
.btn-destructive:disabled {
  opacity: 0.4;
  cursor: default;
}
/* Held while a permanent delete is in flight (the title-block toggle) — the
   double-fire guard's belt-and-braces alongside the backdrop. aria-disabled (not
   the `disabled` attribute) keeps it semantic without dropping focus order. */
.btn-destructive[aria-disabled="true"] {
  opacity: 0.4;
}

/* btn-destructive-strong — the HIGH-EMPHASIS solid destructive (FEAT-026): the
   real irreversible gate (the permanent-delete / Empty-Trash confirm footer),
   distinct from the ghost .btn-destructive used for the inline trash-list
   affordances. Solid error fill + white text. Mirrored from the FEAT-026 confirm
   mockups (002-000). */
.btn-destructive-strong {
  background: var(--color-error);
  color: var(--on-error);
  border: 1px solid var(--color-error);
}
.btn-destructive-strong:hover {
  filter: brightness(0.92);
}
.btn-destructive-strong:disabled {
  opacity: 0.4;
  cursor: default;
}
/* Submitting / in-flight — stays solid (it was the ready gate), shows progress,
   inert. Mirrors .btn-primary[aria-busy]; the aria-disabled guard rides alongside
   without muting it (the destroy confirm has no not-ready state). */
.btn-destructive-strong[aria-busy="true"],
.btn-destructive-strong[aria-busy="true"]:hover {
  background: var(--color-error);
  border-color: var(--color-error);
  color: var(--on-error);
  filter: none;
  cursor: progress;
}
/* White spinner on the solid error fill — the shared .btn-spinner is tuned for the
   amber primary; re-tint it for the destructive footer. */
.btn-destructive-strong .btn-spinner {
  border-color: color-mix(in srgb, var(--on-error) 45%, transparent);
  border-top-color: var(--on-error);
}

/* The permanent-removal confirm's irreversibility line ("This can't be undone.")
   — quiet, beneath the count body. Mirrored from the FEAT-026 confirm mockups. */
.delete-confirm-pane .warning {
  font-size: 13px;
  color: var(--text-tertiary);
  margin-top: 2px;
}

/* btn-compact — the dense actions-row / toolbar scale (28px). Mirrored from
   STYLE-GUIDE .btn-compact; width:auto resets the runtime .btn's form-tailored
   width:100% so a compact button is content-width. */
.btn-compact {
  min-height: 28px;
  width: auto;
  padding: 0 0.75rem;
  border-radius: var(--radius-sm);
}

/* badge — canonical semantic status pill, mirrored from STYLE-GUIDE. Generic
   (the prior runtime `.badge` was only ever scoped to `.availability`). */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: var(--radius-full);
  font-size: var(--font-size-xs);
  font-weight: 600;
  letter-spacing: 0.01em;
}
.badge-info {
  background: var(--color-info-bg);
  color: var(--color-info-text);
}

/* === Modal-footer button sizing (Layer 4.5 — fixes Layer 4.4 defect) =========
   The base .btn (FEAT-005 public-pages) is `width: 100%; min-height: 44px;
   padding: 0.625rem 1rem; font-size: var(--font-size-sm)` — sized for full-
   width landing/signup/login forms. Modal-footer buttons want content-width
   at lg with smaller padding. Pass 4.4's create-panel inherited the full-
   width cascade because the @media (max-width: 899px) block was the only
   modal-footer-btn override; at lg the buttons rendered as 50/50 instead
   of right-aligned content-width. This shared rule (no @media) fixes both
   create-panel and delete-confirm-panel at lg.

   xs reflows: both panels grow buttons to 44px touch targets; delete-confirm
   additionally sets flex:1 for equal side-by-side (per its xs canonical
   mockup). create-panel keeps content-width at xs with justify-content:
   flex-end on modal-footer. */
.space-form-panel .modal-footer .btn,
.delete-confirm-panel .modal-footer .btn {
  width: auto;
  min-height: auto;
  padding: 8px 14px;
  font-size: 13px;
  border-radius: var(--radius-sm);
}

@media (max-width: 899px) {
  .space-form-panel .modal-footer .btn,
  .delete-confirm-panel .modal-footer .btn {
    padding: 12px 20px;
    font-size: 15px;
    border-radius: var(--radius-md);
    min-height: 44px;
  }
  /* delete-confirm-panel at xs: full-width equal-flex buttons side-by-side
     (per canonical xs mockup). create-panel at xs stays content-width right-
     aligned via modal-footer's justify-content: flex-end. */
  .delete-confirm-panel .modal-footer .btn {
    flex: 1;
  }
}

/* === container-delete-confirm-panel xs reflow ================================
   Per canonical xs mockup: position:absolute centered, 90% width capped at
   340px, surface-card bg with border + radius-lg + shadow-lg. The modal-
   backdrop's xs rule (display:none !important from 4.3 access-popover) is
   too aggressive — delete-confirm at xs DOES use the scrim. Override the
   suppression locally for this panel. */
@media (max-width: 899px) {
  /* Re-enable modal-backdrop at xs when the open panel is the delete-
     confirm (access-popover full-covers the viewport so it doesn't need
     a scrim; delete-confirm is a centered alert so it does). The :has
     selector keys the rule on which panel is open. */
  .studio-body:has(.delete-confirm-panel.is-open) .modal-backdrop.is-open {
    display: block !important;
  }

  .delete-confirm-panel {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 90%;
    max-width: 340px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
  }

  .delete-confirm-pane {
    padding: 20px 20px 16px;
    gap: 10px;
  }
  .delete-confirm-pane h2 {
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.01em;
  }
  .delete-confirm-pane .description { font-size: 14px; line-height: 1.5; }
  .delete-confirm-pane .child-count { margin-top: 4px; }

  .delete-confirm-panel .modal-footer {
    padding: 0 20px 16px;
    justify-content: stretch;
  }
}


/* === command-bar-panel (Layer 4.6) ==========================================
   Per IA overlay-region.note: the command-bar-panel is menu-shaped at its
   baseline state — no scrim, floats over the underlying studio (modal scrim
   only appears in chat mode, deferred to FEAT-013). Hosts two panes:
   cmd-results-pane (above) + cmd-bar-pane (below). Composite-widget listbox
   pattern: the input keeps DOM focus while arrow keys move
   aria-activedescendant to point at the active option; [aria-selected="true"]
   styles the active option. Tab moves OUT of the entire cmd-bar (one
   composite tab stop). Matches VS Code / Spotlight / browser-autocomplete
   convention.

   Dual sizing:
   - lg / xl-md: fixed-positioned, bottom:24px, centered, 640px wide.
   - xs:        absolute-positioned, bottom:16px, inset:0 left+right 16px
                (fills the bottom of the viewport with margins).

   Per canonical mockups mockups-panes-lg-artifact-split-inspector-open-
   cmd-nav-search.html and mockups-panes-xs-artifact-source-inspector-closed-
   cmd-nav-search.html.
*/
.command-bar-panel {
  display: none;
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  width: 640px;
  flex-direction: column;
  gap: 16px;
  z-index: 20;
}
.command-bar-panel.is-open { display: flex; }

.cmd-results-pane {
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 4px 12px rgba(0, 0, 0, 0.06);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.cmd-grip {
  height: 14px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: ns-resize;
  border-bottom: 1px solid var(--border-subtle);
}
.cmd-grip::before {
  content: '';
  width: 36px;
  height: 4px;
  background: var(--color-gray-300);
  border-radius: 2px;
}

.cmd-results-list {
  flex: 1;
  min-height: 0;
  max-height: 360px;
  overflow-y: auto;
  list-style: none;
  margin: 0;
  padding: 0;
}

.cmd-result {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 20px;
  font-size: 14px;
  color: var(--text-secondary);
  cursor: pointer;
  border-bottom: 1px solid var(--border-subtle);
  transition: background var(--transition-fast), color var(--transition-fast);
}
.cmd-result:last-child { border-bottom: none; }
.cmd-result:hover {
  background: var(--color-gray-100);
  color: var(--text-primary);
}
.cmd-result[aria-selected="true"] {
  background: var(--color-gray-200);
  color: var(--text-primary);
}
.cmd-result-kind {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  width: 64px;
  flex-shrink: 0;
}
.cmd-result-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cmd-results-hint {
  display: flex;
  gap: 16px;
  padding: 8px 20px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--surface-bg);
  border-top: 1px solid var(--border-default);
  flex-shrink: 0;
}

.cmd-bar-pane {
  height: 64px;
  background: var(--surface-card);
  border: 2px solid var(--color-gray-400);
  border-radius: var(--radius-md);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18), 0 4px 12px rgba(0, 0, 0, 0.06);
  display: flex;
  align-items: stretch;
  padding: 0 24px;
}
.cmd-bar-input {
  flex: 1;
  appearance: none;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-primary);
  font-size: 14px;
  color: var(--text-primary);
  caret-color: var(--text-primary);
  padding: 0;
}
.cmd-bar-input::placeholder { color: var(--text-muted); }

/* FEAT-017-R3: raise the studio's sub-16px focusable inputs to 16px on the mobile
   breakpoint so focusing them does not trip iOS auto-zoom — the viewport stays
   unpinned, so pinch-zoom remains available. Placed AFTER the base .cmd-bar-input
   (14px) and .space-form-pane .field input (14px) rules so it wins at equal
   specificity by source order. Desktop sizes are untouched. The public auth
   inputs (.input) already ship at 16px; the editor is handled in its own mobile
   block (.cm-scroller -> 16px). */
@media (max-width: 899px) {
  .cmd-bar-input,
  .space-form-pane .field input { font-size: 16px; }
}

/* xs reflow: absolute-positioned filling the bottom of the viewport with
   16px margins. cmd-results-pane gains a max-height calc derived from the
   viewport minus the header strips, location-region, gap, cmd-bar-pane,
   and bottom offset — keeps the results pane from overshooting the top
   of the screen. cmd-bar-pane shrinks (56px tall, 18px padding) and the
   cmd-result row's typography tightens for the narrower width. */
@media (max-width: 899px) {
  .command-bar-panel {
    position: absolute;
    bottom: 16px;
    left: 16px;
    right: 16px;
    transform: none;
    width: auto;
  }

  .cmd-results-pane {
    box-shadow: 0 20px 48px rgba(0, 0, 0, 0.18), 0 4px 12px rgba(0, 0, 0, 0.06);
    max-height: calc(100vh - 56px - 36px - 44px - 56px - 64px);
  }

  .cmd-result {
    gap: 10px;
    padding: 12px 16px;
  }
  .cmd-result-kind {
    font-size: 9.5px;
    width: 56px;
  }

  .cmd-results-hint {
    gap: 12px;
    padding: 8px 16px;
    font-size: 10px;
  }

  .cmd-bar-pane {
    height: 56px;
    box-shadow: 0 20px 48px rgba(0, 0, 0, 0.18), 0 4px 12px rgba(0, 0, 0, 0.06);
    padding: 0 18px;
  }
}

/* === panel shake — ctrl+space blocked feedback (Layer 4.6 refinement) =======
   When ctrl+space is pressed while a modal or drawer (any panel with a
   visible backdrop per IA backdrop convention) is open, the cmd-bar
   declines to steal focus. Instead the open panel shakes briefly to
   communicate "your shortcut was received, but I'm holding the floor."

   The shake is on the panel itself — attention goes to where the user
   is already looking AND points implicitly at the dismissal affordances
   inside the panel (close-X, Cancel). Spatially correct; in voice (the
   app draws attention to existing affordances rather than adding new
   chrome); unambiguous (the panel saying "no, dismiss me first").

   Macro pattern: macOS shakes the password field on a wrong login. Same
   shape of feedback for an unmet precondition.

   Implementation uses the modern CSS `translate` property (independent
   of `transform`) so the shake composes cleanly with panels that carry
   a base `transform: translate(-50%, -50%)` for centering (container-
   create-panel, container-delete-confirm-panel, access-popover-panel at
   lg). The two CSS properties apply in sequence — `transform` centers
   the panel; the `translate` property adds the horizontal wiggle.
*/
@keyframes panel-shake {
  0%, 100%                       { translate: 0 0; }
  10%, 30%, 50%, 70%, 90%        { translate: -4px 0; }
  20%, 40%, 60%, 80%             { translate: 4px 0; }
}
.is-shaking {
  animation: panel-shake 400ms ease-in-out;
}

@media (prefers-reduced-motion: reduce) {
  /* Honor reduced-motion preference — drop the animation entirely.
     Users who opt out of motion get no visual feedback for the blocked
     shortcut; the muscle-memory rule "Esc-first" carries the load. */
  .is-shaking { animation: none; }
}

/* === FEAT-050 beta signup ==================================================
   Component styles lifted from surface/mockups/FEAT-050-beta-signup (001-001).
   textarea variant of .input; the page-attached email-use-note; the style
   guide's banner anatomy scoped to the form; the confirmation block that
   renders in place of the form. */
textarea.input {
  resize: vertical;
  min-height: 88px;
  line-height: 1.5;
}
.form-note a {
  color: var(--color-primary-dark);
  text-decoration: none;
  font-weight: 500;
}
.form-note a:hover { text-decoration: underline; }
.form-note a:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.25);
  border-radius: var(--radius-sm);
}
.form-note {
  font-size: var(--font-size-xs);
  color: var(--text-tertiary);
  margin-bottom: var(--space-2);
}
.banner {
  padding: 0.75rem 1rem;
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.banner-error { background: var(--color-error-bg); }
.banner .dot-banner { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.banner-error .dot-banner { background: var(--color-error); }
.form-banner { margin-bottom: var(--space-2); }
.confirmation {
  font-size: var(--font-size-sm);
  color: var(--text-primary);
  text-align: center;
  line-height: 1.6;
  margin-top: var(--space-1);
}

/* FEAT-048: public-doc xs reflow (matches the xs mockups). */
@media (max-width: 600px) {
  .doc-header { padding: var(--space-4) var(--space-2) 0; }
  .public-doc { padding: 0 var(--space-2) var(--space-6); }
  .doc-title { font-size: var(--font-size-2xl); margin: var(--space-4) 0 var(--space-2); }
}
