:root {
  --coral: #FF3D5A;
  --ink:   #000000;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
button { font-family: inherit; }

/* ── Custom cursors (Figma-style) ── */
html, body, * {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='18' height='22' viewBox='0 0 18 22'><path d='M2 2 L2 16.5 L5.8 13 L7.9 17.8 L10.2 16.9 L8 12 L13 12 Z' fill='black' stroke='white' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'/></svg>") 2 2, default;
}

/* Pointer / clickable elements — same custom arrow, kept BLACK (no black→red
   hover swap; the cursor no longer changes colour). */
button,
[role="button"],
.opt,
.color-swatch, .color-add,
.pattern-slot, .pattern-slot .del-btn,
#collapse-btn,
.picker-sv, .picker-hue, .picker-done, .swatch-x,
input[type="range"] {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='18' height='22' viewBox='0 0 18 22'><path d='M2 2 L2 16.5 L5.8 13 L7.9 17.8 L10.2 16.9 L8 12 L13 12 Z' fill='black' stroke='white' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'/></svg>") 2 2, pointer;
}

/* Landing buttons (smiley + randomize) + tile-shape icons: native pointer cursor.
   ID/class specificity overrides [role="button"] and the * { default } rules.
   Children targeted explicitly so SVG descendants can't revert to default. */
#toggle-btn, #toggle-btn *,
#center-randomize, #center-randomize *,
#edit-btn, #edit-btn *,
#mode-btn, #mode-btn *,
#made-by-tag, #made-by-tag *,
.tag-chip, .tag-chip *,
.opt-icon, .opt-icon * {
  cursor: pointer;
}

/* Text input — I-beam */
input[type="text"], .picker-hex {
  cursor: text;
}

/* ── Custom product tooltips (black pill · white text · Host Grotesk) ── */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%);
  background: #000;
  color: #fff;
  font-family: 'Host Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 5px 13px;
  border-radius: 100px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  /* No delay on the way out — leaving the element hides the tooltip at once. */
  transition: opacity 0.12s;
  z-index: 500;
}
/* 0.6s delay before the tooltip appears on hover (duration 0.12s, delay 0.6s). */
[data-tooltip]:hover::after { opacity: 1; transition: opacity 0.12s 0.6s; }
/* Hide tooltip while the user is holding or dragging */
body.swatch-watching [data-tooltip]::after,
body.swatch-dragging [data-tooltip]::after { opacity: 0 !important; }

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #dfff00;
  font-family: 'Host Grotesk', -apple-system, BlinkMacSystemFont, sans-serif;
  font-weight: 600;
  font-size: 16px;
  color: #000;
  -webkit-font-smoothing: antialiased;
}

/* ── Canvas (full viewport, behind everything) ── */
#canvas {
  position: fixed;
  inset: 0;
  display: block;
  z-index: 0;
  background: #1a1a1f;
}

/* ── Edit-mode spotlight overlay ──
   Full-viewport, click-through layer ABOVE the canvas (z:0) and BELOW all UI
   (panel z:90, shelf z:115). The cutout child is positioned/sized by JS to the
   centered 3×3 editable tile; its large box-shadow SPREAD paints the dim tint
   everywhere outside the cutout — a hole-punch without clip-path. The tint color
   is the existing edit background (#1a1a1f) at 80% opacity. The element's own
   opacity is the ONLY animated property (120ms ease-out) on enter/exit. */
#edit-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;          /* clicks pass through to the canvas for painting */
  opacity: 0;
  display: none;
  transition: opacity 0.12s ease-out;
}
#edit-overlay.visible { opacity: 1; }
#edit-overlay-cutout {
  position: absolute;            /* left/top/width/height set by JS each render */
  /* Outset spread = the dim surround. Not `inset`: an inset shadow would tint
     INSIDE the cutout; the surround must be painted OUTWARD to leave a clean hole. */
  box-shadow: 0 0 0 9999px rgba(26, 26, 31, 0.8);
}

/* ============ TOGGLE BUTTON ============ */
#toggle-btn {
  position: fixed;
  top: 16px;
  left: 16px;
  width: 85px;
  height: 85px;
  cursor: pointer;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  transition: transform 0.12s ease;
}
#toggle-btn:hover  { transform: scale(1.05); }
#toggle-btn:active { transform: scale(0.96); }

/* ============ LANDING RANDOMIZE (sits right of the smiley toggle) ============ */
#center-randomize {
  position: fixed;
  top: 16px;
  left: 113px;            /* 16 (toggle.left) + 85 (toggle.width) + 12 gap */
  width: 85px;
  height: 85px;
  cursor: pointer;
  z-index: 100;
  display: none;          /* shown when panel is closed */
  transition: transform 0.12s ease;
  user-select: none;
}
#center-randomize:hover  { transform: scale(1.05); }
#center-randomize:active { transform: scale(0.96); }
#center-randomize svg {
  display: block;
  width: 100%;
  height: 100%;
}
#toggle-btn > svg:first-child {
  display: block;
  width: 100%;
  height: 100%;
}
#toggle-btn .face-inner {
  position: absolute;
  /* Percentage-based so the face scales with the button (85px desktop → 60px phone).
     left 22.5/85 = 26.47%, top 36/85 = 42.35%, width 40/85 = 47.06% */
  left: 26.47%;
  top: 42.35%;
  width: 47.06%;
  height: auto;
  pointer-events: none;
}

/* ============ PANEL ============ */
#panel {
  position: fixed;
  top: 16px;
  left: 16px;
  width: 420px;
  font-family: 'Host Grotesk', sans-serif;
  font-weight: 600;
  user-select: none;
  -webkit-user-select: none;
  z-index: 90;
  /* IMPORTANT: keep overflow visible so the color picker popup can overflow */
  overflow: visible;
  display: none;
}

.panel-card {
  background: #fff;
  border-radius: 20px;
  border: 5px solid #000;
  box-shadow: 0 4px 24px rgba(0,0,0,0.16);
  padding: 20px;
  box-sizing: border-box;
  /* DO NOT set overflow: hidden — color picker popup must be able to overflow */
}

/* ── Single grid that contains header + all rows so the
      "controls" title aligns with col-2 controls below ── */
.controls-body {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 16px;
  row-gap: 14px;
  align-items: center;
}

/* Header cells: extra space below the header row */
.header-cell {
  margin-bottom: 6px;
  margin-top: 6px;
}
#collapse-btn {
  cursor: pointer;
  display: flex;
  align-items: center;
  width: max-content;          /* hug the icon, don't stretch the grid column */
  justify-self: start;
  position: relative;          /* anchor for the ::after hit-area */
  overflow: visible;
  transition: transform 0.12s ease;
}
#collapse-btn svg { display: block; pointer-events: none; }
/* Invisible pseudo-element extends the clickable area without resizing the SVG */
#collapse-btn::after {
  content: '';
  position: absolute;
  top: -28px;
  right: -22px;   /* stays clear of the "controls" title text */
  bottom: -28px;
  left: -28px;
}
#collapse-btn:hover  { transform: scale(1.06); }
#collapse-btn:active { transform: scale(0.95); }
.controls-title {
  font-size: 28px;
  font-weight: 700;
  color: #000;
  line-height: 1;
  justify-self: start;
}

.ctrl-label {
  font-size: 18px;
  font-weight: 600;
  color: #000;
  white-space: nowrap;
  grid-column: 1;
}
.ctrl-value {
  font-size: 18px;
  font-weight: 600;
  color: #000;
  grid-column: 2;
  justify-self: start;
}

/* ── Preview-repeat toggle pill (small lowercase 'on'/'off') ── */
/* Container for tile preview toggles (tile preview + grid on/off) */
.preview-controls {
  grid-column: 2;
  /* stretch to full column width so the grid section can align with pill 4 */
  display: flex;
  align-items: center;
  gap: 8px;
}
/* "grid" label — pushed to the right end of the row via auto margin,
   landing the toggle above the 4th color pill.
   Typography matches .ctrl-label exactly (18px / 600). */
.preview-label {
  font-size: 18px;
  font-weight: 600;
  color: #000;
  white-space: nowrap;
  line-height: 1;
  margin-left: auto;
}

