/*
 * components.css — Shared UI component library for PGM Map Studio.
 *
 * Rule: no activity-specific ID prefixes (ov-, pt-, obj-).
 * Structural IDs live in editor.css. This file owns all reusable blocks.
 */

/* ══════════════════════════════════════════════════════════════════════════
   ICONS
   ══════════════════════════════════════════════════════════════════════════ */

/* Every lucide glyph is --icon-md unless the context it sits in says otherwise. It stands FIRST in this
   file on purpose: a context rule is `.thing svg`, the same specificity as this one, so only source order
   decides — and every one of them is either later in this file or in a stylesheet loaded after it.
   `svg.lucide` and not a bare `svg`: the server-rendered previews (a style swatch, a plateau section) are
   pictures, not glyphs, and size themselves. */
svg.lucide { width: var(--icon-md); height: var(--icon-md); }

/* ══════════════════════════════════════════════════════════════════════════
   SECTIONS & HEADERS
   ══════════════════════════════════════════════════════════════════════════ */

.panel-section { display: flex; flex-direction: column; gap: var(--space-3); }
.panel-stack { display: flex; flex-direction: column; gap: var(--space-6); }
.section-body { display: flex; flex-direction: column; gap: var(--space-3); }
.section-heading { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

.section-title {
  font-size: var(--font-xs);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
  user-select: none;
}

/*
 * Flex row wrapping a .section-title + optional right slot (button or badge).
 * Two context modifiers — use exactly ONE per instance:
 *   --ruled  : right-panel inspector sections (adds border-bottom)
 *   --list   : left-panel list-section headers (adds outer padding)
 */
.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  flex-shrink: 0;
}
.section-header--ruled { border-bottom: 1px solid var(--border); padding-bottom: var(--space-2); }
/* Compact add-button sizing when placed in any section header */
.section-header .action-btn { padding: 2px 10px; font-size: var(--font-base); }

/* Collapsible left-sidebar section — a <details> whose <summary> reads as a ruled section header, with a
   caret that rotates when open. (No JS: native disclosure; Blazor doesn't render/touch the `open` attr.) */
.panel-accordion > summary { cursor: pointer; list-style: none; }
.panel-accordion > summary::-webkit-details-marker { display: none; }
.panel-accordion > summary::after { content: "›"; color: var(--text-muted); transition: transform .15s ease; }
.panel-accordion[open] > summary::after { transform: rotate(90deg); }


/* ══════════════════════════════════════════════════════════════════════════
   FORM FIELDS
   ══════════════════════════════════════════════════════════════════════════ */

.field { display: flex; flex-direction: column; gap: var(--space-1); }
.field-row { display: flex; gap: var(--space-3); }
.field-row > .field { flex: 1; }

/* Icon-tile chooser — a row of clickable tiles replacing a select (footprint, symmetry mode). The
   thumbnails are SVG geometry in the canvas-add colours, so a tile reads as the shape it stands for. */
.choice-row { display: flex; gap: var(--space-2); }
.choice-tile {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-1);
  padding: var(--space-2) var(--space-1);
  background: var(--surface-2, var(--surface)); border: 1px solid var(--border);
  border-radius: var(--radius-sm); cursor: pointer; color: var(--text-muted); font-size: var(--font-xs);
}
.choice-tile:hover { border-color: var(--accent); color: var(--text); }
.choice-tile--active {
  border-color: var(--accent); color: var(--accent);
  background: color-mix(in oklch, var(--accent) 12%, transparent);
}
.choice-thumb { width: 100%; height: 30px; }
.choice-fill    { fill: var(--canvas-add-fill); stroke: var(--canvas-add-stroke); stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.choice-outline { fill: none; stroke: var(--canvas-add-stroke); stroke-width: 1.2; stroke-dasharray: 3 2; vector-effect: non-scaling-stroke; }
.choice-axis    { stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 2 2; vector-effect: non-scaling-stroke; }
.choice-dot     { fill: var(--text-muted); }
.choice-name { line-height: 1; white-space: nowrap; }
.field--compact { flex: 0 0 64px !important; }

.field-label { font-size: var(--font-sm); color: var(--text-secondary); user-select: none; }
.field-required { color: var(--color-error); }
/* A muted note after the label ("hides a player, so it is mirrored"). The margin is the separator rather
   than a space in the markup: Razor drops whitespace between an expression and the code block that follows
   it, so a hint written as bare text renders flush against its label. */
.field-hint { margin-left: var(--space-1); font-size: var(--font-xs); color: var(--text-muted); }
/* Split label row — label text left, a live value readout right (range sliders, counters). */
.field-label--split { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); }
.field-label--split strong { color: var(--text-bright); }
/* Full-width range slider tinted to the accent. */
.field-range { width: 100%; accent-color: var(--accent); }

.field-value {
  font-size: var(--font-md);
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.field-input {
  background: var(--bg-panel);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  font-size: var(--font-md);
  font-family: inherit;
  outline: none;
  width: 100%;
  box-sizing: border-box;
}
.field-input:focus     { border-color: var(--accent); }
.field-input--textarea { resize: vertical; min-height: 34px; line-height: 1.4; }
.field-input--error    { border-color: var(--color-error) !important; }
.field-error {
  margin-top: var(--space-1);
  color: var(--color-error);
  font-size: var(--font-sm);
  line-height: 1.4;
}

.field-pick-row { display: flex; align-items: center; gap: var(--space-2); }
.field-pick-row .field-input { flex: 1; }
.field-swatch {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  display: inline-block;
  border: 1px solid var(--border-swatch);
}

/* Author rows */
.author-list { display: flex; flex-direction: column; gap: var(--space-3); }
.author-row {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-1) var(--space-2);
}
.author-mark {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  user-select: none;
  color: var(--text-secondary);
  background: var(--bg-deep);
}
/* An account answered for this row: its head covers the initial, and the edge says so where it has none. */
.author-mark--account { box-shadow: 0 0 0 1px var(--border); }
.author-name { min-width: 0; }
.author-contribution { min-width: 0; grid-column: 2; }
.author-row .btn-remove { grid-column: 3; grid-row: 1 / span 2; }


/* ══════════════════════════════════════════════════════════════════════════
   LIST ROWS
   ══════════════════════════════════════════════════════════════════════════ */

.list-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 4px;
  min-height: 34px;
  cursor: pointer;
  user-select: none;
  border-radius: var(--radius-sm);
}
.list-row:hover     { background: var(--bg-selected-hover); }
.list-row--selected {
  background: var(--bg-selected);
  box-shadow: var(--shadow-ring);
}
.list-row--selected .list-label { color: var(--text-bright); }
.list-row--compact  { min-height: 28px; padding: 5px 4px; gap: 8px; }
.list-row--muted    { pointer-events: none; }

.list-swatch {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-swatch);
  display: inline-block;
}

.list-label {
  flex: 1;
  min-width: 0;
  font-size: var(--font-md);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.list-label--mono { font-family: ui-monospace, monospace; font-size: var(--font-base); }

.list-tag { font-size: var(--font-sm); color: var(--text-muted); flex-shrink: 0; }

.list-empty {
  padding: 10px 12px;
  font-size: var(--font-base);
  color: var(--text-muted);
  font-style: italic;
}

/* A row that carries its own inline action: the row takes the free width and the action sits beside
   it, so the control is never nested inside the row's link. */
.list-row-pair { display: flex; align-items: center; gap: var(--space-2); }
.list-row-pair > .list-row { flex: 1; min-width: 0; }

/* A list of action-carrying rows. One grid whose second column is sized to the widest action and
   shared by every row, so a row that has no action still lays its content out at the same width as one
   that does — the rows' labels are flex-sized, so an unreserved column ragged-edges the whole list.
   Each pair dissolves into the grid (display: contents) to put both of its parts in that grid. Every
   row must still emit its action cell, empty or not, or auto-placement takes the column. */
.panel-list--actions {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: var(--space-2);
}
.panel-list--actions > .list-row-pair { display: contents; }
.panel-list--actions > .list-empty { grid-column: 1 / -1; }

/* The map overview's layer cell — one link per authoring layer the map holds, right-aligned in the
   reserved column. An imported world has only Configure; a planned, sketched, configured map has all
   three, and each goes straight to that tool. --now marks the layer the map's stage currently names, so a
   row says where the map stands as well as where it can be opened. */
.map-row-action { display: flex; justify-content: flex-end; }

.map-artifacts { display: flex; justify-content: flex-end; gap: var(--space-1); flex-wrap: wrap; }

.map-layer {
  padding: 2px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--font-sm);
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color .12s, border-color .12s, background .12s;
}

