/* 02-shell.css — app frame: top bar, panes, status bar, command palette.
   Owned by: agent-shell. Later numbered files override earlier ones.
   Only --hui-* aliases and theme variables from 01-foundations.css are used. */

/* ---- Frame -------------------------------------------------------------- */
/* The shell is the viewport. Nothing above it scrolls; every pane owns its own
   scroll box, so there is never a scrollbar inside a scrollbar. */
html, body { height: 100%; overflow: hidden; overscroll-behavior: none; }

/* Pane geometry. These four are the shell's layout contract:
   - the two widths are also written as inline custom properties on <html> by
     the splitter drag handler (lib/components/shell/pane_dom_web.dart), which
     is why no responsive rule may try to own them;
   - the status height is a real grid track, not a min-height, so no pane can
     ever extend under the status bar. */
:root {
  --hui-rail-width: 240px;
  --hui-inspector-width: 320px;
  --hui-splitter-width: 6px;
  --hui-status-height: 26px;
}

/* The boot fade is opacity only, and that is not a stylistic choice: the top
   bar, the rail and the inspector all host Arcane dropdown, select and popover
   surfaces, which render `position: fixed` inline in the tree rather than in a
   portal. Any transform on an ancestor — including one an animation is holding
   — would make that ancestor their containing block and drop every one of them
   in the wrong place. Opacity creates a stacking context but not a containing
   block, so it is safe. Same rule applies to anything Wave 3 adds here. */
.hui-shell {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) var(--hui-status-height);
  /* Load-bearing. The implicit column is `auto`, whose minimum is the widest
     row's MIN-CONTENT. Capping it at the container lets the document label and
     the permanent selectors contract while whole action clusters hand off to
     the overflow menu, so header content never widens the shell. */
  grid-template-columns: minmax(0, 1fr);
  min-height: 0;
  height: 100dvh;
  overflow: hidden;
  background: var(--hui-bg);
  color: var(--hui-text);
  animation: hui-fade-in var(--hui-dur-4) var(--hui-ease-out) backwards;
}

.hui-shell.has-sync {
  grid-template-rows: auto auto minmax(0, 1fr) var(--hui-status-height);
}