.preview-toggle {
  height: 28px;
  min-width: 72px;   /* sized for "off" — pins both states to the same width */
  padding: 0 18px;
  border-radius: 100px;
  border: 5px solid #000;
  background: #fff;
  color: #000;
  font-family: 'Host Grotesk', sans-serif;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.preview-toggle.active {
  background: #FF3131;
  color: #000;
}
/* Locked in edit mode: greyed out, not interactive */
.preview-toggle:disabled {
  cursor: default;
  opacity: 0.4;
}

/* ── Pattern color swatches ──
   Each pill has its own ✕ on hover (only when >1). An inline + slot
   appears at the end of the row when colors < 3. */
.color-swatches {
  display: flex;
  align-items: center;
  gap: 8px;
  grid-column: 2;
  flex-wrap: nowrap;
}
.color-swatch {
  flex: 1;
  min-width: 36px;
  height: 28px;
  border-radius: 100px;
  border: 5px solid #000;
  cursor: pointer;
  position: relative;
  overflow: visible;       /* allow ✕ to overflow the pill edge */
}
/* Empty slot (when colors < 3): greyed dashed pill */
.color-swatch.empty {
  background: transparent !important;
  border-style: dashed;
  border-color: #BBB;
  display: flex;
  align-items: center;
  justify-content: center;
}
.color-swatch.empty:hover { border-color: #000; }
.color-swatch.empty .empty-plus {
  color: #BBB;
  font-size: 14px;
  font-weight: 700;
  pointer-events: none;
  line-height: 1;
}
.color-swatch.empty:hover .empty-plus { color: #000; }
.color-swatch .pill-fill {
  position: absolute;
  inset: 0;
  border-radius: 100px;
  overflow: hidden;
}
.color-swatch input[type="color"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  border: none;
  padding: 0;
  cursor: pointer;
}
.color-swatch .swatch-x {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #FF3131;
  color: #000;
  border: 2px solid #fff;
  font-size: 10px;
  line-height: 1;
  font-weight: 900;
  cursor: pointer;
  padding: 0;
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 3;
}
.color-swatch:hover .swatch-x { display: flex; }
.color-swatch .swatch-x:hover { filter: brightness(1.1); }

/* ── Drag cursors (body classes set by JS) ──
   hover  → red pointer (from the global pointer selector above)
   hold   → grab  (mousedown crosses the swatch; 'watching' phase)
   drag   → grabbing (threshold crossed; 'dragging' phase)           */
body.swatch-watching,
body.swatch-watching * {
  cursor: grab !important;
}
body.swatch-dragging,
body.swatch-dragging *,
body.slider-dragging,
body.slider-dragging * {
  cursor: grabbing !important;
}

/* ── Mouse-drag reorder: ghost (floating copy) + placeholder (drop target) ── */
.swatch-ghost {
  border: 5px solid #000;
  border-radius: 100px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.28);
}
.swatch-placeholder {
  background: transparent !important;
  border-style: dashed !important;
  border-color: #FF3131 !important;
  pointer-events: none;
}

/* Inline "add color" slot — same width as a swatch, dashed grey, "+" inside */
.color-add {
  flex: 1;
  min-width: 36px;
  height: 28px;
  border-radius: 100px;
  border: 2px dashed #BBB;
  background: transparent;
  color: #BBB;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  transition: border-color 0.1s, color 0.1s;
}
.color-add:hover { border-color: #000; color: #000; }

/* ── Slider ── */
.slider-row {
  grid-column: 1 / -1;
  position: relative;
  padding-bottom: 4px;
}
.slider-track {
  position: relative;
  height: 5px;
  background: #000;
  border-radius: 0;
  margin: 16px 0 20px;
}
/* The real range input is invisible and overlays the track; the visible thumb is
   the .slider-thumb div positioned by JS. Freehand sliders share this treatment. */
#grid-size,
#fh-scale,
#fh-weight,
#fh-density,
#ai-detail {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  top: -16px;
  left: 0;
  width: 100%;
  height: 32px;
  opacity: 0;
  cursor: pointer;
  z-index: 3;
  margin: 0;
  background: transparent;
}
.slider-thumb {
  position: absolute;
  top: 50%;                                   /* center of 5px track */
  left: 0;
  transform: translate(-50%, -50%);           /* thumb's own center sits on track center */
  background: #fff;
  border: 5px solid #000;
  pointer-events: none;
  z-index: 2;
  box-sizing: border-box;                     /* width/height set by JS = OUTER size */
  /* width/height set dynamically by JS */
}

/* Level-of-detail slider end labels (AI survey, step 1) */
/* Detail row: "level of detail" label + current value on one line — same shape
   as the controls "scale / 8 px/cell" row (label left, value right). */
.ai-detail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
.ai-detail-head .ai-step-section-label { margin: 14px 0 0; }
.ai-detail-value {
  font-size: 18px;
  font-weight: 600;
  color: #000;
}
/* Match the controls "scale" slider gap: its grid row-gap (14px) + the track's
   own top margin (16px) = 30px above the track. Block flow here only gives the
   16px track margin, so add the missing 14px as padding (padding doesn't collapse). */
#ai-panel .slider-row { padding-top: 14px; }

/* ── Scrollable text options ── */
.options-scroll {
  display: flex;
  gap: 14px;
  overflow-x: auto;
  scrollbar-width: none;
  cursor: grab;
  white-space: nowrap;
  -webkit-overflow-scrolling: touch;
  grid-column: 2;
  user-select: none;
}
.options-scroll::-webkit-scrollbar { display: none; }
.options-scroll.dragging { cursor: grabbing; }
.opt {
  font-size: 18px;
  font-weight: 600;
  color: #000;
  cursor: pointer;
  flex-shrink: 0;
  white-space: nowrap;
}
.opt.active {
  color: #FF3131;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 2px;
}

/* Icon-mode options (used by tile shape) — fixed, evenly spaced, no scroll */
#tile-shape-scroll {
  display: flex !important;
  justify-content: space-between;
  align-items: center;
  overflow: visible !important;
  overflow-x: visible !important;
  cursor: default;
  gap: 0;
  white-space: normal;
  user-select: none;
}
.opt-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 28px;      /* match pill height */
  color: #BBB;
  cursor: pointer;
  transition: color 0.12s;
  flex-shrink: 0;
}
.opt-icon:hover { color: #555; }
.opt-icon.active {
  color: #FF3131;
  /* no underline, no layout shift — only color changes */
}
.opt-icon svg { display: block; }

/* ── Button rows ── */
.btn-row {
  grid-column: 1 / -1;
  display: flex;
  gap: 8px;
}
/* Extra gap ONLY between the style section and the first button row */
#style-scroll + .btn-row {
  margin-top: 16px;
}
.btn {
  background: #FF3131;
  color: #000;
  border: none;
  border-radius: 100px;
  font-family: inherit;
  font-size: 18px;
  font-weight: 700;
  padding: 11px 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0;
  justify-content: center;
  transition: filter 0.1s, transform 0.08s;
}
.btn:hover  { filter: brightness(1.08); }
.btn:active { transform: scale(0.97); }
.btn-wide   { flex: 2; }
.btn-narrow { flex: 1; }
.btn-full   { flex: 1; }
.btn-half   { flex: 1; }
.btn-export {
  flex: 1;
  letter-spacing: 0.04em;
}

/* ============ CUSTOM COLOR PICKER POPUP ============ */
.color-picker-popup {
  position: fixed;
  z-index: 300;
  background: #fff;
  border: 5px solid #000;
  border-radius: 20px;
  padding: 14px;
  width: 240px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.22);
  display: none;          /* shown via JS */
  flex-direction: column;
  gap: 10px;
  font-family: 'Host Grotesk', sans-serif;
}
.picker-sv {
  border-radius: 10px;
  display: block;
  width: 100%;
  aspect-ratio: 1 / 0.75;
  cursor: crosshair;
  touch-action: none;
}
.picker-hue {
  border-radius: 100px;
  display: block;
  width: 100%;
  height: 16px;
  cursor: ew-resize;
  touch-action: none;
}
.picker-footer {
  display: flex;
  align-items: center;
  gap: 8px;
}
.picker-swatch {
  width: 28px;
  height: 28px;
  border-radius: 100px;
  border: 3px solid #000;
  flex-shrink: 0;
}
.picker-hex {
  flex: 1;
  font-family: 'Host Grotesk', sans-serif;
  font-size: 14px;
  font-weight: 700;
  color: #000;
  border: 3px solid #000;
  border-radius: 100px;
  padding: 4px 10px;
  outline: none;
  width: 100%;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.picker-hex:focus { border-color: #000; }
.picker-done {
  flex-shrink: 0;
  background: #FF3131;
  color: #000;
  border: none;
  border-radius: 100px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  padding: 6px 14px;
  cursor: pointer;
  transition: filter 0.1s, transform 0.08s;
}
.picker-done:hover  { filter: brightness(1.08); }
.picker-done:active { transform: scale(0.97); }

/* ============ SAVED PATTERNS ============ */
/* 3×3 grid to the right of the panel-card. Width/height/rows are set by JS
   (repositionSavedPatterns) from the card's measured height so the grid
   always fills the panel height exactly: tile×3 + gap×2 = panel height. */
#saved-patterns {
  position: fixed;
  display: none;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  z-index: 80;
}
.pattern-slot {
  aspect-ratio: 1;
  border-radius: 20px;      /* match panel-card */
  border: 5px solid #000;  /* match panel-card */
  overflow: hidden;
  cursor: pointer;
  position: relative;
  background: #ebebeb;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pattern-slot:not(.filled) { cursor: default; }
.pattern-slot .plus {
  font-size: 22px;
  color: #bbb;
  pointer-events: none;
}
.pattern-slot img,
.pattern-slot canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
.pattern-slot .del-btn {
  display: none;
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #FF3131;
  color: #000;
  font-size: 10px;
  font-weight: 900;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: 2px solid #fff;
  z-index: 1;
  padding: 0;
  line-height: 1;
}
.pattern-slot:hover .del-btn { display: flex; }
.pattern-slot .del-btn:hover { filter: brightness(1.1); }

/* ============================================================
   EDIT PATTERN MODE
   ============================================================ */

/* Dark backdrop behind the editor so a resize gap never flashes body-yellow */
body.editing { background: #1a1a1f; }
/* Edit mode now carries its own palette + tools, so the CONTROLS toggle is no longer
   needed here; it and the mode switch are both hidden as clutter while editing.
   CSS-driven so they auto-restore when the mode class is removed on exit. */
body.editing #mode-toggle,
body.editing #toggle-btn { display: none !important; }
/* Freehand vector editor is a modal scrim (z-index 200): keep every chrome button
   hidden EXCEPT the controls toggle, and elevate the toggle + its panel ABOVE the
   scrim so they stay usable. Only z-index is forced on the toggle (not display), so
   the normal open/close logic still hides it while the panel is up (no overlap). */
body.fh-editing #mode-toggle,
body.fh-editing #fh-saved,
body.fh-editing #center-randomize,
body.fh-editing #edit-btn,
body.fh-editing #ai-btn { display: none !important; }
/* The controls-panel opener (smiley) is redundant while editing — scale lives in the
   right-click menu, colours in the shelf palette — so hide it during edit mode. */
body.fh-editing #toggle-btn { display: none !important; }
body.fh-editing #fh-panel { z-index: 210; }

/* + button (landing only). Mirrors the smiley: it HIDES when the menu opens,
   and a matching ✕ appears in the menu where the + glyph was. */
#edit-btn {
  position: fixed;
  bottom: 16px;
  left: 16px;
  width: 85px;
  height: 85px;
  cursor: pointer;
  z-index: 120;
  display: flex;           /* visible by default; hidden only in edit mode / compose */
  align-items: center;
  justify-content: center;
  transition: transform 0.12s ease;
}
#edit-btn:hover  { transform: scale(1.05); }
#edit-btn:active { transform: scale(0.96); }
#edit-btn > svg:first-child { display: block; }
.plus-inner {
  position: absolute;
  left: 27.5px;            /* (85 - 30) / 2 */
  top: 27.5px;
  pointer-events: none;
}
/* + tooltip points ABOVE (the button lives at the bottom-left corner) */
#edit-btn[data-tooltip]::after { top: auto; bottom: calc(100% + 10px); }

/* Toolshelf = the menu. Always display:flex (position:fixed, no layout impact).
   Hidden when not editing via opacity+pointer-events. Opens instantly (transition:none
   on body.editing), collapses smoothly on close (transition on body.editing.edit-closing). */
/* Horizontal tool bar, centered BELOW the editable tile (JS sets `top`) — mirrors
   the Freehand editor bar. Fades with the other below-canvas bars. */
#edit-shelf {
  position: fixed;
  left: 50%;
  top: 0;                  /* real value set per-render by _positionEditBars */
  z-index: 115;
  background: #fff;
  border: 5px solid #000;        /* same outline weight as the color pills */
  border-radius: 100px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.22);
  padding: 7px 12px;
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap: nowrap;          /* always a single horizontal row — "done" never stacks */
  justify-content: center;
  gap: 7px;
  max-width: 96vw;
  transform: translateX(-50%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}
body.editing #edit-shelf {
  opacity: 1;
  pointer-events: auto;
  transition: none;           /* opens instantly — parity with the other bars */
}
body.editing.edit-closing #edit-shelf {
  opacity: 0;
  transition: opacity 0.2s ease;   /* fades out on close */
}
/* Controls panel opened over the editor: the toolshelf yields INSTANTLY
   (display:none — no transition) and reappears instantly when it closes. */
body.editing.edit-panel-open #edit-shelf { display: none; }
.tool {
  position: relative;       /* anchors the .tk ghost letter */
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: #000;
  cursor: pointer;
  transition: color 0.12s;
}
/* Pixel-art glyph — colour follows the button's currentColor (black at rest,
   red when active). Sits ABOVE the ghost letter. */
