/**
 * Command Bar — the two-row frame (CMD-2).
 *
 * Loaded AFTER toolbar.css. `toolbar.css` still styles everything inside the
 * `row1.legacy` compatibility slot and shrinks phase by phase as slots move
 * across; it is deleted in CMD-9.
 *
 * Motion values come from `docs/command-bar-contracts.md` §9 and are the spec,
 * not an illustration. Reduced motion is `transition: none`, matching
 * builder.css — NOT a 1 ms duration, which still schedules work and still
 * animates on a slow machine.
 */

/* The bar's geometry and motion constants live in `tokens.css` `:root`, NOT on
   `.pb-cb`. CMD-2 declared them here and left three design-token gates red for
   it: a class-scoped `--pb-*` definition is a scoped fork (the coarse-pointer
   axis cannot reach it) and a private fork (a register of one). CMD-3 pays that
   down — the gate's own instruction is "re-point it at tokens.css, or promote
   the value into :root if the register genuinely lacks it", and the register
   genuinely lacked a two-row-bar geometry.

   `--pb-cb-ease-out` and `--pb-cb-ease-in` are NOT promoted: CMD-2 minted them
   for the row-2 morph, nothing referenced them, and `--pb-ease-entrance` in
   tokens.css is already byte-identical to the first. CMD-4 mints what the morph
   actually needs when it builds the morph. */
.pb-cb {
  display: flex;
  flex-direction: column;
  background: var(--pb-toolbar-bg);
  color: var(--pb-text);
  -webkit-backdrop-filter: blur(var(--pb-toolbar-blur));
  backdrop-filter: blur(var(--pb-toolbar-blur));
  container-type: inline-size;
  z-index: var(--pb-z-raised);
}

.pb-cb-row {
  display: flex;
  align-items: center;
  gap: var(--pb-space-4);
  min-width: 0;
}

/* Row 1 owns its own padding as of CMD-3. It used to inherit it from
   `.pb-toolbar` on the legacy slot, which stopped being the whole row the
   moment the first control moved out. */
.pb-cb-row1 {
  min-height: var(--pb-cb-row1-h);
  padding: 0 var(--pb-space-5);
  gap: var(--pb-space-3);
}

.pb-cb-row2 {
  position: relative;
  min-height: var(--pb-cb-row2-h);
  padding: 0 var(--pb-space-6);
  gap: var(--pb-space-3);
  background: var(--pb-surface-2);
  border-top: 1px solid var(--pb-border-soft);
  border-bottom: 1px solid var(--pb-border);
}

/* The scope hairline that used to paint row 2's edge violet for section scope
   and blue for element scope is REMOVED (2026-08-28, user request). `data-ctx`
   stays on the row: it is the scope state the panes read, and it was never the
   hairline's private hook. */

.pb-cb-slot {
  display: flex;
  align-items: center;
  gap: var(--pb-space-2);
  min-width: 0;
}

/**
 * Focus groups (CMD-8). One element per Tab stop, `role="group"` applied by
 * `focus-model.js`.
 *
 * Row 2's groups ARE its slots — the class lands on the slot element, so row 2's
 * box tree is byte-identical to CMD-7's and the browser geometry gate did not
 * have to move. Row 1's two groups WRAP slots, which is why they carry the row's
 * own gap: five slots became two flex children, and without the gap the
 * identity, page and legacy slots would have collapsed against each other.
 */
.pb-cb-row1 > .pb-cb-group {
  display: flex;
  align-items: center;
  gap: var(--pb-space-3);
  min-width: 0;
}

.pb-cb-group-row1-identity {
  flex: 0 1 auto;
}

/* Everything after the page strip is pushed right. The rule moved here from
   `.pb-cb-slot-state` in CMD-8 with the group wrapper — same effect, one level
   up, so the gap between "where am I" and "how do I ship it" stays a property
   of the row rather than of whichever control happens to be last on the left. */
.pb-cb-group-row1-destination {
  flex: 0 0 auto;
  margin-inline-start: auto;
}

/* The legacy slot IS what is left of the old bar; let it own its own layout.
   It no longer takes the free space — the page strip does, because the strip is
   the only region whose width decides anything (adaptive collapse). */
.pb-cb-slot-legacy {
  flex: 0 0 auto;
}

.pb-cb-slot-identity,
.pb-cb-slot-state,
.pb-cb-slot-destination {
  flex: 0 0 auto;
}


.pb-cb-slot-destination {
  gap: var(--pb-space-3);
}

/* The two anchors never move. The zone between them is the only thing that
   changes, which is what makes the anchors targetable without looking. */
.pb-cb-slot-history {
  flex: 0 0 auto;
}

.pb-cb-slot-view {
  flex: 0 0 auto;
  margin-inline-start: auto;
}

/* ── Preview toggle (CMD-8, moved from toolbar.css with the control) ──
   ONE button, two glyphs, and the swap is keyed on `aria-pressed` — so the
   accessible state and the visible state cannot drift apart, because there is
   no second assignment to forget. The selectors need this specificity to beat
   the row's generic `button svg` rule. */
.pb-preview-toggle[aria-pressed='true'] {
  background: var(--pb-accent);
  border-color: var(--pb-accent);
  color: var(--pb-on-accent);
}

.pb-preview-toggle .pb-preview-icon-stop,
.pb-preview-toggle[aria-pressed='true'] .pb-preview-icon-play {
  display: none;
}

.pb-preview-toggle[aria-pressed='true'] .pb-preview-icon-stop {
  display: block;
}

/**
 * The morph zone. Height is FIXED from the start even though it is empty until
 * CMD-4: a row that grows when its first pane arrives would move the canvas
 * under the user's cursor, which is the mis-click generator this whole design
 * exists to avoid.
 */
.pb-cb-slot-context {
  position: relative;
  flex: 1 1 auto;
  overflow: hidden;
  height: var(--pb-cb-row2-h);
}

.pb-cb-divider {
  inline-size: 1px;
  block-size: 20px;
  flex: none;
  background: var(--pb-border);
  margin-inline: var(--pb-space-2);
}

 /* Sized from the register, NEVER a literal. `--pb-icon-btn` is 28px on a fine
   pointer and `--pb-tap-min` (44px) on a coarse one; a hardcoded box is exactly
   the shape `ui/icons.js` exists to retire, one level up — a glyph that can
   grow inside a button that cannot is no better than one that cannot grow at
   all. Both rows are `min-height`, so they follow. */
.pb-cb-icon {
  inline-size: var(--pb-icon-btn);
  block-size: var(--pb-icon-btn);
  justify-content: center;
  padding: 0;
}

/* ── Page switcher (CMD-3) ───────────────────────────────────────────────
   The strip and its measurement proxy carry IDENTICAL tab styling. That is
   load-bearing, not incidental: the proxy exists to answer "how wide would the
   tabs be", and a proxy styled differently answers a different question. */

.pb-cb-pages {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
  flex: 1 1 auto;
}

