@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url('../fonts/onest-300-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src: url('../fonts/onest-300-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/onest-400-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/onest-400-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../fonts/onest-500-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url('../fonts/onest-500-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('../fonts/onest-600-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url('../fonts/onest-600-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/onest-700-normal-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Onest';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/onest-700-normal-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

main#workspace {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  padding: var(--space-xl);
}

/* View placeholders — filled in later phases */
.view {
  display: block;
}

/* Toast notifications (errors.js) */
#toast-root {
  position: fixed;
  bottom: var(--space-lg);
  right: var(--space-lg);
  display: flex;
  flex-direction: column-reverse;
  gap: var(--space-sm);
  z-index: 1000;
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  background: var(--surface);
  color: var(--text);
  padding: var(--space-md) var(--space-lg);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  max-width: 380px;
  display: flex;
  align-items: center;
  gap: var(--space-md);
  font-size: var(--t-sm);
  transition: opacity var(--dur-med) var(--ease-out);
}

.toast-warn  { border-color: var(--warn); }
.toast-error { border-color: var(--danger); }

.toast-close {
  background: transparent;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: var(--t-lg);
  line-height: 1;
  padding: 0 var(--space-xs);
  margin-left: auto;
}

.toast-close:hover {
  color: var(--text);
  background-color: transparent;
}

/* --- Queue view (queueView.js) --- */
#queue-view {
  display: grid;
  grid-template-columns: 1fr 360px;
  gap: var(--space-lg);
  padding: var(--space-xl);
  align-items: start;
}
/* Empty state takes the whole row */
#queue-view .queue-empty { grid-column: 1 / -1; }

.queue-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 220px));
  gap: var(--space-md);
  justify-content: start;
}

.queue-thumb {
  position: relative;
  display: block;
  padding: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  aspect-ratio: 1;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.queue-thumb:hover { border-color: var(--border-strong); }
.queue-thumb.is-active { outline: 2px solid var(--accent); outline-offset: -1px; }

.queue-thumb img {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  background: var(--surface-2);
}

.queue-thumb-remove {
  position: absolute;
  top: var(--space-sm); right: var(--space-sm);
  width: 24px; height: 24px;
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  display: grid; place-items: center;
  font-size: var(--t-md);
  line-height: 1;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.queue-thumb:hover .queue-thumb-remove,
.queue-thumb-remove:focus-visible {
  opacity: 1;
}

/* --- Queue intro (empty-state landing copy) ---------------------------- */
/* Sits ABOVE the drop zone when queue.length === 0. The <h1> here is the
   single content heading on the page — the topbar wordmark is a <p> so
   crawlers and screen-readers see exactly one h1. */
.queue-intro {
  grid-column: 1 / -1;
  max-width: 65ch;
  margin: var(--space-2xl) auto var(--space-xl);
  padding: 0 var(--space-xl);
  text-align: left;
}
.intro-title {
  font-size: var(--t-2xl);
  font-weight: 600;
  letter-spacing: -0.015em;
  margin: 0 0 var(--space-md);
  color: var(--text);
}
.intro-lead {
  font-size: var(--t-lg);
  line-height: 1.5;
  color: var(--text-secondary);
  margin: 0 0 var(--space-md);
}
.intro-tags {
  font-size: var(--t-sm);
  color: var(--text-muted);
  margin: 0 0 var(--space-xl);
}
.intro-features {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-sm);
}
.intro-features li {
  font-size: var(--t-md);
  line-height: 1.4;
  color: var(--text-secondary);
  padding-left: var(--space-md);
  position: relative;
}
.intro-features li::before {
  content: '•';
  color: var(--accent);
  position: absolute;
  left: 0;
  top: 0;
}

.queue-empty {
  display: grid;
  place-items: center;
  min-height: 180px;
  padding: var(--space-xl);
  border: 1px dashed var(--border);
  border-radius: var(--radius-card);
  margin: var(--space-lg);
}

.queue-empty p {
  color: var(--text-muted);
  font-size: var(--t-md);
  text-align: center;
}

.queue-empty button.text-link {
  background: none;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  padding: 0;
}

body.is-drag-active::after {
  content: '';
  position: fixed; inset: 0;
  pointer-events: none;
  background: var(--surface);
  opacity: 0.15;
  z-index: 999;
}

/* --- Editor view (editor.js + render/previewRenderer.js) --- */

/* When the editor is visible, let it fill the workspace edge to edge. The
   queue view keeps the default padded layout. */
main#workspace:has(> #editor-view:not([hidden])) {
  padding: 0;
}

#editor-view {
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: 1fr 320px;
  grid-template-areas:
    "toolbar toolbar"
    "canvas  panel";
  height: calc(100vh - 56px);
  min-height: 0;
}
/* `display: grid` above would override the UA stylesheet's [hidden] rule, so
   restore the expected behavior explicitly. Same for queue-view. */
.view[hidden], #queue-view[hidden], #editor-view[hidden], .batch-panel[hidden] { display: none !important; }

.editor-toolbar {
  grid-area: toolbar;
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  flex-wrap: wrap;
}
.editor-toolbar button[data-tool],
.editor-toolbar #undo-btn,
.editor-toolbar #redo-btn,
.editor-toolbar #back-to-queue {
  flex: 0 0 auto;
}
.editor-toolbar button[data-tool] {
  font-size: var(--t-md);
  width: 36px; height: 36px;
  display: grid; place-items: center;
}
.editor-toolbar button[data-tool].is-active,
.editor-toolbar #compare-toggle.is-active {
  background: var(--surface-2);
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}
.editor-toolbar .divider {
  width: 1px;
  height: 24px;
  background: var(--border);
  margin: 0 var(--space-xs);
}
.editor-toolbar .spacer { flex: 1; }

/* v1.1.1: .canvas-area is the non-scrolling wrapper that owns the grid
   slot. The scrolling .canvas-frame lives inside it, alongside the
   sticky .zoom-controls. Without this wrapper, the zoom pill scrolled
   away with the canvas content when the user panned a zoomed image. */
.canvas-area {
  grid-area: canvas;
  position: relative;
  min-width: 0;
  min-height: 0;
}
.canvas-frame {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  /* `auto` lets the canvas overflow + scroll when the user zooms past fit.
     Previously `hidden` + `max-width: 100%` on the canvas meant zoom-in
     clamped the canvas to the frame, producing visible stretch artefacts
     instead of a real bigger view. */
  overflow: auto;
}
.canvas-frame canvas {
  display: block;
  /* No max-width/max-height clamp — JS sizes the canvas to image × zoom and
     we want it to overflow the frame at high zoom so the user can pan. */
}
/* Pan tool cursors: grab when armed, grabbing while actively dragging.
   Both the dedicated Pan tool (is-pan-tool-active) and the Space-held
   temporary accelerator (is-pan-temporarily-active) use the same visual
   styling — only difference is whether the active tool changes or not. */
.canvas-frame.is-pan-tool-active,
.canvas-frame.is-pan-temporarily-active { cursor: grab; }
.canvas-frame.is-pan-tool-active.is-panning,
.canvas-frame.is-pan-temporarily-active.is-panning { cursor: grabbing; }

/* Checkerboard backdrop for the Transparent PNG tool (v1.3 Feature 16).
   Toggled by transparentPngTool.js via state.ui.transparentPng.checkerboardOn.
   The four overlapping linear-gradients are the canonical CSS technique —
   no SVG, no image asset. Dark theme uses a darker gray so the pattern stays
   subtle (it's a backdrop, not a focal point). */