.tool .px {
  position: relative;
  z-index: 1;
  height: 27px;
  width: auto;
  display: block;
}
.tool .px rect { fill: currentColor; transition: fill 0.12s; }
/* Ghost keyboard-shortcut letter — hidden at rest, revealed faintly on hover
   (grey) and active (red). Rendered BEHIND the glyph so it peeks around it. */
.tool .tk {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Host Grotesk', sans-serif;
  font-weight: 800;
  font-size: 40px;                 /* large ghost letter behind the glyph, per Figma */
  line-height: 1;
  color: currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
}
.tool:hover .tk  { opacity: 0.16; }
.tool.active     { color: #FF3131; }
.tool.active .tk { opacity: 0.22; }
.tool svg        { display: block; }
/* Pixelated editor's "done" exit button. Edits persist live, so the exit is a finish,
   not a cancel. Id-level selectors win over #edit-shelf .tool. */
/* Shared by both editors' shelves so clear/done read identically. */
#edit-shelf .editor-clear,
#edit-shelf .editor-done,
#fh-edit-shelf .editor-clear,
#fh-edit-shelf .editor-done {
  font-family: 'Host Grotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.01em;
  padding: 9px 20px;
  border-radius: 100px;
  border: 3px solid #000;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
/* Clear — white pill with a black outline. */
#edit-shelf .editor-clear,
#fh-edit-shelf .editor-clear { background: #fff; color: #000; }
#edit-shelf .editor-clear:hover,
#fh-edit-shelf .editor-clear:hover { background: #ececec; }
/* Done — solid red pill; edits persist, it's a finish. */
#edit-shelf .editor-done,
#fh-edit-shelf .editor-done { background: #FF3131; color: #000; border-color: #FF3131; }
#edit-shelf .editor-done:hover,
#fh-edit-shelf .editor-done:hover { background: #e11d1d; border-color: #e11d1d; }

/* Toolshelf tooltips sit ABOVE the horizontal toolbar (no clipping) */
#edit-shelf [data-tooltip]::after {
  bottom: calc(100% + 10px);
  top: auto;
  left: 50%;
  transform: translateX(-50%);
}
.tool-sep {
  width: 2px;
  height: 28px;
  background: #ececec;
  border-radius: 2px;
}
.swatch-btn {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 4px solid #000;
  cursor: pointer;
  padding: 0;
  background: #000;        /* live color set by JS */
  transition: box-shadow 0.12s;
}
.swatch-btn.active { box-shadow: 0 0 0 3px #fff, 0 0 0 6px #FF3131; }

/* Edit-shelf pattern palette — visually identical to the controls-panel color
   blocks (.color-swatch / .swatch-x). The selected paint color keeps a red ring;
   [x]/swap cycles the selection through all colors. */
#edit-palette { display: flex; align-items: center; gap: 8px; }
.ep-swatch {
  position: relative;
  width: 46px; height: 28px;        /* pill — matches .color-swatch (height/border/radius) */
  border-radius: 100px;
  border: 5px solid #000;
  cursor: pointer;
  padding: 0;
  overflow: visible;                /* let the ✕ overflow the pill edge */
  background: #000;                 /* live color set by JS */
}
.ep-swatch.active { box-shadow: 0 0 0 3px #fff, 0 0 0 6px #FF3131; }
/* Empty slot: dashed grey pill with a "+", identical to the controls' empty slot.
   Always shown so the palette keeps four slots (empties never disappear). */
.ep-swatch.empty {
  background: transparent !important;
  border-style: dashed;
  border-color: #BBB;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ep-swatch.empty:hover { border-color: #000; }
.ep-swatch.empty .empty-plus {
  color: #BBB;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}
.ep-swatch.empty:hover .empty-plus { color: #000; }
/* ✕ remove — red circle top-right, matching the controls' .swatch-x. */
.ep-x {
  position: absolute;
  top: -8px; right: -8px;
  width: 20px; height: 20px;
  display: none;
  align-items: center; justify-content: center;
  padding: 0;
  border: 2px solid #fff;
  border-radius: 50%;
  background: #FF3131; color: #000;
  font-size: 10px; line-height: 1; font-weight: 900;
  cursor: pointer;
  z-index: 3;
}
.ep-swatch:hover .ep-x { display: flex; }
.ep-x:hover { filter: brightness(1.1); }

/* Hint pills — sit directly below the edit canvas (JS sets `top`). */
#edit-hints {
  position: fixed;
  left: 50%;
  top: 0;                  /* real value set per-render by JS */
  z-index: 110;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  max-width: calc(100vw - 32px);
  transform: translateX(-50%);
  opacity: 0;
  pointer-events: none;
}
body.editing #edit-hints {
  opacity: 1;
  pointer-events: none;    /* hints are read-only labels, no interaction needed */
  transition: none;
}
body.editing.edit-closing #edit-hints {
  opacity: 0;
  transition: opacity 0.2s ease;
}
.edit-hint {
  background: rgba(0, 0, 0, 0.5);         /* same language as the bottom-menu pills */
  color: rgba(255, 255, 255, 0.7);
  font-family: 'Host Grotesk', sans-serif;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 7px 14px;
  border-radius: 100px;
  white-space: nowrap;
  pointer-events: none;
}

/* ============ EDIT CONTROLS BAR — one centered row of two grey pills ============
   Left pill = tint + grid; right pill = symmetry. JS sets `top`; the whole row
   snaps in/out with editing, matching the toolbar and hint rows. */
#edit-controls {
  position: fixed;
  left: 0;
  right: 0;                   /* full-width band; pills are centered inside it */
  top: 0;                     /* real value set per-render by JS */
  z-index: 110;
  display: flex;
  flex-direction: row;
  align-items: stretch;       /* both pills take the taller pill's height (equal heights) */
  justify-content: center;
  flex-wrap: wrap;            /* the two pills stack only when they exceed the viewport */
  gap: 10px;                  /* horiz gap = tools→bottom gap; JS keeps them in sync */
  padding: 0 12px;
  opacity: 0;
  pointer-events: none;       /* the band is click-through; only the pills catch clicks */
}
body.editing #edit-controls {
  opacity: 1;
  transition: none;
}
body.editing.edit-closing #edit-controls {
  opacity: 0;
  transition: opacity 0.2s ease;
}
body.editing #edit-controls .ec-pill { pointer-events: auto; }
/* Rounded pill grouping a cluster of controls — solid black at 25% opacity. */
.ec-pill {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 100px;
  padding: 8px 14px;
}
/* Symmetry cluster: circular buttons get uniform 8px breathing room on every side. */
#edit-symmetry { gap: 6px; padding: 8px; }
/* Symmetry buttons — 28px circles that share the grid pills' visual language:
   grey outline when idle, solid white fill when selected. Same 28px control height
   as .eg-btn so both bottom pills read at the same density. */
.sym-btn {
  box-sizing: border-box;
  width: 28px;
  height: 28px;
  border: 2px solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  background: transparent;
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
}
.sym-btn:hover { border-color: rgba(255, 255, 255, 0.6); background: rgba(255, 255, 255, 0.08); color: #fff; }
.sym-btn.active { background: #fff; border-color: #fff; color: #111; }
.sym-btn svg { display: block; }
/* Divider between the mirror group and the independent tile-sync toggle */
.sym-sep {
  width: 2px;
  height: 20px;
  background: rgba(255, 255, 255, 0.25);
  border-radius: 2px;
  margin: 0 3px;
  flex-shrink: 0;
}

/* Tint slider — controls the dark overlay opacity. Inline group inside the left
   control pill (show/hide + positioning handled by #edit-controls). */
#edit-tint {
  display: flex;
  align-items: center;
  gap: 10px;
}
#edit-tint .et-label,
#fhe-tint .et-label {
  color: rgba(255, 255, 255, 0.7);
  font-family: 'Host Grotesk', sans-serif;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: lowercase;
}
#edit-tint input[type="range"],
#fhe-tint input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 96px;               /* baseline; JS trims/extends so the bottom row = tools row width */
  height: 4px;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.28);
  outline: none;
  margin: 0;
}
#edit-tint input[type="range"]::-webkit-slider-thumb,
#fhe-tint input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #cfcfcf;      /* grey thumb, no outline */
  border: none;
  box-sizing: border-box;
}
#edit-tint input[type="range"]::-moz-range-thumb,
#fhe-tint input[type="range"]::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #cfcfcf;
  border: none;
  box-sizing: border-box;
}

