/* Light is the default (no attribute needed) — chosen so a diagram
   exported to Google Docs, which always lands on a white page, matches
   what the exporter was looking at while drawing it. Dark is opt-in via
   Settings (see ui/AppMenu.js / src/theme.js), applied by setting
   data-theme="dark" on <html>.
   FAB buttons (the floating add/delete/color/parent-nav controls) and the
   color swatch palette they open are deliberately left out of this token
   system — they stay a fixed dark, high-contrast "glass" chrome in both
   themes, the same way a floating toolbar often does regardless of the
   page behind it. */
:root {
  /* Not a color token — the height every floating HUD chip and control-bar
     button is built around, so the two stay visually matched. */
  --topbar-height: 44px;
  /* A step darker than --bg-surface — Figma-style neutral canvas gray, not
     this app's old near-white, so a paper-white block (see --bg-surface /
     BlockRenderer's blockFill) actually reads as a sheet resting on the
     canvas instead of blending into it. */
  --bg-canvas: #e7e9ee;
  --bg-surface: #ffffff;
  --bg-surface-2: #ffffff;
  --bg-well: #f3f5f8;
  --bg-hover: #eef1f5;
  --bg-hover-strong: #e2e7ee;
  --border: #e1e5eb;
  --border-strong: #c9d0da;
  --text-primary: #1c2431;
  --text-strongest: #000000;
  --text-secondary: #48505c;
  --text-muted: #6b7280;
  --text-faint: #99a1ad;
  --text-faint-2: #b0b7c3;
  --accent: #2f6fed;
  --accent-strong: #2f6fed;
  --accent-strong-hover: #1d5fe0;
  --success: #3ecf5d;
  --danger: #e5484d;
  --danger-tint: rgba(229, 72, 77, 0.08);
  --warning: #ffb454;
  --warning-tint: rgba(255, 180, 84, 0.12);
  --warning-text: #8a5a12;
  --warning-border: #f0c988;
  --backdrop: rgba(15, 18, 24, 0.35);
}

:root[data-theme='dark'] {
  --bg-canvas: #12161d;
  --bg-surface: #1e2530;
  --bg-surface-2: #1a1f28;
  --bg-well: #10151c;
  --bg-hover: #2c3644;
  --bg-hover-strong: #3a4556;
  --border: #2c3644;
  --border-strong: #3a4556;
  --text-primary: #e6e9ef;
  --text-strongest: #ffffff;
  --text-secondary: #c3c9d4;
  --text-muted: #8b93a3;
  --text-faint: #6b7889;
  --text-faint-2: #4a5568;
  --accent: #4f8cff;
  --accent-strong: #2f6fed;
  --accent-strong-hover: #3f7bf5;
  --success: #3ecf5d;
  --danger: #e5484d;
  --danger-tint: rgba(229, 72, 77, 0.12);
  --warning: #ffb454;
  --warning-tint: rgba(255, 180, 84, 0.08);
  --warning-text: #d8a760;
  --warning-border: #4a3a1c;
  --backdrop: rgba(0, 0, 0, 0.35);
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  margin: 0;
  height: 100%;
  font-family: -apple-system, Segoe UI, Roboto, sans-serif;
  overflow: hidden;
  background: var(--bg-canvas);
}

/* The canvas now fills the whole viewport on its own — every other piece
   of chrome (the HUD chips, the control bar, the inspector) is a floating,
   `position: fixed` layer over it rather than a grid row that reserves its
   own strip of screen, Figma-editor style. */
#app {
  position: relative;
  height: 100%;
  height: 100dvh;
  overflow: hidden;
}

/* Spans the full width at the very top so its two chips can be laid out
   with real flexbox space-between (never colliding on a narrow screen),
   but carries no background/border of its own — only the two chips inside
   it look like anything, so this reads as "two floating HUDs", not a bar. */
.topbar-hud {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 31;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 10px 0;
  /* Lets a stray tap between the two chips (or below them, once they wrap
     tall) fall through to the canvas underneath instead of this row eating
     it — each chip below opts back into being clickable. */
  pointer-events: none;
}