.canvas-frame.is-checkerboard {
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #ddd 25%, transparent 25%),
    linear-gradient(-45deg, #ddd 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ddd 75%),
    linear-gradient(-45deg, transparent 75%, #ddd 75%);
  background-size: 20px 20px;
  background-position: 0 0, 0 10px, 10px -10px, -10px 0;
}
html[data-theme="dark"] .canvas-frame.is-checkerboard {
  background-color: #2a2a2a;
  background-image:
    linear-gradient(45deg, #444 25%, transparent 25%),
    linear-gradient(-45deg, #444 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #444 75%),
    linear-gradient(-45deg, transparent 75%, #444 75%);
}

#base-canvas, #overlay-canvas {
  position: absolute;
  /* Suppress browser pan/zoom gestures so tools own touch input. The
     pointer.js helper sets this dynamically per-element on attach; the CSS
     here is a defense-in-depth measure for canvases that aren't wired via
     attachPointer (e.g. before a tool activates). */
  touch-action: none;
}
#overlay-canvas {
  pointer-events: none; /* tools will enable as needed in later phases */
}
.zoom-controls {
  position: absolute;
  bottom: var(--space-md);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: var(--space-xs);
  background: var(--surface);
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  align-items: center;
  /* v1.1.1: lives in .canvas-area (sibling of the scrolling .canvas-frame),
     so it stays stuck to the visible frame's bottom-center even when the
     user pans a zoomed image. z-index keeps it above the canvas. */
  z-index: 2;
}
.zoom-controls button, .zoom-controls select {
  background: transparent;
  border: none;
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.zoom-controls .zoom-readout {
  font-variant-numeric: tabular-nums;
  min-width: 4ch;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--t-sm);
}

.editor-panel {
  grid-area: panel;
  border-left: 1px solid var(--border);
  background: var(--surface);
  overflow-y: auto;
  padding: var(--space-md);
}
.editor-panel details {
  border-bottom: 1px solid var(--border);
  padding: var(--space-md) 0;
}
.editor-panel details:last-child { border-bottom: none; }
.editor-panel summary {
  cursor: pointer;
  font-weight: 600;
  font-size: var(--t-sm);
  padding: var(--space-xs) 0;
  list-style: none;
}
.editor-panel summary::-webkit-details-marker { display: none; }
.editor-panel summary::after {
  content: '▾';
  float: right;
  color: var(--text-muted);
  transition: transform var(--dur-fast) var(--ease-out);
}
.editor-panel details:not([open]) summary::after {
  transform: rotate(-90deg);
}

/* --- Side-panel tool / resize controls (Phase 4) --- */

.editor-panel .panel-body {
  padding: var(--space-sm) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.editor-panel .panel-heading {
  margin: 0;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--text-muted);
}
.editor-panel .crop-row,
.editor-panel .resize-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  font-size: var(--t-sm);
}
/* `display: flex` above overrides the HTML `hidden` attribute's default
   `display: none`. Re-assert hiding for hidden rows. */
.editor-panel .crop-row[hidden],
.editor-panel .resize-row[hidden] { display: none !important; }
.editor-panel .crop-row select,
.editor-panel .resize-row select,
.editor-panel .crop-row input[type="text"],
.editor-panel .resize-row input[type="number"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  min-width: 0;
  flex: 0 1 60%;
}
.editor-panel .crop-actions {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}
.editor-panel .crop-actions button {
  flex: 1;
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  cursor: pointer;
  font: inherit;
  font-size: var(--t-sm);
}
.editor-panel .crop-actions button.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}
.editor-panel .resize-readout {
  font-size: var(--t-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
/* Apply button for the single-image Resize panel. Mirrors the .resize-trim
   button styling so it visually belongs to the same group, but uses the
   accent color when enabled so the user sees a clear call-to-action. */
.editor-panel .resize-apply {
  padding: var(--space-xs) var(--space-sm);
  background: var(--accent);
  border: 1px solid var(--accent);
  color: var(--accent-contrast);
  border-radius: var(--radius-control);
  font: inherit;
  cursor: pointer;
  margin-top: var(--space-xs);
}
.editor-panel .resize-apply:hover:not(:disabled) {
  filter: brightness(1.1);
}
.editor-panel .resize-apply:disabled {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text-muted);
  cursor: default;
  opacity: 0.7;
}
.editor-panel .rotate-group,
.editor-panel .flip-group {
  display: flex;
  gap: var(--space-sm);
}
.editor-panel .rotate-group button,
.editor-panel .flip-group button {
  flex: 1;
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  cursor: pointer;
  font: inherit;
  font-size: var(--t-sm);
}
.editor-panel .flip-group button.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}
.editor-panel .rotate-slider {
  width: 100%;
}
.editor-panel .rotate-readout {
  font-size: var(--t-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* --- Trim subsection (v1.1 Feature 3) --- */
.editor-panel .resize-trim-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-top: var(--space-sm);
  margin-top: var(--space-xs);
  border-top: 1px solid var(--border);
}
.editor-panel .resize-trim-group > button {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  cursor: pointer;
  font: inherit;
  font-size: var(--t-sm);
  text-align: left;
}
.editor-panel .resize-trim-group > button:disabled {
  opacity: 0.5;
  cursor: progress;
}
.editor-panel .resize-trim-tol-row input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
}
.editor-panel .resize-trim-tol-readout {
  font-variant-numeric: tabular-nums;
  min-width: 2ch;
  text-align: right;
  color: var(--text-muted);
}

.batch-trim-hint {
  margin: 0 0 var(--space-sm);
  font-size: var(--t-sm);
  color: var(--text-muted);
}
.batch-trim-tol-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin: var(--space-sm) 0;
}
.batch-trim-tol-row > input[type="range"] {
  flex: 1 1 auto;
  min-width: 0;
}
.batch-trim-tol-readout {
  font-variant-numeric: tabular-nums;
  min-width: 2ch;
  text-align: right;
  color: var(--text-muted);
}

/* --- Adjust panel (Phase 5) --- */

.adjust-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.preset-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-sm);
  align-items: center;
  padding: var(--space-sm) 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-sm);
}
.preset-row span {
  font-size: var(--t-sm);
  color: var(--text-secondary);
}
.preset-row select {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  min-width: 0;
}

.adjust-row {
  display: grid;
  grid-template-columns: 80px 1fr 4ch auto;
  gap: var(--space-sm);
  align-items: center;
  padding: var(--space-xs) 0;
}
.adjust-row label { font-size: var(--t-sm); color: var(--text-secondary); }
.adjust-row .adjust-slider-wrap { display: flex; align-items: center; min-width: 0; }
.adjust-row input[type="range"] { width: 100%; }
.adjust-row .readout {
  font-variant-numeric: tabular-nums;
  font-size: var(--t-sm);
  color: var(--text-muted);
  text-align: right;
  min-width: 4ch;
}
.adjust-row .reset-btn {
  width: 24px; height: 24px;
  padding: 0;
  font-size: var(--t-sm);
  color: var(--text-muted);
  background: transparent;
}
.adjust-row .reset-btn:hover { color: var(--text); background: transparent; }
.adjust-reset-all {
  margin-top: var(--space-md);
  display: block;
  align-self: flex-start;
  padding: var(--space-xs) var(--space-md);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font-size: var(--t-sm);
}
.adjust-reset-all:hover { background: var(--surface); border-color: var(--border-strong); }

/* --- Eyedropper / chromakey panel (Phase 6) --- */

.editor-panel .eyedropper-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--t-sm);
}
.editor-panel .eyedropper-color-row {
  justify-content: flex-start;
}
.editor-panel .eyedropper-swatch {
  display: inline-block;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-control);
  border: 1px solid var(--border);
  background: transparent;
  flex: 0 0 auto;
}
.editor-panel .eyedropper-hex {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  flex: 1 1 auto;
  min-width: 0;
  font-variant-numeric: tabular-nums;
}
.editor-panel .eyedropper-tol-row {
  display: grid;
  grid-template-columns: 80px 1fr 4ch;
  gap: var(--space-sm);
  align-items: center;
}
.editor-panel .eyedropper-tol-row span:first-child {
  color: var(--text-secondary);
}
.editor-panel .eyedropper-tolerance {
  width: 100%;
}
.editor-panel .eyedropper-tol-readout {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: right;
  min-width: 4ch;
}
.editor-panel .eyedropper-actions {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}
.editor-panel .eyedropper-actions button {
  flex: 1;
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  cursor: pointer;
  font: inherit;
  font-size: var(--t-sm);
}
.editor-panel .eyedropper-actions button.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

/* --- Text tool panel (Phase 7A) --- */