.map-layer:hover { color: var(--text-bright); border-color: var(--text-muted); }

.map-layer--now {
  color: var(--text-bright);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* Container for a vertical stack of .list-row elements inside a panel */
.panel-list { padding: 4px 0; }

/* ══════════════════════════════════════════════════════════════════════════
   BUTTONS
   ══════════════════════════════════════════════════════════════════════════ */

.action-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: var(--bg-panel);
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px 14px;
  font-size: var(--font-base);
  font-family: inherit;
  cursor: pointer;
  flex-shrink: 0;
  text-decoration: none;
}
.action-btn:hover           { background: var(--border); color: var(--text-bright); }
.action-btn:disabled        { opacity: 0.35; cursor: default; }
/* Icon-only variant — equal padding on all sides, and a 1:1 box so it stays square wherever it lands. The
   padding alone only squared it in a row that sized to content: in a stretching row the height comes from
   the row and the width stayed padding + glyph, which drew it as a tall pill. With the ratio the width is
   read off whatever height the row settles on, so it tracks the row rather than a pinned number. */
.action-btn--icon           { padding: 4px; aspect-ratio: 1; justify-content: center; }
.action-btn--fill         { flex: 1; justify-content: center; }
.action-btn--push-end     { margin-left: auto; }
.action-btn--full         { width: 100%; justify-content: center; }
.action-btn--separated    { margin-top: var(--space-2); }

/* The meaning variants <Button Variant> emits. Each is the tinted-fill + coloured-edge treatment the rest
   of the studio already marks a state with (.filter-chip--active, .draw-tool-btn--active), in that
   meaning's own token — so a destructive action reads as destructive rather than as the Cancel beside it. */
.action-btn--primary        { background: var(--bg-selected); border-color: var(--accent); color: var(--accent-lighter); }
.action-btn--primary:hover  { background: var(--accent); border-color: var(--accent); color: var(--text-white); }
.action-btn--danger         { background: var(--color-error-bg); border-color: var(--color-error); color: var(--color-error); }
.action-btn--danger:hover   { background: var(--color-error); border-color: var(--color-error); color: var(--text-white); }
.action-btn--warn           { background: var(--color-warning-bg); border-color: var(--color-warning); color: var(--color-warning); }
.action-btn--warn:hover     { background: var(--color-warning); border-color: var(--color-warning); color: var(--text-on-status); }

/* ══════════════════════════════════════════════════════════════════════════
   SMART SUGGESTION — sparkle-headed "intelligent" callout (reusable; <SmartSuggestion>)
   ══════════════════════════════════════════════════════════════════════════ */

.smart-suggestion {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin: var(--space-2) var(--space-1) var(--space-3);
  padding: var(--space-3);
  border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--border));
  border-radius: var(--radius-md);
  background: linear-gradient(165deg, color-mix(in srgb, var(--accent) 11%, var(--bg-panel)), var(--bg-panel) 68%);
  box-shadow: var(--shadow-raised);
}
.smart-suggestion__head { display: flex; align-items: center; gap: var(--space-2); }
.smart-suggestion__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--accent-light);
  line-height: 0;
}
.smart-suggestion__icon svg { width: var(--icon-md); height: var(--icon-md); }
.smart-suggestion__title {
  flex: 1;
  min-width: 0;
  font-size: var(--font-md);
  font-weight: 600;
  color: var(--text-bright);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.smart-suggestion__text {
  margin: 0;
  font-size: var(--font-sm);
  line-height: 1.45;
  color: var(--text-secondary);
}
.smart-suggestion__foot { display: flex; gap: var(--space-2); justify-content: flex-end; }
.smart-suggestion__foot .action-btn { padding: 3px 12px; font-size: var(--font-base); }

/* suggested-team preview — always a 2-column grid (swatch + name only); 2 teams → one row, 4 → two */
.smart-suggestion-teams { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--space-3); row-gap: 2px; }
.smart-suggestion-team { display: flex; align-items: center; gap: var(--space-2); min-height: 22px; font-size: var(--font-md); color: var(--text-primary); }

.list-swatch--sm { width: 11px; height: 11px; }

.btn-remove {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 4px 6px;
}
.btn-remove:hover { color: var(--color-error); }
.btn-remove--hover-only { opacity: 0; transition: opacity .1s, color .1s; }
.list-row:hover .btn-remove--hover-only { opacity: 1; }


/* ══════════════════════════════════════════════════════════════════════════
   PANEL UTILITIES
   ══════════════════════════════════════════════════════════════════════════ */

.panel-empty { flex: 1; display: flex; align-items: center; justify-content: center; }
.panel-empty-msg { font-size: var(--font-base); color: var(--text-muted); text-align: center; padding: var(--space-4); }
.text-muted { color: var(--text-muted); }

.section-footer,
/* Wraps: an action row is as wide as the panel it sits in, and a third button (save · copy · delete) in a
   narrow one has to go somewhere other than off the right edge. */
.panel-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); padding-top: var(--space-1); }
.section-footer--end { justify-content: flex-end; }
.section-footer--separated { margin-top: var(--space-1); }
.panel-save-status { font-size: var(--font-base); color: var(--text-secondary); }
.panel-warning {
  padding: var(--space-3);
  color: var(--color-warning);
  background: var(--color-warning-bg);
  border: 1px solid var(--color-warning);
  border-radius: var(--radius-sm);
  font-size: var(--font-base);
  line-height: 1.5;
}