/* The strip SCROLLS rather than clipping. `min-width: 0` is what lets a flex
   item shrink below its content; without it the strip sizes to its tabs and
   pushes the destination cluster off the bar — which is exactly the overlap
   this replaced. */
.pb-cb-pages-strip {
  display: flex;
  align-items: center;
  gap: var(--pb-space-1);
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  /* A horizontal fling that runs out of tabs must not hand the remaining
     momentum to the page or to the browser's back gesture — inherited from the
     pre-CMD toolbar's mobile rule, which had the same problem. */
  overscroll-behavior-x: contain;
}

/* `[hidden]` is a UA `display: none`, and the author `display: flex` above beats
   it. Without this rule the collapsed button and the full strip paint on top of
   each other. `.pb-cb-pages-collapsed[hidden]` below exists for the same reason. */
.pb-cb-pages-strip[hidden] {
  display: none;
}

.pb-cb-pages-strip::-webkit-scrollbar {
  display: none;
}

/* The tabs "go behind" the edge. A MASK, not a painted white gradient: masking
   is background-independent, so it stays correct in dark mode and over the
   bar's translucent blurred surface, where a hard-coded white would show as a
   light rectangle. Applied only while there is genuinely more to scroll to, or
   the last visible tab would be faded for no reason. */
.pb-cb-pages-strip[data-overflow='end'] {
  mask-image: linear-gradient(to right, #000 calc(100% - var(--pb-space-8)), transparent);
}

.pb-cb-pages-strip[data-overflow='start'] {
  mask-image: linear-gradient(to left, #000 calc(100% - var(--pb-space-8)), transparent);
}

.pb-cb-pages-strip[data-overflow='both'] {
  mask-image: linear-gradient(
    to right,
    transparent,
    #000 var(--pb-space-8) calc(100% - var(--pb-space-8)),
    transparent
  );
}

/* Laid out at natural width, off-screen, always. Never collapses, so the
   measurement it yields is independent of the current mode — which is what
   stops the collapse decision oscillating. `visibility: hidden` rather than
   `display: none`: a display-none subtree has no geometry to measure. */
.pb-cb-pages-proxy {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: -99999px;
  display: flex;
  align-items: center;
  gap: var(--pb-space-1);
  visibility: hidden;
  pointer-events: none;
  white-space: nowrap;
}

.pb-cb-ptab,
.pb-cb-ptab-ghost {
  display: inline-flex;
  align-items: center;
  gap: var(--pb-space-2);
  block-size: var(--pb-icon-btn);
  padding: 0 var(--pb-space-4);
  border: 1px solid transparent;
  border-radius: var(--pb-radius-pill);
  background: transparent;
  color: var(--pb-text-dim);
  font-size: var(--pb-font-md);
  white-space: nowrap;
  cursor: pointer;
  transition:
    background var(--pb-cb-quick) var(--pb-cb-ease),
    color var(--pb-cb-quick) var(--pb-cb-ease),
    border-color var(--pb-cb-quick) var(--pb-cb-ease);
}

.pb-cb-ptab:hover {
  background: var(--pb-bg-hover);
  color: var(--pb-text-bright);
}

.pb-cb-ptab[aria-current='page'] {
  border-color: var(--pb-border-soft);
  background: var(--pb-chip-bg);
  color: var(--pb-text-bright);
  font-weight: 600;
}

.pb-cb-ptab-dot {
  inline-size: 5px;
  block-size: 5px;
  border-radius: 50%;
  background: var(--pb-warning);
  flex: none;
}

.pb-cb-ptab-dot[hidden] {
  display: none;
}

.pb-cb-ptab.pb-dragging {
  opacity: 0.5;
}

.pb-cb-ptab.pb-drop-before {
  box-shadow: inset 2px 0 0 var(--pb-accent);
}

.pb-cb-ptab.pb-drop-after {
  box-shadow: inset -2px 0 0 var(--pb-accent);
}

.pb-cb-ptab-rename {
  inline-size: 10ch;
  min-inline-size: 6ch;
  padding: 0 var(--pb-space-2);
  border: 1px solid var(--pb-accent);
  border-radius: var(--pb-radius-sm);
  background: var(--pb-surface);
  color: var(--pb-text-bright);
  font: inherit;
}

/* The ghost must match the real add button's footprint too, or the measured
   natural width is short by one control and the strip collapses too late. */
.pb-cb-ptab-add,
.pb-cb-pages-proxy .pb-cb-ptab-ghost:last-child {
  inline-size: var(--pb-icon-btn);
  block-size: var(--pb-icon-btn);
  justify-content: center;
  padding: 0;
  /* BC-13: a control's glyph, not incidental text. */
  color: var(--pb-text-dim);
}

/* BC-9: the visible home for page actions. Beside the strip (never inside its
   scroll), the same footprint as the add button, and a DIM glyph rather than a
   faint one — it is a control the user is meant to find. */
.pb-cb-ptab-actions {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--pb-icon-btn);
  block-size: var(--pb-icon-btn);
  padding: 0;
  border-radius: var(--pb-radius-pill);
  color: var(--pb-text-dim);
}

.pb-cb-ptab-actions:hover:not([aria-disabled='true']) {
  color: var(--pb-text-bright);
  background: var(--pb-chip-bg);
}

.pb-cb-ptab-actions[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
}

.pb-cb-pages-collapsed {
  display: inline-flex;
  align-items: center;
  gap: var(--pb-space-2);
  block-size: var(--pb-icon-btn);
  padding: 0 var(--pb-space-4);
  border: 1px solid var(--pb-border-soft);
  border-radius: var(--pb-radius-pill);
  background: var(--pb-chip-bg);
  color: var(--pb-text-bright);
  font-size: var(--pb-font-md);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

.pb-cb-pages-collapsed-count {
  color: var(--pb-text-dim);
  font-weight: 400;
}

.pb-cb-pages-collapsed-count:empty {
  display: none;
}

/* Found by the CMD-8 browser focus gate: the trigger rendered while `hidden`.
   `display: inline-flex` above is an AUTHOR rule and `[hidden] { display: none }`
   is a UA rule, so the author one wins — the same trap `builder.css` restates for
   `.pb-btn[hidden]`, which this button does not carry because it is a `bare`
   variant. At any width wide enough for the tabs, BOTH the strip and its
   collapsed replacement were on screen, and the replacement was a Tab stop. */
.pb-cb-pages-collapsed[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  /* The GHOST too. It shares every styling rule with a real tab — that is what
     keeps the measurement honest — so it shared the transition and not the
     suppression, which is the gap the CMD-8 coverage gate found. An offscreen
     proxy animating is not visible, but it is still work scheduled for a user
     who asked for none. */
  .pb-cb-ptab,
  .pb-cb-ptab-ghost {
    transition: none;
  }
}

/* ── Row 1 regions (CMD-3) ───────────────────────────────────────────────
   Identity · location · state · destination. The row never morphs, so nothing
   in here is scope-dependent and nothing animates on selection. */

.pb-cb-identity {
  gap: var(--pb-space-3);
}

/* The breadcrumb slash between the project and the page. It is punctuation, not
   a control: `aria-hidden`, unselectable, and it disappears with the chip label
   at narrow widths rather than floating on its own. */
.pb-cb-crumb {
  color: var(--pb-text-faint);
  font-size: var(--pb-font-md);
  user-select: none;
  flex: none;
}

/* ── Project chip ── */
.pb-cb-project-wrap {
  position: relative;
  display: inline-flex;
  align-items: stretch;
  max-width: 230px;
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-pill);
  background: var(--pb-chip-bg);
  transition:
    background var(--pb-cb-quick) var(--pb-cb-ease),
    border-color var(--pb-cb-quick) var(--pb-cb-ease);
}

.pb-cb-project-wrap:hover {
  background: var(--pb-chip-bg-hover);
  border-color: var(--pb-accent-subtle);
}

.pb-cb-identity .pb-project-chip,
.pb-cb-identity .pb-project-chip-arrow {
  block-size: var(--pb-icon-btn);
  display: inline-flex;
  align-items: center;
  color: var(--pb-text-bright);
  cursor: pointer;
}

.pb-cb-identity .pb-project-chip {
  gap: var(--pb-space-3);
  min-width: 0;
  padding: 0 var(--pb-space-4) 0 var(--pb-space-4);
  border-start-start-radius: var(--pb-radius-pill);
  border-end-start-radius: var(--pb-radius-pill);
  font-size: var(--pb-font-md);
  font-weight: 700;
}

.pb-cb-identity .pb-project-chip .pb-btn-ico {
  display: inline-flex;
  color: var(--pb-accent);
  flex: 0 0 auto;
}

.pb-cb-identity .pb-project-chip-name {
  min-width: 0;
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pb-cb-identity .pb-project-chip-arrow {
  inline-size: var(--pb-icon-btn);
  justify-content: center;
  padding: 0;
  border-start-end-radius: var(--pb-radius-pill);
  border-end-end-radius: var(--pb-radius-pill);
  border-inline-start: 1px solid color-mix(in srgb, var(--pb-border) 75%, transparent);
  color: var(--pb-text-dim);
}

.pb-cb-identity .pb-project-chip-arrow:hover {
  color: var(--pb-text-bright);
  background: var(--pb-bg-hover);
}

/* ── Save pill + presence ── */
.pb-cb-state {
  gap: var(--pb-space-3);
}

.pb-cb-state .pb-telemetry-chip {
  max-width: 190px;
  block-size: var(--pb-icon-btn);
  display: inline-flex;
  align-items: center;
  gap: var(--pb-space-2);
  padding: 0 var(--pb-space-4);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-pill);
  background: var(--pb-chip-bg);
  color: var(--pb-text-dim);
  font-size: var(--pb-font-sm);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  transition:
    background var(--pb-cb-quick) var(--pb-cb-ease),
    border-color var(--pb-cb-quick) var(--pb-cb-ease),
    color var(--pb-cb-quick) var(--pb-cb-ease);
}

.pb-cb-state .pb-telemetry-chip:hover {
  background: var(--pb-chip-bg-hover);
  color: var(--pb-text);
}

.pb-cb-state .pb-telemetry-dot {
  inline-size: 8px;
  block-size: 8px;
  flex: 0 0 8px;
  border-radius: 50%;
  background: var(--pb-success);
}

.pb-cb-state .pb-telemetry-text {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* CMD-4: the tone is ONE attribute written from the two-axis table
   (`save-state.js`), not four classes toggled by an if/else in the component.
   Six tones, one owner — and a state the table can produce but the CSS has no
   rule for degrades to the resting chip rather than to a blank one. */
.pb-cb-state .pb-telemetry-chip[data-tone='saving'] {
  color: var(--pb-accent);
  border-color: var(--pb-accent-subtle);
}

.pb-cb-state .pb-telemetry-chip[data-tone='saving'] .pb-telemetry-dot {
  background: var(--pb-accent);
}

/* Contracts §9: the dot pulses ONLY while saving, and never at rest. A chip
   that animates when nothing is happening is a chip nobody reads.

   `pb-skeleton-pulse` is the repo's existing "work is in flight" animation and
   is already on the PP-2 attention-keyframe ledger, which only shrinks. Minting
   a fifteenth pulse for this dot would have grown it. The contract fixes the
   1.05s cadence, which is a call-site value; it does not fix the property, and
   opacity is what the shared pulse animates. */
.pb-cb-state .pb-telemetry-chip.pb-telemetry-pulsing .pb-telemetry-dot {
  animation: pb-skeleton-pulse 1.05s ease-in-out infinite;
}

.pb-cb-state .pb-telemetry-chip[data-tone='dirty'] .pb-telemetry-dot {
  background: var(--pb-warning);
}

/* Offline is NOT an error. Grey says "nothing is wrong and nothing is
   pending" — which is exactly true of work saved locally with no connection. */
.pb-cb-state .pb-telemetry-chip[data-tone='offline'] .pb-telemetry-dot {
  background: var(--pb-text-faint);
}

.pb-cb-state .pb-telemetry-chip[data-tone='error'] {
  border-color: rgba(var(--pb-danger-rgb), 0.45);
  color: var(--pb-danger);
  background: var(--pb-danger-muted);
}

.pb-cb-state .pb-telemetry-chip[data-tone='error'] .pb-telemetry-dot {
  background: var(--pb-danger);
}

/* Session expired — cloud sync paused. Warning tint, distinct from the danger
   red of a local save failure, because only one of the two can lose work. */
.pb-cb-state .pb-telemetry-chip[data-tone='paused'] {
  border-color: color-mix(in srgb, var(--pb-warning-icon) 45%, transparent);
  color: var(--pb-warning);
  background: color-mix(in srgb, var(--pb-warning-icon) 15%, transparent);
}

.pb-cb-state .pb-telemetry-chip[data-tone='paused'] .pb-telemetry-dot {
  background: var(--pb-warning-icon);
}

.pb-cb-presence:empty {
  display: none;
}

/* ── Destination: agent · Share · Publish · account ── */
.pb-cb-defs {
  position: absolute;
  inline-size: 0;
  block-size: 0;
  overflow: hidden;
}

.pb-cb-destination .pb-agent-trigger {
  inline-size: var(--pb-icon-btn);
  block-size: var(--pb-icon-btn);
  justify-content: center;
  padding: 0;
  border-radius: var(--pb-radius-md);
  cursor: pointer;
}

/* The brand gradient survives the glyph moving into a registry that only emits
   `currentColor`: the outline comes from `icon('skin')` and the paint from the
   defs block the region renders once. CSS beats the presentation attributes on
   the registry root, which is why `fill:none` there does not win. */
.pb-cb-destination .pb-agent-trigger .pb-icon path {
  fill: url(#pb-agent-trigger-grad);
  stroke: none;
}

.pb-cb-destination .pb-agent-trigger:hover {
  background: var(--pb-bg-hover);
}

.pb-cb-destination .pb-agent-trigger.pb-active {
  background: var(--pb-accent-muted);
}

.pb-cb-share {
  block-size: var(--pb-icon-btn);
  padding: 0 var(--pb-space-4);
  gap: var(--pb-space-2);
}

.pb-cb-menu-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

/* The split control: one visual button, two targets. Busy disables BOTH — the
   old bar guarded only the primary, so the arrow stayed live and opened a menu
   whose every row was already disabled. */
.pb-cb-split {
  border-radius: var(--pb-radius-md);
  overflow: hidden;
}

.pb-cb-publish {
  block-size: var(--pb-icon-btn);
  padding: 0 var(--pb-space-4);
  gap: var(--pb-space-2);
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

.pb-cb-publish-arrow {
  block-size: var(--pb-icon-btn);
  inline-size: var(--pb-icon-btn);
  justify-content: center;
  padding: 0;
  border-start-start-radius: 0;
  border-end-start-radius: 0;
  border-inline-start: 1px solid color-mix(in srgb, var(--pb-on-accent) 28%, transparent);
}

.pb-cb-publish-arrow[disabled] {
  cursor: default;
  opacity: 0.7;
}

/* P9 ambient export progress: the control under the pointer becomes the track
   and remains the cancel target. No canvas-covering progress dialog. Moved here
   from toolbar.css with the button it paints. */
.pb-cb-publish.pb-export-tracking {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.pb-cb-publish.pb-export-tracking::before {
  content: '';
  position: absolute;
  z-index: 0;
  inset: 0 auto 0 0;
  inline-size: var(--pb-export-progress, 0%);
  background: color-mix(in srgb, var(--pb-on-accent) 18%, transparent);
  transition: width 120ms linear;
}

.pb-cb-publish.pb-export-tracking #pb-export-btn-label {
  position: relative;
  z-index: 1;
}

.pb-cb-destination .pb-auth-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--pb-space-2);
  block-size: var(--pb-icon-btn);
  padding: 0 var(--pb-space-3) 0 var(--pb-space-2);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-pill);
  background: var(--pb-surface-2);
  color: var(--pb-text-bright);
  font-size: var(--pb-font-base);
  cursor: pointer;
  max-width: 200px;
  transition:
    background var(--pb-cb-quick) var(--pb-cb-ease),
    border-color var(--pb-cb-quick) var(--pb-cb-ease);
}

.pb-cb-destination .pb-auth-chip[hidden] {
  display: none;
}

/**
 * The avatar host, and it MUST carry a size.
 *
 * `.pb-account-avatar-img` is `width: 100%; height: 100%` (account-settings.css),
 * so its host is what decides how big the picture is. `toolbar.css` sized this
 * span at 22×22 for the old bar; CMD-3 deleted ~380 rules with the layout they
 * described and this one went with them, unnoticed because a user with INITIALS
 * renders a text span that sizes itself. A user with a PHOTO got the image at
 * its intrinsic size — a 400px portrait hanging out of a 46px row.
 *
 * This is the repo's percentage-of-an-unsized-ancestor rule (builder/CLAUDE.md,
 * the `.logo-slot` RCA) in its other direction: there, 100% of nothing
 * collapsed to zero; here, 100% of nothing means "however big the file is".
 */
.pb-cb-destination .pb-auth-chip-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: 22px;
  block-size: 22px;
  overflow: hidden;
  border-radius: 50%;
  background: var(--pb-accent);
  color: var(--pb-on-accent);
  font-size: var(--pb-font-xs);
  font-weight: 700;
  line-height: 1;
}