.editor-panel .text-tool-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.editor-panel .text-empty {
  color: var(--text-muted);
  font-size: var(--t-sm);
  margin: 0;
}
.editor-panel .text-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.editor-panel .text-row {
  display: grid;
  grid-template-columns: 80px 1fr;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.editor-panel .text-row > span {
  color: var(--text-secondary);
}
.editor-panel .text-text-row {
  align-items: start;
}
.editor-panel .text-input {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  resize: vertical;
  min-height: 60px;
}
.editor-panel .text-font,
.editor-panel .text-weight {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
}
.editor-panel .text-size {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
}
.editor-panel .text-color {
  width: 100%;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  cursor: pointer;
}
.editor-panel .text-align-row {
  align-items: center;
}
.editor-panel .text-align-group {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-xs);
}
.editor-panel .text-align-group button {
  padding: var(--space-xs);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-md);
  cursor: pointer;
}
.editor-panel .text-align-group button.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}
.editor-panel .text-delete {
  margin-top: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.editor-panel .text-delete:hover {
  color: var(--danger);
  border-color: var(--danger);
}

/* --- Overlays panel (Phase 7A) --- */

.overlays-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.overlay-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--space-sm);
  align-items: center;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  cursor: pointer;
  background: var(--surface);
}
.overlay-row:hover { background: var(--surface-2); }
.overlay-row.is-active {
  border-color: var(--accent);
  background: var(--surface-2);
}
.overlay-row.is-dragging { opacity: 0.5; }
.overlay-row.is-drop-target {
  border-style: dashed;
  border-color: var(--accent);
}
.overlay-row .overlay-icon {
  width: 24px; height: 24px;
  display: grid; place-items: center;
  font-size: var(--t-md);
  color: var(--text-muted);
}
.overlay-row .overlay-label {
  font-size: var(--t-sm);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.overlay-row .overlay-delete {
  width: 24px; height: 24px;
  padding: 0;
  color: var(--text-muted);
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: var(--t-md);
  line-height: 1;
}
.overlay-row .overlay-delete:hover { color: var(--danger); }
.overlay-empty {
  color: var(--text-muted);
  font-size: var(--t-sm);
  padding: var(--space-md);
  text-align: center;
  margin: 0;
}

/* --- Brush tool panel (Phase 7B) --- */

.editor-panel .brush-tool-panel,
.editor-panel .shape-tool-panel,
.editor-panel .redact-tool-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.editor-panel .brush-row,
.editor-panel .shape-row,
.editor-panel .redact-row {
  display: grid;
  grid-template-columns: 80px 1fr auto;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
/* display:grid above would override the UA [hidden] rule — restore it. */
.editor-panel .brush-row[hidden],
.editor-panel .shape-row[hidden],
.editor-panel .redact-row[hidden] { display: none; }
.editor-panel .brush-row > span:first-child,
.editor-panel .shape-row > span:first-child,
.editor-panel .redact-row > span:first-child {
  color: var(--text-secondary);
}
.editor-panel .brush-color,
.editor-panel .shape-stroke,
.editor-panel .shape-fill {
  width: 100%;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  cursor: pointer;
}
.editor-panel .brush-size,
.editor-panel .shape-stroke-width,
.editor-panel .redact-strength {
  width: 100%;
}
.editor-panel .brush-size-readout,
.editor-panel .shape-stroke-width-readout,
.editor-panel .redact-strength-readout {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: right;
  min-width: 3ch;
}
.editor-panel .brush-swatch {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}
.editor-panel .brush-hint,
.editor-panel .shape-hint,
.editor-panel .redact-hint {
  color: var(--text-muted);
  font-size: var(--t-sm);
  margin: 0;
}

/* --- Shape tool panel (Phase 7B) --- */
.editor-panel .shape-kind-group {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-xs);
  grid-column: 2 / 4;
}
.editor-panel .shape-kind-group button {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.editor-panel .shape-kind-group button.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}
.editor-panel .shape-fill-group {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.editor-panel .shape-fill-group input[type="color"] {
  flex: 1;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  cursor: pointer;
}
.editor-panel .shape-fill-group input[type="color"]:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* --- Redact tool panel (Phase 7B) --- */
.editor-panel .redact-mode-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-xs);
  grid-column: 2 / 4;
}
.editor-panel .redact-mode-group button {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.editor-panel .redact-mode-group button.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

/* AI-detect sensitivity row inside the redact panel. Mirrors the mode
   group's chip-pill look so users connect "this is a preset like Mode."
   Shape-based active state (filled bg + bolder weight) so colorblind
   users can tell the active chip apart even in monochrome. */
.editor-panel .redact-sensitivity-group {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-xs);
  grid-column: 2 / 4;
}
.editor-panel .redact-sensitivity-group button {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.editor-panel .redact-sensitivity-group button.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
  font-weight: 600;
}