/* Grid-size selector — inline group inside the left control pill, beside tint. */
#edit-gridsize {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: 6px;
}
#edit-gridsize .eg-label,
#fhe-sym-group .eg-label {
  color: rgba(255, 255, 255, 0.7);
  font-family: 'Host Grotesk', sans-serif;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: lowercase;
  margin-right: 2px;
}
.eg-btn {
  background: transparent;
  border: 2px solid rgba(255, 255, 255, 0.3);
  color: rgba(255, 255, 255, 0.7);
  font-family: 'Host Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.02em;
  border-radius: 100px;
  padding: 6px 11px;              /* 28px tall — matches the symmetry buttons */
  cursor: pointer;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
}
.eg-btn:hover { border-color: rgba(255, 255, 255, 0.6); color: #fff; }
.eg-btn.active { background: #fff; border-color: #fff; color: #000; }

/* Right-click brush menu — same card language as the panel + color picker */
.brush-menu {
  position: fixed;
  z-index: 400;
  width: 210px;
  background: #fff;
  border: 5px solid #000;
  border-radius: 18px;
  box-shadow: 0 8px 28px rgba(0,0,0,0.25);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-family: 'Host Grotesk', sans-serif;
  user-select: none;
}
.brush-menu .bm-label {
  font-size: 15px;
  font-weight: 700;
  color: #000;
}
.brush-menu .bm-shapes {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.brush-menu .bm-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.bm-track {
  position: relative;
  height: 5px;
  background: #000;
  margin: 8px 0 20px;
}
.bm-track-input,
.bm-scale-input {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  top: -13px;
  left: 0;
  width: 100%;
  height: 30px;
  opacity: 0;
  cursor: pointer !important; /* override input[type="range"] red-cursor rule — parity with #grid-size */
  z-index: 3;
  margin: 0;
  background: transparent;
}
.bm-thumb {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 2;
  color: #000;
}
.bm-thumb svg { display: block; }
/* Pattern-scale thumb — morphing tile-shape icon, identical to the controls scale
   slider and the brush-size thumb above it (sizing/positioning handled in JS). */
.bm-scale-thumb {
  position: absolute;
  top: 50%;
  left: 0;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 2;
  color: #000;
}
.bm-scale-thumb svg { display: block; }
/* Freehand line-style options — wider than the 36px shape glyphs so the
   horizontal line preview (Illustrator-style) reads clearly. Inherits the
   .opt-icon active colour, so previews (which stroke with currentColor) turn
   red when selected. */
.bm-line-opt { width: 52px; height: 30px; }

/* ── Tool cursors on the edit canvas — pixel-art glyphs (black fill, white outline)
      matching the toolbar icons; crosshair fallback. Toggled by body.edit-<tool>. ── */
body.edit-brush #canvas { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 35 35'><g fill='white'><rect x='0.0' y='24.0' width='5.0' height='5.0'/><rect x='0.0' y='27.0' width='5.0' height='5.0'/><rect x='0.0' y='30.0' width='5.0' height='5.0'/><rect x='3.0' y='30.0' width='5.0' height='5.0'/><rect x='6.0' y='30.0' width='5.0' height='5.0'/><rect x='9.0' y='27.0' width='5.0' height='5.0'/><rect x='3.0' y='21.0' width='5.0' height='5.0'/><rect x='12.0' y='24.0' width='5.0' height='5.0'/><rect x='6.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='21.0' width='5.0' height='5.0'/><rect x='9.0' y='15.0' width='5.0' height='5.0'/><rect x='18.0' y='18.0' width='5.0' height='5.0'/><rect x='12.0' y='12.0' width='5.0' height='5.0'/><rect x='21.0' y='15.0' width='5.0' height='5.0'/><rect x='15.0' y='9.0' width='5.0' height='5.0'/><rect x='24.0' y='12.0' width='5.0' height='5.0'/><rect x='27.0' y='9.0' width='5.0' height='5.0'/><rect x='30.0' y='6.0' width='5.0' height='5.0'/><rect x='18.0' y='6.0' width='5.0' height='5.0'/><rect x='21.0' y='3.0' width='5.0' height='5.0'/><rect x='24.0' y='0.0' width='5.0' height='5.0'/><rect x='21.0' y='9.0' width='5.0' height='5.0'/><rect x='24.0' y='6.0' width='5.0' height='5.0'/><rect x='27.0' y='3.0' width='5.0' height='5.0'/><rect x='24.0' y='3.0' width='5.0' height='5.0'/><rect x='21.0' y='6.0' width='5.0' height='5.0'/><rect x='27.0' y='6.0' width='5.0' height='5.0'/><rect x='18.0' y='9.0' width='5.0' height='5.0'/><rect x='24.0' y='9.0' width='5.0' height='5.0'/><rect x='15.0' y='12.0' width='5.0' height='5.0'/><rect x='18.0' y='12.0' width='5.0' height='5.0'/><rect x='21.0' y='12.0' width='5.0' height='5.0'/><rect x='12.0' y='15.0' width='5.0' height='5.0'/><rect x='15.0' y='15.0' width='5.0' height='5.0'/><rect x='18.0' y='15.0' width='5.0' height='5.0'/><rect x='9.0' y='18.0' width='5.0' height='5.0'/><rect x='12.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='18.0' width='5.0' height='5.0'/><rect x='6.0' y='21.0' width='5.0' height='5.0'/><rect x='9.0' y='21.0' width='5.0' height='5.0'/><rect x='12.0' y='21.0' width='5.0' height='5.0'/><rect x='3.0' y='24.0' width='5.0' height='5.0'/><rect x='6.0' y='24.0' width='5.0' height='5.0'/><rect x='9.0' y='24.0' width='5.0' height='5.0'/><rect x='3.0' y='27.0' width='5.0' height='5.0'/><rect x='6.0' y='27.0' width='5.0' height='5.0'/></g><g fill='black'><rect x='1' y='25' width='3' height='3'/><rect x='1' y='28' width='3' height='3'/><rect x='1' y='31' width='3' height='3'/><rect x='4' y='31' width='3' height='3'/><rect x='7' y='31' width='3' height='3'/><rect x='10' y='28' width='3' height='3'/><rect x='4' y='22' width='3' height='3'/><rect x='13' y='25' width='3' height='3'/><rect x='7' y='19' width='3' height='3'/><rect x='16' y='22' width='3' height='3'/><rect x='10' y='16' width='3' height='3'/><rect x='19' y='19' width='3' height='3'/><rect x='13' y='13' width='3' height='3'/><rect x='22' y='16' width='3' height='3'/><rect x='16' y='10' width='3' height='3'/><rect x='25' y='13' width='3' height='3'/><rect x='28' y='10' width='3' height='3'/><rect x='31' y='7' width='3' height='3'/><rect x='19' y='7' width='3' height='3'/><rect x='22' y='4' width='3' height='3'/><rect x='25' y='1' width='3' height='3'/><rect x='22' y='10' width='3' height='3'/><rect x='25' y='7' width='3' height='3'/><rect x='28' y='4' width='3' height='3'/></g></svg>") 1 29, crosshair; }
body.edit-eraser #canvas { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='25.6' viewBox='0 0 41 35'><g fill='white'><rect x='21.0' y='27.0' width='5.0' height='5.0'/><rect x='9.0' y='15.0' width='5.0' height='5.0'/><rect x='6.0' y='18.0' width='5.0' height='5.0'/><rect x='3.0' y='21.0' width='5.0' height='5.0'/><rect x='0.0' y='24.0' width='5.0' height='5.0'/><rect x='3.0' y='27.0' width='5.0' height='5.0'/><rect x='6.0' y='30.0' width='5.0' height='5.0'/><rect x='12.0' y='30.0' width='5.0' height='5.0'/><rect x='18.0' y='30.0' width='5.0' height='5.0'/><rect x='24.0' y='24.0' width='5.0' height='5.0'/><rect x='12.0' y='12.0' width='5.0' height='5.0'/><rect x='27.0' y='21.0' width='5.0' height='5.0'/><rect x='15.0' y='9.0' width='5.0' height='5.0'/><rect x='30.0' y='18.0' width='5.0' height='5.0'/><rect x='18.0' y='6.0' width='5.0' height='5.0'/><rect x='33.0' y='15.0' width='5.0' height='5.0'/><rect x='21.0' y='3.0' width='5.0' height='5.0'/><rect x='36.0' y='12.0' width='5.0' height='5.0'/><rect x='24.0' y='0.0' width='5.0' height='5.0'/><rect x='33.0' y='9.0' width='5.0' height='5.0'/><rect x='30.0' y='6.0' width='5.0' height='5.0'/><rect x='27.0' y='3.0' width='5.0' height='5.0'/><rect x='18.0' y='24.0' width='5.0' height='5.0'/><rect x='15.0' y='27.0' width='5.0' height='5.0'/><rect x='15.0' y='21.0' width='5.0' height='5.0'/><rect x='12.0' y='24.0' width='5.0' height='5.0'/><rect x='12.0' y='18.0' width='5.0' height='5.0'/><rect x='9.0' y='21.0' width='5.0' height='5.0'/><rect x='24.0' y='3.0' width='5.0' height='5.0'/><rect x='21.0' y='6.0' width='5.0' height='5.0'/><rect x='24.0' y='6.0' width='5.0' height='5.0'/><rect x='27.0' y='6.0' width='5.0' height='5.0'/><rect x='18.0' y='9.0' width='5.0' height='5.0'/><rect x='21.0' y='9.0' width='5.0' height='5.0'/><rect x='24.0' y='9.0' width='5.0' height='5.0'/><rect x='27.0' y='9.0' width='5.0' height='5.0'/><rect x='30.0' y='9.0' width='5.0' height='5.0'/><rect x='15.0' y='12.0' width='5.0' height='5.0'/><rect x='18.0' y='12.0' width='5.0' height='5.0'/><rect x='21.0' y='12.0' width='5.0' height='5.0'/><rect x='24.0' y='12.0' width='5.0' height='5.0'/><rect x='27.0' y='12.0' width='5.0' height='5.0'/><rect x='30.0' y='12.0' width='5.0' height='5.0'/><rect x='33.0' y='12.0' width='5.0' height='5.0'/><rect x='12.0' y='15.0' width='5.0' height='5.0'/><rect x='15.0' y='15.0' width='5.0' height='5.0'/><rect x='18.0' y='15.0' width='5.0' height='5.0'/><rect x='21.0' y='15.0' width='5.0' height='5.0'/><rect x='24.0' y='15.0' width='5.0' height='5.0'/><rect x='27.0' y='15.0' width='5.0' height='5.0'/><rect x='30.0' y='15.0' width='5.0' height='5.0'/><rect x='9.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='18.0' width='5.0' height='5.0'/><rect x='18.0' y='18.0' width='5.0' height='5.0'/><rect x='21.0' y='18.0' width='5.0' height='5.0'/><rect x='24.0' y='18.0' width='5.0' height='5.0'/><rect x='27.0' y='18.0' width='5.0' height='5.0'/><rect x='12.0' y='21.0' width='5.0' height='5.0'/><rect x='18.0' y='21.0' width='5.0' height='5.0'/><rect x='21.0' y='21.0' width='5.0' height='5.0'/><rect x='24.0' y='21.0' width='5.0' height='5.0'/><rect x='15.0' y='24.0' width='5.0' height='5.0'/><rect x='21.0' y='24.0' width='5.0' height='5.0'/><rect x='18.0' y='27.0' width='5.0' height='5.0'/></g><g fill='black'><rect x='22' y='28' width='3' height='3'/><rect x='10' y='16' width='3' height='3'/><rect x='7' y='19' width='3' height='3'/><rect x='4' y='22' width='3' height='3'/><rect x='1' y='25' width='3' height='3'/><rect x='4' y='28' width='3' height='3'/><rect x='7' y='31' width='3' height='3'/><rect x='13' y='31' width='3' height='3'/><rect x='19' y='31' width='3' height='3'/><rect x='25' y='25' width='3' height='3'/><rect x='13' y='13' width='3' height='3'/><rect x='28' y='22' width='3' height='3'/><rect x='16' y='10' width='3' height='3'/><rect x='31' y='19' width='3' height='3'/><rect x='19' y='7' width='3' height='3'/><rect x='34' y='16' width='3' height='3'/><rect x='22' y='4' width='3' height='3'/><rect x='37' y='13' width='3' height='3'/><rect x='25' y='1' width='3' height='3'/><rect x='34' y='10' width='3' height='3'/><rect x='31' y='7' width='3' height='3'/><rect x='28' y='4' width='3' height='3'/><rect x='19' y='25' width='3' height='3'/><rect x='16' y='28' width='3' height='3'/><rect x='16' y='22' width='3' height='3'/><rect x='13' y='25' width='3' height='3'/><rect x='13' y='19' width='3' height='3'/><rect x='10' y='22' width='3' height='3'/></g></svg>") 5 25, crosshair; }
body.edit-picker #canvas { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 35 35'><g fill='white'><rect x='0.0' y='27.0' width='5.0' height='5.0'/><rect x='0.0' y='30.0' width='5.0' height='5.0'/><rect x='3.0' y='30.0' width='5.0' height='5.0'/><rect x='3.0' y='24.0' width='5.0' height='5.0'/><rect x='6.0' y='27.0' width='5.0' height='5.0'/><rect x='9.0' y='27.0' width='5.0' height='5.0'/><rect x='3.0' y='21.0' width='5.0' height='5.0'/><rect x='12.0' y='24.0' width='5.0' height='5.0'/><rect x='6.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='21.0' width='5.0' height='5.0'/><rect x='9.0' y='15.0' width='5.0' height='5.0'/><rect x='18.0' y='18.0' width='5.0' height='5.0'/><rect x='12.0' y='12.0' width='5.0' height='5.0'/><rect x='21.0' y='15.0' width='5.0' height='5.0'/><rect x='24.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='9.0' width='5.0' height='5.0'/><rect x='12.0' y='6.0' width='5.0' height='5.0'/><rect x='15.0' y='3.0' width='5.0' height='5.0'/><rect x='24.0' y='12.0' width='5.0' height='5.0'/><rect x='27.0' y='15.0' width='5.0' height='5.0'/><rect x='27.0' y='9.0' width='5.0' height='5.0'/><rect x='30.0' y='6.0' width='5.0' height='5.0'/><rect x='18.0' y='6.0' width='5.0' height='5.0'/><rect x='21.0' y='3.0' width='5.0' height='5.0'/><rect x='24.0' y='0.0' width='5.0' height='5.0'/><rect x='21.0' y='9.0' width='5.0' height='5.0'/><rect x='27.0' y='3.0' width='5.0' height='5.0'/><rect x='18.0' y='12.0' width='5.0' height='5.0'/><rect x='24.0' y='3.0' width='5.0' height='5.0'/><rect x='15.0' y='6.0' width='5.0' height='5.0'/><rect x='21.0' y='6.0' width='5.0' height='5.0'/><rect x='24.0' y='6.0' width='5.0' height='5.0'/><rect x='27.0' y='6.0' width='5.0' height='5.0'/><rect x='18.0' y='9.0' width='5.0' height='5.0'/><rect x='24.0' y='9.0' width='5.0' height='5.0'/><rect x='15.0' y='12.0' width='5.0' height='5.0'/><rect x='21.0' y='12.0' width='5.0' height='5.0'/><rect x='12.0' y='15.0' width='5.0' height='5.0'/><rect x='15.0' y='15.0' width='5.0' height='5.0'/><rect x='18.0' y='15.0' width='5.0' height='5.0'/><rect x='24.0' y='15.0' width='5.0' height='5.0'/><rect x='9.0' y='18.0' width='5.0' height='5.0'/><rect x='12.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='18.0' width='5.0' height='5.0'/><rect x='6.0' y='21.0' width='5.0' height='5.0'/><rect x='9.0' y='21.0' width='5.0' height='5.0'/><rect x='12.0' y='21.0' width='5.0' height='5.0'/><rect x='6.0' y='24.0' width='5.0' height='5.0'/><rect x='9.0' y='24.0' width='5.0' height='5.0'/><rect x='3.0' y='27.0' width='5.0' height='5.0'/></g><g fill='black'><rect x='1' y='28' width='3' height='3'/><rect x='1' y='31' width='3' height='3'/><rect x='4' y='31' width='3' height='3'/><rect x='4' y='25' width='3' height='3'/><rect x='7' y='28' width='3' height='3'/><rect x='10' y='28' width='3' height='3'/><rect x='4' y='22' width='3' height='3'/><rect x='13' y='25' width='3' height='3'/><rect x='7' y='19' width='3' height='3'/><rect x='16' y='22' width='3' height='3'/><rect x='10' y='16' width='3' height='3'/><rect x='19' y='19' width='3' height='3'/><rect x='13' y='13' width='3' height='3'/><rect x='22' y='16' width='3' height='3'/><rect x='25' y='19' width='3' height='3'/><rect x='16' y='10' width='3' height='3'/><rect x='13' y='7' width='3' height='3'/><rect x='16' y='4' width='3' height='3'/><rect x='25' y='13' width='3' height='3'/><rect x='28' y='16' width='3' height='3'/><rect x='28' y='10' width='3' height='3'/><rect x='31' y='7' width='3' height='3'/><rect x='19' y='7' width='3' height='3'/><rect x='22' y='4' width='3' height='3'/><rect x='25' y='1' width='3' height='3'/><rect x='22' y='10' width='3' height='3'/><rect x='28' y='4' width='3' height='3'/><rect x='19' y='13' width='3' height='3'/></g></svg>") 1 29, crosshair; }
body.edit-smudge #canvas { cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 35 35'><g fill='white'><rect x='12.0' y='30.0' width='5.0' height='5.0'/><rect x='6.0' y='24.0' width='5.0' height='5.0'/><rect x='3.0' y='21.0' width='5.0' height='5.0'/><rect x='0.0' y='18.0' width='5.0' height='5.0'/><rect x='9.0' y='27.0' width='5.0' height='5.0'/><rect x='12.0' y='24.0' width='5.0' height='5.0'/><rect x='6.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='21.0' width='5.0' height='5.0'/><rect x='9.0' y='15.0' width='5.0' height='5.0'/><rect x='18.0' y='18.0' width='5.0' height='5.0'/><rect x='12.0' y='12.0' width='5.0' height='5.0'/><rect x='21.0' y='15.0' width='5.0' height='5.0'/><rect x='15.0' y='9.0' width='5.0' height='5.0'/><rect x='24.0' y='12.0' width='5.0' height='5.0'/><rect x='18.0' y='6.0' width='5.0' height='5.0'/><rect x='27.0' y='9.0' width='5.0' height='5.0'/><rect x='30.0' y='6.0' width='5.0' height='5.0'/><rect x='21.0' y='3.0' width='5.0' height='5.0'/><rect x='24.0' y='0.0' width='5.0' height='5.0'/><rect x='27.0' y='3.0' width='5.0' height='5.0'/><rect x='12.0' y='18.0' width='5.0' height='5.0'/><rect x='24.0' y='3.0' width='5.0' height='5.0'/><rect x='21.0' y='6.0' width='5.0' height='5.0'/><rect x='24.0' y='6.0' width='5.0' height='5.0'/><rect x='27.0' y='6.0' width='5.0' height='5.0'/><rect x='18.0' y='9.0' width='5.0' height='5.0'/><rect x='21.0' y='9.0' width='5.0' height='5.0'/><rect x='24.0' y='9.0' width='5.0' height='5.0'/><rect x='15.0' y='12.0' width='5.0' height='5.0'/><rect x='18.0' y='12.0' width='5.0' height='5.0'/><rect x='21.0' y='12.0' width='5.0' height='5.0'/><rect x='12.0' y='15.0' width='5.0' height='5.0'/><rect x='15.0' y='15.0' width='5.0' height='5.0'/><rect x='18.0' y='15.0' width='5.0' height='5.0'/><rect x='9.0' y='18.0' width='5.0' height='5.0'/><rect x='15.0' y='18.0' width='5.0' height='5.0'/><rect x='6.0' y='21.0' width='5.0' height='5.0'/><rect x='9.0' y='21.0' width='5.0' height='5.0'/><rect x='12.0' y='21.0' width='5.0' height='5.0'/><rect x='9.0' y='24.0' width='5.0' height='5.0'/></g><g fill='black'><rect x='13' y='31' width='3' height='3'/><rect x='7' y='25' width='3' height='3'/><rect x='4' y='22' width='3' height='3'/><rect x='1' y='19' width='3' height='3'/><rect x='10' y='28' width='3' height='3'/><rect x='13' y='25' width='3' height='3'/><rect x='7' y='19' width='3' height='3'/><rect x='16' y='22' width='3' height='3'/><rect x='10' y='16' width='3' height='3'/><rect x='19' y='19' width='3' height='3'/><rect x='13' y='13' width='3' height='3'/><rect x='22' y='16' width='3' height='3'/><rect x='16' y='10' width='3' height='3'/><rect x='25' y='13' width='3' height='3'/><rect x='19' y='7' width='3' height='3'/><rect x='28' y='10' width='3' height='3'/><rect x='31' y='7' width='3' height='3'/><rect x='22' y='4' width='3' height='3'/><rect x='25' y='1' width='3' height='3'/><rect x='28' y='4' width='3' height='3'/><rect x='13' y='19' width='3' height='3'/></g></svg>") 11 29, crosshair; }

/* ============================================================
   RESPONSIVE  —  tablet & phone
   ============================================================ */

/* ── Touch devices (no hover): kill dead hover affordances,
      give the pill-drag gesture ownership of the touch. ── */
@media (hover: none), (pointer: coarse) {
  /* Tooltips are hover-only — they can never fire on touch, so hide them */
  [data-tooltip]::after,
  [data-tooltip]::before { display: none !important; }

  /* A touch that lands on a pill should drag-reorder, not scroll the sheet */
  .color-swatch { touch-action: none; }

  /* ✕ delete (pills) and del-btn (saved patterns) are revealed on :hover —
     surface them permanently so touch users can actually reach them */
  .color-swatch .swatch-x { display: flex; }
  .pattern-slot .del-btn   { display: flex; }
}

/* ── Tablet (≤768px): keep the desktop layout, just stop the
      fixed-width panel from running off a narrower screen. ── */
@media (max-width: 768px) {
  #panel { width: min(420px, calc(100vw - 32px)); }
}

/* ── Phone (≤480px): controls become a full-width bottom sheet.
      Placed AFTER the tablet block so it wins the cascade. ── */
@media (max-width: 480px) {
  /* Shrink the floating launch icons (smiley + randomize) */
  #toggle-btn,
  #center-randomize { width: 60px; height: 60px; }
  #center-randomize { left: 84px; }   /* 16 + 60 + 8px gap */

  /* Panel → bottom sheet pinned to the bottom edge, full width */
  #panel {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    max-height: 85vh;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding: 0 8px;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  /* Square off the bottom corners so the sheet meets the screen edge */
  .panel-card {
    border-radius: 20px 20px 0 0;
    border-bottom: none;
    padding: 16px 16px 20px;
  }

  /* Saved patterns: leave the fixed-positioning model and flow inside
     the scrolling sheet (the JS-set inline top/left become inert under
     position:static). */
  #saved-patterns {
    position: static !important;
    width: 100% !important;
    margin: 12px 0 0;
  }
}

/* ============================================================
   AI FEATURE — Button, Panel, Survey
   ============================================================ */

/* ── AI button (top-right, mirrors toggle-btn) ── */
#ai-btn {
  position: fixed;
  top: 16px;
  right: 16px;
  width: 85px;
  height: 85px;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  transition: transform 0.12s ease;
  cursor: pointer;
}
#ai-btn:hover  { transform: scale(1.05); }
#ai-btn:active { transform: scale(0.96); }
#ai-btn > svg:first-child { display: block; width: 100%; height: 100%; }

/* Sleepy face — the default AI icon at rest. Centered in the badge; the z accent
   sits in its own upper-right corner of the viewBox. */
.ai-face-sleepy {
  position: absolute;
  /* Nudged up + slightly right so the eyes/mouth read as centred in the badge
     (the z accent sits in the upper-right of the artwork, which otherwise pulls
     the visual weight down-left). */
  left: 52%;
  top: 45%;
  transform: translate(-50%, -50%);
  /* Figma sizes the eyes+mouth at ~58% of the badge (z is a separate accent
     upper-right). The exported viewBox (76×41) packs both, so the SVG runs
     a little wider than the eyes+mouth alone. Trimmed a step per design QA —
     the face was reading heavy inside the frame. */
  width: 54%;
  height: auto;
  pointer-events: none;
}

/* ── AI journey faces in the panel header (#ai-collapse) ── */
/* One face shows at a time, keyed by the state class on #ai-collapse. */
#ai-collapse .ai-hdr-face,
#ai-collapse .ai-hdr-loading { display: none; }
/* Same rendered size as the controls button's smile (.face-inner ≈ 40px). */
#ai-collapse .ai-hdr-face { width: 40px; height: auto; }
#ai-collapse.face-opened .ai-hdr-opened { display: block; }
#ai-collapse.face-step2  .ai-hdr-step2  { display: block; }
#ai-collapse.face-loading .ai-hdr-loading { display: inline-block; position: relative; width: 43px; height: 15px; }
/* Generating: hard CUT between the two loading frames — a 2-frame flipbook,
   like a hand-drawn blink. step-end holds each frame full-strength and swaps
   instantly at the midpoint (no crossfade ghosting of the two ring-eyes). */
#ai-collapse .ai-load { position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 43px; height: auto; }
#ai-collapse .ai-load-2 { opacity: 0; }
#ai-collapse.face-loading .ai-load-2 { animation: ai-load-cut 0.8s step-end infinite; }
@keyframes ai-load-cut { 0% { opacity: 0; } 50% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  #ai-collapse.face-loading .ai-load-2 { animation: none; opacity: 1; }
}

/* Tooltip points down (button is top-right) */
#ai-btn[data-tooltip]::after {
  left: auto;
  right: 0;
  transform: none;
}