.pb-cb-destination .pb-auth-chip:hover {
  background: var(--pb-bg-hover);
  border-color: color-mix(in srgb, var(--pb-accent) 40%, var(--pb-border));
}

/* ── Command-bar menus ───────────────────────────────────────────────────
   These nodes live inert in the bar until `ToolbarMenu` relocates them into the
   shared `.pb-popover-menu` shell, which owns the surface, positioning, focus
   trap and dismissal. Only the items are styled here. */

.pb-cb-menu {
  display: none;
}

.pb-popover-menu .pb-cb-menu {
  display: block;
}

.pb-cb-menu-item {
  display: flex;
  align-items: center;
  gap: var(--pb-menu-item-gap);
  inline-size: 100%;
  min-block-size: 32px;
  padding: var(--pb-menu-item-pad);
  text-align: start;
  font: inherit;
  font-size: var(--pb-menu-item-font);
  color: var(--pb-menu-item-color);
  background: none;
  border: none;
  border-radius: var(--pb-menu-item-radius);
  cursor: pointer;
  transition: background var(--pb-cb-quick) var(--pb-cb-ease);
}

.pb-cb-menu-item[hidden] {
  display: none;
}

.pb-cb-menu-item:not(:disabled):hover,
.pb-cb-menu-item:focus-visible {
  outline: none;
  background: var(--pb-menu-item-hover);
}