.control-list { display: flex; flex-direction: column; gap: var(--space-2); }
.check-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 24px;
  color: var(--text-primary);
  font-size: var(--font-md);
  cursor: pointer;
}
.check-row input { accent-color: var(--accent); }
.control-input--hidden { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   CANVAS SUBBAR
   ══════════════════════════════════════════════════════════════════════════ */

.canvas-subbar {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 36px;
  flex-shrink: 0;
  padding: 0 8px;
  background: var(--bg-panel);
  border-bottom: 1px solid var(--border);
}
.canvas-toolbar { display: flex; gap: 2px; margin-right: 8px; flex-shrink: 0; }
.canvas-cursor {
  flex: 1;
  text-align: center;
  font-size: var(--font-base);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  user-select: none;
  pointer-events: none;
}
.canvas-subbar-right { display: flex; align-items: center; gap: 8px; margin-left: auto; }

/* ══════════════════════════════════════════════════════════════════════════
   CANVAS CHROME — the controls that float ON the viewport

   One bar above a canvas has to hold everything at once: what the pointer does, what is drawn, where the
   camera is, and how big the thing under it is. Across a dozen icons that row overlaps itself on a narrow
   screen, and it puts the drawing tools at the far edge of the surface they draw on. So the bar is gone and
   its contents are placed by what they are:

     top-left     what the canvas IS       — cursor position, live size, zoom. Read-only, pointer-transparent.
     top-right    what the canvas SHOWS    — layer chips, then fit/rotate, then the 2-D/3-D toggle in the corner.
     bottom-centre what the pointer DOES   — the dock: grouped tools, near the hand that uses them.

   Every surface here shares one look (.canvas-float): panel-coloured glass, a hairline border and a lift
   shadow, so a control reads as a control over terrain of any colour without hiding the terrain. The
   viewport is themed (dark by default, light under [data-theme=light]) and every colour below is a token,
   so the chrome follows it.
   ══════════════════════════════════════════════════════════════════════════ */

/* The shared floating surface. Composed into the chip / round button / dock group rules below rather than
   applied as a second class in markup, so a component's markup names one thing. */
.canvas-chip,
.canvas-round-btn,
.canvas-mode-toggle,
.canvas-select,
.canvas-dock-group,
.canvas-dock-flyout {
  background: var(--canvas-float-bg);
  backdrop-filter: blur(6px);
  border: 1px solid var(--canvas-float-border);
  box-shadow: var(--canvas-float-shadow);
}

/* ── top-left: the readout ── */
.canvas-readout {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 6px;
  pointer-events: none;   /* informational — a drag that crosses it must not stall */
  user-select: none;
}
.canvas-readout-item {
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  background: color-mix(in oklch, var(--bg-canvas) 55%, transparent);
  border: 1px solid var(--canvas-float-border);
  font-size: var(--font-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* An item with nothing to report takes no room — the cursor before the pointer enters, the size between
   measurements. The cluster is then only as wide as it has something to say. */
.canvas-readout-item:empty { display: none; }
/* The live size — what is being drawn or measured right now. Accented, because it is the one readout that
   answers a question the author is actively asking. */
.canvas-readout-item--size { color: var(--accent-light); font-weight: 600; }

/* ── top-right: the layer bar ── */
.canvas-layer-bar {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
  max-width: calc(100% - 20px);
}
.canvas-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  font-size: var(--font-sm);
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast);
}
.canvas-chip svg { width: 13px; height: 13px; flex: none; }
.canvas-chip:hover:not(:disabled) { color: var(--text-bright); border-color: var(--text-muted); }
.canvas-chip--on {
  color: var(--accent-lighter);
  border-color: var(--accent);
  background: color-mix(in oklch, var(--accent) 26%, var(--canvas-float-bg));
}
.canvas-chip:disabled { opacity: 0.4; cursor: default; }

/* A choice among many in the layer bar — which island to fit. A chip states one thing and toggles it; a
   list of twenty islands is not a row of chips, so it stays a native <select> and only takes the floating
   surface. Sized to sit at chip height rather than at .field-input's panel height. */
.canvas-select {
  flex: none;
  max-width: 140px;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: var(--font-sm);
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  outline: none;
}
.canvas-select:hover { color: var(--text-bright); border-color: var(--text-muted); }
.canvas-select:focus { border-color: var(--accent); }
/* The options drop into the browser's own menu, which has no glass behind it — give them the panel. */
.canvas-select option { background: var(--bg-panel); color: var(--text-primary); }

/* A plain word in the layer bar — why a control beside it can't do its job ("no WebGL"). Not a chip: there
   is nothing to press. */
.canvas-note {
  flex: none;
  padding: 0 4px;
  font-size: var(--font-sm);
  color: var(--text-muted);
  white-space: nowrap;
  user-select: none;
}

/* A shape the board's own algebra discarded. The preview cannot show it — that is the point — so the note
   is the only thing that tells an absence apart from ground nobody drew. */
.canvas-note--warn { color: var(--color-warning); cursor: help; }

/* The 3-D preview while its world is being built. The view swaps at once and fills in when the columns land,
   so this covers an empty scene for about a second — long enough that its absence would read as a dead click,
   short enough that a progress bar would be worse than a word. */
.canvas-iso-busy {
  position: absolute;
  inset: 0;
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: center;
  font-size: var(--font-sm);
  color: var(--text-muted);
  pointer-events: none;
  user-select: none;
}

.canvas-iso-busy::before {
  content: "";
  width: 14px;
  height: 14px;
  border: 2px solid var(--text-muted);
  border-top-color: transparent;
  border-radius: 50%;
  animation: canvas-iso-spin 0.7s linear infinite;
}

@keyframes canvas-iso-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .canvas-iso-busy::before { animation: none; }
}

.canvas-round-btn {
  width: 30px;
  height: 30px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}
.canvas-round-btn svg { width: 15px; height: 15px; }
.canvas-round-btn:hover:not(:disabled) { color: var(--text-bright); border-color: var(--text-muted); }
.canvas-round-btn:disabled { opacity: 0.4; cursor: default; }

/* The projection toggle — the corner control. Wider than the round buttons because it carries a word, and
   coloured by the mode it is in: accent in 2-D, the symmetry violet in 3-D, which is the colour the
   isometric preview itself is drawn against. */
.canvas-mode-toggle {
  flex: none;
  padding: 6px 13px;
  border-radius: var(--radius-pill);
  font-size: var(--font-sm);
  font-weight: 700;
  letter-spacing: 0.03em;
  cursor: pointer;
  color: var(--accent-light);
  border-color: color-mix(in oklch, var(--accent) 55%, transparent);
  transition: color var(--transition-fast), border-color var(--transition-fast);
}
.canvas-mode-toggle--3d {
  color: var(--canvas-axis);
  border-color: color-mix(in oklch, var(--canvas-axis) 55%, transparent);
}
.canvas-mode-toggle:disabled { opacity: 0.4; cursor: default; }

/* ── bottom-centre: the dock ── */
.canvas-dock {
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  /* wrap-reverse so a viewport too narrow for every group stacks them UPWARD and the first group — always
     select/move — stays on the bottom row under the pointer. */
  flex-wrap: wrap-reverse;
  justify-content: center;
  align-items: flex-end;
  gap: 8px;
  max-width: calc(100% - 20px);
}
.canvas-dock-group {
  position: relative;   /* anchors a group's flyout */
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  border-radius: 10px;
}
/* The controls fade, the box does not: the group still reads as present and stays clickable, because
   setting a mode before reaching for the tool it applies to is a reasonable order to work in. */
.canvas-dock-group--idle > * { opacity: 0.5; }
.canvas-dock-btn {
  width: 30px;
  height: 30px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: transparent;
  /* --dock-accent is the armed mode's colour, set on the group. A group without one stays neutral. */
  color: var(--dock-accent, var(--text-muted));
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
}
.canvas-dock-btn svg { width: 15px; height: 15px; }
.canvas-dock-btn:hover:not(:disabled) { background: color-mix(in oklch, var(--text-muted) 22%, transparent); }
.canvas-dock-btn:disabled { opacity: 0.35; cursor: default; }
.canvas-dock-btn--active {
  border-color: var(--dock-accent, var(--accent));
  background: color-mix(in oklch, var(--dock-accent, var(--accent)) 22%, transparent);
  color: var(--dock-accent, var(--accent-lighter));
}

/* A tool told apart by the colour it draws in rather than by an outline. --swatch-color carries the fill. */
.canvas-dock-swatch {
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 3px;
  background: var(--swatch-color, var(--text-muted));
}

/* Swatches for the tools whose canvas fill is a pattern, not a flat colour — so the dock reads like the
   drawing. Build = the dashed, translucent accent zone; water lane = the same rect in its deeper blue; buffer = its single-diagonal reserved-gap hatch;
   a box is unfilled with a dashed border in its kind's colour. The hatch is inline SVG rather than a
   repeating gradient: a gradient's stripe phase is anchored to a box corner, so its lines cannot be centred
   in the swatch. Its colour is the fixed role hex (buffer orange), matching the RolePalette. */
.canvas-dock-swatch--build {
  background: color-mix(in oklch, var(--accent) 22%, transparent);
  border: 1px dashed var(--accent);
}
/* A water lane is the build zone's closed twin — the same dashed rect over the void, drawn denser and in the
   deeper blue the canvas paints it, because the difference between them is when they open, not what they are. */