/* Extend cursor rule to cover ai-btn */
#ai-btn, #ai-btn * { cursor: pointer; }

/* ── AI panel (top-right — replaces the AI button in place, like controls) ── */
#ai-panel {
  position: fixed;
  top: 16px;
  right: 16px;
  width: 420px;
  z-index: 90;
  display: none;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  overflow-x: hidden;
}

.ai-panel-card {
  padding: 20px;
}

/* ── Header row: title + face (mirrors the controls panel header) ── */
.ai-step-indicator {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}
/* Step title — same treatment as the controls panel's "controls" title. Sits at
   the header's left edge; the face floats to the top-right corner. */
.ai-header-title {
  font-size: 28px;
  font-weight: 700;
  color: #000;
  line-height: 1;
}
/* In-panel collapse face — sits at the top-right corner of the header, so the
   sleepy close button appears to "wake up" in place. The two panels share the
   same header treatment (glyph + offset title); here the glyph is on the right. */
#ai-collapse {
  margin-left: auto;    /* push the face to the far right of the header */
  cursor: pointer;
  display: flex;
  align-items: center;
  position: relative;   /* anchor for the ::after hit-area (mirrors #collapse-btn) */
}
/* pointer-events:none lets hover/click fall through to #ai-collapse, so the
   pointer cursor shows over the glyph and the whole face is one click target. */