.pb-cb-menu-item .pb-icon {
  color: var(--pb-menu-item-icon);
  flex: none;
}

/* BC-2: a secondary fact on a menu row (a backup's file extension). Pushed to
   the trailing edge so the label reads first. */
.pb-cb-menu-item-hint {
  margin-inline-start: auto;
  padding-inline-start: 8px;
  color: var(--pb-text-dim);
  font-size: var(--pb-font-sm);
  flex: none;
}

/* BC-2: the popover leads with what is wrong. Same group shape as the rest,
   with the danger tint that marks it as the one thing to act on. */
.pb-cb-save-status {
  border-inline-start: 3px solid var(--pb-danger);
  background: var(--pb-danger-muted);
  border-radius: var(--pb-menu-item-radius);
}

.pb-cb-menu-item.pb-menu-danger,
.pb-cb-menu-item.pb-menu-danger .pb-icon {
  color: var(--pb-menu-item-danger);
}

.pb-cb-menu-item.pb-menu-danger:not(:disabled):hover,
.pb-cb-menu-item.pb-menu-danger:focus-visible {
  background: var(--pb-menu-item-danger-hover);
}

.pb-cb-menu-divider {
  block-size: 1px;
  margin: 6px 4px;
  background: var(--pb-menu-divider);
}

.pb-cb-menu-group-label {
  padding: var(--pb-space-2) var(--pb-space-3) var(--pb-space-1);
  font-size: var(--pb-font-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--pb-text-dim);
  user-select: none;
}

.pb-cb-menu-group[hidden] {
  display: none;
}

.pb-project-menu-rename {
  display: grid;
  gap: var(--pb-space-2);
  padding: var(--pb-space-3) var(--pb-space-5) var(--pb-space-4);
}

.pb-project-menu-rename span {
  font-size: var(--pb-font-xs);
  font-weight: 700;
  color: var(--pb-text-dim);
  text-transform: uppercase;
}

/* ── Row 1 responsive ────────────────────────────────────────────────────
   The bar is a container (`container-type: inline-size` on `.pb-cb`), so these
   are container queries, matching what the old bar did. Labels go before
   controls do: a control that disappears cannot be reached at all, whereas a
   control that loses its label still has an accessible name — which is why
   every one of these sets it explicitly. */