.canvas-dock-swatch--water-lane {
  background: color-mix(in oklch, #2563eb 40%, transparent);
  border: 1px dashed #2563eb;
}
.canvas-dock-swatch--buffer {
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'><rect x='.7' y='.7' width='12.6' height='12.6' rx='2' fill='%23f2792b' fill-opacity='.18'/><g stroke='%23f2792b' stroke-width='1.2' stroke-linecap='round'><line x1='1' y1='9.5' x2='9.5' y2='1'/><line x1='4.5' y1='13' x2='13' y2='4.5'/></g><rect x='.7' y='.7' width='12.6' height='12.6' rx='2' fill='none' stroke='%23f2792b' stroke-dasharray='2 1.4'/></svg>") center / 100% 100% no-repeat;
}
.canvas-dock-swatch--box {
  background: color-mix(in oklch, var(--swatch-color, var(--text-muted)) 12%, transparent);
  border: 1.5px dashed var(--swatch-color, var(--text-muted));
}

/* A dock control names itself ABOVE itself.
   The browser's own tooltip renders below the pointer, and every control in here is a few pixels off the
   bottom of the canvas — so the native one lands on the viewport edge or outside it entirely. These carry
   the name as data-tip (with aria-label for the accessible name a glyph-only button would otherwise lack)
   and CSS draws it upward. It appears on a delay, so crossing the dock on the way somewhere does not trail
   a caption. */
.canvas-dock [data-tip] { position: relative; }
.canvas-dock [data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 9px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 1;
  width: max-content;
  max-width: 210px;
  padding: 4px 9px;
  border-radius: var(--radius-md);
  background: var(--canvas-float-bg);
  backdrop-filter: blur(6px);
  border: 1px solid var(--canvas-float-border);
  box-shadow: var(--canvas-float-shadow);
  color: var(--text-primary);
  font-size: var(--font-sm);
  font-weight: 500;
  line-height: 1.4;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-fast);
}
.canvas-dock [data-tip]:hover::after,
.canvas-dock [data-tip]:focus-visible::after { opacity: 1; transition-delay: 0.35s; }

/* ── a collapsed group: the option in hand, a chevron, and a flyout above ── */
.canvas-dock-collapsed { display: flex; align-items: center; gap: 1px; }
/* Narrow and shorter than the option beside it — it is the handle on that button, not a tool of its own.
   It is also the flyout's anchor: the menu hangs from the box that opened it. */
.canvas-dock-chevron-anchor { position: relative; display: flex; align-items: center; }
.canvas-dock-chevron {
  width: 13px;
  height: 26px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast);
}
.canvas-dock-chevron svg { width: 11px; height: 11px; }
.canvas-dock-chevron:hover { color: var(--text-bright); background: color-mix(in oklch, var(--text-muted) 22%, transparent); }

/* Opens upward (the dock is at the bottom of the canvas, so a downward menu would open off it) and from the
   chevron's left edge — the chevron is what opened it, so that is where it comes from. */
.canvas-dock-flyout {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 1px;
  min-width: max-content;
  padding: 4px;
  border-radius: 10px;
  animation: dock-flyout-rise var(--transition-fast);
}
@keyframes dock-flyout-rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* One option: a tick for the one that is armed, its glyph or swatch, its name. Three columns and not two,
   so the names line up whatever is or is not ticked beside them. */
.canvas-dock-flyout-row {
  display: grid;
  grid-template-columns: 14px 16px 1fr;
  align-items: center;
  gap: 8px;
  padding: 5px 9px 5px 6px;
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: var(--font-sm);
  font-weight: 500;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast);
}
.canvas-dock-flyout-row:hover { color: var(--text-bright); background: color-mix(in oklch, var(--text-muted) 20%, transparent); }
.canvas-dock-flyout-row[aria-current="true"] { color: var(--accent-lighter); }
.canvas-dock-flyout-check,
.canvas-dock-flyout-mark { display: flex; align-items: center; justify-content: center; }
.canvas-dock-flyout-check svg { width: 12px; height: 12px; }
.canvas-dock-flyout-mark svg { width: 15px; height: 15px; }
/* Catches the click that dismisses an open flyout, so it cannot instead start a drag on the map the author
   could not see past the menu. Below the chrome (z-index 3), above the canvas. */
.canvas-dock-scrim { position: absolute; inset: 0; z-index: 2; }

/* The mode word — Build/Carve, Bridge/Hole. Wears the group's accent as text, fill and border at once. */
.canvas-dock-mode {
  flex: none;
  height: 30px;
  padding: 0 11px;
  border-radius: var(--radius-md);
  font-size: var(--font-sm);
  font-weight: 700;
  cursor: pointer;
  color: var(--dock-accent, var(--text-muted));
  background: color-mix(in oklch, var(--dock-accent, var(--text-muted)) 15%, transparent);
  border: 1px solid color-mix(in oklch, var(--dock-accent, var(--text-muted)) 50%, transparent);
  transition: background var(--transition-fast);
}
.canvas-dock-mode:hover { background: color-mix(in oklch, var(--dock-accent, var(--text-muted)) 28%, transparent); }

/* One option of a set the dock picks between. It shares the mode pill's shape — a word among glyphs — and
   differs in what it has to say: which of several holds, so the one that does takes the fill and the rest sit
   quiet beside it. */
.canvas-dock-choice {
  flex: none;
  height: 30px;
  padding: 0 11px;
  border-radius: var(--radius-md);
  font-size: var(--font-sm);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid transparent;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.canvas-dock-choice:hover { color: var(--text-bright); background: color-mix(in oklch, var(--text-muted) 16%, transparent); }
.canvas-dock-choice--on {
  color: var(--text-strong);
  font-weight: 700;
  background: var(--bg-selected);
  border-color: var(--accent);
}

/* Layers panel (S7b) — active row highlight + inline delete. */
.list-row--active { background: var(--accent-light); border-left: 2px solid var(--accent); }
.list-row-btn { margin-left: auto; display: inline-flex; align-items: center; background: none; border: none;
  color: var(--text-muted); cursor: pointer; padding: 2px; border-radius: 3px; }
.list-row-btn:hover { color: var(--error, var(--text)); background: var(--surface-2, transparent); }

.canvas-surface {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  position: relative;
}
.canvas-surface--svg > svg,
svg.canvas-surface { display: block; width: 100%; height: 100%; }

/* ══════════════════════════════════════════════════════════════════════════
   TOAST (Type 2 notification — Operation feedback)
   ══════════════════════════════════════════════════════════════════════════ */

.toast {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 9000;
  padding: 9px 16px;
  border-radius: var(--radius-md);
  font-size: var(--font-md);
  font-family: ui-monospace, monospace;
  pointer-events: none;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  border: 1px solid transparent;
}
.toast.visible {
  opacity: 1;
  transform: translateY(0);
}
.toast--success {
  background: var(--color-success-bg);
  border-color: var(--color-success);
  color: var(--color-success);
}
.toast--error {
  background: var(--color-error-bg);
  border-color: var(--color-error);
  color: var(--color-error);
}
.toast--info {
  background: var(--bg-panel);
  border-color: var(--border);
  color: var(--text-primary);
}


/* ══════════════════════════════════════════════════════════════════════════
   BADGE
   Single chip system for status, validation, and type labels.
   Variants are named by meaning — pick the one that fits semantically.
   Works both inline (in text) and standalone (in list rows, panel headers).
   ══════════════════════════════════════════════════════════════════════════ */

.badge {
  display: inline-block;
  font-size: var(--font-xs);
  font-weight: 600;
  padding: 2px 7px;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.badge--success { color: var(--color-success);  background: var(--color-success-bg); }
.badge--warning { color: var(--color-warning);  background: var(--color-warning-bg); }
.badge--error   { color: var(--color-error);    background: var(--color-error-bg); }
.badge--neutral { color: var(--text-secondary); background: var(--bg-panel); border: 1px solid var(--border); }
.badge--dim     { color: var(--text-muted);       background: var(--bg-base); }


/* ══════════════════════════════════════════════════════════════════════════
   METER — thin confidence / score bar (track + fill).
   Width is set inline; the colour band is a modifier on the fill
   (default = high/success, --mid = warning, --low = muted).
   Used for ranked monument suggestions (confidence) and similar scores.
   ══════════════════════════════════════════════════════════════════════════ */

.meter {
  position: relative;
  height: 5px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
}
.meter-fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 999px;
  background: var(--color-success);
}
.meter-fill--mid { background: var(--color-warning); }
.meter-fill--low { background: var(--text-muted); }


/* ══════════════════════════════════════════════════════════════════════════
   FILTER CHIP  (toggleable filter option inside a filter panel)
   ══════════════════════════════════════════════════════════════════════════ */

.filter-group-options { display: flex; flex-wrap: wrap; gap: var(--space-1); }
/* Expanded chip row — chips stretch to fill available width */

/* Generic horizontal control row — label + inputs/buttons/chips side-by-side */
.ctrl-row { display: flex; align-items: stretch; gap: var(--space-2); }
.ctrl-row .filter-group-options,
.ctrl-row .coord-field { flex: 1; }

/* Stack of labeled chip rows */

.filter-chip {
  display: inline-flex; align-items: center;
  font-size: var(--font-sm); font-family: inherit;
  padding: 2px 8px; border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-base); color: var(--text-secondary);
  cursor: pointer;
}
.filter-chip:hover:not(:disabled) { border-color: var(--text-muted); color: var(--text-primary); }
.filter-chip--active   { background: var(--bg-selected); border-color: var(--accent); color: var(--accent-lighter); }
.filter-chip:disabled  { opacity: 0.35; cursor: default; }
/* How many of the scanned boards carried this form — the chip saying what the request produces. */
.filter-chip-n {
  margin-left: 5px; font-family: ui-monospace, monospace; font-size: var(--font-xs);
  font-variant-numeric: tabular-nums; color: var(--text-muted);
}
/* Never produced by the current request, on a sample big enough to mean it. Dimmed rather than disabled:
   the count is the evidence, and a rare form should stay pickable if the author wants to hunt for it. */