/* --- Export panel (Phase 9) --- */
.editor-panel .export-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.editor-panel .export-panel .format-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-sm);
}
.editor-panel .export-panel .format-chip {
  padding: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  text-align: center;
  cursor: pointer;
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.editor-panel .export-panel .format-chip:hover {
  background: var(--surface-2);
}
.editor-panel .export-panel .format-chip.is-active {
  border-color: var(--accent);
  background: var(--surface-2);
  color: var(--accent);
  /* A box-shadow plus the border-color gives a stronger active cue than
     color alone — Dan is colorblind so two-channel signaling matters. */
  box-shadow: inset 0 0 0 1px var(--accent);
}
.editor-panel .export-panel .quality-row {
  display: grid;
  grid-template-columns: 80px 1fr 4ch;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.editor-panel .export-panel .quality-row[hidden] {
  display: none;
}
.editor-panel .export-panel .quality-row > span:first-child {
  color: var(--text-secondary);
}
.editor-panel .export-panel .quality-slider {
  width: 100%;
}
.editor-panel .export-panel .quality-readout {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: right;
}
.editor-panel .export-panel .filename-row {
  display: grid;
  grid-template-columns: 80px 1fr;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.editor-panel .export-panel .filename-row > span {
  color: var(--text-secondary);
}
.editor-panel .export-panel .filename-template {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  min-width: 0;
}
.editor-panel .export-panel .filename-help {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-muted);
}
.editor-panel .export-panel .output-dims {
  font-size: var(--t-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
/* Predicted-size readout — same monospace digit metric as the dims line so
   they stack as a coherent pair. The value is the REAL encoded byte count
   (not a heuristic), so it stays in a slightly stronger color to read as
   "ground truth" rather than estimate. */
.editor-panel .export-panel .predicted-size,
.batch-panel .batch-predicted-size {
  font-size: var(--t-sm);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
/* "Smallest size" preset button. Quieter than the primary Download — it's
   a helper that adjusts settings, not the main action. */
.editor-panel .export-panel .smallest-preset-btn,
.batch-panel .batch-smallest-btn {
  align-self: flex-start;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out);
}
.editor-panel .export-panel .smallest-preset-btn:hover,
.batch-panel .batch-smallest-btn:hover {
  background-color: var(--surface-2);
}
.editor-panel .export-panel .smallest-preset-btn:disabled,
.batch-panel .batch-smallest-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.editor-panel .export-panel .smallest-preset-btn.is-working,
.batch-panel .batch-smallest-btn.is-working {
  /* Visual cue beyond the disabled state — Dan is colorblind, so we lean on
     a slight italic + reduced opacity rather than only a color change. */
  font-style: italic;
  opacity: 0.8;
}
.editor-panel .export-panel .download-btn {
  margin-top: var(--space-sm);
  background: var(--accent);
  color: var(--accent-contrast);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
}
.editor-panel .export-panel .download-btn:hover {
  background: var(--accent-hover, var(--accent));
  filter: brightness(0.95);
}
.editor-panel .export-panel .download-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* --- EXIF / GPS strip disclosure (v1.1) ---
   Always-on privacy guarantee. The check glyph reads as "stripped"; the
   text label conveys the same meaning for assistive tech and colorblind
   users (we don't rely on green alone). The Verify button is the only
   active control here — it inspects the last exported Blob's bytes. */
/* v1.1.2: metadata toggle. Replaces the v1.1.x "EXIF stripped" badge +
   Verify button (which incorrectly implied the site retained export
   bytes for later inspection). The .exif-status class is now a checkbox
   row: input + label, optional hint paragraph below shown when the user
   opts out of stripping. */
.editor-panel .export-panel .exif-status,
.batch-panel .exif-status {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  flex-wrap: wrap;
  margin-top: var(--space-sm);
  padding-top: var(--space-sm);
  border-top: 1px dashed var(--border);
  font-size: var(--t-xs);
  color: var(--text-secondary);
  cursor: pointer;
}
.editor-panel .export-panel .exif-label,
.batch-panel .exif-label {
  cursor: help;
}
.editor-panel .export-panel .strip-metadata,
.batch-panel .strip-metadata {
  margin: 0;
  cursor: pointer;
}
.editor-panel .export-panel .strip-metadata-hint,
.batch-panel .strip-metadata-hint {
  margin: var(--space-xs) 0 0;
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border-radius: var(--radius-control);
  font-size: var(--t-xs);
  color: var(--text-secondary);
  line-height: 1.4;
}

/* Passive WebP-over-PNG nudge under the format chip row. Quieter than the
   strip-metadata hint (no surface background): it's a discovery affordance,
   not a warning, so it shouldn't compete with primary controls. Italic +
   muted ties it to existing helper-text styling. Lives in BOTH the editor
   export panel and the batch panel — same selector covers both. */
.editor-panel .export-panel .export-format-hint,
.batch-panel .export-format-hint {
  margin: var(--space-xs) 0 0;
  font-size: var(--t-xs);
  font-style: italic;
  color: var(--text-muted);
  line-height: 1.4;
}

/* v1.2 "View source metadata" button under the strip-metadata toggle. */
.editor-panel .export-panel .view-metadata-btn {
  display: block;
  margin-top: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  color: var(--text);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}
.editor-panel .export-panel .view-metadata-btn:hover {
  background: var(--surface-2);
}

/* v1.2 source-metadata audit modal. Plain table; the "Will be stripped /
   kept" column is the column users actually look at. */
.metadata-audit-dialog {
  max-width: 640px;
  width: calc(100vw - var(--space-xl) * 2);
  padding: var(--space-lg);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--surface);
  color: var(--text);
}
.metadata-audit-dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
}
.metadata-audit-dialog .metadata-audit-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  margin-bottom: var(--space-sm);
}
.metadata-audit-dialog h2 {
  margin: 0;
  font-size: var(--t-lg);
  font-weight: 600;
}
.metadata-audit-dialog .dialog-close {
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 1.5em;
  line-height: 1;
  cursor: pointer;
  padding: var(--space-xs);
}
.metadata-audit-dialog .metadata-audit-lead {
  margin: 0 0 var(--space-md);
  color: var(--text-secondary);
  font-size: var(--t-sm);
}
.metadata-audit-dialog .metadata-audit-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--t-sm);
}
.metadata-audit-dialog .metadata-audit-table th,
.metadata-audit-dialog .metadata-audit-table td {
  padding: var(--space-xs) var(--space-sm);
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.metadata-audit-dialog .metadata-audit-table thead th {
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 500;
}
.metadata-audit-dialog .metadata-audit-table tbody th {
  font-weight: 600;
  white-space: nowrap;
  width: 30%;
}
.metadata-audit-dialog .metadata-audit-table .metadata-action {
  font-weight: 600;
  white-space: nowrap;
  text-align: right;
}
.metadata-audit-dialog .metadata-audit-table tr.severity-high th {
  /* GPS row gets a left bar so colorblind users see the emphasis without
     relying on red text. */
  border-left: 3px solid var(--accent);
  padding-left: calc(var(--space-sm) - 3px);
}
.metadata-audit-dialog .metadata-audit-footer {
  margin-top: var(--space-md);
}
.metadata-audit-dialog .metadata-audit-footnote {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--t-xs);
  line-height: 1.4;
}

/* --- Batch panel + thumb badge (queueView.js, Phase 10) --- */

.batch-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-md);
  position: sticky;
  top: calc(56px + var(--space-md));
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  /* Cap height so the panel scrolls internally on tall screens with many
     open sections instead of pushing the page. */
  max-height: calc(100vh - 56px - var(--space-2xl));
  overflow-y: auto;
}

.batch-panel-heading {
  margin: 0 0 var(--space-xs);
  font-size: var(--t-md);
  font-weight: 600;
}

/* History controls at the top of the batch panel (v1.1.2). Two icon-only
   buttons sitting side-by-side below the heading; same glyphs as the
   editor toolbar's Undo/Redo. */
.batch-panel .batch-history {
  display: flex;
  gap: var(--space-xs);
  margin: 0 0 var(--space-sm);
}
.batch-panel .batch-history button {
  flex: 0 0 auto;
  padding: var(--space-xs) var(--space-sm);
  font-size: var(--t-md);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  cursor: pointer;
}
.batch-panel .batch-history button:hover:not(:disabled) {
  background: var(--surface);
}
.batch-panel .batch-history button:disabled {
  opacity: 0.4;
  cursor: default;
}

.batch-section {
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--space-sm);
}
.batch-section:last-child { border-bottom: none; }

.batch-section summary {
  font-weight: 600;
  font-size: var(--t-sm);
  cursor: pointer;
  padding: var(--space-xs) 0;
  list-style: none;
}
.batch-section summary::-webkit-details-marker { display: none; }
.batch-section summary::after {
  content: '▾';
  float: right;
  color: var(--text-muted);
  transition: transform var(--dur-fast) var(--ease-out);
}
.batch-section:not([open]) summary::after {
  transform: rotate(-90deg);
}