.hud-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  height: var(--topbar-height);
  padding: 0 10px;
  background: var(--bg-surface);
  color: var(--text-primary);
  border-radius: calc(var(--topbar-height) / 2);
  box-shadow: 0 2px 10px rgba(15, 18, 24, 0.12), 0 0 0 1px var(--border);
  pointer-events: auto;
  min-width: 0;
}

.hud-chip-left {
  /* Can genuinely be the widest thing on the row (a long breadcrumb) — caps
     it against the right-hand chip's own width instead of the two ever
     fighting over the same pixels; the breadcrumb itself scrolls inside
     that budget (see .breadcrumb's overflow-x). */
  flex: 1 1 auto;
  min-width: 0;
  max-width: min(560px, 70vw);
}

.hud-chip-right {
  flex: 0 0 auto;
}

#topbar .brand {
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.doc-sync {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Opens the sliding app menu — visible on every screen size now, always
   the leftmost thing in the header. */
.menu-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  flex-shrink: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-secondary);
  cursor: pointer;
  /* The open drawer sits at the same left edge as this button (see
     .topbar-menu) — without its own stacking position it would be
     covered and unclickable, the only way to close the drawer being the
     backdrop or Escape. */
  position: relative;
  z-index: 31;
}

.menu-toggle:hover:not(:disabled) {
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.menu-toggle:disabled {
  opacity: 0.4;
  cursor: default;
}

.topbar-menu-backdrop:not([hidden]) {
  display: block;
  position: fixed;
  inset: 0;
  background: var(--backdrop);
  z-index: 29;
}

/* A drawer fixed to the left edge and off-screen until opened — the same
   panel and behavior on every screen size, rather than a desktop inline
   row plus a separate mobile dropdown. Starts below the header rather
   than at the very top of the viewport, so its own first row (New)
   doesn't sit underneath the header's hamburger/brand/breadcrumb. */
.topbar-menu {
  position: fixed;
  /* Full height, not offset below a reserved header row (there isn't one
     any more) — but the top-left HUD chip (see .topbar-hud) floats at a
     higher z-index than this drawer and sits in that same top-left
     corner, so the drawer's own top padding has to clear the chip's
     height itself (below) or its first row or two would render underneath
     the chip and be genuinely unclickable, not just visually crowded. */
  top: 0;
  bottom: 0;
  left: 0;
  width: min(280px, 85vw);
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--bg-surface);
  border-right: 1px solid var(--border);
  box-shadow: 12px 0 32px rgba(0, 0, 0, 0.5);
  padding: calc(10px + var(--topbar-height) + 12px) 12px 16px;
  overflow-y: auto;
  transform: translateX(-100%);
  transition: transform 0.22s ease;
  z-index: 30;
}

.topbar-menu.open {
  transform: translateX(0);
}

/* The doc-sync cluster is flag-gated (see config.js) and, unlike the
   app-menu rows above it, was never designed as a vertical list — a
   compact row still reads fine inside the drawer. */
.topbar-menu .doc-sync {
  padding: 10px;
  flex-wrap: wrap;
}

/* Always-visible header actions: undo/redo, Share, Start Session. */
.header-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Who else is here right now — a small avatar stack, top-right, present
   only once someone actually has joined (see ui/OnlineUsers.js). Overlaps
   each avatar slightly, the same "stack of coins" layout most
   presence-avatar rows use, rather than a wide row that grows without
   bound as more people join. */
.online-users {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  margin-left: 4px;
}

.online-users[hidden] {
  display: none;
}

.online-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  margin-left: -8px;
  border-radius: 50%;
  border: 2px solid var(--bg-surface);
  color: #0b0e13;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.online-avatar:first-child {
  margin-left: 0;
}

.header-icon-button {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-secondary);
  cursor: pointer;
}

.header-icon-button:hover:not(:disabled) {
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.header-icon-button:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Live-session state — a mode the whole app is in, so the icon itself
   changes color rather than relying on a label. */
.session-button.active {
  color: var(--success);
  border-color: var(--success);
}

.session-button.connecting {
  color: var(--warning);
  border-color: var(--warning);
}

.session-peer-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border-radius: 8px;
  background: var(--success);
  color: #0b1017;
  font-size: 9px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
}