.filter-chip--unseen { opacity: 0.45; border-style: dashed; }
.filter-chip--unseen .filter-chip-n { color: var(--text-muted); }


/* ══════════════════════════════════════════════════════════════════════════
   FLOW BAR + FLOW STEPS  (clean wizard step strip — the authoring nav model's
   in-phase progress. The `.flow-bar` is a standalone header strip ABOVE the
   workspace — it is NOT part of the canvas sub-bar, which keeps its own draw
   toolbar. Holds the step labels on the left and Back/Next on the right.
   Deliberately flat: text labels, an accent underline on the active step, a check
   on completed ones. Replaces the numbered circles + dashes of .cfg-step-bar.)
   ══════════════════════════════════════════════════════════════════════════ */
.flow-bar {
  display: flex; align-items: center;
  gap: var(--space-4);
  height: 42px; padding: 0 var(--space-4);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-2);
}
/* Phase identity — the current phase's icon + name, left-aligned ahead of the steps,
   so the flow bar always says which of the 6 rail phases you're in. */
.flow-bar-phase {
  display: flex; align-items: center; gap: var(--space-2);
  height: 60%;
  color: var(--text-strong); font-weight: 600; font-size: var(--font-md);
  white-space: nowrap;
}
/* Divider only when steps follow — a single-step phase (Map Info) shows the name alone. */
.flow-bar-phase:has(+ .flow-steps) { padding-right: var(--space-4); border-right: 1px solid var(--border); }
.flow-bar-phase svg { width: var(--icon-md); height: var(--icon-md); color: var(--accent); }
.flow-bar-actions { display: flex; align-items: center; gap: var(--space-2); margin-left: auto; }
.flow-steps { display: flex; align-items: center; gap: var(--space-5); height: 100%; }
.flow-step {
  position: relative;
  display: flex; align-items: center; gap: 5px;
  height: 100%; padding: 0;
  background: none; border: none;
  font-family: inherit; font-size: var(--font-base);
  color: var(--text-muted); cursor: pointer; white-space: nowrap;
}
.flow-step:hover         { color: var(--text-bright); }
.flow-step:disabled      { opacity: 0.35; cursor: default; }
.flow-step:disabled:hover { color: var(--text-muted); }
.flow-step svg           { width: var(--icon-xs); height: var(--icon-xs); }
.flow-step--done         { color: var(--text-secondary); }
.flow-step--done svg     { color: var(--color-success); }
.flow-step--active       { color: var(--text-strong); }
.flow-step--active::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--accent); border-radius: 2px 2px 0 0;
}


/* ══════════════════════════════════════════════════════════════════════════
   STEP ROW  (pipeline status lists)
   ══════════════════════════════════════════════════════════════════════════ */

.step-row { display: flex; align-items: center; gap: 10px; font-size: var(--font-md); }
.step-dot {
  width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
  background: var(--border);
}
.step-dot--done    { background: var(--color-success); }
.step-dot--error   { background: var(--color-error); }
@keyframes step-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.45; } }
.step-label  { color: var(--text-secondary); min-width: 70px; }
.step-detail { color: var(--text-muted); font-size: var(--font-sm); flex: 1; }


/* ══════════════════════════════════════════════════════════════════════════
   CONSOLE  (pipeline log output — shared across dashboard, configure, etc.)
   ══════════════════════════════════════════════════════════════════════════ */

.console-wrap { display: flex; flex-direction: column; gap: var(--space-2); }
.console-header { display: flex; align-items: center; gap: var(--space-2); }
.console-title {
  font-size: var(--font-xs); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-muted);
}
.console-clear-btn {
  background: none; border: none; color: var(--text-muted); font-size: var(--font-sm);
  cursor: pointer; font-family: inherit; padding: 0;
}
.console-clear-btn:hover { color: var(--text-secondary); }
.console-output {
  background: var(--bg-base);
  border: 1px solid var(--bg-panel);
  border-radius: var(--radius-sm);
  padding: var(--space-2) 10px;
  height: 160px; overflow-y: auto;
  font-size: var(--font-base); line-height: 1.6;
}
.console-line          { color: var(--text-muted);      white-space: pre-wrap; }
.console-line--success { color: var(--color-success); }
.console-line--error   { color: var(--color-error); }
.console-line--warning { color: var(--color-warning); }
.console-line--info    { color: var(--text-muted); }


/* ══════════════════════════════════════════════════════════════════════════
   VISIBILITY TOGGLE BUTTON  (island list, region sidebar)
   ══════════════════════════════════════════════════════════════════════════ */

.vis-btn {
  background: none; border: none; cursor: pointer; padding: 4px 3px;
  color: var(--text-primary); flex-shrink: 0;
  display: flex; align-items: center; line-height: 0;
  opacity: 0; transition: opacity var(--transition-fast);
}
.list-row:hover .vis-btn { opacity: 1; }
.vis-btn:hover   { background: var(--bg-vis-hover); }


/* ══════════════════════════════════════════════════════════════════════════
   SECTION DESCRIPTION  (body text inside a panel section)
   ══════════════════════════════════════════════════════════════════════════ */

.section-desc {
  font-size: var(--font-base); color: var(--text-secondary);
  line-height: 1.6; margin-bottom: var(--space-2);
}

/* The Dressing inspector's two sentences: what the last canvas operation did, and why the gate will not take
   the prop as it stands. Both are `.section-desc` first, so they read as panel body text and only the ground
   under them says which kind of sentence it is. */
.dressing-note,
.dressing-refusal {
  border-radius: var(--radius-sm);
  padding: var(--space-2);
}

.dressing-note { background: var(--color-success-bg); color: var(--text-primary); }

.dressing-refusal { background: var(--color-error-bg); color: var(--color-error); }

/* The ground colour a biome tints with, beside the biome it belongs to. A flat fill rather than a picture,
   so it carries its own size — `.lib-bind-swatch` takes its from the SVG it wraps and collapses without one. */
.biome-swatch {
  flex: 0 0 auto; width: 1rem; height: 1rem;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}

/* <HelpMark> — the "?" that sits in a heading row or beside the control it explains, and the note the row's
   owner renders under it. The two are separate elements on purpose: a note has to run the full width of the
   panel, which it cannot do from inside the flex row the mark lives in. */