.hui-sync-bar {
  min-width: 0;
  min-height: 34px;
  padding: 4px 10px;
  border-bottom: 1px solid var(--hui-border);
  background: color-mix(in srgb, var(--hui-surface) 92%, transparent);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.hui-sync-identity,
.hui-sync-actions {
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

.hui-sync-identity code,
.hui-sync-identity span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hui-sync-bar.is-conflict,
.hui-sync-bar.is-rejected,
.hui-sync-bar.is-expired,
.hui-sync-bar.is-revoked,
.hui-sync-bar.is-unavailable {
  border-bottom-color: var(--hui-danger);
}

/* Five tracks: rail, splitter, canvas, splitter, inspector. The `min()` caps
   are a floor under the canvas — a pane restored from a wide session can never
   eat a narrow viewport, and the Dart clamp handles the live case. */
.hui-shell-body {
  display: grid;
  grid-template-columns:
    min(var(--hui-rail-width), 34vw)
    var(--hui-splitter-width)
    minmax(0, 1fr)
    var(--hui-splitter-width)
    min(var(--hui-inspector-width), 38vw);
  min-height: 0;
  overflow: hidden;
}

.hui-pane { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.hui-rail,
.hui-inspector {
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--hui-panel);
}
.hui-rail { border-right: 1px solid var(--hui-border-soft); }
.hui-inspector { border-left: 1px solid var(--hui-border-soft); }

/* Load-bearing: a flex child defaults to `flex-shrink: 1`, so the pane content
   root was being squashed to the pane height and its own overflow left
   unreachable — which is what buried the last rail row under the status bar.
   Natural height + the pane's own scroll box is the whole fix. */
.hui-rail > *,
.hui-inspector > * { flex: 0 0 auto; }

/* ---- Pane splitters ------------------------------------------------------ */
/* A 6px grab strip on the grid line between a pane and the canvas. Dragging
   writes --hui-rail-width / --hui-inspector-width onto the document root, so a
   resize costs no rebuild; the widget only sees the released value. */
.hui-splitter {
  position: relative;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  cursor: col-resize;
  /* Load-bearing: without it the browser claims the drag as a scroll gesture. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Tint only on contact: at rest the pane's own hairline is the whole divider.
   The tint is a fixed-size layer faded in, not a width change — a 6px strip
   that grew on hover would nudge both panes while the pointer crossed it. */
.hui-splitter::before {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--hui-accent) 18%, transparent);
  opacity: 0;
  transition: opacity var(--hui-dur-2) var(--hui-ease-out);
}
.hui-splitter:hover::before,
.hui-splitter:focus-visible::before,
.hui-splitter:active::before { opacity: 1; }

/* The grip is the affordance: at rest it is a stub, and it grows into a full
   handle as the pointer arrives. Scale rather than height, so the flex centre
   never moves and the grid track never reflows. */
.hui-splitter-grip {
  position: relative;
  width: 2px;
  height: 26px;
  border-radius: var(--hui-radius);
  background: color-mix(in srgb, var(--hui-accent) 70%, transparent);
  opacity: 0;
  transform: scaleY(.35);
  transition:
    opacity var(--hui-dur-2) var(--hui-ease-out),
    transform var(--hui-dur-2) var(--hui-ease-spring);
}
.hui-splitter:hover .hui-splitter-grip,
.hui-splitter:focus-visible .hui-splitter-grip,
.hui-splitter:active .hui-splitter-grip {
  opacity: 1;
  transform: scaleY(1);
}
/* While a drag is live the pointer is usually somewhere else entirely, so the
   grip is held open by the body class rather than by :hover. */
body.hui-resizing .hui-splitter-grip { opacity: 1; transform: scaleY(1); }

/* While a splitter is captured the pointer is regularly outside it, over panes
   that set their own cursor (the canvas stage sets `grab`). `!important` is the
   only thing that beats those, and it is scoped to the drag. */
body.hui-resizing { user-select: none; -webkit-user-select: none; }
body.hui-resizing * { cursor: col-resize !important; }

/* All three views are the same grid; only the column count changes. The canvas
   cell is always present in the markup so a view switch never unmounts it (see
   _CenterArea) — in the code view it is hidden here instead. */
.hui-center {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  overflow: hidden;
  background: var(--hui-bg);
}
.hui-center.is-split {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.hui-center.is-code > .hui-split-cell:not(.is-code) { display: none; }
.hui-center.is-code > .hui-split-cell.is-code { border-left: 0; }

.hui-split-cell { position: relative; min-width: 0; min-height: 0; overflow: hidden; }
.hui-split-cell.is-code { border-left: 1px solid var(--hui-border-soft); }

.hui-shell :focus-visible {
  outline: 2px solid var(--hui-ring);
  outline-offset: 2px;
}

/* ---- Top bar ------------------------------------------------------------ */
.hui-bar {
  --hui-command-height: 36px;
  --hui-context-height: 30px;
  position: relative;
  z-index: 20;
  display: grid;
  grid-template-rows: var(--hui-command-height) var(--hui-context-height);
  grid-template-columns: minmax(0, 1fr);
  min-height: var(--hui-bar-height);
  border-bottom: 1px solid var(--hui-border-soft);
  background: var(--hui-panel);
}

.hui-bar-primary,
.hui-bar-context {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  min-width: 0;
  padding: 0 8px;
}

.hui-bar-primary {
  gap: 12px;
}

.hui-bar-context {
  gap: 8px;
  border-top: 1px solid var(--hui-border-soft);
  background: var(--hui-surface);
}

.hui-bar-group { display: flex; align-items: center; gap: 0; min-width: 0; }
.hui-bar-left { max-width: 100%; }
.hui-bar-center { max-width: 100%; }
.hui-bar-right {
  justify-content: flex-end;
  max-width: 100%;
}
.hui-bar-context-tools { justify-content: flex-end; }

/* One hairline per boundary, drawn by the cluster that follows it. Grouping the
   bar this way is what stops eleven identical icon buttons reading as one run. */
.hui-bar-cluster {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}
.hui-bar-cluster + .hui-bar-cluster {
  margin-inline-start: 6px;
  padding-inline-start: 6px;
  border-inline-start: 1px solid var(--hui-border-soft);
}

/* Every overflow tier is mounted at every width and chosen here. The kind and
   view selectors are permanent; nothing in Dart measures the viewport. */
.hui-bar-overflow,
.hui-mobile-panes,
.hui-pane-scrim { display: none; }

.hui-kind-picker { display: flex; }
.hui-bar-view-picker { display: flex; }
.hui-bar-overflow.is-tier1 { display: flex; }

.hui-bar .arcane-button {
  min-height: 28px;
  padding-inline: 6px !important;
  border: 1px solid transparent !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  color: var(--hui-muted) !important;
  font-size: .75rem;
}

.hui-bar-context .arcane-button {
  height: 28px !important;
  min-height: 28px;
}

.hui-bar .arcane-button:hover,
.hui-bar .arcane-button:focus-visible {
  background: var(--hui-panel-soft) !important;
  color: var(--hui-text) !important;
}

.hui-bar .arcane-button:active,
.hui-bar .arcane-button[aria-expanded="true"],
.hui-bar .arcane-button[aria-pressed="true"] {
  background: var(--hui-surface-raised) !important;
  box-shadow: inset 0 -2px 0 var(--hui-text) !important;
  color: var(--hui-text) !important;
}

.hui-bar .arcane-button:disabled {
  background: transparent !important;
  box-shadow: none !important;
  color: var(--hui-muted) !important;
}

.hui-bar-action {
  display: inline-flex;
  transition: transform var(--hui-dur-1) var(--hui-ease-out);
}
.hui-bar-action:active { transform: translateY(1px); }

.hui-brand { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.hui-brand-mark { display: block; width: 18px; height: 18px; }
.hui-brand-word {
  font-size: 0.82rem;
  font-weight: 650;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.hui-bar-doc { flex: 1 1 auto; }
.hui-doc { display: flex; flex: 1 1 auto; align-items: center; gap: 2px; min-width: 0; }
.hui-doc-id {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 260px;
  overflow: hidden;
  font-size: 0.8rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hui-doc-ext {
  color: var(--hui-muted);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  /* The suffix used to be ".json" and could not wrap; it now carries the kind
     noun, and "· flow map" broke across two lines inside the bar. The id beside
     it is the part that gives way, by ellipsis. */
  white-space: nowrap;
}

/* Two-step destructive control: arm in the menu, confirm in the bar. It is
   mounted by the arming click, so the entrance runs exactly once, when it
   appears — the one moment the user needs to notice a new control in a bar
   they were not looking at. */
.hui-armed {
  animation: hui-pop-in var(--hui-dur-3) var(--hui-ease-spring) backwards;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 4px 6px 4px 12px;
  border: 1px solid color-mix(in srgb, var(--hui-danger) 50%, var(--hui-border));
  border-radius: var(--hui-radius);
  background: color-mix(in srgb, var(--hui-danger) 12%, transparent);
  font-size: 0.8rem;
  white-space: nowrap;
}
/* The question gives way before the two buttons do: a confirm whose Delete has
   been pushed off the column is worse than one whose file name is elided. */
.hui-armed > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hui-armed > .arcane-button { flex: 0 0 auto; }
.hui-armed > .arcane-button[data-variant="destructive"] {
  border-color: var(--hui-danger-fill) !important;
  background: var(--hui-danger-fill) !important;
  color: var(--destructive-foreground) !important;
}
.hui-armed > .arcane-button[data-variant="destructive"]:hover,
.hui-armed > .arcane-button[data-variant="destructive"]:focus-visible {
  background: color-mix(in srgb, var(--hui-danger-fill) 84%, white) !important;
}

/* Permanent compact selectors keep kind and view state visible without a tab
   wall. Their labels are the only part allowed to contract. */
.hui-kind-picker .arcane-button,
.hui-bar-view-picker .arcane-button {
  min-width: 0;
  max-width: 38vw;
}
.hui-kind-picker .hui-bar-menu,
.hui-bar-view-picker .hui-bar-menu { min-width: 0; max-width: 100%; }
.hui-bar-menu-panel {
  max-width: calc(100vw - 16px);
  max-height: calc(100dvh - var(--hui-command-height) - 12px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.hui-bar-context .hui-bar-menu-panel {
  max-height: calc(
    100dvh - var(--hui-command-height) - var(--hui-context-height) - 12px
  );
}
.hui-bar-menu-item:hover:not(:disabled),
.hui-bar-menu-item:focus-visible {
  background: var(--hui-panel-soft) !important;
}
[data-hui-picker-label="true"] {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.hui-bar-language [data-locale][aria-current="true"] { opacity: 1 !important; }

html[dir="rtl"] .hui-shell { direction: rtl; }
html[dir="rtl"] .hui-shell-body,
html[dir="rtl"] .hui-bar { direction: ltr; }
html[dir="rtl"] .hui-rail,
html[dir="rtl"] .hui-inspector,
html[dir="rtl"] .hui-center,
html[dir="rtl"] .hui-status { direction: rtl; }
html[dir="rtl"] .hui-bar-left,
html[dir="rtl"] .hui-bar-center,
html[dir="rtl"] .hui-bar-context-tools { direction: rtl; }
html[dir="rtl"] .hui-bar-right { direction: ltr; }
html[dir="rtl"] .hui-bar-right > .hui-bar-cluster { direction: rtl; }
html[dir="rtl"] code,
html[dir="rtl"] pre,
html[dir="rtl"] .hui-code,
html[dir="rtl"] .hui-canvas,
html[dir="rtl"] .hui-preview-stage { direction: ltr; unicode-bidi: isolate; }

.hui-ltr,
.hui-image-tile-path,
.hui-frame-path,
.hui-scoreboard-sim-id,
.hui-emoji-cell-name,
.hui-board-runtime-json {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: left;
}

input[dir="ltr"],
textarea[dir="ltr"] {
  unicode-bidi: isolate;
  text-align: left;
}

.hui-tip { display: grid; gap: 4px; }
.hui-tip-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.8rem;
  font-weight: 620;
}
.hui-tip-hint {
  max-width: 220px;
  margin: 0;
  color: var(--hui-muted);
  font-size: 0.74rem;
  line-height: 1.35;
}

[data-hui-responsive-label="mobile-pane"] { font-size: 0 !important; }

/* ---- Status bar --------------------------------------------------------- */
/* Fixed height, matching the grid track above: if the strip could grow it would
   take the space from the panes rather than from itself. */
.hui-status {
  display: flex;
  align-items: center;
  gap: var(--hui-space-2);
  height: var(--hui-status-height);
  min-height: var(--hui-status-height);
  padding: 0 var(--hui-space-3);
  border-top: 1px solid var(--hui-border-soft);
  background: color-mix(in srgb, var(--hui-panel-soft) 40%, transparent);
  color: var(--hui-muted);
  font-size: 0.76rem;
}
.hui-status-left,
.hui-status-right {
  display: flex;
  align-items: center;
  gap: var(--hui-space-2);
  min-width: 0;
}
.hui-status-spacer { flex: 1 1 auto; }
.hui-status-item { white-space: nowrap; }
.hui-status-item.is-numeric {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.hui-status-item.is-muted { color: var(--hui-muted); }
.hui-status-version {
  padding: 2px 8px;
  border: 1px solid var(--hui-border-soft);
  border-radius: var(--hui-radius);
  background: color-mix(in srgb, var(--hui-panel-soft) 60%, transparent);
  color: var(--hui-muted);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
.hui-status-item.is-error { color: var(--hui-danger); }
.hui-status-item.is-hint {
  max-width: 44ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The chip changes colour as issues appear and clear while the user types in
   the inspector. Crossfading it stops a keystroke reading as a flash. */
.hui-status-chip button {
  min-height: 24px;
  padding: 0 8px;
  border-radius: var(--hui-radius);
  font-size: 0.76rem;
  font-weight: 600;
  transition:
    color var(--hui-dur-3) var(--hui-ease-out),
    background-color var(--hui-dur-3) var(--hui-ease-out);
}
.hui-status-chip.is-clean button { color: var(--hui-muted); }
.hui-status-chip.is-warning button {
  color: var(--hui-warning);
  background: color-mix(in srgb, var(--hui-warning) 10%, transparent);
}
.hui-status-chip.is-error button {
  color: var(--hui-danger);
  background: color-mix(in srgb, var(--hui-danger) 10%, transparent);
}

/* ---- Command palette ---------------------------------------------------- */
.hui-cmd-scrim {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 16vh 16px 16px;
  background: color-mix(in srgb, #000 52%, transparent);
  animation: hui-fade-in var(--hui-dur-2) var(--hui-ease-out) backwards;
}

.hui-cmd-dialog {
  display: flex;
  flex: 0 1 auto;
  flex-direction: column;
  width: min(640px, 100%);
  max-height: 64vh;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--hui-border) 78%, transparent);
  border-radius: var(--hui-radius);
  background: var(--hui-surface-raised);
  box-shadow: var(--hui-shadow-md);
  /* Drops from above rather than rising: the palette is anchored to the top of
     the viewport, so it should arrive from the edge it is docked to. */
  animation: hui-slide-in-down var(--hui-dur-3) var(--hui-ease-out) backwards;
}

.hui-cmd-search {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--hui-border-soft);
  color: var(--hui-muted);
}
.hui-cmd-input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--hui-text);
  font-size: 0.92rem;
  outline: none;
}
.hui-cmd-input::placeholder { color: var(--hui-muted); }

.hui-cmd-list {
  min-height: 0;
  padding: 6px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.hui-cmd-heading {
  padding: 10px 10px 6px;
  color: var(--hui-muted);
  font-size: 0.68rem;
  font-weight: 680;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.hui-cmd-item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: var(--hui-radius);
  color: var(--hui-text);
  font-size: 0.86rem;
  cursor: pointer;
  transition:
    background var(--hui-dur-1) var(--hui-ease-out),
    box-shadow var(--hui-dur-1) var(--hui-ease-out);
}
.hui-cmd-item:hover { background: var(--hui-panel-soft); }
.hui-cmd-item.is-active {
  border-color: color-mix(in srgb, var(--hui-accent) 42%, var(--hui-border));
  background: color-mix(in srgb, var(--hui-accent) 14%, transparent);
}
.hui-cmd-icon {
  display: inline-flex;
  flex: 0 0 auto;
  justify-content: center;
  width: 18px;
  color: var(--hui-muted);
}
.hui-cmd-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hui-cmd-empty {
  margin: 0;
  padding: 20px 12px;
  color: var(--hui-muted);
  font-size: 0.84rem;
  text-align: center;
}
.hui-cmd-footer {
  display: flex;
  gap: 14px;
  padding: 8px 14px;
  border-top: 1px solid var(--hui-border-soft);
  color: var(--hui-muted);
  font-size: 0.72rem;
}

/* The node the injected runtime's ⌘K handler clicks (see `_triggerPalette`).
   `pointer-events: none` still allows a programmatic `.click()`, so the script
   reaches it and a user never can. */
.hui-cmd-trigger {
  position: fixed;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  background: none;
  opacity: 0;
  pointer-events: none;
}

/* ---- Shortcut sheet ------------------------------------------------------ */
/* Same lane as the palette and deliberately the same shape: they are the two
   surfaces that answer "what can I do here", and reading as one control makes
   the pair learnable at once. It sits BELOW the palette in z so the palette
   opened from the sheet's own command lands on top. */
.hui-keys-scrim {
  position: fixed;
  inset: 0;
  z-index: 78;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--hui-space-5) 16px;
  background: color-mix(in srgb, #000 52%, transparent);
  animation: hui-fade-in var(--hui-dur-2) var(--hui-ease-out) backwards;
}

.hui-keys-dialog {
  display: flex;
  flex: 0 1 auto;
  flex-direction: column;
  width: min(900px, 100%);
  max-height: 82vh;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--hui-border) 78%, transparent);
  border-radius: var(--hui-radius);
  background: var(--hui-surface-raised);
  box-shadow: var(--hui-shadow-md);
  animation: hui-pop-in var(--hui-dur-3) var(--hui-ease-spring) backwards;
}

.hui-keys-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 12px 12px 14px;
  border-bottom: 1px solid var(--hui-border-soft);
  color: var(--hui-muted);
}
.hui-keys-title {
  margin: 0;
  color: var(--hui-text);
  font-size: 0.95rem;
  font-weight: 660;
  letter-spacing: -0.01em;
}
.hui-keys-spacer { flex: 1 1 auto; }

.hui-keys-body {
  min-height: 0;
  padding: 4px 14px 14px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* auto-fit rather than a fixed count: the sheet is the one surface that has to
   stay readable from a 1600px desktop down to a phone, and the groups have no
   reading order between them. */
.hui-keys-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 4px 26px;
  align-items: start;
}

.hui-keys-group { min-width: 0; padding-top: 12px; }
.hui-keys-group-title {
  margin: 0;
  color: var(--hui-muted);
  font-size: 0.68rem;
  font-weight: 680;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.hui-keys-group-note {
  margin: 5px 0 0;
  color: var(--hui-muted);
  font-size: 0.71rem;
  line-height: 1.45;
}

.hui-keys-list { margin: 8px 0 0; padding: 0; list-style: none; }

/* Baseline-aligned rather than centred: the chip is one line and the label is
   often two, and centring made every wrapped row look nudged upward. */
.hui-keys-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  min-height: 28px;
  padding: 4px 0;
}
.hui-keys-row + .hui-keys-row {
  border-top: 1px solid color-mix(in srgb, var(--hui-border-soft) 60%, transparent);
}
.hui-keys-text { flex: 1 1 auto; min-width: 0; }
.hui-keys-label {
  display: block;
  color: var(--hui-text);
  font-size: 0.82rem;
  line-height: 1.35;
}
.hui-keys-note {
  display: block;
  margin-top: 2px;
  color: var(--hui-muted);
  font-size: 0.7rem;
  line-height: 1.4;
}

.hui-keys-foot {
  padding: 8px 14px;
  border-top: 1px solid var(--hui-border-soft);
  color: var(--hui-muted);
  font-size: 0.72rem;
}

/* ---- Guided tour --------------------------------------------------------- */
/* The wrapper exists only because a build returns one node: `.hui-shell` is a
   three-row grid, and an in-flow child here would open a fourth implicit row.
   Its children are all fixed, so contents costs nothing. */
.hui-tour { display: contents; }

/* Four dimming rects, not one scrim with a shadow cut-out: pointer events do
   not hit a box-shadow, so the cut-out form cannot block clicks at all. These
   do, which leaves the highlighted region — and only it — usable. */
.hui-tour-dim {
  position: fixed;
  z-index: 88;
  background: color-mix(in srgb, #000 58%, transparent);
  animation: hui-fade-in var(--hui-dur-3) var(--hui-ease-out) backwards;
}

/* Ring only, never a filter or a transform: the highlight sits over the top
   bar and the panes, which host the fixed-position Arcane surfaces, and any
   transform in that chain would become their containing block. */
.hui-tour-ring {
  position: fixed;
  z-index: 89;
  border-radius: var(--hui-radius);
  box-shadow:
    0 0 0 2px var(--hui-accent),
    0 0 0 7px color-mix(in srgb, var(--hui-accent) 24%, transparent);
  pointer-events: none;
  animation: hui-fade-in var(--hui-dur-2) var(--hui-ease-out) backwards;
}

.hui-tour-card {
  position: fixed;
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 340px;
  max-width: calc(100vw - 32px);
  padding: 14px 16px 12px;
  border: 1px solid color-mix(in srgb, var(--hui-accent) 34%, var(--hui-border));
  border-radius: var(--hui-radius);
  background: var(--hui-panel);
  box-shadow: var(--hui-shadow-md);
  animation: hui-pop-in var(--hui-dur-3) var(--hui-ease-spring) backwards;
}

.hui-tour-eyebrow {
  color: var(--hui-accent);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.hui-tour-title {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 660;
  letter-spacing: -0.015em;
}
.hui-tour-body {
  margin: 0;
  color: var(--hui-muted);
  font-size: 0.8rem;
  line-height: 1.5;
}
/* The sharp edge of each region, set apart so it does not read as more prose:
   this is the line the user will remember two days later. */
.hui-tour-hint {
  margin: 2px 0 0;
  padding: 7px 9px;
  border: 1px solid color-mix(in srgb, var(--hui-accent) 38%, var(--hui-border));
  border-radius: 0;
  background: color-mix(in srgb, var(--hui-accent) 6%, transparent);
  color: var(--hui-text);
  font-size: 0.75rem;
  line-height: 1.45;
}

.hui-tour-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  flex-wrap: wrap;
}
.hui-tour-spacer { flex: 1 1 auto; }

/* ---- Drag-and-drop overlay ---------------------------------------------- */
/* Mounted the moment a file enters the window, so its entrance is the whole
   signal that the editor has taken the drag. */
.hui-dropzone {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--hui-bg) 72%, transparent);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  pointer-events: none;
  animation: hui-fade-in var(--hui-dur-2) var(--hui-ease-out) backwards;
}
.hui-dropzone-card {
  display: grid;
  gap: 10px;
  justify-items: center;
  padding: 26px 34px;
  border: 2px dashed color-mix(in srgb, var(--hui-accent) 60%, var(--hui-border));
  border-radius: var(--hui-radius);
  background: var(--hui-panel);
  color: var(--hui-text);
  font-size: 0.9rem;
  animation: hui-pop-in var(--hui-dur-3) var(--hui-ease-spring) backwards;
}
.hui-dropzone-card p { margin: 0; }

/* ---- Toasts ------------------------------------------------------------- */
/* The stack is mounted top-centre (see EditorShell): the only region no pane
   owns. Capping the width stops a long message spanning a wide viewport; the
   toasts themselves carry an inline 320-420px range, so this only bites on a
   narrow one. */
.arcane-toaster[data-position="topCenter"] {
  max-width: min(440px, calc(100vw - var(--hui-space-5) * 2));
}

/* The renderer writes `animation: arcane-toast-enter 300ms ... forwards` inline
   on every toast, `arcane-toast-exit 200ms` on an exiting one, and
   `arcane-toast-progress <duration>ms` on the countdown bar — and arcane_jaspr
   defines none of those three @keyframes anywhere. An unresolved
   animation-name is simply ignored, so out of the box a toast appears and
   vanishes as a hard cut and the countdown bar is a zero-width sliver.
   Defining the names here binds the framework's own inline animations to our
   motion, with no !important and no markup change. */
@keyframes arcane-toast-enter {
  from { opacity: 0; transform: translate3d(0, -14px, 0) scale(.97); }
}

@keyframes arcane-toast-exit {
  to { opacity: 0; transform: translate3d(0, -10px, 0) scale(.97); }
}

/* The bar has no inline width, so it is ours to size; scaling from the left
   edge keeps the countdown on the compositor instead of relaying out a 2px
   strip sixty times a second. */
.arcane-toast-progress {
  width: 100%;
  transform-origin: left center;
  opacity: .5;
}

@keyframes arcane-toast-progress {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}

/* Stack depth. The manager inserts at index 0 and the top-anchored container
   lays out in document order, so :nth-child(1) is the newest toast and the
   ones behind it recede. Transform and opacity only — the boxes keep their
   places, so nothing below them moves when the stack changes. */
.arcane-toaster[data-position="topCenter"] .arcane-toast:nth-child(2) {
  transform: scale(.965);
  opacity: .82;
}
.arcane-toaster[data-position="topCenter"] .arcane-toast:nth-child(3) {
  transform: scale(.93);
  opacity: .64;
}

/* ---- Responsive --------------------------------------------------------- */
/* Pane widths remain user-owned. Secondary commands live in tier 1 at every
   width; each narrower tier adds the complete cluster removed from the row. */
@media (max-width: 1400px) {
  .hui-bar-assets { display: none; }
  .hui-bar-overflow.is-tier1 { display: none; }
  .hui-bar-overflow.is-tier2 { display: flex; }
  .hui-doc-id { max-width: 200px; }
}
@media (max-width: 1150px) {
  .hui-bar-file { display: none; }
  .hui-bar-overflow.is-tier2 { display: none; }
  .hui-bar-overflow.is-tier3 { display: flex; }
}
@media (max-width: 1100px) {
  .hui-brand-word { display: none; }
  .hui-doc-id { max-width: 150px; }
}
@media (max-width: 1024px) {
  .hui-bar-history { display: none; }
  .hui-bar-overflow.is-tier3 { display: none; }
  .hui-bar-overflow.is-tier4 { display: flex; }
  [data-hui-responsive-label="mobile-pane"] { font-size: .8rem !important; }
  .hui-splitter { display: none; }
  .hui-shell-body {
    position: relative;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
  }

  .hui-rail,
  .hui-inspector {
    position: absolute;
    inset-block: 0;
    z-index: 32;
    width: min(360px, calc(100vw - 48px));
    max-height: none;
    visibility: hidden;
    pointer-events: none;
    background: var(--hui-panel);
    box-shadow: var(--hui-shadow-md);
    transition:
      transform var(--hui-dur-3) var(--hui-ease-out),
      visibility 0s linear var(--hui-dur-3);
  }

  .hui-rail {
    left: 0;
    border-right: 1px solid var(--hui-border);
    border-bottom: 0;
    transform: translateX(-100%);
  }

  .hui-inspector {
    right: 0;
    border-top: 0;
    border-left: 1px solid var(--hui-border);
    transform: translateX(100%);
  }

  .hui-rail.is-mobile-open,
  .hui-inspector.is-mobile-open {
    visibility: visible;
    pointer-events: auto;
    transform: translateX(0);
    transition-delay: 0s;
  }

  .hui-pane-scrim {
    position: absolute;
    inset: 0;
    z-index: 31;
    display: block;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: color-mix(in srgb, #000 38%, transparent);
    appearance: none;
    cursor: default;
  }

  .hui-mobile-panes { display: flex; }

  .hui-center.is-split {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
  }
  .hui-split-cell.is-code {
    border-top: 1px solid var(--hui-border-soft);
    border-left: 0;
  }
  .hui-bar { width: 100%; min-width: 0; }
  .hui-bar-primary,
  .hui-bar-context { padding-inline: 6px; }
  .hui-doc-id { max-width: 132px; }
  .hui-bar-cluster + .hui-bar-cluster {
    margin-inline-start: 5px;
    padding-inline-start: 5px;
  }
}

/* ---- Phone -------------------------------------------------------------- */
@media (max-width: 700px) {
  .hui-bar {
    --hui-command-height: 44px;
    --hui-context-height: 44px;
  }
  .hui-bar-primary { gap: 4px; padding-inline: 8px; }
  .hui-bar-context {
    grid-template-columns: minmax(0, 1fr) minmax(0, 2.15fr);
    gap: 0;
    padding-inline: 4px;
  }
  .hui-bar-left { min-width: 0; }
  .hui-bar-center { width: 100%; min-width: 0; }
  .hui-bar-context-tools {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    width: 100%;
    min-width: 0;
  }

  .hui-brand { display: flex; }
  .hui-doc-id { max-width: none; }
  .hui-doc-ext { display: none; }

  [data-hui-responsive-label="mobile-pane"] { font-size: .8rem !important; }
  .hui-kind-picker,
  .hui-bar-view-picker {
    min-width: 0;
  }
  .hui-kind-picker {
    width: 100%;
    max-width: 100%;
  }
  .hui-bar-view-picker { width: 100%; max-width: 100%; }
  .hui-mobile-panes {
    display: grid;
    grid-template-columns: auto auto;
  }
  .hui-kind-picker .hui-bar-menu,
  .hui-bar-view-picker .hui-bar-menu { width: 100%; }
  .hui-kind-picker .arcane-button,
  .hui-bar-view-picker .arcane-button {
    width: 100%;
    min-width: 0;
    max-width: 100%;
  }

  .hui-bar-context .hui-bar-cluster + .hui-bar-cluster {
    margin-inline-start: 2px;
    padding-inline-start: 2px;
    border-inline-start: 0;
  }

  .hui-sync-bar {
    flex-wrap: wrap;
    gap: 6px;
    padding: 6px 8px;
  }
  .hui-sync-identity { flex: 1 1 100%; }
  .hui-sync-actions {
    flex: 1 1 100%;
    justify-content: flex-end;
  }

  .hui-status {
    gap: 4px;
    overflow: hidden;
    padding-inline: 8px;
  }
  .hui-status-left { flex: 1 1 auto; overflow: hidden; }
  .hui-status-selection,
  .hui-status-right,
  .hui-status-version { display: none; }
  .hui-status-item { overflow: hidden; text-overflow: ellipsis; }
}

@media (max-width: 430px) {
  #hui-doc-menu {
    position: fixed !important;
    top: calc(var(--hui-command-height) + 4px) !important;
    right: 8px !important;
    left: 8px !important;
    width: auto;
    min-width: 0 !important;
  }

  .hui-brand { display: none; }
  .hui-kind-picker .arcane-button > i:first-child,
  .hui-bar-view-picker .arcane-button > i:first-child,
  .hui-mobile-panes .arcane-button > i { display: none !important; }

  /* The rename pencil is a 40px press target buying an action the rail also
     offers, and it was taking that 40px straight out of the document name —
     at 360px the name was down to an ellipsis. The name wins here; renaming
     stays available from the library row's own menu.
     `MutableText` writes `display: inline-flex` inline on that button, and an
     inline declaration cannot be outranked by a plain author rule; this is the
     same situation the keycap override at the foot of this file documents, so
     `!important` is the mechanism rather than a shortcut. */
  .hui-doc-id .arcane-row > .arcane-button { display: none !important; }
  .hui-doc-id .arcane-row { min-width: 0; }
  .hui-doc-id .arcane-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

@media (pointer: coarse) and (max-width: 1024px) {
  .hui-bar {
    --hui-command-height: 44px;
    --hui-context-height: 44px;
  }
}

@media (pointer: coarse) and (max-width: 1024px), (max-width: 700px) {
  .hui-bar .arcane-button,
  .hui-canvas-toolbar .arcane-button,
  .hui-preview-toolbar .arcane-button,
  .hui-sync-actions .arcane-button,
  .hui-library-create .arcane-button,
  .hui-library-actions .arcane-button,
  .hui-inspector .arcane-button,
  .hui-gloss-preview-controls .arcane-button,
  .hui-hologram-controls .arcane-button,
  [data-arcane-surface="dialog"] .arcane-button {
    min-width: 40px;
    min-height: 40px;
  }

  .hui-library-row { min-height: 44px; }

  .hui-bar-menu-item {
    min-height: 44px;
    padding: 10px 12px !important;
  }
}

/* Everything in this file that moves resolves its duration from --hui-dur-*,
   which 06-motion.css zeroes, so none of it needs an override here. The three
   exceptions are the toast animations: their durations are written inline by
   the renderer and cannot be reached from CSS without !important. What CAN be
   reached is the @keyframes they name — redefining them as no-ops flattens the
   toast layer as completely as zeroing a duration would. */
@media (prefers-reduced-motion: reduce) {
  @keyframes arcane-toast-enter { from { opacity: 1; } }
  @keyframes arcane-toast-exit { to { opacity: 1; } }
  /* The countdown is decoration on top of a timer the user cannot influence;
     with motion off it simply is not drawn. */
  @keyframes arcane-toast-progress { from, to { transform: scaleX(0); } }
}

/* ---------------------------------------------------------------------------
   Framework keycaps and the busy spinner: the two remaining crescents.

   `ArcaneKbd` writes its whole look INLINE on the <kbd> element — a top-lit
   `linear-gradient(180deg, var(--muted), var(--card))` fill plus
   `inset 0 1px 0 rgba(var(--foreground-rgb), .1)`. On a 4px radius that inset
   bends around both top corners into exactly the sliver of light this project
   bans, and the gradient is the top-anchored fade the same rule forbids on a
   chip. There are 19 of them across the palette, the shortcut sheet and the
   top-bar tooltips.

   An inline declaration cannot be outranked by a plain author rule, so these
   are the one place `!important` is the mechanism rather than a shortcut, and
   the override is kept to the two properties that carry the crescent. The
   solid `0 2px 0` drop stays: it is a uniform dark offset, not a light arc,
   and it is what still reads as a key. arcane_jaspr is not edited. */
kbd {
  background: var(--hui-panel-soft) !important;
  box-shadow: 0 2px 0 var(--hui-border) !important;
}

/* The framework loader is a ring with one opaque border side and the rest
   transparent — a rotating arc. Inside a pill button that is a crescent by
   another name, which is the case the audit called out. Scoped to buttons:
   a uniform ring that pulses reads as busy with no arc anywhere in it. */
.arcane-button .arcane-loader,
button .arcane-loader {
  border-color: currentColor !important;
  opacity: .55;
  animation: hui-busy-pulse 1s var(--hui-ease-out) infinite alternate !important;
}

@keyframes hui-busy-pulse {
  from { opacity: .25; }
  to { opacity: .8; }
}

@media (prefers-reduced-motion: reduce) {
  .arcane-button .arcane-loader,
  button .arcane-loader { animation: none !important; opacity: .55; }
}