@container (max-width: 1279px) {
  /* BC-10: only a SETTLED state may shrink to a dot. "Save failed", a sync
     conflict or unsynced work reduced to a coloured dot is a failure hidden at
     exactly the widths people work at on a laptop. */
  .pb-cb-state .pb-telemetry-chip[data-settled='true'] .pb-telemetry-text {
    display: none;
  }

  .pb-cb-state .pb-telemetry-chip[data-settled='true'] {
    padding-inline: var(--pb-space-3);
  }
}

@container (max-width: 1023px) {
  .pb-cb-identity .pb-project-chip-name,
  .pb-cb-crumb {
    display: none;
  }

  .pb-cb-share .pb-btn-label,
  .pb-cb-destination .pb-auth-chip-label {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .pb-cb-project-wrap,
  .pb-cb-state .pb-telemetry-chip,
  .pb-cb-destination .pb-auth-chip,
  .pb-cb-menu-item,
  .pb-cb-publish.pb-export-tracking::before {
    transition: none;
  }

  .pb-cb-state .pb-telemetry-chip.pb-telemetry-pulsing .pb-telemetry-dot {
    animation: none;
  }
}

/* Phone-only controls are absent everywhere else. Declared BEFORE the query
   that reveals them: equal specificity means source order decides, and a
   default written after its own override is a default that never applies. */
.pb-cb-phone-only {
  display: none;
}

/* The bar is the topmost painted element, so it carries the top safe-area inset
   for the whole shell. Do NOT pad `.pb-app` instead: it is `height: 100dvh`, so
   padding there double-counts and pushes the bottom off-screen.
   `--pb-toolbar-height-actual` is JS-measured, so everything positioned below
   follows this padding automatically.

   Moved here from `.pb-toolbar` in toolbar.css, which stopped being the bar's
   root the moment row 1 grew owners of its own. A media query, not a container
   query: `.pb-cb` cannot query its own container.

   Literally `640px` — `design-tokens-tests.js` PHONE_BAND [560,760] fails
   anything else in band. */
@media (max-width: 640px) {
  .pb-cb-row1 {
    padding-block-start: env(safe-area-inset-top, 0px);
    padding-inline: var(--pb-space-3);
    gap: var(--pb-space-2);
  }

  /* ── The bottom bar (CMD-7) ──
     Row 2 does not get REPLACED by a bottom bar; it BECOMES one. Every pane,
     the morph, the live region and both anchors move as a single element, so
     "exact action parity with desktop" is not a thing anyone has to maintain.

     `position: fixed` takes it out of flow, which is why its height has to be
     published separately — `--pb-toolbar-height-actual` now measures row 1
     alone, which is correct, because row 1 alone is what sits at the top.

     Below the modal layer and above everything that scrolls: a sheet opened
     FROM the bar has to paint over it, and the canvas underneath must not. */
  .pb-cb-row2 {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: var(--pb-z-toolbar-float);
    padding-inline: var(--pb-space-3);
    padding-block-end: env(safe-area-inset-bottom, 0px);
    background: var(--pb-toolbar-bg);
    -webkit-backdrop-filter: blur(var(--pb-toolbar-blur));
    backdrop-filter: blur(var(--pb-toolbar-blur));
    border-block-start: 1px solid var(--pb-border);
    border-block-end: none;
  }

  /* A popover anchored INSIDE the bottom bar must open UPWARD.
     `.pb-zoom-popover` is `top: 100%` (toolbar.css) because on desktop its chip
     lives in a row near the top of the window, where downward is right. Row 2
     becomes bottom-fixed at this exact breakpoint and the chip goes with it, so
     `top: 100%` resolves to the edge of the screen: measured at 390x844 with
     the popover OPEN, top 844 / bottom 949 — every one of its three controls
     (zoom out, reset, zoom in) below the fold and untappable.

     The flip lives here, under the SAME media query that bottom-fixes the row,
     so the two cannot drift apart. Anchoring `inset-block-end: 100%` rather
     than a fixed offset keeps it correct whatever the bar's height resolves to
     (it is published, not assumed — see `--pb-command-bottom-height-actual`).

     NOTE: `toolbar.css` also carries an `@container (max-width: 640px)` block
     for this popover. It does not apply — the popover's computed `max-width` is
     `none` at 390px because no ancestor establishes that container — so its
     width clamp is restated here where it takes effect. */
  .pb-zoom-popover {
    /* Anchored to the BAR, not to the chip.

       `right: 0` against `.pb-zoom-chip-wrap` was putting the popover's right
       edge wherever the chip's is — and the chip's is off-screen: row 2 lays
       out 461px of controls into a 390px viewport (identical at 320, nothing in
       the row shrinks), so the view slot runs from 219 to 461 and the chip with
       it. Measured: popover right 413 against innerWidth 390, the `+` clipped
       at 404. Anchoring to the chip inherits the chip's overflow; anchoring to
       the bar does not, and stays correct if the row's own overflow is fixed
       later — which is a separate change.

       `position: fixed` deliberately, and the containing block is row 2 rather
       than the viewport because the bar carries `backdrop-filter` (a filter
       makes an ancestor the containing block for fixed descendants). Both
       resolve the same here: row 2 is `inset-inline: 0` / `inset-block-end: 0`,
       so its box IS the viewport's width and bottom edge. The offset is
       therefore written in published units, never as a percentage — a
       percentage would mean two different things under the two containing
       blocks. */
    position: fixed;
    top: auto;
    inset-inline: auto var(--pb-space-3);
    inset-block-end: calc(
      var(--pb-command-bottom-height-actual) + env(safe-area-inset-bottom, 0px) +
        var(--pb-space-2)
    );
    margin-top: 0;
    margin-bottom: 0;
    transform-origin: bottom right;
    max-width: calc(100vw - 2 * var(--pb-space-3));
    min-width: 0;
  }

  .pb-cb-phone-only {
    display: inline-flex;
  }

  /* Row 1 keeps identity and Publish. Everything else is in the More sheet —
     which is why that sheet has to carry every one of them (CMD-0 matrix). */
  .pb-cb-slot-pages,
  .pb-cb-slot-state,
  .pb-cb-identity .pb-cb-crumb,
  .pb-cb-destination .pb-cb-share,
  .pb-cb-destination .pb-agent-trigger,
  .pb-cb-destination .pb-auth-chip {
    display: none;
  }

  /* The morph zone takes the width the hidden regions gave up. */
  .pb-cb-slot-context {
    flex: 1 1 auto;
  }

  /* BC-10: row 2's whole right anchor leaves the phone bar. The device
     switcher and the zoom chip pushed it to 461px of controls in a 390px
     screen, clipping the controls at its right edge; they live in More → View
     now, beside Preview and Help (CMD-8), so nothing is lost. */
  .pb-cb-slot-view {
    display: none;
  }

  /* The pane scrolls rather than clipping. On a desktop the zone clips a pane
     that overflows; at 390px that hid real controls with no way to reach them. */
  .pb-cb-slot-context .pb-cb-pane {
    overflow-x: auto;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
  }

  /* BC-10: status survives the phone. The save pill is still dropped at rest —
     row 1 is identity and Publish — but anything that is NOT settled (a failure,
     a conflict, unsynced work, a save in flight) is shown, with its short label. */
  .pb-cb-slot-state:has(.pb-telemetry-chip[data-settled='false']) {
    display: flex;
  }

  .pb-cb-slot-state .pb-cb-presence {
    display: none;
  }

  .pb-cb-state .pb-telemetry-text {
    font-size: 0;
  }

  .pb-cb-state .pb-telemetry-text::after {
    content: attr(data-short);
    font-size: var(--pb-font-sm);
  }

  /* BC-10: the way out of preview, on screen above the bottom bar. */
  .pb-cb-preview-exit {
    position: fixed;
    inset-inline-start: 50%;
    inset-block-end: calc(
      var(--pb-command-bottom-height-actual) + env(safe-area-inset-bottom, 0px) +
        var(--pb-space-4)
    );
    transform: translateX(-50%);
    z-index: var(--pb-z-toolbar-float);
    min-block-size: var(--pb-tap-min);
    box-shadow: var(--pb-shadow-popover);
  }
}

/* The View sheet (BC-10): the device switcher across the sheet, zoom beneath. */
.pb-cb-view-sheet .pb-cb-view-devices {
  inline-size: 100%;
}

.pb-cb-view-sheet .pb-cb-view-devices [role='radio'] {
  flex: 1 1 0;
  min-block-size: var(--pb-tap-min);
}

.pb-cb-view-zoom {
  display: flex;
  gap: var(--pb-space-2);
}

.pb-cb-view-zoom .pb-cb-view-zoom-btn {
  flex: 1 1 0;
  justify-content: center;
}

.pb-cb-slot-mobile {
  flex: 0 0 auto;
  gap: var(--pb-space-1);
}

/* ── Sheet rows (CMD-7) ─────────────────────────────────────────────────── */

.pb-cb-sheet-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.pb-cb-sheet-row {
  display: flex;
  align-items: center;
  gap: var(--pb-space-4);
  inline-size: 100%;
  min-block-size: var(--pb-tap-min);
  padding: 0 var(--pb-space-4);
  border: none;
  border-radius: var(--pb-radius-md);
  background: transparent;
  color: var(--pb-text-bright);
  font: inherit;
  font-size: var(--pb-font-md);
  text-align: start;
  cursor: pointer;
}

.pb-cb-sheet-page {
  display: flex;
  align-items: stretch;
  gap: var(--pb-space-1);
}

.pb-cb-sheet-page > .pb-cb-sheet-row {
  flex: 1;
  min-inline-size: 0;
}

/* FN-8: a Share row carries what the RECIPIENT can do, on its own line. The row
   becomes two-line, so its block size grows from the tap minimum rather than
   being clamped to it, and the label column wraps instead of the hint pushing
   the glyph out of the row. */
.pb-cb-sheet-row-hint {
  display: block;
  inline-size: 100%;
  font-size: var(--pb-font-sm);
  font-weight: 400;
  line-height: 1.35;
  color: var(--pb-text-dim);
  text-align: start;
  white-space: normal;
}

.pb-cb-sheet-row:has(.pb-cb-sheet-row-hint) {
  flex-wrap: wrap;
  align-items: start;
  block-size: auto;
  padding-block: var(--pb-space-3);
}

/* BC-9: a disabled page action's reason, directly under its row. It is a
   sibling, not a child, so the row's accessible name stays the action. */
.pb-cb-sheet-row-reason {
  margin: calc(-1 * var(--pb-space-2)) 0 var(--pb-space-2);
  padding-inline-start: calc(var(--pb-icon-sm) + var(--pb-space-4) + var(--pb-space-4));
}

.pb-cb-sheet-row:has(.pb-cb-sheet-row-hint) > .pb-cb-sheet-row-hint {
  /* Second line, aligned under the label rather than under the glyph. */
  padding-inline-start: calc(var(--pb-icon-sm) + var(--pb-space-4));
}

.pb-cb-sheet-page-actions {
  display: grid;
  place-items: center;
  flex: 0 0 var(--pb-tap-min);
  min-inline-size: var(--pb-tap-min);
  min-block-size: var(--pb-tap-min);
  border-radius: var(--pb-radius-md);
}

.pb-cb-sheet-page-actions:hover,
.pb-cb-sheet-page-actions:focus-visible {
  background: var(--pb-bg-hover);
}

.pb-cb-sheet-row:hover,
.pb-cb-sheet-row:focus-visible {
  background: var(--pb-bg-hover);
}

.pb-cb-sheet-row[aria-current='page'] {
  font-weight: 700;
}

/* ── The save popover: a HUB, not a chronology (CMD-4) ───────────────────
   Three groups with different persistence and different restore semantics.
   They are never interleaved, and the visible separation IS the contract —
   "restore a checkpoint" and "undo an edit" are not the same promise, and a
   single merged list would imply they were. */

.pb-cb-save-popover .pb-cb-save-group {
  padding: var(--pb-space-3) var(--pb-space-2);
}

.pb-cb-save-popover .pb-cb-save-group + .pb-cb-save-group {
  border-block-start: 1px solid var(--pb-menu-divider);
}

.pb-cb-save-heading {
  display: flex;
  align-items: center;
  gap: var(--pb-space-2);
  margin: 0 0 2px;
  padding-inline: var(--pb-space-3);
  font-size: var(--pb-font-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--pb-text-dim);
}

.pb-cb-save-count {
  padding: 0 var(--pb-space-2);
  border-radius: var(--pb-radius-pill);
  background: var(--pb-chip-bg);
  color: var(--pb-text);
  font-weight: 700;
  letter-spacing: 0;
}

.pb-cb-save-blurb {
  margin: 0 0 var(--pb-space-2);
  padding-inline: var(--pb-space-3);
  font-size: var(--pb-font-sm);
  /* BC-13: this sentence is the status ("No edits yet", "3 named versions"). */
  color: var(--pb-text-dim);
}

/* Active session time. Sits below the blurb, dimmer than it: the group's
   subject is the edit history, and how long the tab has been open is a
   footnote to that — never the headline the pill used to make it. */
.pb-cb-save-meta {
  margin: calc(var(--pb-space-2) * -1) 0 var(--pb-space-2);
  padding-inline: var(--pb-space-3);
  font-size: var(--pb-font-xs);
  font-variant-numeric: tabular-nums;
  color: var(--pb-text-dim);
}

/* ── The destination card (CMD-4) ────────────────────────────────────────
   First thing in the publish menu, because "where does this go and is it
   current" is the question the arrow is opened to answer. Its copy is a STATE,
   never a count — there is no diff service, and a fabricated number beside a
   Publish button is the kind of detail a user trusts BECAUSE it looks
   computed. */

.pb-cb-destination-card {
  padding: var(--pb-space-3) var(--pb-space-3) var(--pb-space-2);
}

.pb-cb-destination-address {
  margin: 0;
  font-size: var(--pb-font-md);
  font-weight: 700;
  color: var(--pb-text-bright);
  overflow-wrap: anywhere;
}

.pb-cb-destination-detail {
  margin: 2px 0 var(--pb-space-2);
  font-size: var(--pb-font-sm);
  /* BC-13: "Up to date" / "Changes since you published" is the answer the
     card exists to give. */
  color: var(--pb-text-dim);
}

/* "Behind" is the only state with something to do about it, so it is the only
   one that gets ink. Up-to-date and unpublished are statements of fact. */
.pb-cb-destination-card[data-state='behind'] .pb-cb-destination-detail {
  color: var(--pb-warning);
}

/* ── The morph zone and its panes (CMD-5) ────────────────────────────────
   The zone's HEIGHT is fixed and was fixed before there was anything in it —
   see `.pb-cb-slot-context` above. Everything here lays out INSIDE that height;
   nothing may change it, or the canvas moves under the cursor. */

.pb-cb-pane {
  display: flex;
  align-items: center;
  gap: var(--pb-space-2);
  block-size: 100%;
  min-width: 0;
  /* The pane is allowed to overflow horizontally and be clipped by the zone;
     it is never allowed to wrap, because a wrapped pane is a taller pane. */
  flex-wrap: nowrap;
  overflow: hidden;
}

/* Contracts §9: 190ms, out `translateY(-5px)` + fade, in `+5px → 0`. Height is
   NOT animated and is not animatable here — the zone owns it. */
@keyframes pb-cb-pane-in {
  from {
    opacity: 0;
    transform: translateY(5px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.pb-cb-pane-enter {
  animation: pb-cb-pane-in var(--pb-cb-morph) var(--pb-cb-ease) 1;
}

.pb-cb-pane-label {
  display: inline-flex;
  align-items: center;
  gap: var(--pb-space-2);
  padding-inline-end: var(--pb-space-2);
  font-size: var(--pb-font-md);
  font-weight: 600;
  color: var(--pb-text-bright);
  white-space: nowrap;
  min-width: 0;
}

.pb-cb-pane-name {
  overflow: hidden;
  text-overflow: ellipsis;
  max-inline-size: 22ch;
}

/* The same two-colour hierarchy the canvas uses, so the bar and the canvas
   agree about what is selected without either explaining itself. */
.pb-cb-pane-swatch {
  inline-size: 8px;
  block-size: 8px;
  flex: none;
  border-radius: 2px;
  background: var(--pb-text-faint);
}

.pb-cb-pane-swatch[data-accent='section'] {
  background: var(--pb-accent-section);
}

.pb-cb-pane-swatch[data-accent='element'] {
  background: var(--pb-accent-element);
}

.pb-cb-pane-sub,
.pb-cb-pane-note {
  font-style: normal;
  font-weight: 400;
  /* BC-13: row 2's context line names what the pane acts on — 12px and the
     AA-tuned secondary colour, not the 11px faint step. */
  font-size: var(--pb-font-base);
  color: var(--pb-text-dim);
  white-space: nowrap;
}

.pb-cb-pane-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--pb-space-2);
  block-size: var(--pb-icon-btn);
  padding: 0 var(--pb-space-3);
  border: 1px solid transparent;
  border-radius: var(--pb-radius-md);
  background: transparent;
  color: var(--pb-text);
  font-size: var(--pb-font-sm);
  white-space: nowrap;
  cursor: pointer;
  transition:
    background var(--pb-cb-quick) var(--pb-cb-ease),
    color var(--pb-cb-quick) var(--pb-cb-ease);
}

.pb-cb-pane-btn:not(:disabled):hover {
  background: var(--pb-bg-hover);
  color: var(--pb-text-bright);
}

.pb-cb-pane-btn:disabled {
  color: var(--pb-text-faint);
  cursor: default;
}

/* Danger is ink, not a filled surface: a red button in a row of grey ones reads
   as the primary action, which is the opposite of what a delete should look
   like sitting next to Duplicate. */
.pb-cb-pane-btn-danger:not(:disabled) {
  color: var(--pb-danger);
}

.pb-cb-pane-btn-danger:not(:disabled):hover {
  background: var(--pb-danger-muted);
  color: var(--pb-danger);
}

@media (prefers-reduced-motion: reduce) {
  .pb-cb-pane-enter {
    animation: none;
  }

  .pb-cb-pane-btn {
    transition: none;
  }
}

/* ── Element-pane controls (CMD-6) ───────────────────────────────────────
   The numeric stepper is hand-built because the kit has no numeric field —
   `createLabeledInput` is a text input and design-widgets' sliders are
   panel-width. Three elements, one commit path, and it stays that way. */

.pb-cb-stepper {
  display: inline-flex;
  align-items: center;
  block-size: var(--pb-icon-btn);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-md);
  background: var(--pb-chip-bg);
  overflow: hidden;
}

.pb-cb-stepper-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 22px;
  block-size: 100%;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--pb-text-dim);
  cursor: pointer;
}

