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

:root {
  --bg-deep: #121214;
  --bg-panel: #1c1c21;
  --bg-card: #26262d;
  --accent: #ff6b4a;
  --accent-soft: #ff6b4a22;
  --text: #e8e6e3;
  --text-muted: #8a8a8e;
  --active: #a78bfa;
  --active-bg: #a78bfa33;
  --border: #333338;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--bg-deep);
  color: var(--text);
  display: flex;
  height: 100vh;
  overflow: hidden;
}

/* Left Panel */
.panel {
  width: 460px;
  min-width: 460px;
  background: var(--bg-panel);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  padding: 24px;
  gap: 20px;
}

.panel h1 {
  font-size: 24px;
  color: var(--accent);
  font-weight: 600;
}
.twitter-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 16px;
  padding: 10px 16px;
  background: var(--bg-card);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 8px;
  text-decoration: none;
  font-size: 14px;
  transition: all 0.15s;
}
.twitter-btn:hover { color: var(--text); border-color: #555; }
.twitter-btn svg { width: 16px; height: 16px; fill: currentColor; }

/* Character selector */
.char-select {
  width: 100%;
  padding: 12px 16px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
}

/* Trial scene per-stand placement rows. Each row is "Stand <i> | thumb | name"
   when filled, or "Stand <i> | —" when empty. Clicking a row makes it the
   active drop target for the next snapshot library click; the .active class
   surfaces that state. */
.stand-list { display: flex; flex-direction: column; gap: 4px; }
.stand-slot {
  display: grid;
  grid-template-columns: 72px 40px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  color: var(--text);
}
.stand-slot:hover { border-color: #555; }
.stand-slot.active {
  border-color: var(--active);
  background: var(--active-bg);
}
.stand-slot-idx { color: var(--text-muted); font-size: 13px; }
.stand-slot-thumb {
  width: 40px;
  height: 40px;
  object-fit: contain;
  border-radius: 4px;
  background: var(--bg-card);
}
.stand-slot-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stand-slot-empty {
  grid-template-columns: 72px 1fr;
}
.stand-slot-placeholder {
  color: var(--text-muted);
  font-style: italic;
}
/* Per-row remove (×). Stays subtle until hovered so it doesn't compete
   visually with the row's main content. Stops click propagation in JS to
   avoid re-selecting the row right before clearing it. */
.stand-slot-remove {
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--text-muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.stand-slot-remove:hover {
  color: #ff8266;
  border-color: var(--border);
}

/* Preset buttons */
.section-label {
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.presets {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.preset-btn {
  padding: 8px 18px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 20px;
  color: var(--text-muted);
  font-size: 15px;
  cursor: pointer;
  transition: all 0.15s;
}
.preset-btn:hover { background: #333338; color: var(--text); }
.preset-btn.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 0 12px var(--accent-soft);
  transform: translateY(-1px);
}
/* Disabled preset pills — used by trial composition row when no look
   character is selected (composition has nothing to apply to). */
.preset-btn:disabled {
  opacity: 0.4;
  cursor: default;
}
.preset-btn:disabled:hover { background: var(--bg-card); color: var(--text-muted); }

/* Layer groups */
.group {
  background: var(--bg-card);
  border-radius: 8px;
  overflow: hidden;
}

.group-header {
  padding: 12px 16px;
  font-size: 15px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  user-select: none;
}
.group-header:hover { color: var(--text); }
.group-header .arrow {
  transition: transform 0.2s;
  font-size: 12px;
}
.group.collapsed .arrow { transform: rotate(-90deg); }

.group-options {
  padding: 6px 14px 14px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-height: 9999px;
  overflow: hidden;
  transition: max-height 0.25s ease, padding 0.25s ease;
}
.group.collapsed .group-options {
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}
/* Vertical-stack variant of .group-options for groups whose contents are
   labeled rows rather than wrapping pills (e.g. the trial scene editor's
   Advanced group, which stacks five labeled sliders). */
.group-options-stack {
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}

/* Trial-scene Advanced group: each slider gets a small uppercase label
   above the slider+number-input row. The wrapper is just a vertical pair;
   spacing between sliders comes from .group-options-stack's gap. */
.trial-slider-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-muted);
  margin-bottom: 4px;
}
/* Inspector-row outside a group keeps its 8 px stack margin (placement
   inspector). Inside a group, .group-options-stack's gap is the sole
   spacing source, so kill the extra margin. */
.group-options .inspector-row { margin-bottom: 0; }

/* Inner row of buttons inside a stacked group — used to host two .layer-btn
   rows under one .group (e.g. Look character + Composition merged), wrapping
   horizontally like the default .group-options layout. */
.layer-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.group-divider {
  height: 1px;
  background: var(--border);
  flex-shrink: 0;
}

.group-status {
  font-size: 12px;
  font-weight: 400;
  color: var(--active);
  margin-left: 8px;
  opacity: 0.8;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}

.layer-btn {
  padding: 6px 14px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 5px;
  color: var(--text-muted);
  font-size: 14px;
  cursor: pointer;
  transition: all 0.15s;
}
.layer-btn:hover:not(:disabled) { border-color: #555; color: var(--text); }
.layer-btn.active {
  background: var(--active-bg);
  border-color: var(--active);
  color: #fff;
}
.layer-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.none-btn {
  padding: 6px 14px;
  background: transparent;
  border: 1px dashed #444;
  border-radius: 5px;
  color: #555;
  font-size: 14px;
  cursor: pointer;
}
.none-btn:hover { border-color: #777; color: var(--text-muted); }
.none-btn.active { border-color: var(--accent); color: var(--accent); }

/* In-page confirmation modal — replaces native confirm() for spoiler /
   reset prompts so the dialog matches the app's theme. */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  backdrop-filter: blur(2px);
}
.modal-overlay.active { display: flex; }
.modal {
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
  max-width: 420px;
  margin: 16px;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5);
}
.modal-message {
  color: var(--text);
  font-size: 15px;
  line-height: 1.5;
  margin-bottom: 20px;
  white-space: pre-line;
}
.modal-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
.modal-btn {
  padding: 10px 20px;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--border);
}
.modal-cancel {
  background: var(--bg-card);
  color: var(--text-muted);
}
.modal-cancel:hover { color: var(--text); border-color: #555; }
.modal-confirm {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.modal-confirm:hover { background: #ff8266; }

/* Rename modal: subtitle showing the current name + the text input. The
   modal frame itself reuses .modal / .modal-overlay above. */
.rename-current {
  color: var(--text-muted);
  font-size: 13px;
  margin-bottom: 10px;
  word-break: break-all;
}
.rename-input {
  width: 100%;
  padding: 10px 12px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: inherit;
  font-size: 15px;
  margin-bottom: 20px;
}
.rename-input:focus { outline: none; border-color: var(--active); }

/* Switch-style toggle (pill track + sliding knob) for stateful flags. */
.switch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 10px 16px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition: color 0.15s;
}
.switch:hover { color: var(--text); }
.switch input { display: none; }
.switch-track {
  position: relative;
  width: 36px;
  height: 20px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  transition: background 0.2s, border-color 0.2s;
  flex-shrink: 0;
}
.switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  background: var(--text-muted);
  border-radius: 50%;
  transition: transform 0.2s, background 0.2s;
}
.switch input:checked ~ .switch-track {
  background: var(--accent);
  border-color: var(--accent);
}
.switch input:checked ~ .switch-track .switch-knob {
  transform: translateX(16px);
  background: #fff;
}
.switch:has(input:checked) { color: var(--text); }

/* Advanced mode */
.advanced-toggle {
  width: 100%;
  padding: 10px 16px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.15s;
}
.advanced-toggle:hover { color: var(--text); }
.advanced-toggle.active {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}
.group-advanced {
  padding: 10px 14px 14px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  color: var(--text-muted);
  max-height: 9999px;
  overflow: hidden;
  transition: max-height 0.25s ease, padding 0.25s ease;
}
.group.collapsed .group-advanced {
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}
.group-advanced .slider-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.group-advanced .slider-row label {
  width: 14px;
  flex-shrink: 0;
}
.group-advanced input[type="range"] {
  flex: 1;
  min-width: 0;
}
.group-advanced .slider-value {
  width: 50px;
  padding: 2px 4px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text);
  font-size: 13px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.group-advanced .slider-value:focus {
  outline: none;
  border-color: var(--active);
}
.group-advanced .slider-value::-webkit-inner-spin-button,
.group-advanced .slider-value::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.group-advanced .slider-value { -moz-appearance: textfield; }
.slider-step {
  width: 24px;
  height: 24px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.slider-step:hover { color: var(--text); border-color: #555; }
.slider-reset {
  margin-top: 4px;
  padding: 6px 12px;
  background: var(--bg-panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text-muted);
  font-size: 12px;
  cursor: pointer;
  align-self: flex-start;
}
.slider-reset:hover { color: var(--text); border-color: #555; }

/* Action buttons */
.actions {
  display: flex;
  gap: 8px;
  margin-top: auto;
  padding-top: 12px;
}

.action-btn {
  flex: 1;
  padding: 14px;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
  font-weight: 600;
}
.reset-btn { background: var(--bg-card); color: var(--text-muted); border: 1px solid var(--border); }
.reset-btn:hover { background: #333338; color: var(--text); }
.send-btn { background: var(--active); color: #fff; transition: all 0.2s; }
.send-btn:hover { background: #b9a3ff; }
.send-btn.exported,
.export-btn.exported {
  background: #27ae60;
  pointer-events: none;
}
.export-btn { background: var(--accent); color: #fff; transition: all 0.2s; }
.export-btn:hover { background: #ff8266; }

.disclaimer {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-muted);
}

/* Pairs a .section-label with the control immediately below it inside the
   panel. Without this wrapper the panel's flex `gap: 20px` *adds* to the
   label's `margin-bottom: 6px`, leaving ~26 px between label and control —
   visually disconnected. As a block wrapper, the inner spacing collapses
   back to just the 6 px margin, while the panel gap still applies between
   adjacent .field blocks. */
.field { display: block; }
/* `.field { display: block }` would otherwise out-specificity the user-agent
   `[hidden] { display: none }` rule, leaving sceneType-gated trial/adv fields
   visible after their `hidden` attribute is set. Pin the hidden state with a
   matching-specificity selector. Same problem hits `.inspector-row { display:
   flex }` — the trial-scene Roll slider row carries `data-trial-advanced`
   directly (no .trial-slider wrapper) and would stay visible in non-advanced
   mode without this rule. */
.field[hidden] { display: none; }
.inspector-row[hidden] { display: none; }
/* When a .field stacks multiple .switch toggles (e.g. the Overlays group),
   give them a small inter-switch gap so they don't sit flush against each
   other. The first switch keeps the section-label's existing 6px margin. */
.field .switch + .switch { margin-top: 6px; }

/* Cross-page switcher between Character and Scene editors. Lives in the
   panel header so both pages can host it without altering the rest of the
   layout. The non-link half stays as a plain <span> with .active. */
.editor-switch {
  display: flex;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 4px;
  gap: 4px;
}
.editor-switch-btn {
  flex: 1;
  padding: 8px 12px;
  text-align: center;
  border-radius: 6px;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: all 0.15s;
}
.editor-switch-btn:hover { color: var(--text); }
.editor-switch-btn.active {
  background: var(--accent);
  color: #fff;
  cursor: default;
}

/* Multi-line text input for the message label. Mirrors .char-select styling
   so the panel reads as a uniform card stack. */
.text-input {
  width: 100%;
  padding: 12px 16px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-family: inherit;
  font-size: 15px;
  line-height: 1.4;
  resize: vertical;
  min-height: 72px;
}
.text-input:focus {
  outline: none;
  border-color: var(--active);
}

/* Preset-only wrap row under the debate testimony textarea: a row of
   colored squares that each wrap the textarea's selection with
   `<color=#xxxxxx>...</color>`. Each swatch hex is documented in the
   scene.js comment block where the click handler lives. */
.color-wrap-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.color-preset-label {
  color: var(--text-muted);
  font-size: 13px;
}
.color-swatch {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  transition: transform 0.1s;
}
.color-swatch:hover { transform: scale(1.1); }

/* Tag-wrap pills (Bold, Ruby) live in the same row as the color swatches
   and wrap the textarea selection with their respective tag. Sized to
   approximate the swatch footprint so the row stays balanced. */
.tag-wrap-btn {
  height: 26px;
  padding: 0 10px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.1s;
}
.tag-wrap-btn:hover { background: #333338; }
.tag-wrap-btn b { font-weight: 800; }

/* Hiro/Ema portrait picker — spaced off the toggle switch above it. */
#trialChoicePortraitPresets { margin-top: 8px; }

/* Editable trial-choice button list. Each row pairs a variant <select> with a
   label <input> and a remove button; the add button stacks below. */
.trial-choice-list { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.trial-choice-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) 28px;
  align-items: center;
  gap: 6px;
}
.trial-choice-variant,
.trial-choice-label {
  height: 32px;
  padding: 0 8px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  font-family: inherit;
}
.trial-choice-label::placeholder { color: var(--text-muted); }
.trial-choice-variant:focus,
.trial-choice-label:focus { outline: none; border-color: var(--active); }
.trial-choice-remove {
  height: 32px;
  background: var(--bg-card);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.trial-choice-remove:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.trial-choice-remove:disabled { opacity: 0.35; cursor: not-allowed; }
.trial-choice-add {
  margin-top: 8px;
  width: 100%;
  padding: 8px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px dashed var(--border);
  border-radius: 6px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
}
.trial-choice-add:hover { border-color: var(--active); color: var(--active); }
.trial-choice-add:disabled { opacity: 0.4; cursor: not-allowed; }

/* Pre-render placeholder shown inside the preview before the first composite
   completes. Plain text — replaced by the canvas via replaceChildren. */
.loading-msg {
  color: var(--text-muted);
  font-size: 14px;
  padding: 32px;
}

/* --- Snapshot library (scene editor only) --------------------------------
   Shows character snapshots written to localStorage by the character editor.
   Each row is a click-target that places the snapshot in the scene; rows for
   already-placed snapshots get a coloured side-bar so the user can spot them
   without scanning the canvas. */
.section-hint {
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
  font-size: 12px;
  margin-left: 8px;
}
/* "Manage" accordion that hides the search box + bulk delete buttons until
   the user opens it. Default closed: tidy resting state for small libraries
   (where management is unnecessary) while keeping it one click away. */
.snapshot-manage {
  margin-bottom: 8px;
}
.snapshot-manage > summary {
  list-style: none;
  cursor: pointer;
  font-size: 12px;
  color: var(--text-muted);
  padding: 6px 4px;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 6px;
}
.snapshot-manage > summary::-webkit-details-marker { display: none; }
.snapshot-manage > summary::before {
  content: "▸";
  font-size: 10px;
  transition: transform 0.15s;
}
.snapshot-manage[open] > summary::before { transform: rotate(90deg); }
.snapshot-manage > summary:hover { color: var(--text); }

.snapshot-search {
  width: 100%;
  padding: 8px 10px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: inherit;
  font-size: 13px;
  margin-top: 6px;
  margin-bottom: 6px;
}
.snapshot-search:focus { outline: none; border-color: var(--active); }
.snapshot-actions {
  display: flex;
  gap: 6px;
}
.snapshot-action-btn {
  flex: 1;
  padding: 6px 8px;
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.snapshot-action-btn:hover { color: var(--text); border-color: #555; background: var(--bg-card); }
.snapshot-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 300px;
  overflow-y: auto;
}
.snapshot-empty {
  padding: 16px 8px;
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.snapshot-empty p { margin: 0; }
.snapshot-empty-link {
  color: var(--active);
  text-decoration: none;
  font-size: 13px;
  padding: 6px 12px;
  border: 1px solid var(--active);
  border-radius: 6px;
  transition: background 0.15s;
}
.snapshot-empty-link:hover { background: var(--active-bg); }
.snapshot-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
  position: relative;
}
.snapshot-item:hover { border-color: #555; }
.snapshot-item.is-placed { border-left: 3px solid var(--active); padding-left: 6px; }
.snapshot-item.is-selected {
  background: var(--active-bg);
  border-color: var(--active);
}
.snapshot-thumb {
  width: 40px;
  height: 60px;
  object-fit: contain;
  background: #00000044;
  border-radius: 4px;
  flex-shrink: 0;
}
.snapshot-meta {
  flex: 1;
  min-width: 0;
}
.snapshot-name {
  font-size: 14px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.snapshot-time {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 2px;
}
.snapshot-rename {
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.snapshot-rename:hover { background: var(--active-bg); color: var(--active); border-color: var(--active); }
.snapshot-delete {
  width: 24px;
  height: 24px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: var(--text-muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.snapshot-delete:hover { background: #ff6b4a44; color: #ff6b4a; }

/* --- Placement inspector (scene editor only) -----------------------------
   Visible only when a placement is selected. Same .field wrapper as the
   other side-panel sections; uses a tighter row layout for the X/Y/Scale
   numeric controls. */
.inspector-name {
  font-size: 14px;
  color: var(--text);
  margin-bottom: 8px;
}
.inspector-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.inspector-label {
  font-size: 13px;
  color: var(--text-muted);
  width: 36px;
}
.inspector-num {
  flex: 1;
  min-width: 0;
  padding: 6px 8px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 14px;
  font-family: inherit;
}
.inspector-num:focus { outline: none; border-color: var(--active); }
.inspector-num:disabled { color: var(--text-muted); cursor: default; opacity: 0.6; }
.inspector-range { flex: 1; width: 100%; }
.inspector-range:disabled { cursor: default; opacity: 0.5; }
/* Hide the up/down spin buttons so the right side of the box is clear text.
   The slider next to it already covers the "step up/down" use case. */
.inspector-scale-num {
  flex: 0 0 88px;
  min-width: 0;
  text-align: right;
  -moz-appearance: textfield;        /* Firefox */
  appearance: textfield;
}
.inspector-scale-num::-webkit-inner-spin-button,
.inspector-scale-num::-webkit-outer-spin-button {
  -webkit-appearance: none;          /* Chrome / Safari */
  appearance: none;
  margin: 0;
}
/* Vertical stack so the tick rail sits flush under the slider, both flexed
   to fill the row's middle column. */
.scale-control {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* Tick rail: clickable shortcuts at the same percentage offsets as the
   slider's [0.5, 3.0] range. Position math:
     pct(v) = (v - 0.5) / 2.5
     0.5  → 0%      0.75 → 10%   1    → 20%   1.25 → 30%
     1.5  → 40%     1.75 → 50%   2    → 60%   3    → 100%
   The slider thumb's center can only travel from `r` to `width - r` of the
   input element (where `r` ≈ thumb-half-width, ~8px in browser defaults), so
   the rail is inset by `--tick-inset` on each side to keep the labels under
   the actual thumb travel range. The 0.5 and 3 endpoints render flush with
   the inset edges; interior ticks center on their position. */
.inspector-tickrail {
  --tick-inset: 8px;
  position: relative;
  height: 14px;
  margin: 0 var(--tick-inset);
}
/* Decorative labels — not interactive. All centered on their tick position
   via translateX(-50%); endpoints (0.5 and 3) sit at the rail's inset edges
   so their visual centers land where the slider thumb's center actually
   reaches at min/max. */
.tick-label {
  position: absolute;
  top: 0;
  color: var(--text-muted);
  font-size: 11px;
  line-height: 14px;
  white-space: nowrap;
  transform: translateX(-50%);
  pointer-events: none;
  user-select: none;
}
.tick-label[data-v="0.5"]  { left: 0%;   }
.tick-label[data-v="0.75"] { left: 10%;  }
.tick-label[data-v="1"]    { left: 20%;  }
.tick-label[data-v="1.25"] { left: 30%;  }
.tick-label[data-v="1.5"]  { left: 40%;  }
.tick-label[data-v="1.75"] { left: 50%;  }
.tick-label[data-v="2"]    { left: 60%;  }
.tick-label[data-v="3"]    { left: 100%; }
/* Position-slider tick rail: data-v is the ScenePosition % (0..100), so the
   tick's `left` equals its value. Scoped via .pos-tickrail to avoid clashing
   with the scale rail above (which shares data-v="1" at a different left). */
.pos-tickrail .tick-label[data-v="0"]   { left: 0%;   }
.pos-tickrail .tick-label[data-v="20"]  { left: 20%;  }
.pos-tickrail .tick-label[data-v="30"]  { left: 30%;  }
.pos-tickrail .tick-label[data-v="32"]  { left: 32%;  }
.pos-tickrail .tick-label[data-v="50"]  { left: 50%;  }
.pos-tickrail .tick-label[data-v="70"]  { left: 70%;  }
.pos-tickrail .tick-label[data-v="80"]  { left: 80%;  }
.pos-tickrail .tick-label[data-v="100"] { left: 100%; }
.inspector-zorder {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}
.zorder-btn {
  flex: 1;
  padding: 7px 8px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.zorder-btn:hover:not(:disabled) { border-color: var(--active); background: #2c2c34; }
.zorder-btn:disabled { color: var(--text-muted); cursor: default; opacity: 0.5; }
.placement-remove {
  background: transparent;
  color: #ff6b4a;
  border: 1px solid #ff6b4a55;
  margin-top: 4px;
  font-size: 14px;
  padding: 8px;
}
.placement-remove:hover:not(:disabled) { background: #ff6b4a22; }
.placement-remove:disabled { opacity: 0.4; cursor: default; }

/* --- On-canvas selection / drag overlay (scene editor only) --------------
   One absolutely-positioned div per placement, anchored to the canvas inside
   .preview-container. Invisible by default — only the selected one shows an
   outline. All overlays capture pointer events so click-and-drag works
   regardless of the hidden state. */
.placement-overlay {
  position: absolute;
  cursor: move;
  touch-action: none;
  /* No outline by default — but the rect still hit-tests for clicks. */
  border: 2px solid transparent;
  box-sizing: border-box;
}
.placement-overlay.is-selected {
  border-color: var(--active);
  box-shadow: 0 0 0 1px #00000088, inset 0 0 0 1px #00000088;
}

/* Scrollbar */
.panel::-webkit-scrollbar { width: 12px; }
.panel::-webkit-scrollbar-track { background: transparent; }
.panel::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }
.panel::-webkit-scrollbar-thumb:hover { background: #555; }

/* Right Preview */
.preview {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: radial-gradient(ellipse at 50% 60%, #1e1e24 0%, #0a0a0c 70%);
  overflow: hidden;
  position: relative;
  /* Suppress browser-native pan / pinch-zoom so our pointer handlers run. */
  touch-action: none;
}
.preview.bg-white { background: #ffffff; }

.preview-container {
  position: relative;
  display: inline-block;
  transform-origin: center center;
  transition: transform 0.15s ease;
  cursor: grab;
}
.preview-container.dragging { cursor: grabbing; transition: none; }

/* Scene editor has no pan/zoom on the preview — the grab cursor would mislead. */
body.scene-editor .preview-container { cursor: default; }

/* Canvas size is set by JS (height anchored, width derived from aspect);
   container shrink-wraps around it. */
.preview-container img,
.preview-container canvas {
  display: block;
  pointer-events: none;
}

/* Scene editor (both adv and trial): the preview canvas fits the available
   area, preserving its 2560×1440 aspect ratio. The browser's intrinsic-ratio
   handling for replaced elements does the right thing when both width and
   height are auto with max-* constraints. The character editor preview
   stays on the default `display: inline-block` shrink-wrap so its zoom/pan
   transform behaves correctly. */
body.scene-editor .preview-container {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
body.scene-editor .preview-container canvas {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}

.zoom-controls {
  position: absolute;
  bottom: 16px;
  right: 16px;
  display: flex;
  gap: 6px;
  z-index: 100;
}

.zoom-btn {
  width: 40px;
  height: 40px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-panel);
  color: var(--text);
  font-size: 20px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.8;
  transition: opacity 0.15s;
}
.zoom-btn:hover { opacity: 1; background: var(--bg-card); }

/* --- Mobile drawer (panel slides up from the bottom) ---
   Hidden on desktop; only the @media block below activates them. */
.drawer-toggle { display: none; }
.drawer-backdrop { display: none; }
.drawer-handle-row { display: none; }

@media (max-width: 768px) {
  /* Use dynamic viewport height so the layout doesn't jump when the
     mobile browser's address bar collapses or expands. */
  body { height: 100dvh; }

  .preview {
    width: 100%;
    height: 100dvh;
    padding: 8px;
  }

  /* Container fills the preview so the canvas can size against a real box.
     Without this, .preview-container is inline-block and shrink-wraps the
     canvas — leaving nothing for max-width:100% to anchor against. */
  .preview-container {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* Override the inline `style.height = '950px'` set by the JS render path.
     With width/height auto + max-width/max-height 100%, the browser scales
     the canvas down to fit while preserving its intrinsic aspect ratio. */
  .preview-container canvas,
  .preview-container img {
    width: auto !important;
    height: auto !important;
    max-width: 100%;
    max-height: 100%;
  }

  /* Panel becomes a bottom sheet. Fixed-positioned so it leaves the flex
     layout entirely and the preview gets the whole viewport. */
  .panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    min-width: 0;
    height: 88dvh;
    max-height: 88dvh;
    border-right: none;
    border-top: 1px solid var(--border);
    border-top-left-radius: 16px;
    border-top-right-radius: 16px;
    padding: 4px 16px 16px;
    gap: 16px;
    transform: translateY(100%);
    transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
    z-index: 200;
    box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.4);
  }
  body.drawer-open .panel { transform: translateY(0); }

  .panel h1 { font-size: 20px; }

  .drawer-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 150;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
    -webkit-tap-highlight-color: transparent;
  }
  body.drawer-open .drawer-backdrop {
    opacity: 1;
    pointer-events: auto;
  }

  /* Sticky so the close button + grab handle stay reachable when the
     panel content scrolls. Negative margins extend the sticky background
     across the panel's horizontal padding so it covers cleanly. */
  .drawer-handle-row {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 5;
    align-items: center;
    justify-content: flex-end;
    height: 32px;
    margin: 0 -16px;
    padding: 0 8px;
    background: var(--bg-panel);
  }
  .drawer-handle {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 48px;
    height: 5px;
    background: #555;
    border-radius: 3px;
  }
  .drawer-close {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
    border-radius: 50%;
    -webkit-tap-highlight-color: transparent;
  }
  .drawer-close:active { background: var(--bg-card); color: var(--text); }

  .drawer-toggle {
    display: flex;
    position: fixed;
    bottom: 16px;
    left: 16px;
    z-index: 100;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 24px;
    color: var(--text);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    -webkit-tap-highlight-color: transparent;
    transition: opacity 0.2s ease;
  }
  body.drawer-open .drawer-toggle {
    opacity: 0;
    pointer-events: none;
  }

  /* Bigger touch targets for the existing zoom controls. */
  .zoom-btn { width: 44px; height: 44px; }
}