/* Bottom-right, stacked oldest-on-top, so a second toast while one is
   already showing doesn't cover it up. Kept clear of the FAB stack
   (bottom-right too) by sitting above it with margin, not overlapping. */
.app-toast-stack {
  position: fixed;
  right: 16px;
  bottom: 96px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: min(360px, calc(100vw - 32px));
  z-index: 40;
}

.app-toast {
  padding: 12px 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.5;
}

.app-toast-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
}

.app-toast-button {
  padding: 5px 12px;
  background: var(--bg-hover);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  color: var(--text-primary);
  font-size: 12px;
  cursor: pointer;
}

.app-toast-button:hover {
  background: var(--bg-hover-strong);
}

/* The app-menu drawer's own rows (New/Open/Save/Export/Settings). */
.app-menu {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.app-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  background: none;
  border: none;
  border-radius: 6px;
  color: var(--text-secondary);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}

.app-menu-item:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.app-menu-item:disabled {
  opacity: 0.4;
  cursor: default;
}

.app-menu-item svg {
  flex-shrink: 0;
}

/* The dot marks edits not yet written into the page address — same idea
   as before, just living on the Save row instead of a header button. */
.app-menu-item.unsaved::after {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: auto;
  border-radius: 50%;
  background: var(--warning);
  flex-shrink: 0;
}

.app-menu-divider {
  height: 1px;
  margin: 8px 6px;
  background: var(--border);
}

.app-menu-chevron {
  margin-left: auto;
  transition: transform 0.15s ease;
  flex-shrink: 0;
}

.app-menu-item[aria-expanded="true"] .app-menu-chevron {
  transform: rotate(90deg);
}

/* Shared by every expandable row (Settings, Export — see AppMenu.js's
   expandable()), which each toggle via the plain `hidden` attribute. The
   `display: flex` below is what actually needs overriding while hidden:
   same specificity as the bare class rule, so without this the browser's
   own default `[hidden] { display: none }` loses to it and the body never
   visually collapses at all, whatever `.hidden` says. */
.app-menu-settings-body {
  display: flex;
  flex-direction: column;
}

.app-menu-settings-body[hidden] {
  display: none;
}

.app-menu-toggle-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 8px 34px;
  color: var(--text-secondary);
  font-size: 13px;
  cursor: pointer;
}

.app-menu-toggle-row input[type='checkbox'] {
  width: 15px;
  height: 15px;
  accent-color: var(--accent);
}

/* A sub-item inside an expandable body (see AppMenu.js's expandable/
   subItem) — same indentation as a toggle row, so Export's JSON/YAML
   choices line up under "Export" the same way Animate/Dark mode line up
   under Settings. */
.app-menu-subitem {
  padding-left: 34px;
}

.doc-sync-status {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
}

.doc-sync-status[data-state="synced"] {
  color: var(--success);
}

.doc-sync-status[data-state="error"] {
  color: var(--danger);
}

.doc-sync-status[data-state="saving"],
.doc-sync-status[data-state="loading"] {
  color: var(--warning);
}

.doc-sync-button {
  background: var(--accent-strong);
  color: white;
  border: none;
  border-radius: 5px;
  padding: 5px 12px;
  font-size: 12px;
  cursor: pointer;
}

.doc-sync-button:hover {
  background: var(--accent-strong-hover);
}

.doc-sync-button:disabled {
  background: var(--bg-hover);
  color: var(--text-faint);
  cursor: default;
}

.doc-sync-settings {
  background: none;
  border: 1px solid var(--border);
  border-radius: 5px;
  width: 26px;
  height: 26px;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}

.doc-sync-settings:hover {
  color: var(--text-primary);
  border-color: var(--border-strong);
}

/* The navigate-up button, leftmost in the control bar. Muted rather than
   accent-blue so "add a block" (still the bar's rightmost, brightest
   button) stays the one primary action. Two-class selector on purpose:
   the base .fab rules live further down the file, so a single .fab-left
   would lose the background on source order alone. */