.pb-cb-stepper-btn:not(:disabled):hover {
  background: var(--pb-bg-hover);
  color: var(--pb-text-bright);
}

.pb-cb-stepper-field {
  inline-size: 4ch;
  min-inline-size: 0;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--pb-text-bright);
  font: inherit;
  font-size: var(--pb-font-sm);
  text-align: center;
}

.pb-cb-stepper-field:disabled {
  color: var(--pb-text-faint);
}

/* A pressed inline mark. `aria-pressed`, so the state is in the accessibility
   tree and not only in the paint. */
.pb-cb-pane-btn[aria-pressed='true'] {
  background: var(--pb-accent-muted);
  color: var(--pb-accent);
}

/* ── The left rail's route into a glass panel (CMD-6) ────────────────────
   With auto-open off, the rail is one of only two ways into the panel for the
   types a glass panel claims. It sits ABOVE the section body because the user
   is editing an element inside a section and both are true at once. */

.pb-props-element-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--pb-space-3);
  margin-block-end: var(--pb-space-4);
  padding: var(--pb-space-3) var(--pb-space-4);
  border: 1px solid var(--pb-accent-subtle);
  border-radius: var(--pb-radius-md);
  background: var(--pb-accent-muted);
}

.pb-props-element-label {
  margin: 0;
  font-size: var(--pb-font-sm);
  font-weight: 700;
  color: var(--pb-text-bright);
}