.batch-section-body {
  padding: var(--space-sm) 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.batch-row {
  display: grid;
  grid-template-columns: 80px 1fr;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
/* The UA stylesheet's `[hidden] { display: none }` is overridden by the
   `display: grid` declaration above due to specificity. Restore the
   expected hide behavior explicitly so JS can do `el.hidden = true`. */
.batch-row[hidden] { display: none; }
.batch-row > span:first-child { color: var(--text-secondary); }
.batch-row--tol {
  grid-template-columns: 80px 1fr 4ch;
}

.batch-panel select,
.batch-panel input[type="number"],
.batch-panel input[type="text"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  min-width: 0;
}

.batch-panel input[type="range"] {
  width: 100%;
}

.batch-panel input[type="color"] {
  width: 100%;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  cursor: pointer;
}

.batch-apply {
  background: var(--accent);
  color: var(--accent-contrast);
  border: 1px solid var(--accent);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
}
.batch-apply:hover { background: var(--accent-hover); }
.batch-apply:disabled { opacity: 0.5; cursor: not-allowed; }

.batch-rotate-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-xs);
}
.batch-rotate-group button {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.batch-rotate-group button:hover {
  border-color: var(--border-strong);
}

.batch-adjust-row {
  display: grid;
  grid-template-columns: 80px 1fr 4ch;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.batch-adjust-label { color: var(--text-secondary); }
.batch-adjust-readout {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: right;
}

.batch-chroma-tol-readout {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: right;
}

.batch-bg-hint {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-muted);
}

.batch-format-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-sm);
}
.batch-format-chip {
  padding: var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  text-align: center;
  cursor: pointer;
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.batch-format-chip:hover { background: var(--surface-2); }
.batch-format-chip.is-active {
  border-color: var(--accent);
  background: var(--surface-2);
  color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.batch-quality-row {
  display: grid;
  grid-template-columns: 80px 1fr 4ch;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.batch-quality-row[hidden] { display: none; }
.batch-quality-row > span:first-child { color: var(--text-secondary); }
.batch-quality-readout {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: right;
}

.batch-filename-row {
  display: grid;
  grid-template-columns: 80px 1fr;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.batch-filename-row > span { color: var(--text-secondary); }

.batch-filename-help {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-muted);
}

.batch-export-readout {
  margin: 0;
  font-size: var(--t-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.export-queue-btn {
  font-size: var(--t-md);
  padding: var(--space-md);
}

/* Secondary export button — same shape, visually quieter. Sits below
   the primary Export queue (ZIP) button. */
.batch-apply-secondary {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  font: inherit;
  cursor: pointer;
  margin-top: var(--space-xs);
  transition: background-color var(--dur-fast) var(--ease-out);
}
.batch-apply-secondary:hover { background-color: var(--surface-2); }
.batch-apply-secondary:disabled { opacity: 0.5; cursor: not-allowed; }

/* --- Thumb (batch) badge --- */
.queue-thumb-batch-badge {
  position: absolute;
  bottom: var(--space-sm);
  right: var(--space-sm);
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  pointer-events: none;
}

/* --- Mobile chrome injected by js/bottomSheet.js -------------------------
   The trigger button and tab strip exist in the DOM on every viewport so
   tests don't need to special-case desktop vs mobile, but their visual
   chrome only makes sense at <=768 px. Default state: hidden. The mobile
   media query overrides display below. */
.editor-panel-trigger {
  display: none;
}
.editor-panel-tabs {
  display: none;
}

/* --- Responsive: mobile (<=768px) ----------------------------------------
   Phase 13 turns the editor view into a single-column layout with the side
   panel reborn as a bottom sheet, and bumps the queue view to one column.
   The bottom-sheet markup is injected by js/bottomSheet.js; the .editor-panel
   element itself is reused (no separate DOM tree on mobile), so its desktop
   stylesheet remains the source of truth for inner-control styling.            */
@media (max-width: 768px) {
  /* --- Queue view: single column, batch panel below the grid ----------- */
  #queue-view {
    grid-template-columns: 1fr;
    gap: var(--space-md);
    padding: var(--space-md);
  }
  .batch-panel {
    position: static;
    max-height: none;
  }
  .queue-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(120px, 100%), 1fr));
  }
  /* Intro section: tighter rhythm + smaller display heading on phones. */
  .queue-intro {
    margin: var(--space-xl) auto var(--space-lg);
    padding: 0 var(--space-lg);
  }
  .intro-title { font-size: var(--t-xl); }
  .intro-lead  { font-size: var(--t-md); }

  /* --- Editor view: canvas on top, panel always visible on bottom ----
     Phase 14: the bottom sheet trigger is gone. The side panel now sits
     anchored to the bottom 40vh of the viewport at all times; the canvas
     frame takes the space above it. The image is centered in its frame
     via the existing `place-items: center` rule. */
  #editor-view {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr 40vh;
    grid-template-areas:
      "toolbar"
      "canvas"
      "panel";
    height: calc(100vh - 56px);
  }
  /* Panel is in flow inside the grid — no fixed positioning, no transform,
     no drag-to-close. Internal scroll keeps overlong content reachable. */
  .editor-panel {
    grid-area: panel;
    position: static;
    transform: none;
    transition: none;
    background: var(--surface);
    border-left: none;
    border-top: 1px solid var(--border);
    border-radius: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 0;
    max-height: none;
  }
  /* Drag handle is no longer needed — panel doesn't dismiss. */
  .editor-panel::before {
    display: none;
  }

  /* The floating trigger button is obsolete — the panel is always visible.
     The DOM node may still be injected by bottomSheet.js for backwards
     compatibility; we hide it unconditionally on mobile. */
  .editor-panel-trigger {
    display: none !important;
  }

  /* Tab strip is still useful — it switches between the 5 sections that
     would otherwise stack vertically and require scrolling. Horizontal-
     scroll if the labels overflow on a narrow phone. */
  .editor-panel-tabs {
    display: flex;
    overflow-x: auto;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }
  .editor-panel-tab {
    flex: 1 0 auto;
    padding: var(--space-md);
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: var(--t-sm);
    cursor: pointer;
    /* Reset our global :where(button) padding so the bottom-border alignment
       lines up with the tab row baseline. */
    border-radius: 0;
  }
  .editor-panel-tab:hover { background: transparent; color: var(--text); }
  .editor-panel-tab.is-active {
    color: var(--text);
    border-bottom-color: var(--accent);
    /* Secondary cue beyond hue so the active state survives Dan's
       colorblindness — weight bump in addition to the border. */
    font-weight: 600;
  }

  /* Scroll container for the active tab's section body. */
  .editor-panel-content {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--space-md);
  }

  /* Hide the desktop <details> chrome inside the panel; we promote the
     active section's body via a class swap from JS. The active details
     element flexes to fill remaining space and scrolls internally so any
     long content (sliders, color pickers, export options) stays reachable
     without scrolling the page. */
  .editor-panel details {
    border: none;
    padding: 0;
  }
  .editor-panel details summary { display: none; }
  .editor-panel details:not(.is-active-tab) { display: none; }
  .editor-panel details.is-active-tab {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--space-md);
    -webkit-overflow-scrolling: touch;
  }

  /* Top tool strip: horizontal scroll instead of wrapping. Reads better
     than wrapped chips on narrow viewports. */
  .editor-toolbar {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }
}

/* --- Touch device: enlarge hit targets regardless of viewport --------------
   `pointer: coarse` is the only reliable indicator we have for fingertips on
   the screen — laptop trackpads and mice register as `fine`. Big tablets
   running this site land here too, which is exactly what we want: 44 CSS
   pixels is the minimum recommended touch target. We use min-width / min-height
   rather than fixed sizing so existing layouts stay intact where they're
   already larger. */
@media (pointer: coarse) {
  .editor-toolbar button { min-width: 44px; min-height: 44px; }
  .editor-panel button,
  .editor-panel select,
  .editor-panel input[type="range"] {
    min-height: 44px;
  }
  .editor-panel-tab { min-height: 44px; }
  .editor-panel-trigger { min-height: 44px; }
  /* Slightly larger queue × button (desktop default is 24). And: on touch
     there is no hover, so it must be visible at all times instead of
     fading in on hover. */
  .queue-thumb-remove {
    width: 36px;
    height: 36px;
    opacity: 1;
  }
}

/* --- Batch progress dialog (Phase 10) --- */
dialog.batch-progress-dialog {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-xl);
  max-width: 480px;
  width: min(90vw, 480px);
  font-family: inherit;
}
dialog.batch-progress-dialog::backdrop {
  background: rgb(0 0 0 / 0.45);
}
.batch-progress-title {
  margin: 0 0 var(--space-md);
  font-size: var(--t-lg);
  font-weight: 600;
}
.batch-progress-bar {
  width: 100%;
  height: 10px;
  margin-bottom: var(--space-sm);
}
.batch-progress-status {
  margin: 0 0 var(--space-md);
  font-size: var(--t-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.batch-progress-list {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-md);
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
}
.batch-progress-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-sm);
  align-items: center;
  padding: var(--space-xs) var(--space-sm);
  border-bottom: 1px solid var(--border);
  font-size: var(--t-sm);
}
.batch-progress-row:last-child { border-bottom: none; }
.batch-progress-row.is-encoding { background: var(--surface-2); }
.batch-progress-row.is-done .batch-progress-row-status { color: var(--accent); }
.batch-progress-row.is-failed { color: var(--danger); }
.batch-progress-row.is-failed .batch-progress-row-status { color: var(--danger); }
.batch-progress-row-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.batch-progress-row-status {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.batch-progress-actions {
  display: flex;
  justify-content: flex-end;
}
.batch-progress-cancel {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.batch-progress-cancel:hover {
  border-color: var(--border-strong);
}

/* --- Batch confirm dialog --- */
dialog.batch-confirm-dialog {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-xl);
  max-width: 420px;
  font-family: inherit;
}
dialog.batch-confirm-dialog::backdrop { background: rgb(0 0 0 / 0.45); }
dialog.batch-confirm-dialog h2 {
  margin: 0 0 var(--space-md);
  font-size: var(--t-lg);
  font-weight: 600;
}
dialog.batch-confirm-dialog p {
  margin: 0 0 var(--space-lg);
  color: var(--text-secondary);
}
.batch-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}
.batch-confirm-actions button {
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.batch-confirm-actions button.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

/* --- Oversize dialog (importer.js) --- */
dialog.oversize-dialog {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-xl);
  max-width: 420px;
  font-family: inherit;
}

