/* =============================================================================
   pirxe Grid Configurator — design tokens & layout
   Mirrors the motion-designer brand language: white + monochrome surfaces,
   0.5px hairlines, no border-radius, EB Garamond italic logo, DM Sans for UI.
   ============================================================================= */

:root {
  /* Surfaces */
  --c-black: #0a0a0a;
  --c-white: #ffffff;
  --c-surface: #f5f4f0;
  --c-surface-raised: #f9f9f7;
  --c-surface-panel: #faf9f7;

  /* Borders */
  --c-border-subtle: #e8e7e2;
  --c-border-default: #d0cfc8;
  --c-border-strong: #c8c7c0;

  /* Text */
  --c-text: #0a0a0a;
  --c-text-secondary: #555;
  --c-text-muted: #888;
  --c-text-hint: #aaa;
  --c-text-placeholder: #bbb;

  /* Destructive */
  --c-destructive: #993c1d;
  --c-destructive-bg: #faece7;
  --c-destructive-border: #c8a8a0;

  /* Tag */
  --c-tag-bg: #f1efe8;
  --c-tag-border: #d3d1c7;
  --c-tag-text: #5f5e5a;

  /* Canvas viewport tint (matches node-creator + motion-designer) */
  --c-canvas-bg: #f0ede6;

  /* Selection accent (WP-9.8): the dual-ring block highlight + handles. */
  --c-sel-accent: #0a84ff;

  /* Typography */
  --font-ui: 'DM Sans', system-ui, -apple-system, sans-serif;
  --font-logo: 'EB Garamond', Georgia, serif;

  color-scheme: light;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--c-text);
}

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

html,
body {
  height: 100%;
  background: var(--c-surface);
  font-family: var(--font-ui);
  color: var(--c-text);
  overflow: hidden;
}

button,
input,
select,
textarea {
  font-family: inherit;
  color: inherit;
}

button {
  border-radius: 0;
}

input,
select,
textarea {
  border-radius: 0;
}

/* =============================================================================
   App shell
   ============================================================================= */
.app {
  display: grid;
  /* WP-9.11: the left panel column reads a CSS var so the drag handle can
     resize it live (app.js clamps ~260px–50vw and persists the value). */
  grid-template-columns: 48px var(--left-panel-w, 264px) 1fr 280px;
  grid-template-rows: 64px 1fr 32px;
  height: 100vh;
  background: var(--c-surface);
  border: 0.5px solid var(--c-border-default);
  overflow: hidden;
}

/* Embedded in the Adset Maker shell: drop our own topbar. The topbar is the
   first grid row; hiding it means the body panels must fill a 2-row grid
   (panels auto-place into the 1fr row, statusbar into the 32px row). */
.embedded .topbar {
  display: none;
}
.embedded .app {
  grid-template-rows: 1fr 32px;
}

/* Collapsed panel states: redistribute grid + hide the matching panel. */
.app--left-hidden {
  grid-template-columns: 48px 1fr 280px;
}
.app--left-hidden .panel-left {
  display: none;
}
.app--right-hidden {
  grid-template-columns: 48px var(--left-panel-w, 264px) 1fr;
}
.app--right-hidden .panel-right {
  display: none;
}
.app--left-hidden.app--right-hidden {
  grid-template-columns: 48px 1fr;
}

/* =============================================================================
   Topbar (matches motion-designer 64px)
   ============================================================================= */
.topbar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: 12px;
  background: var(--c-white);
  border-bottom: 0.5px solid var(--c-border-default);
  flex-shrink: 0;
}

.logo {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  margin-right: 10px;
}

.logo-svg {
  height: 32px;
  width: auto;
  display: block;
  shape-rendering: geometricPrecision;
  fill: var(--c-black);
}

.tb-sep {
  width: 0.5px;
  height: 22px;
  background: var(--c-border-default);
  flex-shrink: 0;
}

.tb-title {
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-text-secondary);
}

/* =============================================================================
   Icon rail
   ============================================================================= */
.icon-rail {
  background: var(--c-surface-panel);
  border-right: 0.5px solid var(--c-border-default);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 0;
  gap: 1px;
  overflow: hidden;
}

.rail-btn {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: transparent;
  border: 0.5px solid transparent;
  border-left: 0.5px solid transparent;
  position: relative;
  flex-shrink: 0;
  padding: 0;
}

.rail-btn:hover {
  background: #eeede8;
}

.rail-btn.active {
  background: var(--c-white);
  border-color: var(--c-border-strong);
  border-left: 2px solid var(--c-black);
}

.rail-btn i {
  font-size: 22px;
  color: #b0b0a8;
}

.rail-btn:hover i {
  color: #555;
}

.rail-btn.active i {
  color: var(--c-black);
}

.rail-btn::after {
  content: attr(title);
  position: absolute;
  left: 54px;
  top: 50%;
  transform: translateY(-50%);
  background: var(--c-black);
  color: var(--c-white);
  font-size: 11px;
  letter-spacing: 0.06em;
  padding: 5px 9px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 100ms linear;
  z-index: 100;
  font-family: var(--font-ui);
}

.rail-btn:hover::after {
  opacity: 1;
}

.rail-divider {
  width: 20px;
  height: 0.5px;
  background: #e0dfd8;
  margin: 3px 0;
  flex-shrink: 0;
}

/* =============================================================================
   Left panel
   ============================================================================= */
.panel-left {
  background: var(--c-white);
  border-right: 0.5px solid var(--c-border-default);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  position: relative; /* WP-9.11: anchors the resize handle */
}

/* Resizable left panel (WP-9.11): a slim grab strip along the right edge.
   Sits inside the panel (its overflow is hidden) with a generous-enough hit
   width; app.js drives --left-panel-w from drags on it. */
.pl-resize {
  position: absolute;
  top: 0;
  right: 0;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 20;
}

.pl-resize:hover,
.pl-resize.dragging {
  background: rgba(10, 132, 255, 0.25);
}

.pl-header {
  padding: 12px 8px 10px 16px;
  border-bottom: 0.5px solid var(--c-border-subtle);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

.pl-header-title {
  flex: 1;
}

.pl-header-title {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #333;
  font-weight: 500;
}

.pl-body {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0;
}

.pl-context {
  display: block;
}

.pl-context[hidden] {
  display: none;
}

.pl-section {
  padding: 0 16px 14px;
}

.pl-section + .pl-section {
  border-top: 0.5px solid #f0efea;
}

.pl-lbl {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #3a3a3a;
  padding-top: 14px;
  margin-bottom: 10px;
}

/* Collapsible rail sections (WP-9.10): the section's leading .pl-lbl is the
   fold toggle (the WP-8.7 inspector mechanism mirrored onto the rail). Only
   the first child (the header) survives a fold — the section may contain
   further .pl-lbl sub-headers (e.g. "Editing" under Generate placements) that
   belong to the body. */
.pl-section[data-fold] > .pl-lbl:first-child {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
}

.pl-fold-icon {
  font-size: 12px;
  color: #8a8a8a;
  transition: transform 0.12s ease;
}

.pl-section[data-fold]:not(.folded) > .pl-lbl:first-child .pl-fold-icon {
  transform: rotate(90deg);
}

.pl-section.folded > *:not(:first-child) {
  display: none;
}

.pl-section.folded > .pl-lbl:first-child {
  margin-bottom: 4px;
}

/* Placement chips */
.pl-sep {
  border: none;
  border-top: 1px solid #e2e2e2;
  margin: 12px 0;
}
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chip {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid #d0d0d0;
  background: #fff;
  color: #3a3a3a;
  cursor: pointer;
  line-height: 1.4;
}
.chip:hover {
  border-color: #9a9a9a;
}
.chip.is-active {
  background: #111;
  border-color: #111;
  color: #fff;
}
.chip.is-selected {
  border-color: #111;
  box-shadow: inset 0 0 0 1px #111;
}
.chip .chip-x {
  margin-left: 6px;
  opacity: 0.55;
}

/* =============================================================================
   Fields
   ============================================================================= */
.field-select,
.field-inp {
  width: 100%;
  font-size: 12px;
  color: var(--c-text);
  border: 0.5px solid var(--c-border-default);
  background: var(--c-surface-raised);
  padding: 6px 8px;
  outline: none;
  font-family: var(--font-ui);
}

.field-select:focus,
.field-inp:focus {
  border-color: var(--c-black);
}

.field-row-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}

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