/* ── Boot skeleton (CMD-8b) ──────────────────────────────────────────────
   The shape of this bar, before there is one. Built from the SAME row classes
   (`ui/boot-skeleton.js`), so the heights, the borders and the phone rule that
   sends row 2 to the bottom of the screen all apply to it without a second
   description that has to be kept in sync.

   What it replaces: one shimmering block sized by `--pb-toolbar-height` (56px)
   for a bar that measures 90 — so the canvas sat 34px too high for the whole
   load and dropped when the real bar arrived. Measured, not estimated;
   `command-bar-geometry.mjs` now fails on any difference between the two. */

.pb-boot-sk {
  /* It IS the bar's box for the duration: the toolbar takes its height from
     this, which is why the reserved space cannot be wrong.

     Deliberately NOT `.pb-cb`. That class carries `container-type` and
     `backdrop-filter`, either of which establishes a containing block for
     fixed-position descendants — and the phone's row 2 is `position: fixed`.
     Reusing it pinned the bottom band under row 1 instead of to the viewport,
     the same failure CMD-7 hit with the real bar. Column layout is all this
     needs; the rows paint their own surfaces. */
  display: flex;
  flex-direction: column;
  inline-size: 100%;
}

.pb-boot-sk-row {
  /* The real rows lay out their slots; this one lays out blocks. Same gap, so
     the blocks land where the controls will. */
  gap: var(--pb-space-3);
}