#ai-collapse svg { display: block; pointer-events: none; }
#ai-collapse .ai-hdr-loading { pointer-events: none; }
/* Invisible pseudo-element extends the clickable area (mirrors #collapse-btn::after) */
#ai-collapse::after {
  content: '';
  position: absolute;
  top: -28px;
  right: -28px;
  bottom: -28px;
  left: -22px;   /* stays clear of the "imagine" title text on the left */
}

/* ── Survey steps ── */
.survey-step { display: none; }
.survey-step.active { display: block; }

.ai-step-title {
  font-size: 18px;
  font-weight: 700;
  margin-bottom: 4px;
  line-height: 1.2;
}
.ai-step-hint {
  font-size: 12px;
  color: #888;
  margin-bottom: 14px;
}
/* Section labels — same treatment as the controls panel's .ctrl-label
   (18px / 600 / black, sentence case). Kept lowercase via the copy itself. */
.ai-step-section-label {
  font-size: 18px;
  font-weight: 600;
  color: #000;
  margin: 14px 0 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ai-tag-count {
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: #aaa;
}

/* ── Step 1: Theme grid ── */
.theme-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
}
.theme-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 10px 4px;
  border: 2.5px solid #e0e0e0;
  border-radius: 12px;
  background: #fff;
  font-size: 11px;
  font-weight: 700;
  color: #333;
  transition: border-color 0.12s, background 0.12s;
  line-height: 1.1;
  text-align: center;
}
.theme-card span { display: block; }
.theme-card:hover { border-color: #aaa; }
.theme-card.selected {
  border-color: #000;
  background: #000;
  color: #fff;
}

/* ── Step 2: Palette list ── */
.palette-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.palette-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border: 2.5px solid #e0e0e0;
  border-radius: 12px;
  background: #fff;
  text-align: left;
  transition: border-color 0.12s, background 0.12s;
  width: 100%;
}
.palette-row:hover { border-color: #aaa; }
.palette-row.selected { border-color: #000; background: #fafafa; }
.palette-swatches {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}
.ps {
  display: block;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  border: 1.5px solid rgba(0,0,0,0.12);
}
.palette-name {
  font-size: 13px;
  font-weight: 700;
  color: #222;
}
.palette-surprise {
  justify-content: center;
  border-style: dashed;
  border-color: #ccc;
}
.palette-surprise .palette-name {
  letter-spacing: 0.03em;
  color: #555;
}
.palette-surprise.selected { border-color: #000; border-style: solid; }

/* ── Step 3: Tag chips ── */
/* Weighted ranking slots (mood / culture) — position encodes influence */
.weight-slots {
  display: flex;
  gap: 8px;
  margin: 6px 0 10px;
}
.weight-slot {
  flex: 1;
  min-width: 0;
  height: 40px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border: 2.5px solid #000;
  border-radius: 12px;
  background: #fff;
  position: relative;
}
.weight-slot.empty {
  border-style: dashed;
  border-color: #c8c8c8;
  background: #fafafa;
}
.ws-rank {
  flex-shrink: 0;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: #000;
  color: #fff;
  font-size: 11px;
  font-weight: 800;
  display: flex; align-items: center; justify-content: center;
}
.weight-slot.empty .ws-rank { background: #d0d0d0; }
.ws-name {
  font-size: 13px;
  font-weight: 700;
  color: #111;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ws-plus { color: #bbb; font-weight: 700; font-size: 16px; }
.ws-x {
  margin-left: auto;
  border: none;
  background: transparent;
  color: #999;
  font-size: 11px;
  cursor: pointer;
  padding: 2px;
  opacity: 0;
  transition: opacity 0.1s;
}
.weight-slot:hover .ws-x { opacity: 1; }
.ws-x:hover { color: #000; }
.weight-slot[draggable="true"] { cursor: grab; }
.weight-slot.dragging { opacity: 0.4; }
.weight-slot.drop-target { outline: 2px dashed #000; outline-offset: 2px; }

.tag-chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 4px;
}
.tag-chip {
  padding: 5px 11px;
  border: 2px solid #e0e0e0;
  border-radius: 100px;
  background: #fff;
  font-size: 12px;
  font-weight: 700;
  color: #444;
  transition: border-color 0.1s, background 0.1s, color 0.1s;
}
.tag-chip:hover { border-color: #aaa; }
.tag-chip.selected {
  border-color: #000;
  background: #000;
  color: #fff;
}
.tag-other-input-wrap {
  margin: 6px 0 10px;
}
.tag-other-input {
  width: 100%;
  padding: 7px 12px;
  border: 2px solid #000;
  border-radius: 100px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  outline: none;
  background: #fafafa;
}
.tag-other-input:focus { border-color: #000; background: #fff; }

/* ── Step 4: API key + models ── */
.ai-key-row {
  display: flex;
  gap: 8px;
  margin-bottom: 6px;
}
.ai-key-input {
  flex: 1;
  padding: 9px 14px;
  border: 2.5px solid #000;
  border-radius: 12px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  outline: none;
  background: #fafafa;
  min-width: 0;
}
.ai-key-input:focus { background: #fff; }
.ai-key-save {
  padding: 9px 16px;
  border: 2.5px solid #000;
  border-radius: 12px;
  background: #000;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  transition: background 0.1s;
  flex-shrink: 0;
}
.ai-key-save:hover { background: #333; }
.ai-key-hint {
  font-size: 11px;
  color: #999;
  margin-bottom: 4px;
}

.ai-model-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 4px;
}
.ai-model-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  border: 2.5px solid #e0e0e0;
  border-radius: 12px;
  background: #fff;
  text-align: left;
  transition: border-color 0.12s, background 0.12s;
  width: 100%;
}
.ai-model-btn:hover { border-color: #aaa; }
.ai-model-btn.selected { border-color: #000; background: #fafafa; }
.ai-model-name { font-size: 14px; font-weight: 700; color: #111; }
.ai-model-sub  { font-size: 11px; font-weight: 600; color: #888; }

.ai-error {
  margin-top: 12px;
  padding: 10px 14px;
  background: #fff0f0;
  border: 2px solid #FF3131;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 600;
  color: #c0121f;
  line-height: 1.4;
}

/* ── Navigation ── */
.ai-nav {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 14px;   /* matches the controls button-row rhythm */
}
.ai-nav-btn {
  padding: 10px 20px;
  border-radius: 12px;
  font-size: 14px;
  font-weight: 700;
  border: 2.5px solid #000;
  transition: background 0.1s, color 0.1s, transform 0.08s;
}
.ai-back {
  background: #fff;
  color: #000;
  margin-right: auto;
}
.ai-back:hover { background: #f0f0f0; }
.ai-next {
  background: #000;
  color: #fff;
}
.ai-next:hover { background: #333; }
.ai-next:disabled,
.ai-next.is-disabled {
  background: #d8d8d8;
  border-color: #d8d8d8;
  color: #fff;
  cursor: not-allowed;
}
.ai-next:disabled:hover { background: #d8d8d8; }
/* Generate carries the control-panel .btn class (red pill, 18px/700, brightness
   hover, scale active). Here we only stretch it full-width and center content. */
.ai-generate {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
/* Label/spinner are click-through so the pointer cursor shows over the text too,
   not just the padding around it. */
.ai-generate > span { pointer-events: none; }
/* Disabled = quiet outline only: light-grey border + text, no red fill. Padding
   drops 2px to offset the added 2px border so the height matches the enabled pill. */
.ai-generate:disabled {
  opacity: 1;
  background: transparent;
  color: #b3b3b3;
  border: 2px solid #d9d9d9;
  padding: 9px 16px;
  cursor: not-allowed;
}
.ai-generate:disabled:hover { filter: none; }   /* don't brighten while disabled */
.ai-nav-btn:active { transform: scale(0.96); }

/* Shake on validation fail */
@keyframes ai-shake {
  0%,100% { transform: translateX(0); }
  20%      { transform: translateX(-5px); }
  40%      { transform: translateX(5px); }
  60%      { transform: translateX(-4px); }
  80%      { transform: translateX(4px); }
}
.ai-nav-btn.shake { animation: ai-shake 0.35s ease; }

/* Loading spinner */
.ai-spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border: 2.5px solid rgba(255,255,255,0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: ai-spin 0.7s linear infinite;
}
@keyframes ai-spin { to { transform: rotate(360deg); } }

/* ── Result / refine view (Point 3) ── */
#ai-result { display: none; }
.ai-panel-card.result-mode .ai-step-indicator,
.ai-panel-card.result-mode .survey-step,
.ai-panel-card.result-mode #ai-survey-nav { display: none !important; }
.ai-panel-card.result-mode #ai-result { display: block; }

/* Quota failsafe blocker — replaces the whole journey (survey AND result) while
   the shared free-tier budget is exhausted; the close smiley stays reachable. */
#ai-blocker { display: none; }
.ai-panel-card.blocked .survey-step,
.ai-panel-card.blocked #ai-survey-nav,
.ai-panel-card.blocked #ai-generate-busy,
.ai-panel-card.blocked #ai-result { display: none !important; }
.ai-panel-card.blocked #ai-blocker {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  padding: 18px 6px 6px;
}
#ai-blocker .ai-blocker-emoji { font-size: 34px; line-height: 1; opacity: .85; }
#ai-blocker #ai-blocker-recheck { margin-top: 8px; }

#ai-result-name {
  /* AI-given pattern name */
  text-transform: capitalize;
}

.ai-refine-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.ai-refine-chip {
  padding: 6px 13px;
  border: 2px solid #000;
  border-radius: 100px;
  background: #fff;
  font-size: 12px;
  font-weight: 700;
  color: #111;
  transition: background 0.1s, color 0.1s, transform 0.08s;
}
.ai-refine-chip:hover:not(:disabled) { background: #000; color: #fff; }
.ai-refine-chip:active:not(:disabled) { transform: scale(0.95); }
.ai-refine-chip:disabled { opacity: 0.4; cursor: not-allowed; }

.ai-refine-row {
  display: flex;
  gap: 8px;
}
.ai-refine-input {
  flex: 1;
  padding: 9px 14px;
  border: 2.5px solid #000;
  border-radius: 12px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  outline: none;
  background: #fafafa;
  min-width: 0;
}
.ai-refine-input:focus { background: #fff; }
.ai-refine-apply {
  padding: 9px 18px;
  border: 2.5px solid #000;
  border-radius: 12px;
  background: #000;
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  flex-shrink: 0;
  transition: background 0.1s;
}
.ai-refine-apply:hover:not(:disabled) { background: #333; }
.ai-refine-apply:disabled { opacity: 0.5; cursor: not-allowed; }

.ai-refine-busy {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  font-size: 13px;
  font-weight: 700;
  color: #555;
}
.ai-refine-busy .ai-spinner {
  border-color: rgba(0,0,0,0.2);
  border-top-color: #000;
}

.ai-result-nav { margin-top: 16px; }
.ai-result-nav .ai-back { margin-right: auto; }

/* ── Mobile: AI button shrinks with others, panel becomes bottom sheet ── */
@media (max-width: 480px) {
  #ai-btn { width: 60px; height: 60px; top: 16px; right: 16px; }
  /* Mode button shrinks with the other corner icons. */
  .mode-btn { width: 60px; height: 60px; }
  /* Keep the credit tag clear of the 60px corner buttons on narrow screens. */
  #made-by-tag { font-size: 10px; padding: 6px 12px; }
  #ai-panel {
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    max-height: 85vh;
    overflow-y: auto;
    padding: 0 8px;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .ai-panel-card {
    border-radius: 20px 20px 0 0;
    border-bottom: none;
    padding: 16px 16px 20px;
  }
  .theme-grid { grid-template-columns: repeat(4, 1fr); }
}

/* ============================================================
   MODE BUTTON (bottom-right) — swaps Pixelated ⇄ Freehand
   A single badge button, mirroring the other corner icons. It shows the icon of
   the mode it switches TO; CSS keys the visible icon off body.mode-freehand.
   ============================================================ */
#mode-toggle {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 100;
}
.mode-btn {
  position: relative;
  display: block;
  width: 85px;
  height: 85px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  transition: transform 0.12s ease;
}
.mode-btn:hover  { transform: scale(1.05); }
.mode-btn:active { transform: scale(0.96); }
.mode-badge { display: block; width: 100%; height: 100%; }
/* Icon centered in the badge. Which one shows depends on the active mode. */
.mode-icon {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  height: auto;
  pointer-events: none;
  transition: transform 0.12s ease;
}
.mode-icon-freehand  { width: 40%; }
.mode-icon-pixelated { width: 28%; display: none; }
body.mode-freehand .mode-icon-freehand  { display: none; }
body.mode-freehand .mode-icon-pixelated { display: block; }
/* Hover: a touch of life on the glyph (button already scales up). */
.mode-btn:hover .mode-icon-freehand  { transform: translate(-50%, -50%) rotate(-6deg); }
.mode-btn:hover .mode-icon-pixelated { transform: translate(-50%, -50%) scale(1.08); }
/* Tooltip points UP (the button lives at the bottom-right corner). */
#mode-btn[data-tooltip]::after { top: auto; bottom: calc(100% + 10px); left: auto; right: 0; transform: none; }

/* ============================================================
   CREDIT TAG (bottom-center) — "created by @paulrmayer"
   ============================================================ */
#made-by-tag {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  background: #ff3131;
  color: #000;
  font-family: 'Host Grotesk', sans-serif;
  font-weight: 500;
  font-size: 11.5px;
  line-height: 1;
  letter-spacing: 0.01em;
  padding: 7px 14px;
  border-radius: 999px;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.12s ease;
}
/* Behaves like a red button — darkens on hover/press, no scaling. */
#made-by-tag:hover  { background: #e21f1f; }
#made-by-tag:active { background: #c81a1a; }
/* Immersive edit modes hide the chrome — keep the tag out of the way too. */
body.editing #made-by-tag,
body.fh-editing #made-by-tag { display: none !important; }

/* ============================================================
   FREEHAND CONTROLS PANEL (top-left, mirrors #panel)
   ============================================================ */
#fh-panel {
  position: fixed;
  top: 16px;
  left: 16px;
  width: 420px;
  font-family: 'Host Grotesk', sans-serif;
  font-weight: 600;
  user-select: none;
  -webkit-user-select: none;
  z-index: 90;
  overflow: visible;
  display: none;
}

/* ============================================================
   FREEHAND VECTOR EDITOR (symmetry pen + select)
   ============================================================ */
#fh-editor {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  /* Translucent scrim (not opaque) so the live tiled pattern shows through,
     dimmed — parity with the Pixelated editor's dimmed surround. The editor
     tile sits bright in the centre; edits re-render the background live.
     The alpha is driven live by the bottom-menu "tint" slider (default 80 → 0.8). */
  background: rgba(20, 20, 24, 0.8);
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 14px;
}
#fh-editor.open { display: flex; }
#fh-edit-canvas {
  /* Cap by the available height too (subtract the shelf + bottom bar + hints + gaps)
     so the stack always fits and the tools shelf never crowds/overlaps the canvas. */
  width: min(78vmin, 600px, calc(100vh - 250px));
  height: min(78vmin, 600px, calc(100vh - 250px));
  border: 5px solid #000;
  border-radius: 16px;
  background: #fff;
  touch-action: none;
  cursor: none;              /* brush/line: the brush ring IS the cursor — no crosshair */
  box-shadow: 0 12px 40px rgba(0,0,0,0.4);
}
/* Per-tool cursors (toggled by setTool). Pen keeps a precise crosshair; select shows
   the app's Figma-style pointer arrow (same data-URI as the global default cursor). */
#fh-edit-canvas.cur-pen { cursor: crosshair; }
#fh-edit-canvas.cur-select {
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='18' height='22' viewBox='0 0 18 22'><path d='M2 2 L2 16.5 L5.8 13 L7.9 17.8 L10.2 16.9 L8 12 L13 12 Z' fill='black' stroke='white' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'/></svg>") 2 2, default;
}
/* Brush-size ring cursor — follows the pointer over the canvas for brush/line,
   sized to the on-screen stroke width. White ring + dark outline so it reads on
   any colour. JS shows/hides and sizes it. */
#fhe-cursor {
  position: fixed;
  left: 0;
  top: 0;
  border: 1.5px solid #fff;
  box-shadow: 0 0 0 1.5px rgba(0,0,0,0.55);
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%);
  display: none;
  z-index: 205;
}
/* ---- Tools shelf — white pill above the canvas (parity with #edit-shelf) ----
   Same look as the Pixelated shelf, but a plain static flex child (the modal
   centres it — no fixed positioning / JS layout needed). */
#fh-edit-shelf {
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap: nowrap;              /* always one row — "done" never stacks (parity with #edit-shelf) */
  justify-content: center;
  gap: 7px;
  background: #fff;
  border: 5px solid #000;          /* same outline weight as the color pills */
  border-radius: 100px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.22);
  padding: 7px 12px;
  max-width: 96vw;
  font-family: 'Host Grotesk', sans-serif;
}
/* Freehand tools — same 44px circular treatment as the Pixelated .tool: black
   glyph at rest, red (no box) when active. Keeps Freehand's own line icons. */
.fhe-tool {
  position: relative;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: #000;
  cursor: pointer;
  padding: 0;
  transition: color 0.12s, background 0.12s;
}
/* No hover fill — just the ghost letter reveals (see .fhe-tool:hover .tk below). */
.fhe-tool.active { color: #FF3131; background: transparent; }
/* Icon sits ABOVE the ghost letter (parity with the Pixelated .tool .px). */
.fhe-tool svg    { display: block; position: relative; z-index: 1; width: 24px; height: 24px; }
#fhe-undo svg    { width: 22px; height: 22px; }   /* undo reads as a sibling of the draw tools */
/* Ghost keyboard-shortcut letter — hidden at rest, revealed faintly on hover (grey)
   and active (red), BEHIND the glyph. Same treatment as the Pixelated .tool .tk. */
.fhe-tool .tk {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Host Grotesk', sans-serif;
  font-weight: 800;
  font-size: 40px;          /* parity with the Pixelated .tool .tk ghost letter */
  line-height: 1;
  color: currentColor;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
}
.fhe-tool:hover .tk  { opacity: 0.16; }
.fhe-tool.active .tk { opacity: 0.22; }
/* Shelf tooltips sit ABOVE the toolbar (no clipping onto the canvas), parity with #edit-shelf. */
#fh-edit-shelf [data-tooltip]::after {
  bottom: calc(100% + 10px);
  top: auto;
  left: 50%;
  transform: translateX(-50%);
}
/* Palette — pill swatches (see .ep-swatch), spaced like the Pixelated #edit-palette. */
#fhe-colors { display: flex; align-items: center; gap: 8px; }

/* ---- Bottom menu — ONE grey pill, centred below the canvas ---- */
#fh-edit-controls {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  max-width: 96vw;
}
/* tint group — reuses #edit-tint styling (selectors extended in the tint block). */
#fhe-tint { display: flex; align-items: center; gap: 10px; }
/* sym numbers (off/2/4/6/8/12) + the mirror toggle — reuse .eg-btn / .sym-btn; the
   group spaces like #edit-gridsize, with .sym-sep fencing off the mirror control. */
#fhe-sym-group { display: flex; align-items: center; gap: 6px; margin-left: 2px; }

/* ---- Hint pills — read-only labels below the bottom menu (reuse .edit-hint) ---- */
#fh-edit-hints {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  max-width: calc(100vw - 32px);
}

/* Freehand saved patterns — beside the panel, mirrors #saved-patterns */
#fh-saved {
  position: fixed;
  top: 16px;
  left: 452px;            /* 16 + 420 panel + 16 gap */
  width: 200px;
  display: none;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  z-index: 80;
}
.fh-saved-cell {
  aspect-ratio: 1;
  border-radius: 16px;
  border: 4px solid #000;
  background-size: cover;
  background-position: center;
  cursor: pointer;
  padding: 0;
  transition: transform 0.08s;
}
.fh-saved-cell:hover { transform: scale(1.05); }

/* Simple range for Freehand scale (no custom thumb) */
.fh-range {
  width: 100%;
  height: 10px;
  -webkit-appearance: none;
  appearance: none;
  background: #eee;
  border: 2.5px solid #000;
  border-radius: 100px;
  outline: none;
  cursor: pointer;
}
.fh-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #000;
  cursor: pointer;
}
.fh-range::-moz-range-thumb {
  width: 22px;
  height: 22px;
  border: none;
  border-radius: 50%;
  background: #000;
  cursor: pointer;
}

/* Freehand Compose panel (Edit button) — mirrors #fh-panel positioning. */
#fh-compose {
  position: fixed;
  top: 16px;
  left: 16px;
  width: 420px;
  font-family: 'Host Grotesk', sans-serif;
  font-weight: 600;
  user-select: none;
  -webkit-user-select: none;
  z-index: 90;
  overflow: visible;
  display: none;
}

/* ============================================================
   RANDOMIZE UX — history arrows, roll feedback, locks/strength/seed
   ============================================================ */

/* History arrows removed — pattern history is keyboard-only now (← / →). */

/* Dice roll animation — restarted from JS on every roll. */
@keyframes dice-roll {
  0%   { transform: rotate(0deg)   scale(1); }
  45%  { transform: rotate(-14deg) scale(0.92); }
  100% { transform: rotate(0deg)   scale(1); }
}
#center-randomize.rolling { animation: dice-roll 0.3s ease; }

/* Canvas crossfade ghost — the OLD frame, fading out over the new one. */
.canvas-ghost {
  position: fixed;
  z-index: 5;               /* above the canvas, below all UI */
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.14s linear;
}
.canvas-ghost.fade { opacity: 0; }

/* Lock chips — pin palette / motif / layout across rolls. */
/* Lock chips — the Freehand "randomize" locks row. Sits beside its label in
   the panel's second column (like the swatch row) and the three pills share
   the column's full width, so their right edge aligns with the panel edge. */
.lock-row { grid-column: 2; display: flex; gap: 8px; }
.lock-chip {
  flex: 1 1 0;
  text-align: center;
  font-family: 'Host Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  padding: 6px 4px;
  border: 2px solid #000;
  border-radius: 100px;
  background: #fff;
  color: #000;
  cursor: pointer;
  transition: background 0.1s, color 0.1s;
}
.lock-chip:hover { background: #f0f0f0; }
.lock-chip.active { background: #000; color: #fff; }


/* Slider thumbs travel to programmatic new values (randomize/history restore)
   instead of teleporting. Live dragging stays 1:1 via body.slider-dragging. */
.slider-thumb { transition: left 0.18s ease, width 0.18s ease, height 0.18s ease; }
body.slider-dragging .slider-thumb { transition: none; }

/* ============================================================
   AI REFINE PIPELINE — scope chips, staged keep/discard,
   cancel, and region marking
   ============================================================ */

/* Edit-scope chips (aspect group + area marker) */
.ai-scope-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.ai-scope-group {
  display: inline-flex;
  border: 2px solid #000;
  border-radius: 100px;
  overflow: hidden;
}
.ai-scope-group .ai-scope-chip { border: none; border-radius: 0; }
.ai-scope-chip {
  font-family: 'Host Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  padding: 6px 12px;
  background: #fff;
  color: #000;
  border: 2px solid #000;
  border-radius: 100px;
  cursor: pointer;
  transition: background 0.1s, color 0.1s;
}
.ai-scope-chip:hover { background: #f0f0f0; }
.ai-scope-chip.active { background: #000; color: #fff; }
.ai-scope-area.active { background: #FF3131; border-color: #FF3131; color: #fff; }

/* Cancel button inside the busy row */
#ai-refine-cancel,
#ai-generate-cancel {
  margin-left: 10px;
  font-family: 'Host Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 12px;
  border: 2px solid #000;
  border-radius: 100px;
  background: #fff;
  cursor: pointer;
}
#ai-refine-cancel:hover,
#ai-generate-cancel:hover { background: #f0f0f0; }

/* (The inline "try again" button was removed — error copy invites the retry.) */

/* Quality (fast / best) — a connected two-segment toggle in the control-panel
   red language. The two buttons share an edge (no gap); the middle divider is
   best's left border. Height matches .btn: .btn is 11px padding + no border,
   these are 9px padding + 2px border → identical outer height. Active segment =
   solid red, inactive = red outline. */
.ai-quality { display: flex; }
.ai-quality-card {
  flex: 1;
  position: relative;                 /* anchors the data-tooltip */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 9px 16px;
  border: 2px solid #FF3131;
  background: transparent;
  color: #000;
  cursor: pointer;
  font-family: 'Host Grotesk', sans-serif;
  font-size: 18px;
  font-weight: 700;
  transition: background 0.12s, filter 0.12s;
}
.ai-quality-card:first-child { border-radius: 100px 0 0 100px; border-right: none; }
.ai-quality-card:last-child  { border-radius: 0 100px 100px 0; }
.ai-quality-card:hover:not(.active) { background: rgba(255, 49, 49, 0.10); }
.ai-quality-card.active       { background: #FF3131; color: #000; }
.ai-quality-card.active:hover { background: #FF3131; filter: brightness(1.08); }
/* Sub-label tooltip points UP — Generate sits directly below. */
.ai-quality-card[data-tooltip]::after { top: auto; bottom: calc(100% + 10px); }

/* Staged keep/discard bar */
.ai-stage-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 8px 12px;
  border: 2px dashed #000;
  border-radius: 14px;
}
.ai-stage-label {
  font-family: 'Host Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  color: #666;
  margin-right: auto;
}
#ai-stage-keep, #ai-stage-discard {
  font-family: 'Host Grotesk', sans-serif;
  font-size: 12px;
  font-weight: 700;
  padding: 6px 14px;
  border: 2px solid #000;
  border-radius: 100px;
  cursor: pointer;
  transition: background 0.1s, color 0.1s;
}
#ai-stage-keep { background: #000; color: #fff; }
#ai-stage-keep:hover { background: #2a2a30; }
#ai-stage-discard { background: #fff; color: #000; }
#ai-stage-discard:hover { background: #f0f0f0; }

/* Region marking overlay */
body.scope-marking { cursor: crosshair; }
body.scope-marking #canvas { cursor: crosshair; }
#scope-mark-hint {
  position: fixed;
  top: 22px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 300;
  display: flex;
  align-items: center;
  gap: 12px;
  background: #fff;
  border: 3px solid #000;
  border-radius: 100px;
  padding: 10px 20px;
  font-family: 'Host Grotesk', sans-serif;
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
}
#scope-mark-hint button {
  font-family: inherit;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 12px;
  border: 2px solid #000;
  border-radius: 100px;
  background: #fff;
  cursor: pointer;
}
#scope-mark-hint button:hover { background: #f0f0f0; }
#scope-mark-rect {
  position: fixed;
  z-index: 290;
  border: 3px dashed #FF3131;
  background: rgba(255, 49, 49, 0.12);
  pointer-events: none;
  border-radius: 4px;
}

/* ── Responsive edit menus (both modes) ──────────────────────────────
   Tier 1 (≤1100px): compact sizing keeps each bar on a single row far longer.
   Tier 2 (≤800px):  when wrapping becomes unavoidable, the pill becomes a
                     rounded CARD (radius 22px) so stacked rows read as a
                     deliberate layout instead of a squashed capsule.
   Tier 3 (≤520px):  phone-tight sizing. */
@media (max-width: 1100px) {
  /* Freehand shelf mirrors the Pixelated shelf's compact tier. */
  #fh-edit-shelf { gap: 5px; padding: 6px 10px; }
  #fh-edit-shelf .fhe-tool { width: 38px; height: 38px; }
  #fh-edit-shelf .editor-clear,
  #fh-edit-shelf .editor-done { padding: 8px 16px; font-size: 13px; }
  #edit-shelf { gap: 5px; padding: 6px 10px; }
  #edit-shelf .tool { width: 38px; height: 38px; }
  #edit-shelf .tool .tk { font-size: 34px; }
  #edit-shelf .tool-sep { display: none; }
  .ep-swatch { width: 40px; height: 24px; border-radius: 100px; }
  #edit-shelf .editor-clear,
  #edit-shelf .editor-done { padding: 8px 16px; font-size: 13px; }
}
@media (max-width: 800px) {
  /* Both shelves stay nowrap at every width ("done" must never stack) — the
     compact sizing tiers keep them on one row down to phone widths. The bottom
     grey pill wraps naturally when it must; tighten its inner gaps a touch. */
  #fh-edit-controls .ec-pill { gap: 8px; }
  #fhe-sym-group { gap: 5px; }
}
@media (max-width: 520px) {
  #fh-edit-shelf { gap: 3px; padding: 5px 7px; max-width: 98vw; }
  #fh-edit-shelf .fhe-tool { width: 29px; height: 29px; }
  #fh-edit-shelf .fhe-tool svg { width: 16px; height: 16px; }
  #fh-edit-shelf .editor-clear,
  #fh-edit-shelf .editor-done { padding: 6px 11px; font-size: 12px; border-width: 2.5px; }
  #fhe-colors { gap: 6px; }
  #edit-shelf { gap: 3px; padding: 5px 7px; max-width: 98vw; }
  #edit-shelf .tool { width: 29px; height: 29px; }
  #edit-shelf .tool .px { height: 18px; }
  #edit-shelf .tool .tk { font-size: 26px; }
  #edit-palette { gap: 6px; }
  .ep-swatch { width: 34px; height: 22px; border-radius: 100px; border-width: 4px; }
  #edit-shelf .editor-clear,
  #edit-shelf .editor-done { padding: 6px 11px; font-size: 12px; border-width: 2.5px; }
}