dialog.oversize-dialog::backdrop {
  background: rgb(0 0 0 / 0.45);
}

dialog.oversize-dialog h2 {
  margin: 0 0 var(--space-md);
  font-size: var(--t-lg);
  font-weight: 600;
}

dialog.oversize-dialog p {
  margin: 0 0 var(--space-lg);
  color: var(--text-secondary);
}

dialog.oversize-dialog code {
  background: var(--surface-2);
  padding: 0 var(--space-xs);
  border-radius: 2px;
  font-size: 0.95em;
}

.oversize-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}

/* --- Canvas progress overlay (long-running canvas-bound ops) ---
   A centred translucent card overlaid on the editor canvas. Used for
   bg-remove today; conceptually reusable for any heavy op that wants a
   prominent in-canvas indicator instead of relying on the side panel. */
.canvas-progress-overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  /* Sits above both canvases. zoom-controls live in the same frame but
     anchor to the bottom — they don't overlap with the centered card. */
  z-index: 20;
  pointer-events: auto;
  /* Soft scrim so the underlying canvas content is dimmed but still
     glimpsable behind the card. */
  background: light-dark(rgb(255 255 255 / 0.45), rgb(0 0 0 / 0.45));
}
.canvas-progress-overlay[hidden] { display: none; }
.canvas-progress-card {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-xl);
  width: min(88vw, 360px);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  box-shadow: 0 8px 32px rgb(0 0 0 / 0.18);
}
.canvas-progress-title {
  margin: 0;
  font-size: var(--t-md);
  font-weight: 600;
  color: var(--text);
}
.canvas-progress-bar {
  --progress: 0%;
  position: relative;
  height: 8px;
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.canvas-progress-bar::before {
  content: '';
  display: block;
  height: 100%;
  width: var(--progress);
  background: var(--accent);
  border-radius: inherit;
  transition: width var(--dur-med) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .canvas-progress-bar::before { transition: none; }
}
.canvas-progress-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  font-size: var(--t-sm);
  color: var(--text-secondary);
}
.canvas-progress-stage {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.canvas-progress-percent {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  min-width: 4ch;
  text-align: right;
}

/* --- Background-removal consent dialog (Phase 11) --- */
dialog.bgremove-consent-dialog {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-xl);
  max-width: 480px;
  width: min(92vw, 480px);
  font-family: inherit;
}
dialog.bgremove-consent-dialog::backdrop {
  background: rgb(0 0 0 / 0.45);
}
dialog.bgremove-consent-dialog h2 {
  margin: 0 0 var(--space-md);
  font-size: var(--t-lg);
  font-weight: 600;
}
dialog.bgremove-consent-dialog p {
  margin: 0 0 var(--space-md);
  color: var(--text-secondary);
  font-size: var(--t-sm);
  line-height: 1.5;
}
dialog.bgremove-consent-dialog p.bgremove-consent-license {
  font-size: var(--t-xs);
  color: var(--text-muted);
  margin-bottom: var(--space-lg);
}
.bgremove-consent-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}
.bgremove-consent-actions button {
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.bgremove-consent-actions button.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

/* --- Face-detect / text-detect / HEIC consent dialogs ----
   Share the visual language with bg-remove's consent — same disclosure
   shape (download size + provenance + ML-runs-locally) deserves the same
   chrome. Selectors are explicit (no nesting/groups) so a future tweak to
   one doesn't accidentally touch the others. */
dialog.face-consent-dialog,
dialog.text-consent-dialog,
dialog.heic-consent-dialog {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-xl);
  max-width: 480px;
  width: min(92vw, 480px);
  font-family: inherit;
}
dialog.face-consent-dialog::backdrop,
dialog.text-consent-dialog::backdrop,
dialog.heic-consent-dialog::backdrop {
  background: rgb(0 0 0 / 0.45);
}
dialog.face-consent-dialog h2,
dialog.text-consent-dialog h2,
dialog.heic-consent-dialog h2 {
  margin: 0 0 var(--space-md);
  font-size: var(--t-lg);
  font-weight: 600;
}
dialog.face-consent-dialog p,
dialog.text-consent-dialog p,
dialog.heic-consent-dialog p {
  margin: 0 0 var(--space-md);
  color: var(--text-secondary);
  font-size: var(--t-sm);
  line-height: 1.5;
}
.face-consent-actions,
.text-consent-actions,
.heic-consent-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
}
.face-consent-actions button,
.text-consent-actions button,
.heic-consent-actions button {
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.face-consent-actions button.btn-primary,
.text-consent-actions button.btn-primary,
.heic-consent-actions button.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

/* "Auto-detect faces" / "Detect text" buttons inside the redact tool
   panel. Quieter than the primary Apply button — they're side-actions,
   not the main thing you're here to do. */
.editor-panel .redact-detect-faces,
.editor-panel .redact-detect-text {
  display: block;
  margin-top: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
  width: 100%;
}
.editor-panel .redact-detect-faces:hover:not(:disabled),
.editor-panel .redact-detect-text:hover:not(:disabled) {
  background: var(--surface-2);
}
.editor-panel .redact-detect-faces:disabled,
.editor-panel .redact-detect-text:disabled {
  opacity: 0.6;
  cursor: progress;
}

/* --- Background-removal side panel --- */
.bg-remove-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}
.bg-remove-help {
  margin: 0;
  font-size: var(--t-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}
.bg-remove-status {
  margin: 0;
  font-size: var(--t-sm);
  color: var(--text-muted);
}
.bg-remove-apply {
  align-self: flex-start;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.bg-remove-apply.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}
.bg-remove-apply:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.bg-remove-progress {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.bg-remove-undo-hint {
  margin: var(--space-sm) 0 0;
  font-size: var(--t-xs);
  color: var(--text-muted);
}

/* --- Language picker popover ------------------------------------------- */
.language-popover {
  position: fixed;
  z-index: 1100;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-xs);
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 220px;
  max-height: 70vh;
  overflow-y: auto;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
}
.language-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: transparent;
  border: none;
  color: var(--text);
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: var(--t-sm);
  border-radius: var(--radius-control);
  cursor: pointer;
}
.language-row:hover { background: var(--surface-2); }
.language-row.is-active { background: var(--surface-2); font-weight: 600; }
.language-row img {
  width: 20px;
  height: 14px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  object-fit: cover;
}
/* RTL: when <html dir="rtl">, the popover should anchor under the button's
   bottom-LEFT corner instead. We override the JS-set inline `right` style
   via a class on the popover when set, or simply mirror with logical
   properties here. The JS sets right/top inline; for RTL we keep right
   inline (Arabic UIs still expect the menu under the trigger, just mirrored
   when needed). */
[dir="rtl"] .language-row {
  text-align: right;
}