.help-mark {
  /* A round mark, never a stretched pill — it opts out of a stretching row's height. */
  flex: 0 0 auto; align-self: center;
  display: inline-grid; place-items: center; width: 17px; height: 17px; padding: 0;
  cursor: pointer; font-size: var(--font-xs); font-weight: 700; line-height: 1; font-family: inherit;
  color: var(--text-muted); background: var(--bg-panel);
  border: 1px solid var(--border); border-radius: 50%;
}
.help-mark:hover,
.help-mark--on { color: var(--accent-lighter); border-color: var(--accent); }
.help-note-body {
  margin: var(--space-1) 0 var(--space-2); padding-left: var(--space-3);
  border-left: 2px solid var(--border);
  font-size: var(--font-xs); line-height: 1.55; color: var(--text-muted);
}
.help-note-body > p { margin: 0 0 var(--space-2); }
.help-note-body > p:last-child { margin-bottom: 0; }


/* ══════════════════════════════════════════════════════════════════════════
   INSPECTOR HEADER  (selected-shape header row — dot + label)
   ══════════════════════════════════════════════════════════════════════════ */

.detail-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
}
.detail-header-badges {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-1);
}
.detail-dot { width: 12px; height: 12px; border-radius: var(--radius-sm); flex-shrink: 0; }
.detail-label {
  flex: 1;
  font-size: var(--font-md);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.detail-label--mono { font-family: ui-monospace, monospace; }

/* SideDrawer — a reusable right-anchored overlay panel: a dimming backdrop plus a sliding drawer with a
   header (reusing .detail-header / .geo-type-icon / .detail-label), a scrolling body, and an optional
   footer. Used by the plan editor (compile & test, open-from-DB) and the generator (board detail). */
.side-drawer-backdrop { position: fixed; inset: 0; z-index: 40; background: rgba(0, 0, 0, 0.45); }
.side-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 41;
  width: min(560px, 92vw);
  display: flex; flex-direction: column;
  background: var(--bg-panel, var(--bg)); border-left: 1px solid var(--border);
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.3);
}
.side-drawer-head {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-3); border-bottom: 1px solid var(--border);
}
/* The body is the only part that gives ground. A footer holds actions, so it must never be what overflows
   the drawer — a footer that grew past the room left (a confirmation stated above its buttons) pushed those
   buttons off the bottom of the screen with no way to reach them. Capping it at half the drawer bounds what
   it can claim, its own scroll takes anything past that, and the body absorbs the rest. */
.side-drawer-body {
  flex: 1 1 0; min-height: 0; overflow: auto;
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3);
}
.side-drawer-foot {
  flex: 0 0 auto; max-height: 50%; overflow-y: auto;
  padding: var(--space-3); border-top: 1px solid var(--border);
}
.detail-icon--muted { color: var(--text-muted); }


/* ══════════════════════════════════════════════════════════════════════════
   COORDINATE INPUT WIDGET  (prefixed numeric field — X/Z/R labels)
   ══════════════════════════════════════════════════════════════════════════ */

.coord-input {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: ui-monospace, monospace;
  font-size: var(--font-base);
  font-variant-numeric: tabular-nums;
  padding: 3px 6px;
  text-align: right;
  width: 68px;
  outline: none;
}
.coord-input:focus { border-color: var(--accent); }

.coord-field {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.coord-field:focus-within { border-color: var(--accent); }
.coord-prefix {
  font-size: var(--font-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 0 4px 0 8px;
  user-select: none;
  flex-shrink: 0;
}
.coord-field .coord-input {
  background: transparent;
  border: none;
  border-radius: 0;
  flex: 1;
  min-width: 0;
  width: auto;
  padding: 3px 8px 3px 2px;
  text-align: right;
}
.coord-field .coord-input:focus { border-color: transparent; }


/* ══════════════════════════════════════════════════════════════════════════
   DETAIL TABLE  (sketch inspector — rectangle MIN/MAX/SIZE, polygon vertices)
   ══════════════════════════════════════════════════════════════════════════ */

.detail-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-base);
  font-variant-numeric: tabular-nums;
}
.detail-table th {
  font-size: var(--font-sm);
  font-weight: normal;
  color: var(--text-secondary);
  text-align: left;
  padding: 0 4px 4px;
  user-select: none;
}
.detail-size-head { text-align: right; }
.detail-axis {
  color: var(--text-muted);
  font-size: var(--font-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 2px 4px 2px 0;
  user-select: none;
}
.detail-val { padding: 2px 2px; }
.detail-val .coord-input { width: 60px; }
/* When a coord-field sits inside a table cell it has no flex parent,
   so flex:1 won't expand it — this makes it fill the cell instead. */
.detail-val .coord-field { width: 100%; }
.detail-size {
  text-align: right;
  padding: 2px 4px;
  color: var(--text-muted);
  font-size: var(--font-base);
}

/* Scrollable vertex list used for polygon shapes */
.detail-vertex-scroll {
  max-height: 160px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}


/* ── Text-size utilities ─────────────────────────────────────────────── */
/* Mirror the --font-* token scale for use in HTML markup. */
.text-sm   { font-size: var(--font-sm);   }
.text-xs   { font-size: var(--font-xs);   }


/* ══════════════════════════════════════════════════════════════════════════
   LAYER OPTION CARDS  (Configure scan-layer selection)
   ══════════════════════════════════════════════════════════════════════════ */

/* 2×2 grid of layer-type selector chips */
.layer-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}
.layer-grid .filter-chip { justify-content: center; }


/* ══════════════════════════════════════════════════════════════════════════
   TOPBAR ICON BUTTON  (theme toggle, and any future icon-only topbar action)
   ══════════════════════════════════════════════════════════════════════════ */

.topbar-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast);
}
.topbar-icon-btn:hover { color: var(--text-strong); background: var(--bg-vis-hover); }
.topbar-icon-btn svg { width: var(--icon-md); height: var(--icon-md); }

/* Theme toggle — show the icon for the CURRENT theme: moon while dark, sun while light.
   Driven by <html data-theme>. */
.theme-toggle-icon { display: inline-flex; line-height: 0; }
.theme-toggle-icon--sun { display: none; }
:root[data-theme="light"] .theme-toggle-icon--moon { display: none; }
:root[data-theme="light"] .theme-toggle-icon--sun  { display: inline-flex; }

/* Card grid + card — a reusable surface for a set of items (icon + title + description). The grid
   fills as many equal columns as fit (min 240px), so it adapts to the item count and container width. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-3);
}
.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color 0.12s ease, transform 0.12s ease;
}
.card:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
}
.card-head { display: flex; align-items: center; gap: var(--space-2); }
.card-icon {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: var(--radius-md);
  background: var(--bg-selected);
  color: var(--accent-light);
}
.card-icon svg { width: var(--icon-lg); height: var(--icon-lg); }
.card-title { font-size: var(--font-lg); font-weight: 600; color: var(--text-bright); }
.card-step {
  margin-left: auto;
  font-size: var(--font-sm);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.card-desc { font-size: var(--font-base); line-height: 1.5; color: var(--text-secondary); }

/* Friendly info callout — an accent-edged note for a tip or an important aside. */
.callout {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-3) var(--space-4);
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
  font-size: var(--font-base);
  line-height: 1.5;
  color: var(--text-primary);
}
.callout-icon { flex: none; margin-top: 1px; color: var(--accent-light); }
.callout strong { color: var(--text-bright); }

/* ══════════════════════════════════════════════════════════════════════════
   LANDING  (the studio entry — a hero over the three lifecycle cards)
   ══════════════════════════════════════════════════════════════════════════ */
.landing { max-width: 920px; margin: 0 auto; padding-top: var(--space-6); }

.landing-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-6) 0;
}
.landing-logo { transform: scale(1.4); margin-bottom: var(--space-3); }
.landing-title {
  font-size: clamp(22px, 3vw, 30px);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}
.landing-lead {
  max-width: 56ch;
  font-size: var(--font-lg);
  line-height: 1.6;
  color: var(--text-secondary);
}
/* The two ways in, stacked: the featured "plan a layout" origin, a labelled rule, then the
   three lifecycle cards. One flow group so its internal rhythm is tighter than the hero gap. */