.field-group-lbl {
  font-size: 9px;
  color: var(--c-text-muted);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.field-group-inp {
  width: 100%;
  font-size: 12px;
  color: var(--c-text);
  border: 0.5px solid var(--c-border-default);
  background: var(--c-surface-raised);
  padding: 6px 8px;
  outline: none;
  font-family: var(--font-ui);
}

.field-group-inp:focus {
  border-color: var(--c-black);
}

.field-group-action {
  justify-content: flex-end;
}

textarea.field-group-inp {
  resize: vertical;
  min-height: 60px;
  line-height: 1.5;
}

.swatch-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px;
  background: var(--c-surface-raised);
  border: 0.5px solid var(--c-border-default);
}

.swatch-input {
  width: 28px;
  height: 24px;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
}

.swatch-text {
  flex: 1;
  border: none;
  background: transparent;
  padding: 0;
  font-size: 12px;
}

/* Command buttons */
/* .cmd-select to select stojący w rzędzie przycisków poleceń (np. „Load saved"
   w sekcji Library). Dzieli metryki i kolory z .cmd-btn zamiast mieć własne —
   .field-select jest niższy, ma mniejszy tekst i ciemniejszą ramkę, bo należy
   do grup pól z podpisem, nie do rzędu przycisków. */
.cmd-btn,
.cmd-select {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 12px;
  font-size: 13px;
  font-family: var(--font-ui);
  border: 0.5px solid var(--c-border-subtle);
  background: var(--c-surface-raised);
  color: var(--c-text);
  cursor: pointer;
  text-align: left;
  margin-bottom: 4px;
  text-decoration: none;
  transition: background 100ms linear, color 100ms linear, border-color 100ms linear;
}

/* Select to element zastępowany — flex z reguły wyżej nie ma tu sensu i w
   części silników psuje renderowanie natywnej strzałki, więc wracamy do block.
   line-height wyrównuje wysokość do .cmd-btn, którego wysokość wyznacza
   ikona 16px, a nie sam tekst. */
.cmd-select {
  display: block;
  line-height: 16px;
  border-radius: 0;
  outline: none;
}

.cmd-btn:last-child,
.cmd-select:last-child {
  margin-bottom: 0;
}

/* `display: flex` above outranks the browser's own [hidden] rule, so without
   this the download button showed even with nothing to download. */
#downloadResults[hidden] {
  display: none;
}

.cmd-btn:hover,
.cmd-select:hover {
  border-color: var(--c-black);
  background: var(--c-white);
}

.cmd-btn i {
  font-size: 16px;
  color: #888;
  flex-shrink: 0;
}

.cmd-btn:hover i {
  color: var(--c-black);
}

.cmd-btn.primary {
  background: var(--c-black);
  color: var(--c-white);
  border-color: var(--c-black);
}

.cmd-btn.primary:hover {
  background: #222;
}

.cmd-btn.primary i {
  color: var(--c-white);
}

/* Disabled command buttons had no style at all (moonbase bebe71e, AG-20b):
   Load / Rename / Delete wait for a picked template, and Delete selected /
   Duplicate (AG-04) for a selection — without this they looked live. */
.cmd-btn:disabled,
.cmd-btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

.cmd-btn:disabled:hover,
.cmd-btn[disabled]:hover {
  background: var(--c-surface-raised);
}

.cmd-btn.danger {
  border-color: var(--c-destructive-border);
  color: var(--c-destructive);
  background: transparent;
  font-size: 12px;
}

.cmd-btn.danger i {
  color: #aa7867;
  font-size: 14px;
}

.cmd-btn.danger:hover {
  border-color: var(--c-destructive);
  background: var(--c-destructive-bg);
}

.cmd-btn.danger:hover i {
  color: var(--c-destructive);
}

.cmd-btn-compact {
  padding: 6px 10px;
  font-size: 11px;
  justify-content: center;
  margin-bottom: 0;
}

.hidden-file {
  display: none;
}

/* Toggle switch (paired with checkbox) */
.tog-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 0;
  cursor: pointer;
  user-select: none;
  position: relative;
}

.tog-lbl {
  font-size: 13px;
  color: var(--c-text);
}

.tog-input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.tog {
  width: 28px;
  height: 16px;
  background: #d4d3cc;
  border-radius: 8px;
  position: relative;
  flex-shrink: 0;
  transition: background 120ms linear;
}

.tog::after {
  content: '';
  position: absolute;
  width: 12px;
  height: 12px;
  background: var(--c-white);
  border-radius: 50%;
  top: 2px;
  left: 2px;
  transition: left 120ms linear;
}

.tog-input:checked + .tog,
.tog.on {
  background: var(--c-black);
}

.tog-input:checked + .tog::after,
.tog.on::after {
  left: 14px;
}

.hint {
  font-size: 11px;
  color: var(--c-text-hint);
  line-height: 1.5;
  padding: 5px 0 0;
}

/* Inline block-level warning shown under a creative link (e.g. a workflow
   linked to a video-only block) — same amber as .status-warning so the two
   read as the same severity across the app. */
.hint-warning {
  color: #b87100;
  font-weight: 600;
}

/* WP-12.4 (audit F10): panel status lines that carry a FAILURE (mockup /
   explode / lift / refine / workflow render / analyze / plan / placement /
   ideation) switch to this — red, >=12px, bold — instead of the 11px #aaa
   whisper that failed WCAG contrast. The class persists until the next status
   write for that panel. */
.hint-error {
  color: var(--c-destructive);
  font-size: 12px;
  font-weight: 600;
}

/* WP-12.10 (audit F5): numeric X/Y/W/H row at the top of Block Setup. */
.geom-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 10px;
  margin-bottom: 8px;
}

/* display:grid above would override the UA [hidden] rule (same trap as
   .sketch-toolbar) — restate it so the per-corner radius grid really hides. */
.geom-grid[hidden] {
  display: none;
}

.geom-field {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.geom-lbl {
  font-size: 10px;
  font-weight: 600;
  color: var(--c-text-muted);
  width: 10px;
  flex: none;
}

.geom-inp {
  width: 100%;
  min-width: 0;
}

/* WP-12.7 (audit F16): small muted cost/time suffix inside paid buttons
   ("$0.009 · ~10s"). Inherits the button color so it stays legible on
   primary (inverted) buttons; opacity does the muting. */
.cost-badge {
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0.65;
  margin-left: 4px;
  white-space: nowrap;
}

/* =============================================================================
   Canvas area
   ============================================================================= */
.canvas-area {
  background: var(--c-canvas-bg);
  display: flex;
  min-height: 0;
  overflow: hidden;
  /* Fixed top chrome of the canvas area, top to bottom: the ruler strip, the
     band where drawRulers() paints the selection's edge readouts, then the
     pinned toolbar, then clearance. The wrapper's scroll viewport starts below
     all of it, so the toolbar can NEVER cover the canvas at any zoom/scroll.

     Must match RULER_SIZE in app.js. */
  --ruler-size: 20px;
  /* drawRulers() draws the x / x+w / W×H readouts in a band directly BELOW the
     strip — label() fills a 13px box at y = RULER_SIZE + 4, so the band runs
     from 20px to 37px. The toolbar used to start at 26px and sat right on top
     of it: the left readout was clipped by the bar's edge and the centred W×H
     one disappeared under it entirely. The band is reserved here instead. */
  --ruler-label-band: 17px;
  --modebar-gap: 5px;
  --modebar-height: 42px;
  --modebar-top: calc(var(--ruler-size) + var(--ruler-label-band) + var(--modebar-gap));
  --canvas-top-chrome: calc(var(--modebar-top) + var(--modebar-height) + 6px);
}

.canvas-wrapper {
  flex: 1;
  background: var(--c-canvas-bg);
  overflow: auto;
  /* The scroll viewport starts BELOW the ruler strip + toolbar band —
     scrolled content can slide under the left ruler strip (by design; rulers
     float) but never under the top strip or the toolbar. */
  margin-top: var(--canvas-top-chrome);
  padding: 24px 24px 24px 44px;
  display: grid;
  place-content: safe center;
  min-height: 0;
}

/* Holds the scaled canvas footprint so the wrapper centers/scrolls correctly.
   overflow:hidden clips the canvas's un-scaled layout box (transform is paint-only
   and doesn't shrink layout), so this box stays exactly the visual size. */
.canvas-stage {
  position: relative;
  overflow: hidden;
}

/* WP-12.2 (audit F3): empty-canvas hint. Child of .canvas-stage (NOT the
   zoom-transformed #canvas) so it stays readable at any zoom level, and
   pointer-transparent so marquee / Add-block drags pass straight through. */
.canvas-empty-state {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  text-align: center;
  pointer-events: none;
  padding: 12px;
  z-index: 5;
}

.canvas-empty-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--c-text-secondary);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.canvas-empty-copy {
  font-size: 12px;
  line-height: 1.6;
  color: var(--c-text-secondary);
  max-width: 340px;
}