/* --- Settings popover (Phase 12B) -------------------------------------- */
.settings-popover {
  position: fixed;
  z-index: 1100;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  min-width: 320px;
  max-width: 380px;
  max-height: 80vh;
  overflow-y: auto;
  font-size: var(--t-sm);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
}
.settings-row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-md);
  align-items: center;
}
.settings-row label {
  color: var(--text-secondary);
}
.settings-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  /* Anchor the checkbox at the row's right edge so the eye can read down
     the column even with a long label that wraps. */
  justify-self: end;
}
.settings-row select,
.settings-row input[type="range"] {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  padding: var(--space-xs) var(--space-sm);
  min-width: 0;
}
.settings-row input[type="range"] {
  min-width: 140px;
  padding: 0;
}
.settings-divider {
  height: 1px;
  background: var(--border);
  margin: var(--space-xs) 0;
}
.settings-revert-btn {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  align-self: flex-end;
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.settings-revert-btn:hover {
  color: var(--text);
  background: var(--surface-2);
}


/* --- v1.2 Feature 7: find duplicates ----------------------------------
   Batch-panel toolbar row + queue-thumb find-mode overlays + post-remove
   undo toast. The toolbar lives in queueView buildBatchPanel; the click
   logic that drives it lives in js/dedupe.js. */

.batch-dedupe-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  align-items: center;
  margin-bottom: var(--space-md);
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--border);
}
.batch-dedupe-find,
.batch-dedupe-cancel {
  padding: var(--space-xs) var(--space-sm);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.batch-dedupe-find:hover:not(:disabled),
.batch-dedupe-cancel:hover {
  background: var(--surface-2);
}
.batch-dedupe-find:disabled {
  opacity: 0.6;
  cursor: progress;
}
.batch-dedupe-sensitivity {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.batch-dedupe-remove {
  /* btn-primary already supplies background + color; this just ensures
     padding parity with the sibling tertiary buttons in the row. */
  padding: var(--space-xs) var(--space-sm);
  font-size: var(--t-sm);
}
.batch-dedupe-none-pill {
  margin-bottom: var(--space-md);
  padding: var(--space-xs) var(--space-sm);
  background: color-mix(in srgb, var(--success, #1f9d55) 18%, transparent);
  color: var(--text);
  border-radius: var(--radius-pill);
  font-size: var(--t-sm);
  /* Shape-based affordance for colorblind: include a checkmark glyph. */
}
.batch-dedupe-none-pill::before {
  content: '✓ ';
  font-weight: 700;
}

/* Find-mode thumbnail overlay. The dark rgba(0,0,0,0.55) layer is drawn
   via ::before so the underlying <img> stays sharp; the "Duplicate" badge
   is a separate child (queue-thumb-dedupe-badge) for screen-reader
   announcement. Shape-based affordance: badge + greyed-out look, not just
   color shift. */
.queue-thumb.is-dedupe-marked::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  border-radius: inherit;
  pointer-events: none;
  z-index: 1;
}
.queue-thumb-dedupe-badge {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(0, 0, 0, 0.78);
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  pointer-events: none;
  z-index: 2;
  white-space: nowrap;
}
/* In find-mode, the cursor hint is "click to toggle mark," not "open". */
.queue-thumb.is-dedupe-mode {
  cursor: pointer;
}

/* Post-remove undo toast. Floats bottom-center; matches the visual weight
   of the existing showToast() output but with an inline action button. */
.dedupe-undo-toast {
  position: fixed;
  bottom: calc(var(--space-xl) + env(safe-area-inset-bottom, 0));
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: var(--space-md);
  align-items: center;
  padding: var(--space-sm) var(--space-md);
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  font-size: var(--t-sm);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  z-index: 50;
}
.dedupe-undo-link {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  color: var(--accent);
  font: inherit;
  font-size: var(--t-sm);
  padding: 2px 10px;
  cursor: pointer;
}
.dedupe-undo-link:hover {
  background: var(--surface);
}

/* --- v1.2 batch Redact section in the queue panel -----------------------
   Mirrors the editor's redact-tool chip styles so the controls feel like
   the same UI. The Mode and Sensitivity rows are 3-up button grids;
   strength/color are inline rows. */
.batch-redact-mode-group,
.batch-redact-sensitivity-group {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-xs);
}
.batch-redact-mode-group button,
.batch-redact-sensitivity-group button {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.batch-redact-mode-group button.is-active,
.batch-redact-sensitivity-group button.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
  font-weight: 600;
}
.batch-redact-strength-readout {
  margin-left: var(--space-sm);
  font-variant-numeric: tabular-nums;
  font-size: var(--t-sm);
  color: var(--text-secondary);
}
.batch-redact-faces,
.batch-redact-text {
  margin-top: var(--space-sm);
}

/* OCR preview-select section inside the redact tool's side panel.
   Shown only when state.ui.ocrPreview.active is true. The buttons
   replace the normal Apply flow for the duration of the preview. */
.editor-panel .redact-ocr-preview {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-top: var(--space-sm);
  padding: var(--space-sm);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
  border-radius: var(--radius-control);
}
.editor-panel .redact-ocr-preview-status {
  margin: 0 0 var(--space-xs);
  font-size: var(--t-sm);
  color: var(--text-secondary);
}
.editor-panel .redact-ocr-apply-selected,
.editor-panel .redact-ocr-apply-all,
.editor-panel .redact-ocr-cancel {
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.editor-panel .redact-ocr-apply-selected:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ---------------------------------------------------------------------------
   Target file size (v1.3 Feature 11)
   --------------------------------------------------------------------------
   Used in two places with identical structure:
     - .editor-panel .editor-target-size-section (inside the export panel,
       a nested <details>)
     - .batch-panel .batch-target-size-section (a top-level batch section)
   Chip "active" state pairs color with a stronger border + inset shadow +
   font-weight bump — Dan is colorblind, so two-channel signaling matters
   (see CLAUDE.md). The same convention is used by .format-chip.is-active.
   --------------------------------------------------------------------------*/
.target-size-section > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: var(--t-sm);
  padding: var(--space-xs) 0;
  user-select: none;
}
.target-size-section .target-size-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding-top: var(--space-sm);
}

.target-size-mode-chips,
.target-size-unit-toggle,
.target-size-format-group {
  display: inline-flex;
  gap: var(--space-xs);
}

.target-size-mode-chip,
.target-size-preset-chip,
.target-size-unit-chip,
.target-size-format-chip {
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-xs);
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.target-size-mode-chip:hover,
.target-size-preset-chip:hover,
.target-size-unit-chip:hover,
.target-size-format-chip:hover {
  background: var(--surface-2);
}
.target-size-mode-chip.is-active,
.target-size-preset-chip.is-active,
.target-size-unit-chip.is-active,
.target-size-format-chip.is-active {
  border-color: var(--accent);
  background: var(--surface-2);
  color: var(--accent);
  /* Inset shadow gives a "pressed in" cue independent of color — colorblind
     users still get a clear active signal. Bold weight further reinforces. */
  box-shadow: inset 0 0 0 1px var(--accent);
  font-weight: 700;
}

/* Preset chips: wrap onto multiple lines because long labels (e.g.
   "Discord 25 MB") would overflow at panel widths under ~280 px. */
.target-size-preset-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}
/* Avoid the .batch-row[hidden] / display:grid gotcha that bit other rows in
   this codebase — explicitly restore display:none for hidden state. */
.target-size-row[hidden],
.target-size-preset-chips[hidden],
.target-size-custom-row[hidden],
.target-size-format-row[hidden] {
  display: none;
}

.target-size-custom-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.target-size-custom-label {
  color: var(--text-secondary);
}
.target-size-custom-input {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  min-width: 0;
  width: 100%;
}
/* Inline validation hint for the custom value field. Visible only when the
   typed value is invalid (zero / negative / blank). Quiet error styling —
   the disabled Apply button is the primary signal; this just explains why. */
.target-size-custom-hint {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-muted);
  font-style: italic;
}
.target-size-custom-hint[hidden] {
  display: none;
}

.target-size-auto-row {
  display: flex;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
  color: var(--text);
  cursor: pointer;
}
.target-size-auto-row > input[type="checkbox"] {
  cursor: pointer;
}

.target-size-format-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.target-size-format-label {
  color: var(--text-secondary);
}

.target-size-apply {
  align-self: stretch;
  margin-top: var(--space-xs);
  background: var(--accent);
  color: var(--accent-contrast);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
  transition: filter var(--dur-fast) var(--ease-out);
}
.target-size-apply:hover {
  filter: brightness(0.95);
}
.target-size-apply:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: var(--surface-2);
  color: var(--text-muted);
  border-color: var(--border);
}

/* ---------------------------------------------------------------------------
   Upload-ready preset (v1.3 Feature 9)
   --------------------------------------------------------------------------
   Used in two places with identical structure:
     - .editor-panel .editor-upload-ready-section (inside the export panel,
       a nested <details>)
     - .batch-panel .batch-upload-ready-section (a top-level batch section)
   Chip "active" state pairs color with stronger border + inset shadow +
   bolder weight — matches the .target-size-* convention for colorblind
   safety (see CLAUDE.md).
   --------------------------------------------------------------------------*/
.upload-ready-section > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: var(--t-sm);
  padding: var(--space-xs) 0;
  user-select: none;
}
.upload-ready-section .upload-ready-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding-top: var(--space-sm);
}