.landing-choices { display: flex; flex-direction: column; gap: var(--space-4); }
.landing-cards { margin: 0; }

/* Featured origin — a wide horizontal card above the lifecycle trio. Overrides the vertical
   .card layout and the grid-cell height stretch it inherits from .card--action. */
.landing-plan {
  flex-direction: row;
  align-items: center;
  gap: var(--space-4);
  height: auto;
}
.landing-plan .card-icon { width: 44px; height: 44px; }
.landing-plan .card-icon svg { width: var(--icon-xl); height: var(--icon-xl); }
.landing-plan-body { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.landing-plan-body .card-title { font-size: var(--font-lg); }
.landing-plan-body .card-desc { margin: 0; }
.landing-plan-go { flex: none; font-size: var(--font-sm); }

/* Labelled separator between the two paths — a centred caption flanked by hairlines. */
.landing-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-muted);
  font-size: var(--font-sm);
}
.landing-divider::before,
.landing-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* A card that is itself a link to an editor surface — fills its grid cell, no underline. */
.card--action { text-decoration: none; cursor: pointer; height: 100%; }
.card-foot {
  margin-top: auto;
  padding-top: var(--space-3);
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: var(--font-sm);
}
.card-foot-count { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.card-foot-go { display: inline-flex; align-items: center; gap: 4px; color: var(--accent-light); }
.card-foot-go svg { width: var(--icon-sm); height: var(--icon-sm); transition: transform 0.12s ease; }
.card--action:hover .card-foot-go svg { transform: translateX(3px); }

/* Trailing arrow on a navigable list row — muted, brightens with the row. */
.list-go { flex: none; display: inline-flex; color: var(--text-muted); opacity: 0; transition: opacity 0.12s ease; }
.list-go svg { width: var(--icon-md); height: var(--icon-md); }
.list-row:hover .list-go { opacity: 1; color: var(--accent-light); }

/* A rule between two groups of rail buttons — the library's chooser above its six kinds. */
.nav-rail-div { width: 22px; height: 1px; margin: 2px 0; background: var(--border); flex: 0 0 auto; }

/* ══════════════════════════════════════════════════════════════════════════
   KEYBOARD — the sheet that lists the keymap and the palette that runs it.
   Both are drawn by keys-overlay.js from the same registry the dispatcher
   reads, so what is styled here is whatever the app actually binds.
   ══════════════════════════════════════════════════════════════════════════ */

.keys-overlay { position: fixed; inset: 0; z-index: 90; display: flex; align-items: flex-start; justify-content: center; }
.keys-backdrop { position: absolute; inset: 0; background: rgb(0 0 0 / .48); }

.keys-panel {
  position: relative; z-index: 1; margin-top: 9vh; width: min(760px, calc(100vw - 32px));
  max-height: 74vh; display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg-panel); border: 1px solid var(--border);
  border-radius: var(--radius-md, 6px); box-shadow: var(--shadow-float);
}
.keys-panel--palette { width: min(560px, calc(100vw - 32px)); }

.keys-head {
  display: flex; align-items: baseline; gap: var(--space-3); flex-shrink: 0;
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
}
.keys-head h2 { margin: 0; font-size: var(--font-lg); color: var(--text-bright); font-weight: 600; }
.keys-hint { margin-left: auto; font-size: var(--font-xs); color: var(--text-muted); }

.keys-groups {
  overflow-y: auto; padding: var(--space-3) var(--space-4) var(--space-4);
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--space-4);
  align-content: start;
}
.keys-group h3 {
  margin: 0 0 var(--space-2); font-size: var(--font-xs); letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-muted); font-weight: 600;
}
.keys-row { display: flex; align-items: baseline; gap: var(--space-2); padding: 3px 0; font-size: var(--font-base); }
.keys-row-keys { flex: 0 0 auto; display: flex; gap: 3px; }
.keys-row-label { color: var(--text-secondary); min-width: 0; }
.keys-empty { padding: var(--space-4); color: var(--text-muted); font-size: var(--font-sm); text-align: center; }

.keys-chord {
  font-family: ui-monospace, monospace; font-size: var(--font-xs); color: var(--text-primary);
  background: var(--bg-base); border: 1px solid var(--border); border-bottom-width: 2px;
  border-radius: var(--radius-sm); padding: 1px 5px; white-space: nowrap;
}

.keys-search { flex-shrink: 0; padding: var(--space-2); border-bottom: 1px solid var(--border); }
.keys-search-input {
  width: 100%; height: 34px; padding: 0 var(--space-2); font: inherit; font-size: var(--font-md);
  color: var(--text-bright); background: var(--bg-base); border: 1px solid var(--border);
  border-radius: var(--radius-sm); outline: none;
}
.keys-search-input:focus { border-color: var(--accent); }

.keys-list { overflow-y: auto; padding: var(--space-1); display: flex; flex-direction: column; gap: 1px; }
.keys-item {
  display: flex; align-items: center; gap: var(--space-2); width: 100%; text-align: left;
  padding: 6px 8px; border: 0; border-radius: var(--radius-sm); background: transparent;
  color: var(--text-primary); font: inherit; font-size: var(--font-base); cursor: pointer;
}
.keys-item:hover { background: var(--bg-selected-hover); }
.keys-item--at { background: var(--bg-selected); color: var(--text-strong); }
.keys-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.keys-item-group { flex: 0 0 auto; font-size: var(--font-xs); color: var(--text-muted); }
.keys-item-keys { flex: 0 0 auto; display: flex; gap: 3px; }

/* ── the storey strip ──
   Which layer is being drawn on, floating at the foot of the canvas beside the dock. A layer is the
   board's, not a phase's, so this is chrome rather than a panel: every phase that draws the canvas has it,
   and whatever is placed lands on the layer it names. */
.canvas-layer-strip {
  position: absolute; left: 10px; bottom: 16px; z-index: 3;
  display: flex; align-items: stretch; gap: 2px; padding: 3px;
  border-radius: var(--radius-sm);
  background: var(--canvas-float-bg); backdrop-filter: blur(6px);
  border: 1px solid var(--canvas-float-border); box-shadow: var(--canvas-float-shadow);
  /* The dock is centred on the canvas (`.canvas-dock`, left 50% and translated back by half its width), so
     what is left for the strip is the half-canvas beside it less the dock's own half, this element's own
     left offset and a gap. Measured: the Draw dock is 350px wide, so 175 of half-width, and the number here
     clears one up to about 490 — the phases carrying the most tools. Held as a calc against 50% rather than
     as a share of the width, because the dock is a constant number of pixels while a share is not: on a
     narrow canvas a percentage keeps growing past it. */
  max-width: calc(50% - 260px);
}
/* Only the tabs scroll. `min-width: 0` is what lets a flex child shrink below its content and overflow at
   all; without it the strip grows to fit the tabs and the cap above does nothing. */
.canvas-layer-tabs {
  display: flex; align-items: stretch; gap: 2px;
  min-width: 0; overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: thin;
}
.canvas-layer-strip-label, .canvas-layer-add { flex: none; }
.canvas-layer-strip-label {
  display: flex; align-items: center; padding: 0 6px;
  font-size: var(--font-xs); letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted);
}
/* A tab and its delete are one pill, so the wrap carries the state and the two buttons inside it stay
   transparent — a delete nested in the tab button would be a button inside a button. */