.fab.fab-left {
  background: #2c3644;
}

.fab.fab-left:hover {
  background: #3a4556;
}

/* Floats over the canvas exactly where the name it's replacing is drawn,
   so renaming reads as editing the label in place. */
.name-editor {
  position: fixed;
  z-index: 30;
  box-sizing: border-box;
  padding: 2px 6px;
  background: var(--bg-well);
  border: 1px solid var(--accent);
  border-radius: 4px;
  color: var(--text-primary);
  font-family: -apple-system, Segoe UI, Roboto, sans-serif;
  font-size: 13px;
  text-align: center;
  outline: none;
}

.name-editor[hidden] {
  display: none;
}

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  white-space: nowrap;
  scrollbar-width: none;
}

.breadcrumb::-webkit-scrollbar {
  display: none;
}

.breadcrumb .crumb {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 12px;
  padding: 2px 4px;
  cursor: pointer;
  border-radius: 3px;
}

.breadcrumb .crumb:hover {
  color: var(--text-secondary);
  background: var(--bg-hover);
}

/* Clickable too (opens rename), so it keeps the pointer cursor and hover
   feedback of an ordinary crumb — only the color marks it as "you are
   here" rather than "go here". */
.breadcrumb .crumb.current {
  color: var(--text-primary);
}

.breadcrumb .crumb.current:hover {
  color: var(--text-strongest);
}

.breadcrumb .crumb-sep {
  flex-shrink: 0;
  color: var(--text-faint-2);
  font-size: 11px;
}

#scene-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  background: var(--bg-canvas);
  cursor: default;
  touch-action: none;
}

/* The single floating control bar along the bottom edge — every button
   that used to be its own fixed-position FAB (four separate elements
   anchored to three different corners) now lives in this one pill,
   Figma's bottom toolbar being the model: a paper-white strip, softly
   shadowed, centered and clear of the canvas edges on every screen size.
   `env(safe-area-inset-bottom)` keeps it clear of a phone's home-indicator
   gesture bar. */
.control-bar {
  position: fixed;
  left: 50%;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: calc(100vw - 24px);
  padding: 6px;
  background: var(--bg-surface);
  border-radius: 999px;
  box-shadow: 0 8px 24px rgba(15, 18, 24, 0.18), 0 0 0 1px var(--border);
  z-index: 20;
  /* No overflow/scroll here on purpose: every button's own style/colour
     popover (see .style-panel) opens *above* this bar via `position:
     absolute`, and an overflow-clipped ancestor would cut those off along
     with any horizontal scrollbar it grew. With the border/fill/font
     buttons combined into one Style menu the whole bar is short enough to
     fit down to a narrow phone width without needing to scroll at all
     (see the max-width above and the mobile media query's smaller
     buttons/gaps). */
}

.control-bar-divider {
  flex-shrink: 0;
  width: 1px;
  height: 24px;
  background: var(--border);
}

/* A button inside the control bar — the mobile-FAB convention (round,
   solid, shadowed) shrunk to fit a horizontal row of several instead of
   standing alone in a corner. */
.fab {
  position: relative;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--accent-strong);
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease, opacity 0.15s ease;
}

.fab:hover:not(:disabled) {
  background: var(--accent-strong-hover);
  transform: translateY(-1px);
}

.fab:active:not(:disabled) {
  transform: translateY(0) scale(0.94);
}

/* Once something is selected, the mini-FABs in the middle of the bar take
   over (recolor/fill/font/delete) — the add button has nothing to do with
   a selection. Disabled rather than hidden (main.js's draw loop sets the
   actual `disabled` attribute, matching selection count) — the same
   "present but inert" treatment as the mini-FABs (see .fab.fab-mini
   :disabled), so the bar's width never jumps as the selection comes and
   goes. */
.fab:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Selection controls: delete/colour/font for whatever is currently
   selected — one group among several in the same horizontal bar (see
   .control-bar), not its own fixed-position stack any more. */