.pb-boot-sk-spacer {
  flex: 1 1 auto;
}

/* Row 2's real height is its 42px CONTEXT ZONE plus the row's two 1px borders,
   not its min-height — see `.pb-cb-slot-context`. The skeleton's row-2 spacer
   carries the same height so the two rows measure the same; without it the
   skeleton is 2px short and the canvas still moves, just less. */
.pb-boot-sk-zone {
  block-size: var(--pb-cb-row2-h);
}

/**
 * TWO layers: a solid fill and a sweep across it.
 *
 * The fill is `--pb-surface-hover` — the same paint `.pb-skeleton` uses for
 * every other placeholder in the app, so a boot block and a section-list block
 * are the same grey. The first draft used the sidebar's translucent gradient
 * alone and the row-2 blocks were invisible against `--pb-surface-2`: a
 * skeleton nobody can see is a blank bar with extra steps.
 *
 * The sweep reuses `pb-boot-shell-shimmer` (builder.css) rather than minting a
 * keyframe, and only the first layer is sized `200%`, so only it moves.
 */
.pb-boot-sk-block {
  flex: none;
  block-size: var(--pb-icon-btn);
  background-color: var(--pb-surface-hover);
  background-image: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, var(--pb-bg-elevated) 72%, transparent) 50%,
    transparent 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: pb-boot-shell-shimmer 1.4s ease-in-out infinite;
}

.pb-boot-sk-sq {
  inline-size: var(--pb-icon-btn);
  border-radius: var(--pb-radius-md);
}

.pb-boot-sk-pill {
  border-radius: var(--pb-radius-pill);
}

/* Below the phone breakpoint the real row 2 becomes the fixed bottom bar, and
   this one goes with it — the skeleton's whole point is to promise the layout
   that is coming. Literally `640px`: design-tokens-tests PHONE_BAND [560,760]
   fails anything else in band. */
/* The phone-only sheet triggers, hidden everywhere else — the same shape as
   `.pb-cb-phone-only`, which is what they stand in for. */
.pb-boot-sk-as-sheet {
  display: none;
}

@media (max-width: 640px) {
  /* The top strip keeps identity and Publish, and drops everything else — the
     same set the real phone block drops, named rather than counted. */
  .pb-boot-sk-as-tab,
  .pb-boot-sk-as-save,
  .pb-boot-sk-as-agent,
  .pb-boot-sk-as-share,
  .pb-boot-sk-as-account,
  /* And row 2's right anchor cannot carry these two at 390px (CMD-8); they are
     in the More sheet there. */
  .pb-boot-sk-as-preview,
  .pb-boot-sk-as-help {
    display: none;
  }

  .pb-boot-sk-as-sheet {
    display: block;
  }

  /* MEASURED, not guessed: at 390px the real bottom bar gives its two anchors
     105 + 90 + 194 of the 390 available and the morph zone is squeezed to
     ZERO — so a skeleton promising three pane verbs there promises a region
     that does not appear. (That the zone collapses at all is a phone-layout
     finding of its own; it belongs to the bar, not to its skeleton, and it is
     written down in the CMD-8b summary rather than quietly worked around.) */
  .pb-boot-sk-as-verb {
    display: none;
  }

  /* The right anchor's real widths on a phone: three 34px device segments and
     an 82px zoom chip, against 128 + 74 on a desktop. */
  .pb-boot-sk-as-viewport {
    inline-size: 102px;
  }

  .pb-boot-sk-as-zoom {
    inline-size: 82px;
  }
}

/* Contracts §9. The shimmer is an infinite animation and was NOT suppressed —
   the boot shell has run it at 1.4s forever for users who asked for no motion.
   Fixed here for the skeleton and in builder.css for the sidebar and canvas
   blocks it shares the keyframe with. */
@media (prefers-reduced-motion: reduce) {
  .pb-boot-sk-block {
    animation: none;
    /* Still a placeholder, just a still one: the fill stays, the sweep goes. */
    background-image: none;
  }
}

/* ── First-run gate (CMD-8c) ─────────────────────────────────────────────
 * `body[data-state='first-run']` is set by `shell.js` whenever EVERY page has
 * zero sections — so it is not "the first ever run", it is any empty project,
 * including a saved one the user just reopened.
 *
 * What it is for: an empty project has nothing to undo, no breakpoint worth
 * switching, nothing to publish and no pages worth tabbing between, so the
 * stage says "Where do we start?" and the bar gets out of its way.
 *
 * What went wrong: the gate's own comment said it kept "the project chip / the
 * Cmd+K hint / the auth chip", and CMD-3's retarget hid the project chip and
 * the whole destination slot — which contains the account chip. Every control
 * in row 1 was hidden and row 2 with it, so an empty project showed a dead 46px
 * strip and users reasonably asked where the toolbar had gone.
 *
 * So: identity and account stay, because "what am I working on" and "who am I"
 * are true of an empty project. Everything that needs CONTENT goes.
 */
body[data-state='first-run'] #pb-sidebar-toggle,
body[data-state='first-run'] .pb-cb-slot-identity .pb-cb-crumb,
body[data-state='first-run'] .pb-cb-slot-pages,
body[data-state='first-run'] .pb-cb-slot-state,
body[data-state='first-run'] .pb-cb-destination .pb-agent-trigger,
body[data-state='first-run'] .pb-cb-destination .pb-cb-share,
body[data-state='first-run'] .pb-cb-destination .pb-cb-split,
body[data-state='first-run'] .pb-cb-row2 {
  display: none;
}

/* With the pages strip gone there is nothing to take the free space, so the
   destination group's `margin-inline-start: auto` is what keeps the account
   chip at the right edge — same rule as always, just worth knowing it is
   load-bearing here. */

.pb-cb-menu-item-copy {
  display: grid;
  gap: var(--pb-space-1);
}

.pb-cb-menu-item-label {
  min-inline-size: 0;
}

.pb-cb-menu-item-hint {
  font-size: var(--pb-font-sm);
  color: var(--pb-text-dim);
}