.canvas-layer-tab-wrap {
  display: flex; align-items: stretch; border-radius: var(--radius-sm); color: var(--text-secondary);
}
.canvas-layer-tab-wrap:hover { background: var(--bg-vis-hover); color: var(--text-primary); }
.canvas-layer-tab-wrap--on { background: var(--bg-selected); color: var(--text-strong); }
.canvas-layer-tab {
  display: flex; flex-direction: column; align-items: flex-start; gap: 0;
  padding: 3px 8px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: inherit; font: inherit; cursor: pointer; text-align: left;
}
.canvas-layer-tab-name { font-size: var(--font-sm); line-height: 1.2; max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.canvas-layer-tab-y { font-family: ui-monospace, monospace; font-size: 10px; color: var(--text-muted); line-height: 1.2; }
.canvas-layer-tab-wrap--on .canvas-layer-tab-y { color: var(--accent-lighter); }
/* Shown only on the hovered or active tab — a strip of storeys reads as a switch, and a delete on every one
   of them reads as a list of deletes. Faded rather than hidden, and its slot held either way: a strip that
   reflows as the pointer crosses it moves the target out from under the press that was aiming for it, and a
   `visibility: hidden` button cannot be pressed at all until the hover it needs has already resolved. Nobody
   presses one they cannot see — reaching it puts the pointer inside the tab, which is what fades it in. */
.canvas-layer-del {
  display: grid; place-items: center; width: 18px; padding: 0; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted); cursor: pointer;
  opacity: 0; transition: opacity 90ms linear;
}
.canvas-layer-tab-wrap:hover .canvas-layer-del,
.canvas-layer-tab-wrap--on .canvas-layer-del { opacity: 1; }
.canvas-layer-del:hover { color: var(--color-error); }
.canvas-layer-del svg { width: var(--icon-xs); height: var(--icon-xs); }
.canvas-layer-add {
  display: grid; place-items: center; width: 26px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted); cursor: pointer;
}
.canvas-layer-add:hover { background: var(--bg-vis-hover); color: var(--text-primary); }
.canvas-layer-add svg { width: var(--icon-xs); height: var(--icon-xs); }
/* ── the theme strip ──
   The board's themes, floating above the dock: what is in hand, and what a click will paint. A theme is the
   board's rather than a phase's, so like the storey strip this is chrome. It scrolls sideways, which is the
   axis with room — a board carries anything from one theme to a couple of dozen. */
.canvas-theme-strip {
  position: absolute; left: 50%; bottom: 62px; transform: translateX(-50%); z-index: 3;
  display: flex; align-items: center; gap: 4px; padding: 3px 3px 3px 4px;
  max-width: calc(100% - 20px);
  border-radius: var(--radius-sm);
  background: var(--canvas-float-bg); backdrop-filter: blur(6px);
  border: 1px solid var(--canvas-float-border); box-shadow: var(--canvas-float-shadow);
}
.canvas-theme-strip-label {
  flex: 0 0 auto; padding: 0 4px;
  font-size: var(--font-xs); letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted);
}
.canvas-theme-strip-empty { padding: 3px 6px; font-size: var(--font-sm); color: var(--text-muted); }
.canvas-theme-swatches { display: flex; align-items: center; gap: 2px; overflow-x: auto; scrollbar-width: thin; }
.canvas-theme-swatch {
  display: flex; align-items: center; gap: 5px; flex: 0 0 auto;
  padding: 3px 8px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-secondary); font: inherit; cursor: pointer;
}
.canvas-theme-swatch:hover { background: var(--bg-vis-hover); color: var(--text-primary); }
.canvas-theme-swatch--on { background: var(--bg-selected); color: var(--text-strong); }
.canvas-theme-swatch-name {
  font-size: var(--font-sm); line-height: 1.2; max-width: 14ch;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The one theme that paints without being placed. */
.canvas-theme-swatch-tag {
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--accent-light);
}
.canvas-theme-add {
  display: grid; place-items: center; flex: 0 0 auto; width: 26px; height: 24px;
  border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted); cursor: pointer;
}
.canvas-theme-add:hover { background: var(--bg-vis-hover); color: var(--text-primary); }
.canvas-theme-add--on { background: var(--bg-selected); color: var(--text-strong); }
.canvas-theme-add svg { width: var(--icon-xs); height: var(--icon-xs); }

/* The library rows the add panel offers, as a plain list rather than a select: a theme is picked by
   recognising its name, and a dropdown hides every name but one. The panel it sits in scrolls, so this does
   not — two nested scrolls over one list is one too many. */
.lib-picklist { display: flex; flex-direction: column; gap: 2px; }

/* A chord the tool has but cannot run yet — a delete with nothing selected. Listed, because a chord hidden
   until it works is a chord nobody learns; dimmed, because it will not answer now. */
.keys-row--off { opacity: .45; }

/* ── Access: who is signed in, and a page that opens read-only ─────────────────────────────────── */

/* A disabled fieldset greys every field and button inside it; it carries none of the box a fieldset draws. */
.readonly-fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.readonly-fieldset:disabled { opacity: 0.55; }

.invite-link {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  margin-top: var(--space-2);
}
.invite-link .field-input { flex: 1; font-family: ui-monospace, monospace; font-size: var(--font-xs); }

/* A player's face and hat, cut from their skin over the initial beneath; a skin that does not load leaves
   the initial showing. */
.author-mark { position: relative; overflow: hidden; }
.player-head-face {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  image-rendering: pixelated;
}
.user-actions { display: flex; flex: none; align-items: center; gap: var(--space-2); }
.user-actions .field-input { width: auto; }
.invite-link .field-input--slim { width: auto; flex: none; }

/* ── The studio's own bar, the same on every page ──────────────────────────────────────────────── */

.app-nav {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-shrink: 0;
  height: 44px;
  padding: 0 var(--space-3) 0 var(--space-4);
  background: var(--nav-bg);
  border-bottom: 1px solid var(--nav-border);
  color: var(--nav-text);
  font-size: var(--font-md);
}
.app-nav-home { display: flex; align-items: baseline; gap: 4px; text-decoration: none; color: var(--nav-text-strong); }
.app-nav-pgm { font-weight: 700; }
.app-nav-sub { font-size: 10px; letter-spacing: 0.12em; color: var(--nav-text-muted); }
.app-nav-links { display: flex; align-items: center; gap: 2px; min-width: 0; overflow-x: auto; }
.app-nav-link {
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  color: var(--nav-text);
  text-decoration: none;
  white-space: nowrap;
}
.app-nav-link:hover { color: var(--nav-text-strong); background: var(--nav-hover); }
.app-nav-link--active { color: var(--nav-text-strong); background: var(--nav-active); }
.app-nav-right { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }
.app-nav .theme-toggle { color: var(--nav-text); }
.app-nav .theme-toggle:hover { color: var(--nav-text-strong); background: var(--nav-hover); }

.account { position: relative; }
.account-button {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 32px;
  padding: 0 var(--space-2) 0 4px;
  border: 1px solid var(--nav-border);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--nav-text-strong);
  font: inherit;
  cursor: pointer;
}
.account-button:hover, .account-button[aria-expanded="true"] { background: var(--nav-hover); }
.account-button svg { width: 14px; height: 14px; }
.account-name { font-weight: 600; }
.account-role {
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  background: var(--nav-active);
  color: var(--nav-text-strong);
  font-size: var(--font-xs);
}
.account-local { display: flex; align-items: center; gap: var(--space-2); color: var(--nav-text); }
.account-signin {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 32px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  background: #5865f2;
  color: #fff;
  text-decoration: none;
  font-weight: 600;
}
.account-signin svg { width: 16px; height: 16px; }
.account-scrim { position: fixed; inset: 0; z-index: 40; }
.account-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 41;
  width: 240px;
  padding: var(--space-2);
  background: var(--bg-panel);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.account-menu-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.account-menu-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.account-menu-who .account-name { color: var(--text-bright); }
.account-menu-note { color: var(--text-muted); font-size: var(--font-sm); }
.account-menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.account-menu-item:hover { background: var(--bg-selected-hover); }
.account-menu-item svg { width: 16px; height: 16px; color: var(--text-secondary); }

/* A page the caller may not write says so in the tool's bar; the reason is on hover. */
.readonly-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-left: var(--space-3);
  padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--color-warning-bg);
  color: var(--text-primary);
  font-size: var(--font-sm);
}
.readonly-tag svg { width: 14px; height: 14px; color: var(--color-warning); }

button.app-footer-link { border: 0; background: none; padding: 0; font: inherit; cursor: pointer; }
.app-footer-kbd {
  padding: 0 5px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: var(--font-xs);
}