.fab-stack {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Wraps a mini-FAB and its own popover as siblings (see SelectionFabs.js's
   miniFabWithPopover) — positioned so the popover (see .style-panel)
   anchors to this pair rather than to the bar as a whole. */
.fab-mini-wrap {
  position: relative;
}

/* Smaller and muted: these act on an existing selection, so they must not
   compete with "add a block" for the eye. */
.fab.fab-mini {
  width: 38px;
  height: 38px;
  background: #2c3644;
}

.fab.fab-mini:hover:not(:disabled) {
  background: #3a4556;
}

/* Nothing selected: present but inert, rather than gone — a fixed
   landmark in the corner instead of controls that pop in and out as the
   selection comes and goes. */
.fab.fab-mini:disabled {
  opacity: 0.35;
  cursor: default;
}

.fab.fab-danger:hover:not(:disabled) {
  background: #a52a2f;
}

/* Delete mode armed (see main.js's toggleDeleteMode): the one mini-FAB
   that's clickable with nothing selected, so it needs its own "this is
   live" signal — solid red plus a glow, rather than just the ordinary
   idle background it shares with the other three the rest of the time. */
.fab.fab-danger.fab-danger-armed {
  background: #c0392b;
  box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.35);
}

.fab.fab-danger.fab-danger-armed:hover {
  background: #a52a2f;
}

/* A host page's own selection-dependent button (see SelectionFabs.js's
   getExtraFab / main.js's window.nodigraphSelectionFab) — its default
   class, used whenever a host doesn't supply its own `className`. Amber
   rather than the neutral slate the built-in mini-FABs use, so a button
   that isn't one of nodigraph's own four reads as visually distinct at a
   glance, not just a fifth of the same thing. */
.fab.fab-extra {
  background: #8a5a12;
}

.fab.fab-extra:hover:not(:disabled) {
  background: #a86e17;
}

/* The one combined "Style" popover — border colour, fill colour and font
   all live in here as stacked sections instead of each having its own
   button and its own popover, the same way a design tool's single style
   flyout groups everything about a selection's appearance in one place.
   Opens upward, centered on its own button — the control bar runs
   horizontally along the bottom edge, so "above" is the one direction
   guaranteed to be open canvas rather than a neighboring button. */
.style-panel {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 190px;
  max-height: min(70vh, 420px);
  overflow-y: auto;
  padding: 12px;
  background: #1e2530;
  border: 1px solid #2c3644;
  border-radius: 10px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}

.style-panel[hidden] {
  display: none;
}

.style-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.style-section-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #8b93a3;
}

.style-divider {
  height: 1px;
  background: #2c3644;
}

/* Each section's swatch row wraps within the panel's own width rather than
   a fixed grid — the same nine swatches as before (eight presets plus the
   native colour input), just laid out to wrap at whatever width fits. */
.swatch-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* The Order button's own popover — a plain named-action list (Bring to
   Front / Bring Forward / Send Backward / Send to Back) rather than the
   swatch-grid/form shape .style-panel's own sections use, so it gets its
   own lighter set of rules instead of reusing that class. Same popover
   chrome and position as .style-panel (opens upward, centered on its own
   button) for visual consistency between the two menus in this bar. */
.order-menu {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 190px;
  padding: 6px;
  background: #1e2530;
  border: 1px solid #2c3644;
  border-radius: 10px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
}

.order-menu[hidden] {
  display: none;
}

.order-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: none;
  border: none;
  border-radius: 6px;
  color: #e6e9ef;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}

.order-menu-item:hover {
  background: #2c3644;
}

.order-menu-item svg {
  flex-shrink: 0;
  color: #8b93a3;
}

.fab-swatch {
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 6px;
  cursor: pointer;
  background: none;
}

.fab-swatch:hover {
  border-color: #e6e9ef;
}

/* "Back to the default" is the absence of a colour, so it is drawn as an
   absence — an outline, not a sample of the default blue, which would
   read as just another choice. */
.fab-swatch-default {
  position: relative;
  background: #12161d;
}