.upload-ready-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
}
.upload-ready-row[hidden] {
  display: none;
}
.upload-ready-label {
  color: var(--text-secondary);
}

.upload-ready-longedge,
.upload-ready-filename {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  min-width: 0;
  width: 100%;
  grid-column: 2 / 4;
}

.upload-ready-quality {
  grid-column: 2 / 3;
  width: 100%;
}
.upload-ready-quality-readout {
  grid-column: 3 / 4;
  min-width: 2.5em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
  font-size: var(--t-xs);
}

.upload-ready-format-group {
  display: inline-flex;
  gap: var(--space-xs);
  grid-column: 2 / 4;
}
.upload-ready-format-chip {
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-xs);
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.upload-ready-format-chip:hover {
  background: var(--surface-2);
}
.upload-ready-format-chip.is-active {
  border-color: var(--accent);
  background: var(--surface-2);
  color: var(--accent);
  /* Inset shadow + bold weight = colorblind-safe "active" signal. */
  box-shadow: inset 0 0 0 1px var(--accent);
  font-weight: 700;
}

.upload-ready-strip-row {
  display: flex;
  gap: var(--space-sm);
  align-items: center;
  font-size: var(--t-sm);
  color: var(--text);
  cursor: pointer;
}
.upload-ready-strip-row > input[type="checkbox"] {
  cursor: pointer;
}

.upload-ready-filename-help {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-muted);
  font-style: italic;
}

.upload-ready-apply {
  align-self: stretch;
  margin-top: var(--space-xs);
  background: var(--accent);
  color: var(--accent-contrast);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  cursor: pointer;
  transition: filter var(--dur-fast) var(--ease-out);
}
.upload-ready-apply:hover {
  filter: brightness(0.95);
}
.upload-ready-apply:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: var(--surface-2);
  color: var(--text-muted);
  border-color: var(--border);
}

/* --- Transparent PNG tool panel (v1.3 Feature 16) ---------------------- */
.transparent-png-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.transparent-png-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-sm) 0 0;
  border-top: 1px solid var(--border);
}
.transparent-png-panel > .transparent-png-section:first-of-type {
  border-top: none;
  padding-top: 0;
}
.transparent-png-subheading {
  margin: 0;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--text);
}
.transparent-png-help {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-secondary);
  line-height: 1.5;
}
.transparent-png-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--t-sm);
}
.transparent-png-row[hidden] { display: none; }
.transparent-png-row > span:first-child {
  min-width: 5.5rem;
  color: var(--text-secondary);
}
.transparent-png-pad-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-xs) var(--space-sm);
}
.transparent-png-pad-input {
  width: 100%;
  min-width: 0;
  padding: var(--space-xxs) var(--space-xs);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
}
.transparent-png-all-sides {
  align-self: flex-start;
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font: inherit;
  font-size: var(--t-xs);
  text-decoration: underline;
  cursor: pointer;
}
.transparent-png-all-sides:hover { color: var(--text); }
.transparent-png-transparent-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xxs);
  font-size: var(--t-xs);
  color: var(--text-secondary);
}
.transparent-png-pad-color,
.transparent-png-replace-color {
  width: 2.5rem;
  height: 1.75rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  cursor: pointer;
}
.transparent-png-pad-color:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.transparent-png-replace-threshold { flex: 1; min-width: 0; }
.transparent-png-threshold-readout {
  min-width: 2.5rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  font-size: var(--t-xs);
}
.transparent-png-pad-apply,
.transparent-png-replace-apply {
  align-self: flex-start;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-control);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  cursor: pointer;
}
.transparent-png-pad-apply.btn-primary,
.transparent-png-replace-apply.btn-primary {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}
.transparent-png-pad-apply:disabled,
.transparent-png-replace-apply:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.transparent-png-checker-row {
  font-size: var(--t-sm);
  color: var(--text);
}

/* --- Watermark tool panel (v1.3 Feature 12) ----------------------------
   Active-state convention: position chips and type chips use bg + border +
   font-weight changes (NOT color alone) so the active selection reads
   clearly for colorblind users. Matches the .target-size-*-chip pattern.
   The cursor hooks below (.watermark-hover / .watermark-dragging) are
   toggled by the tool module on the overlay canvas, so the user gets a
   `move` cursor when hovering over the rendered watermark and a `grabbing`
   cursor mid-drag. */
.watermark-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}
.watermark-tip {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-secondary);
  line-height: 1.5;
}
.watermark-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--t-sm);
}
.watermark-row[hidden] { display: none; }
.watermark-row > span:first-child {
  min-width: 5.5rem;
  color: var(--text-secondary);
}
.watermark-enable-row {
  font-size: var(--t-sm);
  color: var(--text);
}
.watermark-sub {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  transition: opacity 120ms ease;
}
.watermark-sub.is-disabled {
  opacity: 0.45;
  pointer-events: none;
}
.watermark-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-sm) 0 0;
  border-top: 1px solid var(--border);
}
.watermark-section[hidden] { display: none; }
.watermark-type-group {
  display: flex;
  gap: var(--space-xs);
}
.watermark-type-chip {
  flex: 1;
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 500;
  cursor: pointer;
}
.watermark-type-chip:hover { background: var(--surface-2); }
.watermark-type-chip.is-active {
  background: var(--surface-2);
  border-color: var(--accent);
  border-width: 2px;
  font-weight: 700;
  padding: calc(var(--space-xs) - 1px) calc(var(--space-sm) - 1px);
}
.watermark-text-input,
.watermark-file-input {
  flex: 1;
  min-width: 0;
  padding: var(--space-xxs) var(--space-xs);
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: var(--t-sm);
}
.watermark-text-color {
  width: 2.5rem;
  height: 1.75rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  cursor: pointer;
}
.watermark-file-row { align-items: stretch; }
.watermark-logo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 4rem;
  padding: var(--space-sm);
  border: 1px dashed var(--border);
  border-radius: var(--radius-control);
  background:
    repeating-conic-gradient(var(--surface) 0% 25%, var(--surface-2) 0% 50%)
    50% / 16px 16px;
  color: var(--text-muted);
  font-size: var(--t-xs);
}
.watermark-logo-img {
  max-width: 100%;
  max-height: 5rem;
  object-fit: contain;
}
.watermark-logo-img[hidden] { display: none; }
.watermark-logo-empty[hidden] { display: none; }
.watermark-logo-clear {
  align-self: flex-start;
  background: none;
  border: 1px solid var(--border);
  padding: var(--space-xxs) var(--space-xs);
  border-radius: var(--radius-control);
  color: var(--text);
  font: inherit;
  font-size: var(--t-xs);
  cursor: pointer;
}
.watermark-logo-clear:hover { background: var(--surface-2); }
.watermark-position-label {
  font-weight: 600;
  color: var(--text);
}
.watermark-position-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-xs);
}
.watermark-position-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1.6;
  min-height: 2rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--text-muted);
  font: inherit;
  font-size: var(--t-md);
  font-weight: 500;
  cursor: pointer;
}
.watermark-position-chip:hover { background: var(--surface-2); }
.watermark-position-chip.is-active {
  background: var(--surface-2);
  border-color: var(--accent);
  border-width: 2px;
  font-weight: 700;
  color: var(--text);
}
.watermark-tiled-row {
  margin-top: var(--space-xs);
}
.watermark-position-tiled {
  width: 100%;
  aspect-ratio: auto;
  padding: var(--space-xs) var(--space-sm);
  font-size: var(--t-sm);
}
.watermark-drag-hint {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--text-secondary);
  line-height: 1.4;
}
.watermark-slider-row > input[type="range"] {
  flex: 1;
  min-width: 0;
}
.watermark-readout {
  min-width: 2.75rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  font-size: var(--t-xs);
}

/* Drag affordance on the overlay canvas. Hover sets `move`; mid-drag the
   tool adds .watermark-dragging which overrides to `grabbing`. Specificity
   matters because the canvas may have other cursor classes applied. */
#overlay-canvas.watermark-hover { cursor: move; }
#overlay-canvas.watermark-dragging { cursor: grabbing; }