.canvas-empty-copy kbd {
  font: 600 11px/1 var(--font-ui);
  border: 1px solid var(--c-border-default);
  border-radius: 3px;
  padding: 1px 4px;
  background: var(--c-white);
}

/* Zoom controls — floating bottom-right of the canvas area. */
/* Measurement rulers overlay — covers the whole canvas area, never catches
   the pointer; everything (strips, ticks, selection guides) is drawn by
   drawRulers() in app.js. */
.ruler-overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 5;
}

.canvas-zoom {
  position: absolute;
  bottom: 16px;
  right: 16px;
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--c-surface-raised, #fff);
  border: 0.5px solid var(--c-border-default, #d0d0d0);
  border-radius: 8px;
  padding: 3px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  z-index: 20;
}
.zoom-btn {
  border: 0;
  background: transparent;
  color: #333;
  cursor: pointer;
  font-size: 13px;
  padding: 5px 8px;
  border-radius: 5px;
  line-height: 1;
  display: flex;
  align-items: center;
}
.zoom-btn:hover {
  background: var(--c-surface, #f0f0f0);
}
.zoom-val {
  min-width: 48px;
  justify-content: center;
  font-variant-numeric: tabular-nums;
}

/* Mode toggle + sketch toolbar — floating top-center of the canvas area
   (WP-1.3). Mirrors the .canvas-zoom pill styling. */
.canvas-modebar {
  /* Pinned below BOTH the ruler strip and the selection-readout band beneath
     it (--modebar-top) — the strip alone is not the whole ruler, which is why
     a 26px offset used to clip the readouts. Constant position: it never
     tracks the artboard (which let it float over the canvas at some
     zoom/scroll combinations); the scroll viewport starts below it
     (--canvas-top-chrome), so it never covers the canvas either. */
  position: absolute;
  top: var(--modebar-top);
  height: var(--modebar-height);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  z-index: 20;
}

.mode-toggle,
.sketch-toolbar {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--c-surface-raised, #fff);
  border: 0.5px solid var(--c-border-default, #d0d0d0);
  border-radius: 8px;
  padding: 3px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

/* Add-block split button: the caret next to "Add block" opens the type list;
   it collapses on selection / outside click / tool change. */
.ab-split {
  position: relative;
  display: inline-flex;
  align-items: stretch;
}
.ab-split .ab-main {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.ab-caret {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  padding-left: 4px;
  padding-right: 5px;
  margin-left: -1px;
  border-left: 0.5px solid var(--c-border-default, #d0d0d0);
}
.ab-caret i {
  font-size: 12px;
}
.ab-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  background: var(--c-surface-raised, #fff);
  border: 0.5px solid var(--c-border-default, #d0d0d0);
  border-radius: 8px;
  padding: 3px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.14);
  z-index: 30;
}
.ab-menu[hidden] {
  display: none;
}
.ab-opt {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 12px;
  text-align: left;
  padding: 6px 8px;
  border-radius: 5px;
  line-height: 1;
}
.ab-opt:hover {
  background: var(--c-surface, #f0f0f0);
}
.ab-opt.active {
  background: #111;
  color: #fff;
}

/* WP-13b: "Shape" flyout submenu — one row with a right chevron; the nested
   .ab-menu panel opens to its right (hover intent or click, see app.js). The
   wrapper is the positioning context so the panel tracks the row. */
.ab-sub {
  position: relative;
  display: flex;
  flex-direction: column;
}
.ab-sub-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.ab-sub-trigger i {
  font-size: 11px;
  opacity: 0.6;
}
.ab-sub-trigger.active i {
  opacity: 0.9;
}
.ab-submenu {
  left: calc(100% + 2px);
  top: -4px;
  min-width: 130px;
}

/* WP-13b: segmented rotation-preset control in the inspector (→↓←↑ glyphs
   for arrows, 0°..270° otherwise) — reuses the sketch-toolbar .st-btn idiom,
   boxed so it reads as one control. */
.shape-dir-group {
  border: 0.5px solid var(--c-border-default, #d0d0d0);
  border-radius: 6px;
  padding: 2px;
  background: var(--c-surface-raised, #fff);
}
.shape-dir-group .st-btn {
  font-size: 11px;
  padding: 4px 6px;
  justify-content: center;
  min-width: 26px;
}

.mode-btn {
  border: 0;
  background: transparent;
  color: #333;
  cursor: pointer;
  font-size: 12px;
  letter-spacing: 0.04em;
  padding: 5px 10px;
  border-radius: 5px;
  line-height: 1;
  display: flex;
  align-items: center;
  gap: 5px;
}

.mode-btn:hover {
  background: var(--c-surface, #f0f0f0);
}

.mode-btn.active {
  background: var(--c-black, #111);
  color: var(--c-white, #fff);
}

/* WP-14.1: icon-only modebar buttons (undo/redo) — square, and greyed out
   when the corresponding stack is empty rather than disappearing, so the
   toolbar never reflows mid-edit. */
.mode-btn--icon {
  padding: 5px 7px;
}

.mode-btn:disabled {
  color: var(--c-text-muted, #aaa);
  cursor: default;
}

.mode-btn:disabled:hover {
  background: transparent;
}

/* display:flex above would override the UA [hidden] rule (same trap as
   .pl-context) — restate it so the toolbar actually hides outside sketch mode. */
.sketch-toolbar[hidden] {
  display: none;
}

.st-group {
  display: flex;
  align-items: center;
  gap: 2px;
}

.st-sep {
  width: 0.5px;
  height: 18px;
  background: var(--c-border-default, #d0d0d0);
  margin: 0 3px;
  flex-shrink: 0;
}

.st-btn {
  border: 0;
  background: transparent;
  color: #333;
  cursor: pointer;
  font-size: 12px;
  padding: 5px 7px;
  border-radius: 5px;
  line-height: 1;
  display: flex;
  align-items: center;
}

.st-btn:hover {
  background: var(--c-surface, #f0f0f0);
}

.st-btn.active {
  background: var(--c-black, #111);
  color: var(--c-white, #fff);
}

.st-swatch {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 0.5px solid rgba(0, 0, 0, 0.25);
  background: var(--sw, #111);
  cursor: pointer;
  padding: 0;
}

.st-swatch.active {
  box-shadow: 0 0 0 2px var(--c-surface-raised, #fff), 0 0 0 3.5px var(--c-black, #111);
}

.canvas {
  position: relative;
  background: var(--c-white);
  box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.12);
  /* Work area: blocks parked on the pasteboard around the artboard stay
     visible (the artboard no longer clips its children); the surrounding
     .canvas-stage bounds the pasteboard instead. Output cropping is unchanged
     — exports draw on a state.width×height canvas regardless. */
  overflow: visible;
}

/* A parked block (no artboard overlap): still fully editable, but visually
   marked as "not part of the template" — buildTemplatePayload drops it from
   export/publish/generation. */
.block.off-canvas {
  opacity: 0.55;
  outline: 1px dashed rgba(10, 10, 10, 0.35);
  outline-offset: 2px;
}

/* AG-F08: the canvas is a drawing surface, not text. A gesture whose pointer
   leaves its block (the block held back by the canvas edge, a Shift
   axis-locked move) would otherwise start a text selection, and the browser
   then takes the next press on that block for a drag of the selection — the
   block stops following the pointer. The inline text editor is the one place
   on the canvas where selecting text is the point. */
.canvas {
  -webkit-user-select: none;
  user-select: none;
}

.canvas .text-preview[contenteditable="true"] {
  -webkit-user-select: text;
  user-select: text;
}

.grid-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(rgba(0, 0, 0, 0.12) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 0, 0, 0.12) 1px, transparent 1px);
  background-size: var(--grid-size, 20px) var(--grid-size, 20px);
}

.safe-zone-overlay {
  position: absolute;
  pointer-events: none;
  /* The overlay lives inside the zoomed artboard, so a plain 1.5px line and a
     10px label shrank with the zoom — at Fit (often 30–60%) the zone was a
     faint hairline with an unreadable label. Line width and label size are
     counter-scaled to stay constant on screen, and the line is drawn dark on
     a light halo so it reads on any background. */
  /* 2px before zoom: Chromium floors a border to whole CSS px, so the line
     lands between ~1.5 and 2 screen px at the usual zoom levels. */
  border: calc(2px / var(--canvas-zoom, 1)) dashed rgba(10, 10, 10, 0.75);
  box-shadow: 0 0 0 calc(1px / var(--canvas-zoom, 1)) rgba(255, 255, 255, 0.7);
}

.safe-zone-label {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: top left;
  transform: scale(calc(1 / var(--canvas-zoom, 1)));
  font-size: 10px;
  background: rgba(10, 10, 10, 0.85);
  color: var(--c-white);
  padding: 2px 6px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  font-family: var(--font-ui);
}

.selection {
  position: absolute;
  pointer-events: none;
  border: 1.5px dashed rgba(100, 150, 255, 0.6);
  background: rgba(100, 150, 255, 0.08);
}

/* Blocks */
.block {
  position: absolute;
  cursor: move;
  border: 1.5px dashed rgba(100, 150, 255, 0.4);
  overflow: hidden;
  background: rgba(255, 255, 255, 0.04);
}

/* While selected, the corner/edge resize handles are positioned half outside
   the block's own box so they straddle its edge. `overflow: hidden` above
   (needed to clip media/text to the block bounds normally) was clipping away
   the outer half of every handle, leaving only a sliver that was actually
   clickable - this is why grabbing a corner felt "pixel-perfect" and
   required repeated attempts. Only lift the clip while selected, since that
   is the only state in which handles exist.

   WP-9.8: the old 1.5px black border was easy to lose on busy imagery. The
   selected block now carries a dual ring — a white inner ring (box-shadow)
   between the block edge and a 2px accent outline — so at least one ring
   contrasts against any background. Outline + box-shadow paint outside the
   box without affecting layout (no border-width change → no layout shift),
   and the ring widths counter-divide --canvas-zoom (the .block .handle
   trick, applied to lengths) so the highlight reads the same at any zoom. */
.block.selected {
  --sel-ring: calc(1px / var(--canvas-zoom, 1));
  border-color: var(--c-sel-accent);
  overflow: visible;
  box-shadow: 0 0 0 var(--sel-ring) rgba(255, 255, 255, 0.95);
  outline: calc(2 * var(--sel-ring)) solid var(--c-sel-accent);
  outline-offset: var(--sel-ring);
}

/*
 * Block category fills.
 * Warm, desaturated earth/paper tones that read against the cream surfaces
 * without screaming at you. Each category gets a distinct hue + matching
 * dashed-border accent so blocks are identifiable at a glance even when
 * empty.
 */

.block[data-category="image"] {
  background: rgba(196, 123, 88, 0.55); /* terracotta */
  border-color: rgba(140, 80, 55, 0.85);
}

.block[data-category="video"] {
  background: rgba(112, 140, 168, 0.55); /* dusty blue */
  border-color: rgba(70, 95, 125, 0.85);
}

.block[data-category="image-video"] {
  background: rgba(141, 157, 127, 0.55); /* sage */
  border-color: rgba(92, 109, 80, 0.85);
}

.block[data-category="logo"] {
  background: rgba(212, 168, 102, 0.55); /* ochre */
  border-color: rgba(151, 113, 56, 0.85);
}

.block[data-category="price"] {
  background: rgba(154, 112, 136, 0.55); /* dusty plum */
  border-color: rgba(105, 70, 92, 0.85);
}

.block[data-category="text"] {
  /* Text renders transparent-on-white in the real output — the canvas must
     match (the old clay tint made blocks look different from the export).
     A faint neutral dashed border keeps the block's extent discoverable. */
  background: transparent;
  border-color: rgba(120, 110, 95, 0.45);
}

.block[data-category="background"] {
  background: rgba(232, 226, 213, 0.4); /* paper / warm neutral */
  border-style: solid;
  border-color: rgba(120, 110, 95, 0.7);
}

/* WP-13: non-rectangle Shape blocks (data-shape is only set for those). The
   inline SVG fills the block box; the geometry never exceeds it, but its
   drop-shadow filter does — lift the overflow clip so the shadow renders
   (rectangle blocks don't need this: box-shadow paints outside overflow). */
.block[data-shape] {
  overflow: visible;
}

.block-shape {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none; /* drag/select hits the block element itself */
  /* AG-F09: an outline stroke, a round / square line cap and the first dot of
     a dotted line reach past the block box — an <svg> clips to its box by
     default, which cut them off on the canvas (and in the injector) while the
     PNG export drew them whole. */
  overflow: visible;
  /* WP-13b free rotation: the whole svg rotates about the block center via
     an inline style transform; geometry inside stays at rotation 0. */
  transform-origin: center center;
}

.block-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  color: var(--c-white);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-family: var(--font-ui);
  opacity: 0.7;
  pointer-events: none;
  text-align: center;
  padding: 4px;
}

/* AG-12 (moonbase WP-9.5): clip frame around the media element.
   `.block.selected` lifts the block's own overflow:hidden so resize handles
   can straddle the edge, which would let a zoomed (transform:scale) media
   spill outside a selected block — the frame keeps the injector's
   overflow:hidden clip in every state. */
.block-media-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
}

.block-media {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.block-media img,
.block-media video {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.block[data-category="logo"] .block-media img {
  object-fit: contain;
}

/* Sketch overlay canvas (WP-1.2). Absolutely fills the block; the block's
   overflow:hidden clips it, and pointer-events:none keeps drag/select intact.
   The backing store is sized to block design px in updateBlockElement; the
   100% width/height CSS stretches it with the block, and the CSS transform
   zoom handles screen scale. */
.block-sketch {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Mockup actions (WP-2.3): Generate-mockup button + eye toggle side by side
   in the Mockup inspector panel. */
.mockup-actions-row {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}

/* .cmd-btn's display:flex overrides the UA [hidden] rule (same trap as
   .pl-context / .sketch-toolbar above) — without this the hidden eye toggle
   renders as an empty pill on a block that has no sketch. */
.mockup-actions-row .cmd-btn[hidden] {
  display: none;
}

/* AG-14 (moonbase bcb8641): the layer-count select spans the row like the
   buttons around it. */
.mockup-actions-row select.field-group-inp {
  width: 100%;
}

/* Sketch mode (WP-1.3) and Draw tool (WP-8.4): crosshair signals draw intent
   everywhere on the canvas, including over blocks (which normally show the
   move cursor). */
.canvas.sketch-mode,
.canvas.sketch-mode .block,
.canvas.draw-mode,
.canvas.draw-mode .block {
  cursor: crosshair;
}

/* Marquee multi-select rectangle (WP-8.5) — sibling of .selection (the Draw
   tool's rubber band) with a subtler tint so the two gestures read apart. */
.marquee {
  position: absolute;
  pointer-events: none;
  border: 1px dashed rgba(17, 17, 17, 0.55);
  background: rgba(100, 150, 255, 0.08);
  z-index: 4;
}

/* Group bounding box around a multi-selection (WP-8.5). The box itself is
   pointer-transparent — clicks fall through to blocks / empty canvas — only
   its corner handles take the pointer (group stretch; Shift = uniform scale). */
.group-box {
  position: absolute;
  pointer-events: none;
  border: 1.5px dashed var(--c-black, #111);
  z-index: 3;
}

.group-handle {
  position: absolute;
  width: 10px;
  height: 10px;
  background: var(--c-black, #111);
  border: 1.5px solid var(--c-white, #fff);
  border-radius: 50%;
  pointer-events: auto;
  /* Constant on-screen size at any zoom — same trick as .block .handle. */
  transform: scale(calc(1 / var(--canvas-zoom, 1)));
  z-index: 4;
}

/* Invisible hit-area padding, mirroring .block .handle::before. */
.group-handle::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26px;
  height: 26px;
  transform: translate(-50%, -50%);
}

.group-handle-nw { top: -5px; left: -5px; cursor: nwse-resize; }
.group-handle-ne { top: -5px; right: -5px; cursor: nesw-resize; }
.group-handle-sw { bottom: -5px; left: -5px; cursor: nesw-resize; }
.group-handle-se { bottom: -5px; right: -5px; cursor: nwse-resize; }

.block-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  font-size: 11px;
  color: var(--c-white);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-family: var(--font-ui);
  opacity: 0.6;
}

.text-preview {
  width: 100%;
  height: 100%;
  outline: none;
  font-family: 'Arial Black', Arial, sans-serif;
  font-size: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  word-break: break-word;
  padding: 4px;
}

.text-preview[contenteditable="true"] {
  cursor: text;
  outline: 1px dashed rgba(10, 10, 10, 0.3);
}

.block .handle {
  position: absolute;
  width: 12px;
  height: 12px;
  /* WP-9.8: visible again (they were pure hit targets) — white squares with
     an accent border match the new dual-ring highlight and stay readable on
     busy imagery. Square (vs the round .group-handle dots) keeps single
     selection visually distinct from a group selection. */
  background: var(--c-white);
  border: 1.5px solid var(--c-sel-accent);
  z-index: 2;
  /* Keep the on-screen grab target a constant size regardless of canvas
     zoom. Without this, the 12px handle shrinks along with the canvas
     transform (e.g. to ~3 screen px at 30% zoom) and becomes nearly
     impossible to land a click on. --canvas-zoom is kept in sync with the
     zoom level in applyZoom(). */
  transform: scale(calc(1 / var(--canvas-zoom, 1)));
}

/* Invisible hit-area padding so a resize starts even if the cursor isn't
   landed exactly on the small visible dot - this is scaled together with
   the handle above so it stays a comfortable, constant on-screen size at
   any zoom level. */
.block .handle::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 26px;
  height: 26px;
  transform: translate(-50%, -50%);
}

/* Corner handles (offsets = half the 12px handle, centered on the corner) */
.block .handle-nw {
  top: -6px;
  left: -6px;
  cursor: nw-resize;
}
.block .handle-ne {
  top: -6px;
  right: -6px;
  cursor: ne-resize;
}
.block .handle-sw {
  bottom: -6px;
  left: -6px;
  cursor: sw-resize;
}
.block .handle-se {
  bottom: -6px;
  right: -6px;
  cursor: se-resize;
}

/* Edge handles (text blocks only) */
.block .handle-n {
  top: -6px;
  left: 50%;
  margin-left: -6px;
  cursor: n-resize;
}
.block .handle-s {
  bottom: -6px;
  left: 50%;
  margin-left: -6px;
  cursor: s-resize;
}
.block .handle-e {
  top: 50%;
  right: -6px;
  margin-top: -6px;
  cursor: e-resize;
}
.block .handle-w {
  top: 50%;
  left: -6px;
  margin-top: -6px;
  cursor: w-resize;
}

/* =============================================================================
   Right panel (block inspector)
   ============================================================================= */
.panel-right {
  background: var(--c-white);
  border-left: 0.5px solid var(--c-border-default);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
}

.pr-header {
  padding: 10px 16px 10px 8px;
  border-bottom: 0.5px solid var(--c-border-subtle);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.pr-block-name {
  font-size: 16px;
  color: var(--c-text);
  font-weight: 500;
  letter-spacing: -0.01em;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pr-block-tag {
  font-size: 10px;
  color: var(--c-tag-text);
  background: var(--c-tag-bg);
  border: 0.5px solid var(--c-tag-border);
  padding: 3px 9px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  flex-shrink: 0;
}

.pr-body {
  flex: 1;
  overflow-y: auto;
}

.insp-section {
  border-bottom: 0.5px solid var(--c-border-subtle);
}

/* Collapsible inspector sections (WP-8.7): the header is the fold toggle. */
.insp-hdr {
  padding: 11px 16px 6px;
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
}

.insp-hdr-lbl {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #3a3a3a;
}

.insp-fold-icon {
  font-size: 12px;
  color: #8a8a8a;
  transition: transform 0.12s ease;
}

.insp-section:not(.folded) .insp-fold-icon {
  transform: rotate(90deg);
}

.insp-section.folded .insp-body {
  display: none;
}

.insp-section.folded .insp-hdr {
  padding-bottom: 11px;
}

.insp-body {
  padding: 4px 16px 14px;
}

.insp-actions {
  padding: 14px 16px 18px;
}

.prop-row {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 0;
  border-bottom: 0.5px solid var(--c-border-subtle);
}

.prop-row:last-child {
  border-bottom: none;
}

/* WP-14.4: small square toggle that sits inside a property row (the
   per-corner radius unlink). Pressed state mirrors .mode-btn.active. */
.mini-btn {
  border: 0.5px solid var(--c-border-default);
  background: var(--c-surface-raised);
  color: #555;
  cursor: pointer;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
}

.mini-btn:hover:not(:disabled) {
  background: var(--c-surface);
}

.mini-btn[aria-pressed="true"] {
  background: var(--c-black);
  border-color: var(--c-black);
  color: var(--c-white);
}

.mini-btn:disabled {
  opacity: 0.45;
  cursor: default;
}

/* WP-14.3: checkbox-first property row (Transparent fill). The checkbox leads
   so the row reads as a toggle rather than a labelled field. */
.prop-check {
  cursor: pointer;
}

.prop-check input[type="checkbox"] {
  margin: 0;
  flex-shrink: 0;
}

.prop-check .prop-lbl {
  width: auto;
}

/* Rows the Transparent toggle switches off stay visible but inert — the
   colours are still readable, they just don't apply. */
.prop-row--muted {
  opacity: 0.45;
  pointer-events: none;
}

.prop-lbl {
  font-size: 12px;
  color: var(--c-text-secondary);
  width: 92px;
  flex-shrink: 0;
}

.prop-inp {
  flex: 1;
  font-size: 12px;
  color: var(--c-text);
  border: 0.5px solid var(--c-border-default);
  background: var(--c-surface-raised);
  padding: 6px 8px;
  font-family: var(--font-ui);
  outline: none;
  min-width: 0;
}

.prop-inp:focus {
  border-color: var(--c-black);
}

.prop-inp-narrow {
  flex: 0 0 56px;
  text-align: right;
}

.prop-select {
  flex: 1;
  font-size: 12px;
  color: var(--c-text);
  border: 0.5px solid var(--c-border-default);
  background: var(--c-surface-raised);
  padding: 6px 8px;
  font-family: var(--font-ui);
  outline: none;
  min-width: 0;
}

.prop-select-narrow {
  flex: 0 0 70px;
}

.prop-slider {
  flex: 1;
  height: 2px;
  background: var(--c-border-default);
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  outline: none;
  border: none;
}

.prop-slider::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  background: var(--c-black);
  border-radius: 50%;
  border: none;
  cursor: pointer;
}

.prop-slider::-moz-range-thumb {
  width: 12px;
  height: 12px;
  background: var(--c-black);
  border-radius: 50%;
  border: none;
  cursor: pointer;
}

.prop-swatch {
  flex: 1;
  border: 0.5px solid var(--c-border-default);
  padding: 3px;
}

/* Media pair buttons */
.media-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}

.link-control > .prop-lbl {
  display: block;
  width: auto;
  margin-bottom: 4px;
  font-weight: 600;
  color: var(--c-text);
}

.link-control .prop-select {
  width: 100%;
}

.media-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 7px 9px;
  font-size: 12px;
  font-family: var(--font-ui);
  border: 0.5px solid var(--c-border-subtle);
  background: var(--c-surface-raised);
  color: var(--c-text);
  cursor: pointer;
}

.media-btn:hover:not(:disabled) {
  border-color: var(--c-black);
  background: var(--c-white);
}

.media-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.media-btn i {
  font-size: 13px;
  color: var(--c-text-muted);
}

.media-btn:hover:not(:disabled) i {
  color: var(--c-black);
}

.media-btn.danger {
  border-color: var(--c-destructive-border);
  color: var(--c-destructive);
}

.media-btn.danger:hover:not(:disabled) {
  border-color: var(--c-destructive);
  background: var(--c-destructive-bg);
}

/* Layer order pair */
.layer-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
}

.layer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 8px 10px;
  font-size: 12px;
  font-family: var(--font-ui);
  border: 0.5px solid var(--c-border-subtle);
  background: var(--c-surface-raised);
  color: var(--c-text);
  cursor: pointer;
}

.layer-btn:hover:not(:disabled) {
  border-color: var(--c-black);
  background: var(--c-white);
}

.layer-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.layer-btn i {
  font-size: 13px;
  color: var(--c-text-muted);
}

.layer-btn:hover:not(:disabled) i {
  color: var(--c-black);
}

/* =============================================================================
   Status bar
   ============================================================================= */
.statusbar {
  grid-column: 1 / -1;
  background: var(--c-surface-panel);
  border-top: 0.5px solid var(--c-border-default);
  display: flex;
  align-items: center;
  padding: 0 16px;
  gap: 12px;
  flex-shrink: 0;
}

.status-txt {
  font-size: 11px;
  color: var(--c-text-secondary);
  letter-spacing: 0.04em;
}

.status-warning {
  color: #b87100;
}

.status-error {
  color: var(--c-destructive);
}

/* =============================================================================
   Injector run cards
   ============================================================================= */
.injector-run-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 6px;
}

.injector-run-card {
  border: 0.5px solid var(--c-border-subtle);
  background: var(--c-surface-raised);
  padding: 8px 10px;
  font-size: 11px;
}

.injector-run-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
}

.injector-run-label {
  font-size: 12px;
  color: var(--c-text);
  font-weight: 500;
}

.injector-run-meta {
  font-size: 11px;
  color: var(--c-text-secondary);
  line-height: 1.5;
}

.injector-run-meta code {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 10px;
  color: var(--c-text-muted);
  background: var(--c-surface);
  padding: 1px 4px;
  border: 0.5px solid var(--c-border-subtle);
}

.injector-status-badge {
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 6px;
  border: 0.5px solid var(--c-border-default);
  background: var(--c-surface);
  color: var(--c-text-secondary);
}

/* =============================================================================
   Panel hide / show chevrons + reopener tabs
   ============================================================================= */
.panel-chevron {
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: #888;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
}

.panel-chevron:hover {
  background: var(--c-surface);
  color: var(--c-black);
}

.panel-chevron i {
  font-size: 16px;
}

.canvas-area {
  position: relative;
}

.panel-reopen {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 56px;
  background: var(--c-surface-raised);
  border: 0.5px solid var(--c-border-default);
  color: #666;
  cursor: pointer;
  z-index: 10;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.panel-reopen:hover {
  background: var(--c-white);
  border-color: var(--c-black);
  color: var(--c-black);
}

.panel-reopen i {
  font-size: 14px;
}

.panel-reopen--left {
  left: 0;
}

.panel-reopen--right {
  right: 0;
}

.app--left-hidden .panel-reopen--left {
  display: inline-flex;
}

.app--right-hidden .panel-reopen--right {
  display: inline-flex;
}

/* =============================================================================
   Right-panel overflow guards.
   The block inspector packs a lot of two-input rows (line height + value,
   etc.) into 280px. Prevent horizontal overflow at the body level and let
   any prop-row wrap when its inputs can't shrink further.
   ============================================================================= */
.pr-body {
  overflow-x: hidden;
}

/* No selection → the inspector shows only a hint; every block-editing section
   and the Duplicate/Delete actions hide (body.no-block-selected is kept in
   sync by updateBlockControls in app.js). */
.pr-empty-hint {
  display: none;
  padding: 24px 16px;
  color: var(--c-text-muted, #8a8577);
  font-size: 12px;
  line-height: 1.6;
  text-align: center;
}

.pr-empty-hint .ti {
  display: block;
  font-size: 22px;
  margin-bottom: 8px;
}

body.no-block-selected .pr-body .insp-section,
body.no-block-selected .pr-body .insp-actions {
  display: none;
}

/* The Layers view stays available with nothing selected — it's how a block
   buried under a full-canvas layer gets selected in the first place. */
body.no-block-selected .pr-body .insp-section[data-fold="layers"] {
  display: block;
}

body.no-block-selected .pr-empty-hint {
  display: block;
}

/* Layers view rows */
.layers-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 220px;
  overflow-y: auto;
}

.layers-empty {
  color: var(--c-text-muted, #8a8577);
  font-size: 11px;
  padding: 4px 2px;
}

/* Layer visibility (Photoshop's eye column). The toggle lives OUTSIDE the row
   button — a button inside a button is invalid markup, and keeping it separate
   also means clicking the eye never selects the layer, which is the behaviour
   people expect from that column. */
.layers-row-wrap {
  display: flex;
  align-items: center;
  gap: 2px;
}

.layers-row-wrap .layers-row {
  flex: 1;
  min-width: 0;
}

.layers-eye {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--c-text-muted, #8a8a8a);
  cursor: pointer;
}

.layers-eye:hover {
  background: var(--c-surface-raised);
  color: var(--c-text);
}

/* Off is the state worth spotting at a glance, so it keeps full contrast while
   the row itself dims — the opposite of hiding the control along with the
   layer. */
.layers-eye.is-hidden {
  color: var(--c-text);
}

.layers-row.is-hidden .layers-name,
.layers-row.is-hidden .ti {
  opacity: 0.4;
}

/* Editor-only: the block stays in the template, in exports and in what the
   injector renders — this hides it on the authoring canvas so you can work on
   what is underneath. visibility (not display) keeps its box measurable, which
   fittext.js's follower pass depends on. */
.block--hidden {
  visibility: hidden;
  pointer-events: none;
}

.layers-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 5px 8px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12px;
  text-align: left;
  cursor: pointer;
}

.layers-row:hover {
  background: rgba(0, 0, 0, 0.05);
}

.layers-row.selected {
  background: rgba(0, 0, 0, 0.08);
  font-weight: 600;
}

.layers-row .ti {
  font-size: 14px;
  opacity: 0.7;
  flex: none;
}

.layers-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* AG-26 (moonbase 46bfea1): pictogram picker grid */
.picto-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
  margin-top: 8px;
  max-height: 220px;
  overflow-y: auto;
}

.picto-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  border: 1px solid var(--c-border-subtle, rgba(0, 0, 0, 0.12));
  border-radius: 6px;
  background: transparent;
  color: inherit;
  font-size: 17px;
  cursor: pointer;
}

.picto-btn:hover {
  background: rgba(0, 0, 0, 0.05);
}

.picto-btn.active {
  border-color: var(--c-sel-accent, #1a1a1a);
  box-shadow: inset 0 0 0 1px var(--c-sel-accent, #1a1a1a);
}

.insp-body {
  max-width: 100%;
}

.insp-body > * {
  max-width: 100%;
}

.prop-row {
  flex-wrap: wrap;
}

/* .prop-row's display:flex outranks the UA [hidden] rule (same trap the
   .ab-menu/.pl-context comments document) — restate it so rows that are
   switched off really hide (corner radius and thickness per shape, WP-13). */
.prop-row[hidden] {
  display: none;
}

.prop-row > * {
  min-width: 0;
  max-width: 100%;
}

.prop-row .swatch-row {
  flex: 1 1 100%;
}

textarea.field-group-inp {
  max-width: 100%;
  width: 100%;
}

/* =============================================================================
   Scrollbars
   ============================================================================= */
.pl-body::-webkit-scrollbar,
.pr-body::-webkit-scrollbar,
.canvas-area::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

.pl-body::-webkit-scrollbar-thumb,
.pr-body::-webkit-scrollbar-thumb,
.canvas-area::-webkit-scrollbar-thumb {
  background: #d4d3cc;
  border: 0.5px solid transparent;
}

.pl-body::-webkit-scrollbar-track,
.pr-body::-webkit-scrollbar-track,
.canvas-area::-webkit-scrollbar-track {
  background: transparent;
}

/* =============================================================================
   AI concepts — brief cards in the Brief rail (docs/creative-ideation.md)
   ============================================================================= */
.brief-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.brief-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 11px;
  background: var(--c-white, #fff);
  border: 0.5px solid var(--c-border-default, #d0cfc8);
}

.brief-item.drafted {
  opacity: 0.6;
}

.brief-item-tags {
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-text-muted, #888);
}

.brief-item-title {
  font-size: 12px;
  font-weight: 600;
}

.brief-item-hook {
  font-size: 12px;
  font-style: italic;
}

.brief-item-msg {
  font-size: 11px;
  color: var(--c-text-secondary, #555);
  line-height: 1.45;
}

.brief-item-actions {
  display: flex;
  gap: 6px;
  margin-top: 4px;
}

.brief-item-actions .cmd-btn {
  width: auto;
  flex: 1;
}

/* Tool shortcut numerals (WP-10.5) — tiny subscript in each mode button's
   corner, purely decorative (tooltips carry the same hint textually). */
.mode-btn {
  position: relative;
}

/* A <kbd>, not a <span>: ".mode-btn span" must keep resolving to the single
   label span. */
.mode-key {
  position: absolute;
  right: 3px;
  bottom: 2px;
  font-size: 8px;
  font-family: var(--font-ui);
  line-height: 1;
  color: #b3b3b3;
  pointer-events: none;
  user-select: none;
}

.mode-btn.active .mode-key {
  color: rgba(255, 255, 255, 0.55);
}

/* =============================================================================
   WP-10 — Font picker for text blocks + tool shortcut numerals
   ============================================================================= */

/* 10.1: curated picker. The trigger reuses .prop-inp chrome; the menu reuses
   the .ab-menu dropdown (Add block split button) and anchors to this wrapper. */
.font-picker {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
}

.font-picker-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  cursor: pointer;
  text-align: left;
}

.font-picker-btn i {
  font-size: 12px;
  color: #8a8a8a;
  flex-shrink: 0;
}

/* The label doubles as a preview in the block's own font (app.js sets
   style.fontFamily), so give it a touch more size than the 12px inputs. */
#fontPickerLabel {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
}

/* .prop-row's display:flex outranks the UA [hidden] rule (same trap the
   .ab-menu/.pl-context comments document) — restate it so the custom-font
   rows actually hide outside custom mode. */
.prop-row[hidden] {
  display: none;
}

/* Right-anchored so the 280px inspector never clips it; scrolls when the
   curated list outgrows the viewport. */
.font-menu {
  left: auto;
  right: 0;
  min-width: 216px;
  max-height: 320px;
  overflow-y: auto;
}

.font-group-lbl {
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #9a9a9a;
  padding: 7px 8px 2px;
  user-select: none;
}

/* Options render in their own face (inline font-family from app.js). */
.font-menu .ab-opt {
  font-size: 14px;
  line-height: 1.25;
}

.font-menu-sep {
  border-top: 0.5px solid var(--c-border-subtle, #e4e4e4);
  margin: 4px 2px;
}

.font-custom-opt {
  font-size: 12px !important;
  font-style: italic;
  color: var(--c-text-secondary, #6a6a6a);
}

/* 10.3: branding interplay hint. */
.font-hint {
  font-size: 10.5px;
  color: var(--c-text-secondary, #8a8a8a);
  line-height: 1.4;
  margin: 2px 0 4px;
}

/* 10.4: apply-to-all (extends .layer-btn). */
.font-apply-btn {
  flex: 1;
  font-size: 11.5px;
}

/* 10.5: tool shortcut numerals — tiny subscript in each mode button's corner,
   purely decorative (tooltips carry the same hint textually). */
.mode-btn {
  position: relative;
}

/* A <kbd>, not a <span>: the WP-9.7 test (and any styling) addressing
   ".mode-btn span" must keep resolving to the single label span. */
.mode-key {
  position: absolute;
  right: 3px;
  bottom: 2px;
  font-size: 8px;
  font-family: var(--font-ui);
  line-height: 1;
  color: #b3b3b3;
  pointer-events: none;
  user-select: none;
}

.mode-btn.active .mode-key {
  color: rgba(255, 255, 255, 0.55);
}

/* =============================================================================
   WP-11 — price block preview & typography
   ============================================================================= */
/* Price blocks share the text inspector (#textBlockControls gets .price-mode).
   WP-14.6 (item 10): the injector honours the full text style set on price
   blocks now, so the sections that used to hide here are visible. What still
   hides is only what is MEANINGLESS for a price, not what was unsupported:
   the multi-line content textarea (a price's text is its product's {{price}}
   — the Price copy field replaces it, AG-18) and paragraph spacing.
   Auto-resize is VISIBLE for prices: a pill hugging its value is the
   canonical hug case (AG-F07, layout fidelity). */
#textBlockControls:not(.price-mode) .insp-section[data-fold="price-content"] {
  display: none;
}

#textBlockControls.price-mode .insp-section[data-fold="text-content"],
#textBlockControls.price-mode #textParaSpacingRow {
  display: none;
}

/* WP-11.6 — the custom Google-font input doubles as an autocomplete anchor
   (.font-picker wrapper); the input must fill the wrapper's flex row. */
.font-picker > .prop-inp {
  flex: 1;
  min-width: 0;
}

/* ==========================================================================
   Inspector density pass (2026-08-22): Figma-like right panel.
   Label + control stay on one row (the .prop-row contract), rows tighten to
   28px, per-row hairlines drop (the section header is the divider), section
   headers get a hover affordance, and Duplicate/Delete sit side by side.
   Scoped to .panel-right so the left rail keeps its roomier rhythm.
   ========================================================================== */
.panel-right .insp-hdr {
  padding: 8px 14px 5px;
}

.panel-right .insp-section.folded .insp-hdr {
  padding-bottom: 8px;
}

.panel-right .insp-hdr:hover {
  background: rgba(0, 0, 0, 0.03);
}

.panel-right .insp-body {
  padding: 2px 14px 10px;
}

.panel-right .prop-row {
  padding: 3px 0;
  min-height: 28px;
  gap: 8px;
  border-bottom: none;
}

.panel-right .prop-lbl {
  width: 84px;
  font-size: 11px;
}

.panel-right .prop-inp,
.panel-right .prop-select {
  padding: 4px 7px;
  border-radius: 4px;
}

.panel-right .prop-inp:focus,
.panel-right .prop-select:focus {
  border-color: var(--c-sel-accent, #1a1a1a);
  box-shadow: 0 0 0 1px var(--c-sel-accent, #1a1a1a);
}

.panel-right .swatch-row {
  padding: 2px 4px;
  border-radius: 4px;
}

.panel-right .hint,
.panel-right .font-hint {
  font-size: 10.5px;
  line-height: 1.45;
  margin: 4px 0;
  opacity: 0.75;
}

.panel-right .geom-grid {
  gap: 4px 8px;
  margin-bottom: 4px;
}

.panel-right .layer-pair {
  gap: 6px;
}

.panel-right .insp-actions {
  display: flex;
  gap: 8px;
  padding: 10px 14px 14px;
}

.panel-right .insp-actions .cmd-btn {
  flex: 1;
  margin: 0;
}

/* --- Inspector enhancements (2026-08-22) ---------------------------------- */

/* 6. Sticky section headers: each header pins to the top of the scrolling
   .pr-body while its section's rows scroll under it. */
.panel-right .insp-hdr {
  position: sticky;
  top: 0;
  z-index: 4;
  background: var(--c-white);
}

/* 2. Scrubbable labels: numeric rows advertise the drag affordance. */
.panel-right .prop-row:has(input[type="number"]:not(:disabled)) > .prop-lbl,
.panel-right .geom-field:has(input[type="number"]:not(:disabled)) > .geom-lbl {
  cursor: ew-resize;
}

body.scrubbing {
  cursor: ew-resize;
  user-select: none;
}

/* 3. Generated hex fields sit beside their swatch, same look as the
   hand-authored ones but narrower so tight rows keep breathing. */
.swatch-text--generated {
  width: 68px;
  flex: none;
}

/* 4. Segmented icon controls (H/V align). The original select stays as the
   hidden source of truth. */
.seg-replaced {
  display: none !important;
}

.seg-group {
  display: flex;
  gap: 2px;
  flex: 1;
}

.seg-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 26px;
  border: 0.5px solid var(--c-border-default);
  border-radius: 4px;
  background: var(--c-surface-raised);
  color: var(--c-text-secondary);
  font-size: 14px;
  cursor: pointer;
}

.seg-btn:hover {
  background: rgba(0, 0, 0, 0.05);
}

.seg-btn.active {
  background: var(--c-text, #1a1a1a);
  border-color: var(--c-text, #1a1a1a);
  color: var(--c-white, #fff);
}

/* Layers drag-to-reorder */
.layers-grip {
  font-size: 12px;
  opacity: 0.35;
  cursor: grab;
  flex: none;
}

.layers-row.dragging {
  opacity: 0.45;
}

.layers-row.drop-above {
  box-shadow: 0 -2px 0 0 var(--c-sel-accent, #1a1a1a);
}

.layers-row.drop-below {
  box-shadow: 0 2px 0 0 var(--c-sel-accent, #1a1a1a);
}

/* 1. Rename-in-place: editable header gets a visible field treatment. */
.pr-block-name[contenteditable="true"] {
  outline: 1px solid var(--c-sel-accent, #1a1a1a);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Color rows stay on ONE line — label left, swatch+hex right. The responsive
   overflow guard above forced .swatch-row to flex-basis 100%, wrapping it
   under its label; instead let the hex input shrink within the row. */
.panel-right .prop-row {
  flex-wrap: nowrap;
}

.panel-right .prop-row .swatch-row {
  flex: 1 1 auto;
  min-width: 0;
}

.panel-right .prop-row .swatch-text {
  flex: 1 1 40px;
  min-width: 0;
  width: auto;
}

.panel-right .prop-row .swatch-text--generated {
  flex: 0 1 68px;
}

/* AG-12 (moonbase WP-14.7): the block a dropped file will land on. Drawn as an
   inset ring so it reads at any block size and never shifts layout. */
.block.drop-target {
  box-shadow: inset 0 0 0 2px var(--c-sel-accent), 0 0 0 1px var(--c-sel-accent);
}

/* AG-12 (moonbase WP-14.8): a block whose media is isolated for cursor
   positioning. The dashed ring separates "I am editing the picture" from the
   ordinary selection ring, and the move cursor advertises the drag. */
.block.media-adjust {
  outline: 2px dashed var(--c-sel-accent);
  outline-offset: -2px;
  cursor: move;
}

.block.media-adjust .block-media {
  cursor: move;
}

/* Per-block busy overlay (AG-13, moonbase WP-9.9): painted on the busy block
   itself from the op registry (app.js busyBlocks) — visible regardless of
   selection. A subtle veil + a dark pill with a spinner and the op label; the
   pill counter-scales --canvas-zoom so it reads the same at any zoom. */
.block-busy {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.45);
  pointer-events: none;
}

.block-busy-badge {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border-radius: 999px;
  background: rgba(17, 17, 17, 0.78);
  color: #fff;
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: 0.04em;
  white-space: nowrap;
  transform: scale(calc(1 / var(--canvas-zoom, 1)));
}

.block-busy-spinner {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  animation: block-busy-spin 0.8s linear infinite;
}

@keyframes block-busy-spin {
  to {
    transform: rotate(360deg);
  }
}

/* Polish with AI (AG-15, moonbase WP-4) — question round + result list in the
   AI rail. .cmd-btn and .brief-list set display:flex, which overrides the UA
   [hidden] rule — guard the section's hideable elements explicitly. */
.pl-section .cmd-btn[hidden],
.brief-list[hidden],
.sketch-agent-questions[hidden] {
  display: none;
}

.sketch-agent-questions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 10px;
}

.sketch-q {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.sketch-q-label {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
}

.sketch-q-option {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  cursor: pointer;
}

.sketch-agent-summary {
  font-style: italic;
}

/* Wiring badges (AG-16, moonbase WP-9.3b): tiny corner chips on blocks
   carrying creative links (workflow / motion / draft request) so wiring is
   glanceable. pointer-events:none keeps drag/select intact; the inverse-zoom
   transform is the .block .handle trick — constant on-screen size at any zoom. */
.block-badges {
  position: absolute;
  top: 2px;
  right: 2px;
  display: flex;
  gap: 3px;
  z-index: 3;
  pointer-events: none;
  transform-origin: top right;
  transform: scale(calc(1 / var(--canvas-zoom, 1)));
}

.block-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  background: rgba(17, 17, 17, 0.72);
  color: #fff;
  font-size: 10px;
}

/* Unfulfilled AI-draft request: a dashed placeholder chip, visually "empty"
   until a real workflow is linked. */
.block-badge-request {
  background: rgba(255, 255, 255, 0.65);
  color: rgba(17, 17, 17, 0.75);
  border: 1px dashed rgba(17, 17, 17, 0.55);
}

/* While sketching, the chips sit exactly where strokes land at the block's
   corner — hide them so they never read as part of the drawing. */
.canvas.sketch-mode .block-badges {
  display: none;
}

/* Plan-apply pulse (WP-9.3a): a ~1.5s expanding halo on blocks a Polish plan
   just wired — the class is added transiently in app.js (pulseWiredBlocks). */
.block.wired-pulse {
  animation: wired-pulse 1.5s ease-out 1;
}

@keyframes wired-pulse {
  0% {
    box-shadow: 0 0 0 3px rgba(100, 150, 255, 0.9);
  }
  100% {
    box-shadow: 0 0 0 14px rgba(100, 150, 255, 0);
  }
}

/* Inline text-button in a status line (the WP-9.2 "Revert" link). */
.status-link {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* =============================================================================
   MA-1 microanimations — overlay block design-time glyph + motion preview.
   ============================================================================= */
.block.overlay-block {
  background: transparent;
  border: 1.5px dashed currentColor;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  opacity: 0.85;
}
.block.overlay-block .overlay-glyph {
  width: 40%;
  height: 40%;
  max-width: 48px;
  max-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.block.overlay-block .overlay-glyph svg { width: 100%; height: 100%; }
.block.overlay-block .overlay-glyph-label {
  font-size: 10px;
  letter-spacing: 0.04em;
  color: currentColor;
  background: rgba(255, 255, 255, 0.85);
  padding: 1px 6px;
  border-radius: 3px;
  white-space: nowrap;
  max-width: 95%;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The preview layer draws overlay sprites above the DOM blocks. */
.motion-preview-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 60;
}

/* Hover-and-hold tooltip bubble (moonbase 7cc6d87; AG-34). One engine in
   app.js, copy in TOOLTIP_COPY. Fixed-position so it escapes every scroll
   container and overflow clip. */
.ui-tip {
  position: fixed;
  z-index: 9999;
  max-width: 260px;
  padding: 6px 9px;
  background: rgba(28, 26, 22, 0.96);
  color: #f5f2ea;
  font-size: 11.5px;
  line-height: 1.45;
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.1s ease;
}

.ui-tip.visible {
  opacity: 1;
}