.fab-swatch-default::after {
  content: '';
  position: absolute;
  inset: 6px;
  border: 1px dashed #8b93a3;
  border-radius: 3px;
}

/* A checkerboard, the standard way of drawing "no color here" — plain
   `background: transparent` would just show the popover's own dark
   background through and look indistinguishable from an empty swatch. */
.fab-swatch-transparent {
  background-image:
    linear-gradient(45deg, #6b7383 25%, transparent 25%, transparent 75%, #6b7383 75%),
    linear-gradient(45deg, #6b7383 25%, transparent 25%, transparent 75%, #6b7383 75%);
  background-size: 10px 10px;
  background-position: 0 0, 5px 5px;
  background-color: #1e2530;
}

.fab-swatch-custom {
  -webkit-appearance: none;
  appearance: none;
  background: none;
}

.fab-swatch-custom::-webkit-color-swatch-wrapper {
  padding: 2px;
}

.fab-swatch-custom::-webkit-color-swatch {
  border: none;
  border-radius: 4px;
}

/* The Font section's own controls: family, size, bold, italic — the same
   handful a word processor's font dialog has, now living inside the
   combined .style-panel's "Font" section rather than a popover of its own. */
.style-panel select,
.style-panel input[type='number'] {
  background: #12161d;
  border: 1px solid #2c3644;
  color: #e6e9ef;
  border-radius: 6px;
  padding: 6px 8px;
  font-size: 13px;
}

.fab-font-family {
  width: 100%;
}

.fab-font-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.fab-font-row label {
  color: #8b93a3;
  font-size: 12px;
}

.fab-font-size {
  width: 64px;
}

.fab-font-style-row {
  justify-content: flex-start;
}

.fab-font-style-btn {
  width: 34px;
  height: 30px;
  background: #12161d;
  border: 1px solid #2c3644;
  border-radius: 6px;
  color: #e6e9ef;
  font-size: 14px;
  cursor: pointer;
}

.fab-font-style-btn:hover {
  border-color: #3a4556;
}

.fab-font-style-btn.active {
  background: #2f6fed;
  border-color: #2f6fed;
  color: #fff;
}

/* A fixed panel over the canvas — on desktop it slides in from the right
   (see the min-width media query below), the same overlay treatment the
   left-hand app menu already uses (see .topbar-menu), rather than a grid
   column that pushed the canvas aside and resized it on every open/close.
   Mobile overrides this into a bottom sheet instead (see the max-width
   media query further down). */
#inspector {
  position: fixed;
  /* Full height — there's no reserved header row to sit below any more;
     the top HUD chips float above this panel's own top edge instead (see
     .topbar-hud's z-index). */
  top: 0;
  right: 0;
  bottom: 0;
  width: 300px;
  background: var(--bg-surface-2);
  color: var(--text-primary);
  border-left: 1px solid var(--border);
  box-shadow: -12px 0 32px rgba(0, 0, 0, 0.3);
  padding: 14px;
  overflow-y: auto;
  font-size: 13px;
  z-index: 25;
}

/* Scoped to desktop rather than living in the base rule above: the mobile
   bottom sheet (see the max-width media query) sets its own transform/
   transition for a translateY slide, and `body.inspector-open #inspector`
   would out-specificity `#inspector.open` (an extra type selector beats an
   extra class) and win regardless of source order, sliding sideways
   there too if this weren't kept out of its way entirely. */
@media (min-width: 769px) {
  #inspector {
    transform: translateX(100%);
    transition: transform 0.22s ease;
  }

  body.inspector-open #inspector {
    transform: translateX(0);
  }
}

/* The inspector now overlays the same bottom edge the control bar floats
   over (rather than pushing the canvas, which used to leave it plainly
   visible beside it) — faded out and inert while it's open, the same
   treatment the mobile bottom sheet already used before this was also
   true on desktop. */
body.inspector-open .control-bar {
  opacity: 0;
  pointer-events: none;
}

#inspector h3 {
  margin: 0 0 12px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

#inspector h4 {
  margin: 18px 0 10px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

#inspector .sheet-header {
  display: none;
}

#inspector .tab-bar {
  display: flex;
  gap: 4px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

#inspector .tab-button {
  flex: 1;
  background: none;
  border: none;
  color: var(--text-muted);
  padding: 8px 4px;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

#inspector .tab-button.active {
  color: var(--text-primary);
  border-bottom-color: var(--accent-strong);
}

#inspector .tab-button:hover {
  color: var(--text-secondary);
}

#inspector .hint-text {
  margin: 10px 0 0;
  font-size: 11px;
  color: var(--text-faint);
  line-height: 1.4;
}

#inspector select {
  background: var(--bg-well);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: 4px;
  padding: 5px 7px;
  font-size: 13px;
}

#inspector select:focus {
  outline: 1px solid var(--accent-strong);
}

#inspector .description-editor {
  width: 100%;
  background: var(--bg-well);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: 6px;
  padding: 8px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.5;
  resize: vertical;
}

#inspector .description-editor:focus {
  outline: 1px solid var(--accent-strong);
}

#inspector .description-editor::placeholder {
  color: var(--text-faint-2);
}

#inspector .apply-row {
  margin-top: 8px;
  display: flex;
  justify-content: flex-end;
}

#inspector .apply-row button {
  background: var(--bg-hover);
  color: var(--text-primary);
  border: none;
  border-radius: 4px;
  padding: 6px 12px;
  font-size: 12px;
  cursor: pointer;
}

#inspector .apply-row button:hover {
  background: var(--bg-hover-strong);
}

#inspector .port-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
}

#inspector .port-dir-select {
  flex-shrink: 0;
  width: 58px;
  padding: 5px 4px;
  font-size: 11px;
}

#inspector .port-name-input {
  flex: 1;
  min-width: 0;
}

/* How many pins (see BlockDescription's module doc) a logical port's one
   row actually has on the canvas — only shown past a single pin, since a
   plain unlisted count would just be visual noise for the common case. */
#inspector .port-pin-count {
  flex-shrink: 0;
  font-size: 11px;
  color: var(--text-secondary);
}

#inspector .port-delete-button {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  background: none;
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

#inspector .port-delete-button:hover {
  color: var(--danger);
  border-color: var(--danger);
}

#inspector .port-desc-input {
  width: 100%;
  margin-bottom: 12px;
  font-size: 11px;
  color: var(--text-secondary);
}

#inspector .delete-row {
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

#inspector .delete-button {
  width: 100%;
  background: transparent;
  border: 1px solid var(--danger);
  color: var(--danger);
  border-radius: 4px;
  padding: 8px;
  font-size: 12px;
  cursor: pointer;
}

#inspector .delete-button:hover {
  background: var(--danger-tint);
}

#inspector .field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 10px;
}

#inspector label {
  color: var(--text-muted);
  font-size: 11px;
}

#inspector input {
  background: var(--bg-well);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: 4px;
  padding: 5px 7px;
  font-size: 13px;
}

#inspector input:focus {
  outline: 1px solid var(--accent-strong);
}

#inspector .empty {
  color: var(--text-muted);
  font-style: italic;
}

#inspector .row {
  display: flex;
  gap: 8px;
}

#inspector .row .field {
  flex: 1;
}

/* App-like layout below the mobile breakpoint: the inspector becomes a
   bottom sheet that slides up over the full-bleed canvas instead of eating
   permanent screen width. */
@media (max-width: 768px) {
  .topbar-hud {
    padding: 8px 8px 0;
  }

  .hud-chip {
    height: 40px;
    /* Two independent chips (menu+breadcrumb on the left, actions on the
       right) can still together be too wide for a phone screen — cap the
       left one harder here than the generic desktop budget above, since
       the breadcrumb scrolls inside its own chip rather than needing the
       room. */
    max-width: min(320px, 62vw);
  }

  /* The brand name is the one thing here that isn't a control, so it's
     the first to go on a narrow screen — the hamburger and breadcrumb
     need the room more than it does. */
  #topbar {
    gap: 6px;
    padding: 0 8px;
  }

  #topbar .brand {
    display: none;
  }

  .control-bar {
    gap: 4px;
    padding: 5px;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  }

  #inspector {
    position: fixed;
    /* Reset against the desktop base rule above, which pins `top` to
       anchor its own right-edge slide-in — this sheet anchors to `bottom`
       instead, with `max-height` (not `top`) capping how tall it grows. */
    top: auto;
    width: auto;
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 65vh;
    border-left: none;
    border-top: 1px solid var(--border);
    border-radius: 16px 16px 0 0;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
    padding: 8px 16px 20px;
    padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
    transform: translateY(100%);
    transition: transform 0.25s ease;
    z-index: 15;
  }

  #inspector.open {
    transform: translateY(0);
  }

  #inspector .sheet-header {
    display: flex;
    justify-content: center;
    padding: 6px 0 10px;
  }

  #inspector .sheet-header .grabber {
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--border-strong);
  }

  #inspector h3 {
    display: none;
  }
}

/* --- Share dialog -------------------------------------------------- */

.share-dialog {
  width: min(560px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-surface-2);
  color: var(--text-primary);
  font-family: -apple-system, Segoe UI, Roboto, sans-serif;
  font-size: 13px;
}

.share-dialog::backdrop {
  background: rgba(0, 0, 0, 0.55);
}

.share-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 10px;
}

.share-header h2 {
  margin: 0;
  font-size: 15px;
}

.share-close {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 15px;
  cursor: pointer;
  padding: 4px 6px;
  line-height: 1;
}

.share-close:hover {
  color: var(--text-primary);
}

.share-body {
  padding: 16px;
  max-height: 70vh;
  overflow-y: auto;
}

.share-field {
  margin-bottom: 14px;
}

.share-field label {
  display: block;
  margin-bottom: 5px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

.share-row {
  display: flex;
  gap: 8px;
  align-items: center;
}

.share-row input {
  flex: 1;
  min-width: 0;
  background: var(--bg-well);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 7px 9px;
  color: var(--text-primary);
  font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.share-row input:focus {
  outline: 1px solid var(--accent-strong);
}

/* A text field standing alone in a .share-field, rather than sharing a
   .share-row with a Copy button — GitHubConnectDialog's repo-path and
   token inputs. Same look as .share-row input, but full width since
   there's no sibling button to leave room for. */
.share-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  background: var(--bg-well);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 7px 9px;
  color: var(--text-primary);
  font-size: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.share-input:focus {
  outline: 1px solid var(--accent-strong);
}

.share-button {
  flex-shrink: 0;
  background: var(--bg-hover);
  border: none;
  border-radius: 5px;
  padding: 7px 12px;
  color: var(--text-primary);
  font-size: 12px;
  cursor: pointer;
}

.share-button:hover {
  background: var(--bg-hover-strong);
}

.share-button-primary {
  background: var(--accent-strong);
  color: #fff;
}

.share-button-primary:hover {
  background: var(--accent-strong-hover);
}

.share-hint {
  margin: 6px 0 0;
  font-size: 11px;
  line-height: 1.45;
  color: var(--text-faint);
}

.share-checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 10px 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-muted);
  cursor: pointer;
}

.share-checkbox-row input {
  margin-top: 2px;
}

.share-hint a {
  color: var(--accent);
}

/* A field revealed on demand rather than always shown — see
   GitHubConnectDialog's collapsed token field for a public repo, where
   most opens need no credential at all. */
.share-link-button {
  display: block;
  margin: 0 0 14px;
  padding: 0;
  border: none;
  background: none;
  color: var(--accent);
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}

.share-link-button:hover {
  color: var(--accent-strong);
}

.share-warning {
  margin: 14px 0 0;
  padding: 9px 11px;
  border-radius: 6px;
  border: 1px solid var(--warning-border);
  background: var(--warning-tint);
  color: var(--warning-text);
  font-size: 11px;
  line-height: 1.45;
}

.share-preview {
  margin-bottom: 12px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-canvas);
  text-align: center;
}

.share-preview img {
  max-width: 100%;
  max-height: 220px;
}

