/* ============================================================
   AuraGen: Futuristic Minimalist / Obsidian
   Tokens taken from "My idea of the UI" (DESIGN.md).
   ============================================================ */

:root {
  --background:              #131313;
  --surface:                 #131313;
  --surface-dim:             #131313;
  --surface-bright:          #3a3939;
  --surface-container-lowest:#0e0e0e;
  --surface-container-low:   #1c1b1b;
  --surface-container:       #201f1f;
  --surface-container-high:  #2a2a2a;
  --surface-container-highest:#353534;

  --on-surface:              #e5e2e1;
  --on-surface-variant:      #cbc3d7;
  --outline:                 #958ea0;
  --outline-variant:         #494454;

  --primary:                 #d0bcff;
  --on-primary:              #3c0091;
  --primary-container:       #a078ff;
  --secondary:               #4cd7f6;
  --tertiary:                #ffb869;
  --error:                   #ffb4ab;

  --hairline:                rgba(255, 255, 255, 0.10);
  --hairline-soft:           rgba(255, 255, 255, 0.05);
  --hairline-strong:         rgba(255, 255, 255, 0.20);

  --sidebar-w: 320px;
  --nav-h: 64px;

  /* Shapes: soft-technical */
  --r-sm:   0.125rem;
  --r:      0.25rem;
  --r-md:   0.375rem;
  --r-lg:   0.5rem;
  --r-xl:   0.75rem;
  --r-full: 9999px;

  /* Google Sans family, all three OFL-licensed and self-hosted.
     Google Sans Code keeps the slashed zero that made us drop JetBrains Mono,
     whose dotted zero was unreadable against 8 at 10-11px. */
  --sans:    'Google Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --display: 'Google Sans Flex', 'Google Sans', -apple-system, sans-serif;
  --mono:    'Google Sans Code', ui-monospace, SFMono-Regular, Menlo, monospace;
}

* { box-sizing: border-box; }

html, body { height: 100%; margin: 0; }

body {
  background: var(--background);
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 24px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

.material-symbols-outlined {
  font-family: 'Material Symbols Outlined';
  font-weight: normal;
  font-style: normal;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  direction: ltr;
  font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;
  -webkit-font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  user-select: none;
}

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--surface-container-high); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--outline-variant); }

.hidden { display: none !important; }

/* ============================================================
   TOP NAV
   ============================================================ */

.topnav {
  flex: 0 0 auto;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 24px;
  border-bottom: 1px solid var(--hairline);
  background: rgba(19, 19, 19, 0.8);
  backdrop-filter: blur(20px);
  z-index: 50;
}

.brand {
  font-family: var(--display);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--on-surface);
}

.topnav-spacer { flex: 1; }

/* Start a new project, promoted out of the site menu. */
.new-project-top {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 12px;
  /* Matches the 34px icon buttons it shares the row with. Padding alone left
     it at 28px, which reads as a misaligned control rather than a smaller one. */
  min-height: 34px;
  padding: 0 12px 0 9px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-full);
  background: var(--surface-container-low);
  color: var(--on-surface-variant);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: color .16s ease, border-color .16s ease, background .16s ease;
}

.new-project-top:hover:not(:disabled) {
  color: var(--on-surface);
  border-color: var(--outline-variant);
  background: var(--surface-container);
}

/* Nothing to start away from when the canvas is already a fresh project. */
.new-project-top:disabled { opacity: .35; cursor: default; }
.new-project-top .material-symbols-outlined { font-size: 17px; }

/* The label is the first thing to go when the bar gets tight. */
@media (max-width: 900px) {
  .new-project-top-label { display: none; }
  .new-project-top { padding: 0 9px; margin-left: 8px; }
}

/* ---- Mode switch ---- */

/* ---- Hamburger + current-mode badge ---- */

.menu-btn { margin-right: 2px; margin-left: -6px; }

/* Mode switch, promoted from the drawer into the top bar. Two destinations
   never justified a drawer, and this slot previously held a badge that
   looked exactly like this control but did nothing when clicked. */





/* Icons alone once the bar gets tight. */

/* ---- Slide-out site menu ---- */

.menu-scrim {
  position: fixed;
  inset: 0;
  z-index: 58;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(3px);
  animation: scrim-in .22s ease;
}

@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }

/* The engine sidebar is a drawer below 1280px and gets the same treatment.
   Both right-hand drawers sit at z-index 70, so their scrims share 69; the
   two are never open at once, which the JS enforces. */
.sidebar-scrim {
  position: fixed;
  inset: 0;
  z-index: 69;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(3px);
  animation: scrim-in .22s ease;
}

/* Drawer chrome, invisible while the sidebar is a docked column. */
.sidebar-drawer-head {
  display: none;
  flex: 0 0 auto;
  align-items: center;
  justify-content: space-between;
  height: var(--nav-h);
  padding: 0 12px 0 20px;
  border-bottom: 1px solid var(--hairline-soft);
}

.sidebar-drawer-title { font-weight: 600; font-size: 14px; }

/* The trigger only exists while the sidebar is a drawer. Qualified with
   .icon-btn so it outranks that rule's own `display`, which is declared
   later in this file and would otherwise win on source order. */
.icon-btn.sidebar-toggle { display: none; }
.tune-icon { width: 21px; height: 21px; display: block; }

/* Same treatment for the project-history drawer, which had none. Sits just
   under the panel (z-index 70) and above everything else. */
.thread-scrim {
  position: fixed;
  inset: 0;
  z-index: 69;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(3px);
  animation: scrim-in .22s ease;
}

.site-menu {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 320px;
  max-width: 86vw;
  z-index: 59;
  background: var(--surface-container-lowest);
  border-right: 1px solid var(--hairline);
  box-shadow: 24px 0 60px rgba(0, 0, 0, 0.55);
  display: flex;
  flex-direction: column;
  animation: menu-in .24s cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes menu-in { from { transform: translateX(-100%); } to { transform: translateX(0); } }

@media (prefers-reduced-motion: reduce) {
  .site-menu, .menu-scrim { animation: none; }
}

.site-menu-head {
  flex: 0 0 auto;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 12px 0 24px;
  border-bottom: 1px solid var(--hairline-soft);
}

.site-menu-title {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
}







/* Vertical echo of the underline indicator. */


/* ---- History ---- */

/* Holds the relocated footer on phones. Empty on desktop, where the footer
   stays in the canvas, so it must not draw a stray border there. */
.menu-foot { flex: 0 0 auto; }
.menu-foot:empty { display: none; }

.menu-history {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--hairline-soft);
  margin-top: 8px;
  padding-top: 12px;
}

.menu-history-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px 10px;
}

.history-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  list-style: none;
  margin: 0;
  padding: 0 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.new-project-btn {
  appearance: none;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--r-md);
  background: none;
  color: var(--on-surface-variant);
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  padding: 8px;
  cursor: pointer;
  transition: color .16s ease, border-color .16s ease, background .16s ease;
}

.new-project-btn:hover {
  color: var(--primary);
  border-color: rgba(208, 188, 255, 0.5);
  background: rgba(208, 188, 255, 0.06);
}

.new-project-btn .material-symbols-outlined { font-size: 16px; }

.history-row {
  display: flex;
  align-items: stretch;
  gap: 2px;
  position: relative;
  border-radius: var(--r-md);
}

/* Row actions overlay the right edge instead of sitting in the flow. Four
   buttons in flow took ~136px of a 295px row, truncating the project name
   and its meta line permanently, hover or not. Pinned here they cost the
   title nothing until they are actually wanted.

   The gradient resolves to --surface-container-low, which is also the row's
   hover background, so the fade has no visible seam. */
.history-actions {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 2px;
  padding-left: 30px;
  background: linear-gradient(to right, rgba(28, 27, 27, 0), var(--surface-container-low) 32%);
  opacity: 0;
  pointer-events: none;
  transition: opacity .16s ease;
}

.history-row:hover { background: var(--surface-container-low); }

/* focus-within keeps the actions reachable by keyboard, not just by mouse. */
.history-row:hover .history-actions,
.history-actions:focus-within { opacity: 1; pointer-events: auto; }

.history-del {
  appearance: none;
  flex: 0 0 auto;
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-md);
  color: var(--on-surface-variant);
  padding: 0 6px;
  cursor: pointer;
  opacity: .65;
  transition: opacity .16s ease, color .16s ease, background .16s ease;
}

/* Grab handle. touch-action:none is what stops a touch drag from scrolling
   the menu instead of moving the row. */
.history-handle {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-md);
  color: var(--outline);
  padding: 0 2px;
  cursor: grab;
  opacity: 0;
  touch-action: none;
  transition: opacity .16s ease, color .16s ease;
}

.history-row:hover .history-handle { opacity: .55; }
.history-handle:hover { opacity: 1; color: var(--on-surface); }
.history-handle:focus-visible { opacity: 1; }
.history-handle:active { cursor: grabbing; }
.history-handle svg { width: 16px; height: 16px; display: block; }

.history-entry.is-dragging { opacity: .9; position: relative; z-index: 2; }

.history-entry.is-dragging .history-row {
  background: var(--surface-container-high);
  border-radius: var(--r-lg);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.5);
}

/* While a drag is running, nothing else in the list should react. */
.history-list.is-reordering { user-select: none; }
.history-list.is-reordering .history-item,
.history-list.is-reordering .history-del,
.history-list.is-reordering .history-rename,
.history-list.is-reordering .history-expand { pointer-events: none; }

.reset-order-btn {
  appearance: none;
  border: 0;
  background: none;
  color: var(--secondary);
  font-family: var(--sans);
  font-size: 11px;
  padding: 2px 4px;
  border-radius: var(--r);
  cursor: pointer;
}

.reset-order-btn:hover { color: var(--on-surface); background: rgba(255,255,255,.06); }

.history-export {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-md);
  color: var(--on-surface-variant);
  padding: 0 4px;
  cursor: pointer;
  opacity: .65;
  transition: opacity .16s ease, color .16s ease, background .16s ease;
}

.history-export { opacity: .65; }
.history-export:hover { opacity: 1; color: var(--primary); background: rgba(255,255,255,.06); }
.history-export .material-symbols-outlined { font-size: 17px; }

.history-rename {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-md);
  color: var(--on-surface-variant);
  padding: 0 5px;
  cursor: pointer;
  opacity: .65;
  transition: opacity .16s ease, color .16s ease, background .16s ease;
}

.history-rename { opacity: .65; }
.history-rename:hover { opacity: 1; color: var(--primary); background: rgba(255,255,255,.06); }
.history-rename svg { width: 17px; height: 17px; display: block; }

.history-expand {
  appearance: none;
  flex: 0 0 auto;
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-md);
  color: var(--on-surface-variant);
  padding: 0 4px;
  cursor: pointer;
  opacity: .65;
  transition: opacity .16s ease, color .16s ease, background .16s ease;
}

.history-expand { opacity: .65; }
.history-expand[aria-expanded="true"] { opacity: 1; color: var(--primary); }
.history-expand:hover:not(:disabled) { opacity: 1; color: var(--on-surface); background: rgba(255,255,255,.06); }
.history-expand:disabled { opacity: 0 !important; cursor: default; pointer-events: none; }
.history-expand .material-symbols-outlined { font-size: 18px; transition: transform .2s ease; }
.history-expand[aria-expanded="true"] .material-symbols-outlined { transform: rotate(180deg); }

/* Thumbnails of every image inside an expanded session. */
.history-shots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));
  gap: 4px;
  padding: 6px 6px 8px 60px;
}

.history-shot {
  appearance: none;
  position: relative;
  padding: 0;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: var(--surface-container-low);
  overflow: hidden;
  cursor: pointer;
  line-height: 0;
  transition: border-color .16s ease, transform .16s ease;
}

.history-shot:hover { border-color: var(--outline-variant); transform: translateY(-1px); }
.history-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.history-shot[aria-current="true"] {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}

.history-shot-n {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  background: rgba(14, 14, 14, 0.82);
  color: var(--on-surface);
  font-family: var(--mono);
  font-size: 8px;
  line-height: 12px;
  letter-spacing: 0.04em;
}

.history-del { opacity: .65; }
.history-del:hover { opacity: 1; color: var(--error); background: rgba(255, 180, 171, 0.1); }
.history-del:focus-visible { opacity: 1; outline: 2px solid var(--secondary); }
.history-del .material-symbols-outlined { font-size: 16px; }

.history-item {
  appearance: none;
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-md);
  padding: 6px;
  cursor: pointer;
  text-align: left;
  transition: background .16s ease, border-color .16s ease;
}

.history-item:hover { background: none; border-color: transparent; }

.history-item[aria-current="true"] {
  background: rgba(208, 188, 255, 0.10);
  border-color: rgba(208, 188, 255, 0.35);
}

.history-thumb {
  width: 44px;
  height: 44px;
  flex: 0 0 auto;
  border-radius: var(--r-sm);
  object-fit: cover;
  display: block;
  background: var(--surface-container-low);
}

.history-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }

.history-prompt {
  font-size: 12px;
  line-height: 16px;
  color: var(--on-surface);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.history-sub {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--on-surface-variant);
  opacity: .7;
}

.history-empty {
  font-size: 12px;
  line-height: 18px;
  color: var(--on-surface-variant);
  opacity: .6;
  padding: 18px 12px;
  text-align: center;
}




/* ---- Panels ---- */

.panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}


.token-chip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid rgba(76, 215, 246, 0.3);
  border-radius: var(--r-full);
  background: rgba(76, 215, 246, 0.08);
  color: var(--secondary);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  padding: 4px 12px;
  margin-right: 4px;
  cursor: help;
  transition: background .18s ease;
}

.token-chip:hover { background: rgba(76, 215, 246, 0.16); }
.token-chip .material-symbols-outlined { font-size: 14px; }

.token-meta {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--secondary);
  border: 1px solid rgba(76, 215, 246, 0.3);
  border-radius: var(--r-full);
  padding: 2px 10px;
  cursor: help;
}

.icon-btn {
  appearance: none;
  background: none;
  border: 0;
  padding: 6px;
  border-radius: var(--r-lg);
  color: var(--on-surface-variant);
  cursor: pointer;
  display: inline-flex;
  transition: color .2s ease, background .2s ease;
}

.icon-btn:hover { color: var(--primary); background: rgba(255, 255, 255, 0.05); }
.icon-btn .material-symbols-outlined { font-size: 22px; }

/* Project history toggle. The filled block is the panel itself, so the icon
   reads as "panel showing" or "panel hidden" rather than just "images". */
.panel-icon { width: 22px; height: 22px; display: block; }
.panel-icon-fill { transition: opacity .18s ease; }
.panel-toggle[aria-pressed="false"] .panel-icon-fill { opacity: .28; }

/* ============================================================
   SHELL
   ============================================================ */

.shell { flex: 1; display: flex; min-height: 0; }

.canvas {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface-dim);
  overflow: hidden;
}

/* ---- Output stage ---- */

.stage {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

/* min() rather than a flat 420px: the resume button's label is as wide as the
   project title, and as a flex item .empty was taking that max-content width
   whole, hanging 10px off the left of a 360px stage. Capping it at the stage's
   own content box lets .resume-label ellipsise the way it was written to. */
.empty {
  text-align: center;
  color: var(--on-surface-variant);
  max-width: min(420px, 100%);
  min-width: 0;
}

.empty-icon {
  font-size: 40px !important;
  color: var(--outline-variant);
  margin-bottom: 12px;
  display: block;
}

.empty p {
  margin: 0 0 6px;
  color: var(--on-surface);
  font-weight: 600;
  font-size: 16px;
}

.empty-sub { font-size: 13px; line-height: 20px; opacity: .75; }

/* Result */

.result { display: flex; flex-direction: column; align-items: center; gap: 14px; max-width: 100%; }

.result-frame {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--hairline);
  line-height: 0;
  max-width: 100%;
}

.result-frame img { max-width: 100%; max-height: 62vh; display: block; }

/* A vertical rail of icon actions over the image. Always visible: it
   carries the primary action now, and a hover-gated primary action is a
   hidden one. Wraps into a second column rather than overflowing, for a
   very wide, very short frame on a narrow screen. */
.result-overlay {
  position: absolute;
  top: 8px;
  right: 8px;
  max-height: calc(100% - 16px);
  display: flex;
  flex-direction: column;
  flex-wrap: wrap-reverse;
  align-content: flex-end;
  gap: 6px;
}

.overlay-btn {
  appearance: none;
  border: 1px solid var(--hairline);
  background: rgba(14, 14, 14, 0.75);
  backdrop-filter: blur(12px);
  color: var(--on-surface);
  border-radius: var(--r-md);
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  text-decoration: none;
  transition: background .18s ease, color .18s ease;
}

.overlay-btn:hover { background: var(--surface-container-high); color: var(--primary); }

.overlay-btn[disabled] { opacity: .4; cursor: not-allowed; pointer-events: none; }

/* The one action most people want next, tinted to say so. */
.overlay-btn.is-primary {
  border-color: rgba(208, 188, 255, 0.5);
  background: rgba(208, 188, 255, 0.16);
  color: var(--primary);
}

.overlay-btn.is-primary:hover { background: rgba(208, 188, 255, 0.28); }

/* Set apart from the ordinary actions above it. */
.overlay-btn.is-danger { margin-top: 6px; }

/* A destructive control should not light up in the accent colour every
   other button uses for "yes, do this". */
.overlay-btn.is-danger:hover {
  color: var(--error);
  border-color: rgba(255, 180, 171, 0.45);
}
.overlay-btn .material-symbols-outlined { font-size: 18px; }

.result-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
}

.result-meta b { color: var(--on-surface); font-weight: 500; }
.result-meta .dot { opacity: .4; }

.result-text {
  max-width: 620px;
  text-align: center;
  font-size: 13px;
  line-height: 20px;
  color: var(--on-surface-variant);
  opacity: .8;
}

/* ---- Buttons in the Describe dialog ----
   The result actions used to be a labelled row under the canvas and wore
   these too; they are now the icon rail over the image (.result-overlay). */

.action-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--surface-container-low);
  color: var(--on-surface-variant);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  /* Explicit, because this class is worn by both <button> and <a>. Buttons
     take line-height: normal from the UA stylesheet while the anchor
     inherits body's 24px, which made Download 7px taller than its
     neighbours and knocked the row out of alignment. */
  line-height: 18px;
  padding: 8px 16px;
  cursor: pointer;
  text-decoration: none;
  transition: border-color .18s ease, background .18s ease, color .18s ease;
}

.action-btn:hover { color: var(--on-surface); border-color: var(--outline-variant); background: var(--surface-container); }
.action-btn .material-symbols-outlined { font-size: 17px; }

.action-btn.primary {
  border-color: rgba(208, 188, 255, 0.5);
  background: rgba(208, 188, 255, 0.10);
  color: var(--primary);
}

.action-btn.primary:hover { background: rgba(208, 188, 255, 0.18); color: var(--primary); }

/* ---- Edit banner ---- */

.edit-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  margin-bottom: 8px;
  border: 1px solid rgba(208, 188, 255, 0.4);
  border-radius: var(--r-lg);
  background: rgba(208, 188, 255, 0.07);
}

.edit-banner img {
  width: 38px;
  height: 38px;
  border-radius: var(--r);
  object-fit: cover;
  flex: 0 0 auto;
  border: 1px solid var(--hairline);
}

.edit-banner-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.edit-banner-text b { font-size: 13px; color: var(--primary); }

.edit-banner-text span {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.03em;
  color: var(--on-surface-variant);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.edit-exit {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-full);
  background: none;
  color: var(--on-surface-variant);
  font-family: var(--mono);
  font-size: 11px;
  padding: 5px 12px;
  cursor: pointer;
  transition: color .18s ease, border-color .18s ease, background .18s ease;
}

.edit-exit:hover { color: var(--on-surface); border-color: var(--outline-variant); background: rgba(255,255,255,0.04); }
.edit-exit .material-symbols-outlined { font-size: 14px; }

/* Loading */

.loading { display: flex; flex-direction: column; align-items: center; gap: 14px; color: var(--on-surface-variant); }

.loading-bar {
  width: 200px;
  height: 3px;
  border-radius: var(--r-full);
  background: var(--surface-container-high);
  overflow: hidden;
  position: relative;
}

.loading-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 40%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--secondary), var(--primary));
  animation: sweep 1.15s ease-in-out infinite;
}

@keyframes sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}

.loading span { font-family: var(--mono); font-size: 11px; letter-spacing: 0.05em; }

/* ---- Composer ---- */

.composer-wrap {
  flex: 0 0 auto;
  width: 100%;
  max-width: 896px;
  margin: 0 auto;
  padding: 24px;
}

.composer {
  border: 1px solid var(--hairline);
  border-radius: var(--r-xl);
  background: var(--surface-container-low);
  overflow: hidden;
  transition: border-color .3s ease, box-shadow .3s ease;
}

.composer:focus-within,
.composer.is-dragover {
  border-color: var(--primary);
  box-shadow: 0 0 20px 0 rgba(139, 92, 246, 0.20);
}

.composer textarea {
  display: block;
  width: 100%;
  min-height: 120px;
  max-height: 260px;
  resize: none;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 24px;
  padding: 16px;
}

.composer textarea::placeholder { color: rgba(203, 195, 215, 0.5); }

.composer-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px;
  border-top: 1px solid var(--hairline-soft);
  background: rgba(53, 53, 52, 0.5);
  /* Wrap rather than overlap. The tools row and Generate cannot both fit a
     phone, and without this the row silently overflowed instead: the tools
     box was squeezed to 156px while its buttons needed 220px, and since they
     overflow visibly the last two slid underneath Generate. Describe was then
     unclickable - a tap on it hit Generate. */
  flex-wrap: wrap;
  row-gap: 8px;
}

/* flex-shrink: 0 is the other half of that fix. Squeezing this box never hid
   anything, because the buttons overflow it rather than clipping; it only
   moved them on top of the button next door. */
.composer-tools {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 0 0 auto;
}

.ghost-icon {
  appearance: none;
  background: none;
  border: 0;
  padding: 8px;
  border-radius: var(--r-lg);
  color: var(--on-surface-variant);
  cursor: pointer;
  display: inline-flex;
  transition: color .18s ease, background .18s ease;
}

.ghost-icon:hover { color: var(--on-surface); background: rgba(255, 255, 255, 0.05); }
.ghost-icon .material-symbols-outlined { font-size: 20px; }
.ghost-icon[hidden] { display: none; }

.ghost-icon.is-listening {
  color: var(--secondary);
  background: rgba(76, 215, 246, 0.10);
  animation: pulse 1.6s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(76, 215, 246, 0.30); }
  50%      { box-shadow: 0 0 0 6px rgba(76, 215, 246, 0); }
}

/* A hairline between the tools that act on the prompt in the box and the
   one that fetches a prompt from outside it. */
.tools-divider {
  flex: 0 0 auto;
  width: 1px;
  align-self: stretch;
  margin: 5px 0;
  background: var(--hairline);
}

/* Describe is the odd one out in this row. The others nudge the prompt you
   are already writing; this opens a separate tool that reads a photo and
   writes one for you. As a bare icon among bare icons it read as one more
   small tweak, which is exactly how it got missed.

   What sets it apart is the chip itself, an outline and a fill where its
   neighbours have neither. The colour is the theme accent, the same one
   Generate and the primary action on the canvas wear, rather than a hue of
   its own: it should look like a real button, not like a different app.

   7px of padding against the others' 8px, because the 1px border has to come
   from somewhere: without that it would stand a pixel taller than its
   neighbours and pull the row out of line. */
.ghost-icon.is-standalone {
  padding: 7px;
  border: 1px solid rgba(208, 188, 255, 0.45);
  background: rgba(208, 188, 255, 0.10);
  color: var(--primary);
}

.ghost-icon.is-standalone:hover {
  background: rgba(208, 188, 255, 0.20);
  color: var(--primary);
}

.composer-hint {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
  opacity: .7;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Sits beside Generate, since it is a note about that button. `flex: 1 1 0`
     rather than an auto margin: a wrapping flex row breaks a line on each
     item's own width, so a hint with real width pushes Generate onto a
     second row on a narrow canvas. At a basis of zero it never causes that
     break, and instead soaks up whatever room is left, ending flush against
     the button. */
  flex: 1 1 0;
  min-width: 0;
  text-align: right;
}

.composer-hint.is-error { color: var(--error); opacity: 1; }

.btn-generate {
  appearance: none;
  border: 0;
  border-radius: var(--r-lg);
  background: var(--primary);
  color: var(--on-primary);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  line-height: 20px;
  padding: 8px 24px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  box-shadow: 0 0 15px rgba(208, 188, 255, 0.2);
  transition: background .18s ease, opacity .18s ease;
}

.btn-generate:hover:not(:disabled) { background: var(--primary-container); }
.btn-generate:disabled { opacity: .55; cursor: not-allowed; box-shadow: none; }
.btn-generate .material-symbols-outlined { font-size: 18px; }

.spinner {
  display: none;
  width: 13px;
  height: 13px;
  border: 2px solid rgba(60, 0, 145, 0.3);
  border-top-color: var(--on-primary);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}

.btn-generate.is-busy .spinner { display: inline-block; }
.btn-generate.is-busy .btn-spark { display: none; }

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

/* ---- Footer ---- */

.footer {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 20px 32px;
  border-top: 1px solid var(--hairline-soft);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
  opacity: .8;
}

/* Separators sit between the items, so the gap can be tighter than it was
   when spacing alone had to do the separating. */
.footer-links { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.footer-links a { color: var(--on-surface-variant); text-decoration: none; transition: color .18s ease; }
.footer-links a:hover { color: var(--primary); }

.footer-brand { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* Attribution reads a shade brighter than the surrounding muted footer text. */
.powered-link {
  color: var(--on-surface);
  text-decoration: none;
  font-weight: 600;
  transition: color .18s ease;
}

.powered-link:hover { color: var(--primary); }

/* ============================================================
   SIDEBAR
   ============================================================ */

.sidebar {
  flex: 0 0 var(--sidebar-w);
  width: var(--sidebar-w);
  background: var(--surface-container-lowest);
  border-left: 1px solid var(--hairline);
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 16px 0;
  z-index: 40;
}

.sidebar-head {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hairline-soft);
  width: 100%;
  text-align: left;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 24px 16px;
  margin-bottom: 16px;
  cursor: pointer;
  flex: 0 0 auto;
  color: inherit;
}

.sidebar-head-text { flex: 1; min-width: 0; }

.sidebar-head h2 {
  font-family: var(--display);
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  transition: color .2s ease;
}

.sidebar-head:hover h2 { color: var(--primary); }

.sidebar-head p {
  margin: 2px 0 0;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
  opacity: .7;
}

.sidebar-head .chevron {
  color: var(--on-surface-variant);
  font-size: 22px;
  transition: transform .25s ease, color .2s ease;
}

.sidebar-head[aria-expanded="true"] .chevron { transform: rotate(180deg); }
.sidebar-head:hover .chevron { color: var(--on-surface); }

/* ---- Model picker ---- */

.model-picker { position: relative; flex: 0 0 auto; }

.model-menu {
  position: absolute;
  top: calc(100% - 8px);
  left: 16px;
  right: 16px;
  z-index: 55;
  background: var(--surface-container-high);
  border: 1px solid var(--outline-variant);
  border-radius: var(--r-lg);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(12px);
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-height: 60vh;
  overflow-y: auto;
}

.model-option {
  appearance: none;
  border: 0;
  background: none;
  text-align: left;
  width: 100%;
  padding: 8px 10px;
  border-radius: var(--r-md);
  cursor: pointer;
  color: var(--on-surface-variant);
  display: flex;
  flex-direction: column;
  gap: 2px;
  transition: background .15s ease, color .15s ease;
}

.model-option:hover { background: rgba(255, 255, 255, 0.06); color: var(--on-surface); }

.model-option[aria-selected="true"] {
  background: rgba(208, 188, 255, 0.12);
  color: var(--on-surface);
}

.model-option-name {
  font-size: 13px;
  font-weight: 600;
  line-height: 17px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.model-option[aria-selected="true"] .model-option-name::after {
  content: 'check';
  font-family: 'Material Symbols Outlined';
  font-size: 14px;
  color: var(--primary);
  margin-left: auto;
}

.model-option-sub {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.03em;
  opacity: .7;
}

/* Controls a model can't do are dimmed rather than removed, so the sidebar
   doesn't jump around when switching models. */
.opt[disabled], .segmented button[disabled] {
  opacity: .3;
  cursor: not-allowed;
  pointer-events: none;
}

.tool[disabled] {
  opacity: .4;
  cursor: not-allowed;
  pointer-events: none;
}

.sidebar-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.sidebar-body.hidden { display: none; }
.sidebar-pad { height: 24px; flex: 0 0 auto; }

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

.group-title {
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  line-height: 16px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-surface-variant);
  opacity: .8;
}

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

.field-head { display: flex; align-items: center; justify-content: space-between; }

.field-label {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.05em;
  color: var(--on-surface);
}

.counter {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
}

.counter.is-max { color: var(--tertiary); }

/* ---- Option grids (aspect ratio / resolution) ---- */

.opt-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
}

.opt {
  appearance: none;
  background: none;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 8px 2px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 52px;
  cursor: pointer;
  color: var(--on-surface-variant);
  transition: background .18s ease, border-color .18s ease, color .18s ease;
}

.opt-flat { min-height: 34px; }

/* Each ratio glyph is a different height by definition (12px for 21:9, 24px
   for 2:3), and .opt centres its whole column, so half of every difference
   pushed the label down with it: across one row the labels sat at 29px, 33px
   and 37px from the top of their tiles. Giving the glyph a fixed-height slot
   lines every label up without altering a single glyph's proportions. */
#aspectGrid .opt {
  display: grid;
  grid-template-rows: 24px auto;
  align-content: center;
  justify-items: center;
}

.opt:hover { background: rgba(255, 255, 255, 0.05); }

.opt[aria-checked="true"] {
  border-color: var(--on-surface);
  background: rgba(255, 255, 255, 0.05);
  color: var(--on-surface);
}

.opt[aria-checked="true"]:hover { background: rgba(255, 255, 255, 0.10); }

.opt-label {
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.05em;
}

/* The little ratio glyph inside each aspect button.
   Sizes live here rather than as inline style attributes so the page can run
   under a CSP without style-src 'unsafe-inline'. */
.ar {
  border: 1px solid currentColor;
  border-radius: 2px;
  display: block;
  flex: 0 0 auto;
}

.ar-1-1  { width: 16px; height: 16px; }
.ar-4-3  { width: 20px; height: 15px; }
.ar-16-9 { width: 24px; height: 14px; }
.ar-9-16 { width: 12px; height: 21px; }
.ar-3-2  { width: 24px; height: 16px; }
.ar-2-3  { width: 16px; height: 24px; }
.ar-3-4  { width: 15px; height: 20px; }
.ar-21-9 { width: 28px; height: 12px; }
.ar-4-5  { width: 16px; height: 20px; }
.ar-5-4  { width: 20px; height: 16px; }

.opt-icon { font-size: 16px !important; }

/* ---- Segmented control (thinking level) ---- */

/* Same pattern as the top-bar mode switch: a pill track with inset pills and
   a tinted active state. Kept in mono per the design brief, since these are
   technical values. */
.segmented {
  display: flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-full);
  background: rgba(255, 255, 255, 0.03);
}

.segmented button {
  appearance: none;
  flex: 1;
  border: 0;
  background: none;
  border-radius: var(--r-full);
  color: var(--on-surface-variant);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.05em;
  padding: 5px 0;
  cursor: pointer;
  transition: background .18s ease, color .18s ease;
}

.segmented button:hover { color: var(--on-surface); background: rgba(255, 255, 255, 0.05); }

.segmented button[aria-checked="true"] {
  background: rgba(208, 188, 255, 0.16);
  color: var(--primary);
}

.segmented button[aria-checked="true"]:hover { background: rgba(208, 188, 255, 0.22); }

/* ---- Field hint (one explanatory line under a control) ---- */

.field-hint {
  margin: 0;
  font-size: 11px;
  line-height: 1.45;
  color: var(--on-surface-variant);
  opacity: .75;
}

/* Raised when the pending upload is large enough to be worth noticing. */
.field-hint.is-warn { color: var(--tertiary); opacity: 1; }

/* ---- Reference image uploader ---- */

#referenceImages {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
}












/* ---- Tools (ghost buttons with mini switches) ---- */

.tool {
  appearance: none;
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: none;
  color: var(--on-surface-variant);
  cursor: pointer;
  text-align: left;
  transition: border-color .2s ease, background .2s ease, color .2s ease, box-shadow .2s ease;
}

.tool + .tool { margin-top: 8px; }
.tool:hover { border-color: rgba(208, 188, 255, 0.5); color: var(--on-surface); }

.tool[aria-checked="true"] {
  border-color: rgba(208, 188, 255, 0.5);
  background: rgba(208, 188, 255, 0.05);
  color: var(--on-surface);
  box-shadow: 0 0 10px rgba(208, 188, 255, 0.05);
}

.tool-icon { font-size: 18px !important; flex: 0 0 auto; }
.tool[aria-checked="true"] .tool-icon { color: var(--primary); }

.tool-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }

.tool-name { font-size: 14px; font-weight: 600; line-height: 18px; }

.tool-sub {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.03em;
  opacity: .65;
}

.mini-switch {
  flex: 0 0 auto;
  width: 32px;
  height: 16px;
  border-radius: var(--r-full);
  background: var(--surface-container-high);
  position: relative;
  transition: background .22s ease;
}

.mini-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--on-surface);
  transition: transform .22s cubic-bezier(0.4, 0, 0.2, 1);
}

.tool[aria-checked="true"] .mini-switch { background: var(--primary); }
.tool[aria-checked="true"] .mini-thumb { transform: translateX(16px); }

/* ============================================================
   DESCRIBE (photo -> prompt)
   ============================================================ */



#photoInput {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0; overflow: hidden;
  clip: rect(0 0 0 0);
}

.photo-drop {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Shorter than the 220px it used as a full-canvas panel: inside a dialog
     the result textarea has to stay visible alongside it. */
  min-height: 150px;
  max-height: 34vh;
  padding: 16px;
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--r-lg);
  background: rgba(255, 255, 255, 0.03);
  cursor: pointer;
  text-align: center;
  transition: border-color .18s ease, background .18s ease;
  overflow: hidden;
}

.photo-drop:hover,
.photo-drop.is-dragover {
  border-color: var(--secondary);
  background: rgba(76, 215, 246, 0.06);
}

.photo-drop img {
  max-width: 100%;
  max-height: 340px;
  border-radius: var(--r-md);
  display: block;
}

/* `display` in a class beats the [hidden] attribute, so these must opt back
   out explicitly or the empty state shows alongside the loaded photo. */
[hidden] { display: none !important; }

.photo-drop-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.photo-drop-empty .material-symbols-outlined { font-size: 32px; color: var(--on-surface-variant); }

.photo-drop-text {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--on-surface-variant);
}

.photo-drop-text b { color: var(--primary); font-weight: 500; text-decoration: underline; }


.photo-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--on-surface-variant);
}

.photo-meta button {
  appearance: none;
  border: 0;
  background: none;
  color: var(--on-surface-variant);
  cursor: pointer;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--mono);
  font-size: 10px;
}

/* Sits under the photo, above Prompt focus and Detail, and takes one grid
   column's width rather than the full row: every other segmented control in
   the app runs at roughly that size, and stretching this one across would
   make the least important of the three fields the chunkiest.

   The spacing does the grouping: tight to the meta line above it, roomier
   below, so it reads as part of the photo block rather than as an orphaned
   cell of the options grid. */
.describe-quality {
  max-width: calc(50% - 7px);
  margin: 12px 0 22px;
}

.photo-meta button:hover { color: var(--error); }
.photo-meta button .material-symbols-outlined { font-size: 13px; }

/* Focus and detail, side by side inside the Describe dialog. They were a
   sidebar group ("Extraction") while Describe was a mode.

   Upload quality briefly lived here as a third segmented control and made
   the row read as a settings panel: nine pills across, and a hint line
   underneath that looked like it belonged to the leftmost one. It sits on
   the photo's row now instead, which is both quieter and more honest about
   what it is: a property of the file, not of the description being asked
   for. */
.describe-opts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  /* No top margin: the upload-quality field above already spaces them. */
  margin: 0 0 14px;
}

.describe-opts .field { margin: 0; }

@media (max-width: 620px) {
  .describe-opts { grid-template-columns: 1fr; }

  /* One column is the whole row down here. */
  .describe-quality { max-width: none; }

  /* Five 44px touch targets plus Generate need 469px of a 390px phone, so the
     bar above wraps. Given it has to be two rows, make them deliberate ones:
     the tools spread across the full width, and Generate becomes a full-width
     target underneath rather than a stray button on a short second line. */
  .composer-tools { flex: 1 1 100%; justify-content: space-between; }
  /* The divider is a desktop nicety; across a full-width spread row the
     gaps already separate the tools. */
  .tools-divider { display: none; }
  .composer-bar .btn-generate,
  .composer-bar #cancelBtn { flex: 1 1 100%; justify-content: center; }

  /* "Ctrl + Enter to generate" is not advice a phone can act on. It was only
     out of the way before because the squeezed tools box crushed it to zero
     width; with the row at full width it reappeared and ate the space the
     icons now spread into. Hidden on purpose rather than by accident. */
  #composerHint { display: none; }
}

.describe-controls {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.describe-controls .btn-generate { flex: 0 0 auto; margin-left: auto; }


.prompt-out-wrap {
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--surface-container-low);
  overflow: hidden;
  transition: border-color .25s ease, box-shadow .25s ease;
}

.prompt-out-wrap:focus-within {
  border-color: var(--primary);
  box-shadow: 0 0 20px 0 rgba(139, 92, 246, 0.18);
}

#promptOut {
  display: block;
  width: 100%;
  resize: vertical;
  min-height: 130px;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 22px;
  padding: 14px;
}

#promptOut::placeholder { color: rgba(203, 195, 215, 0.45); }

.describe-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  border-top: 1px solid var(--hairline-soft);
  background: rgba(53, 53, 52, 0.4);
}

.describe-hint {
  flex: 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--on-surface-variant);
  opacity: .7;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.action-btn[disabled] { opacity: .4; cursor: not-allowed; pointer-events: none; }

/* ============================================================
   MODALS
   ============================================================ */

.modal {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.modal-card {
  background: var(--surface-container);
  border: 1px solid rgba(73, 68, 84, 0.5);
  border-radius: var(--r-xl);
  padding: 24px;
  width: 100%;
  max-width: 440px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  max-height: 85vh;
  overflow-y: auto;
}

.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }

.modal-card h2 {
  font-family: var(--display);
  margin: 0 0 8px;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.modal-head h2 { margin-bottom: 0; }

.modal-note {
  margin: 0 0 16px;
  font-size: 13px;
  line-height: 20px;
  color: var(--on-surface-variant);
  opacity: .85;
}

.modal-note a { color: var(--primary); }

.modal-card input[type="password"] {
  width: 100%;
  background: var(--surface-container-lowest);
  border: 1px solid var(--outline-variant);
  border-radius: var(--r-lg);
  padding: 12px;
  color: var(--on-surface);
  font-family: var(--mono);
  font-size: 13px;
  outline: none;
  margin-bottom: 12px;
  transition: border-color .18s ease;
}

.modal-card input[type="password"]:focus { border-color: var(--primary); }

.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--on-surface-variant);
  margin-bottom: 20px;
  cursor: pointer;
}

.check input { accent-color: var(--primary); width: 15px; height: 15px; cursor: pointer; }

.modal-actions { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.modal-actions-right { display: flex; gap: 8px; }

.btn-solid, .btn-ghost, .btn-danger {
  appearance: none;
  border-radius: var(--r-lg);
  padding: 10px 18px;
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}

.btn-solid { border: 0; background: var(--primary); color: var(--on-primary); }
.btn-solid:hover { background: var(--primary-container); }

.btn-ghost { border: 0; background: none; color: var(--on-surface-variant); }
.btn-ghost:hover { background: var(--surface-container-high); color: var(--on-surface); }

.btn-danger { border: 1px solid rgba(255, 180, 171, 0.4); background: none; color: var(--error); }
.btn-danger:hover { background: rgba(255, 180, 171, 0.1); }

.steps { margin: 0 0 16px; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; }

.steps li { font-size: 13px; line-height: 20px; color: var(--on-surface-variant); }
.steps b { color: var(--on-surface); }

.inline-icon { font-size: 15px !important; vertical-align: -3px; opacity: .8; }

/* ============================================================
   ADDITIONS: cancel, variations, library, storage
   ============================================================ */

.token-cost { opacity: .75; }
.token-cost:not(:empty)::before { content: '·'; margin: 0 5px; opacity: .5; }

/* Cancel button (shown only while a request is in flight) */
.btn-cancel {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid rgba(255, 180, 171, 0.4);
  border-radius: var(--r-lg);
  background: none;
  color: var(--error);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  padding: 8px 16px;
  cursor: pointer;
  flex: 0 0 auto;
  transition: background .18s ease;
}

.btn-cancel:hover { background: rgba(255, 180, 171, 0.12); }
.btn-cancel .material-symbols-outlined { font-size: 17px; }

/* Native select, matched to the other sidebar controls.
   The mockup rebuild dropped the old select styles; this restores them so the
   Style preset control isn't rendered as raw browser chrome. */
.field select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 34px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background-color: var(--surface-container-low);
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 13px;
  line-height: 20px;
  padding: 0 32px 0 12px;
  cursor: pointer;
  outline: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--on-surface-variant) 50%),
    linear-gradient(135deg, var(--on-surface-variant) 50%, transparent 50%);
  background-position:
    calc(100% - 17px) calc(50% + 1px),
    calc(100% - 12px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  transition: border-color .18s ease, background-color .18s ease;
}

.field select:hover { border-color: var(--outline-variant); }
.field select:focus-visible { border-color: var(--primary); }
.field select option { background: var(--surface-container-high); color: var(--on-surface); }

/* Custom style instruction box */
#customStyle {
  width: 100%;
  resize: vertical;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: var(--surface-container-low);
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 13px;
  line-height: 19px;
  padding: 9px 12px;
  outline: none;
}

#customStyle:focus { border-color: var(--primary); }
#customStyle::placeholder { color: rgba(203, 195, 215, 0.45); }

/* Batch / variations grid */
.batch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  width: 100%;
  max-width: 780px;
}

.batch-cell {
  appearance: none;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--surface-container-lowest);
  overflow: hidden;
  cursor: pointer;
  position: relative;
  line-height: 0;
  aspect-ratio: 1 / 1;
  transition: border-color .18s ease, transform .18s ease;
}

.batch-cell:hover { border-color: var(--primary); transform: translateY(-2px); }
.batch-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }

.batch-cell .batch-label {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  background: rgba(14, 14, 14, 0.82);
  color: var(--on-surface);
  font-family: var(--mono);
  font-size: 10px;
  line-height: 16px;
  letter-spacing: 0.04em;
}

.batch-cell.is-failed {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: default;
  border-style: dashed;
  color: var(--error);
  font-family: var(--mono);
  font-size: 10px;
  line-height: 14px;
  padding: 10px;
  text-align: center;
  aspect-ratio: 1 / 1;
}

.batch-cell.is-failed:hover { transform: none; border-color: var(--hairline); }

/* Prompt library */
.modal-wide { max-width: 620px; }

.modal-rule {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: 20px 0 14px;
}

.storage-row { display: flex; align-items: center; gap: 12px; }
.storage-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.storage-text b { font-size: 13px; }

.storage-text span {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.03em;
  color: var(--on-surface-variant);
  opacity: .8;
}

.library-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 52vh;
  overflow-y: auto;
}

.library-list li {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--surface-container-low);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.library-empty {
  font-size: 13px;
  color: var(--on-surface-variant);
  opacity: .7;
  text-align: center;
  padding: 24px 0;
}

.library-text {
  font-size: 13px;
  line-height: 19px;
  color: var(--on-surface);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.library-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.library-tag {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: var(--r-full);
  padding: 2px 8px;
  border: 1px solid var(--hairline);
  color: var(--on-surface-variant);
}

.library-tag.is-style { color: var(--secondary); border-color: rgba(76, 215, 246, 0.35); }
.library-actions { display: flex; gap: 6px; margin-left: auto; }

.library-actions button {
  appearance: none;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: none;
  color: var(--on-surface-variant);
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  padding: 4px 10px;
  cursor: pointer;
  transition: color .16s ease, border-color .16s ease, background .16s ease;
}

.library-actions button:hover { color: var(--on-surface); border-color: var(--outline-variant); background: rgba(255,255,255,0.04); }
.library-actions button.danger:hover { color: var(--error); border-color: rgba(255,180,171,0.4); }

.modal-card input[type="text"] {
  width: 100%;
  background: var(--surface-container-lowest);
  border: 1px solid var(--outline-variant);
  border-radius: var(--r-lg);
  padding: 12px;
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 14px;
  outline: none;
  margin-bottom: 16px;
}

.modal-card input[type="text"]:focus { border-color: var(--primary); }
.modal-note code { font-family: var(--mono); font-size: 11px; color: var(--secondary); }

/* ============================================================
   TOAST
   ============================================================ */

.toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  z-index: 70;
  background: var(--surface-container-high);
  border: 1px solid var(--hairline);
  color: var(--on-surface);
  border-radius: var(--r-full);
  padding: 10px 20px;
  font-size: 13px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  max-width: min(560px, calc(100vw - 40px));
  text-align: center;
}

.toast.is-error { border-color: rgba(255, 180, 171, 0.45); color: var(--error); }

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 1279px) {
  body { overflow: auto; }
  .shell { flex-direction: column; }

  /* The engine settings become a drawer rather than a column stacked under
     the canvas. Stacked, every control sat below the fold: on a 455px
     viewport the sidebar began at y=649 of an 1862px page, so changing the
     resolution meant scrolling past the whole canvas and composer first. */
  .sidebar {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(360px, 92vw);
    z-index: 70;
    border-left: 1px solid var(--hairline);
    border-top: 0;
    padding-top: 0;
    box-shadow: -18px 0 50px rgba(0, 0, 0, 0.45);
  }

  /* As a drawer it scrolls internally, so the page behind it does not. */
  .sidebar-body { overflow-y: auto; }

  .sidebar-drawer-head { display: flex; }
  .icon-btn.sidebar-toggle { display: inline-flex; }

  .canvas { min-height: 72vh; }
  .stage { min-height: 320px; }
}

@media (max-width: 767px) {
  .topnav, .composer-wrap { padding-left: 16px; padding-right: 16px; }
  /* The footer was taking 297px of an 844px viewport, 35% of the screen and
     more than the image itself, because it stacks into four rows that each
     inherit body's 24px line-height. Tightened here; the clearance for the
     fixed composer is handled once, by .canvas padding-bottom below. */
  .footer {
    padding: 12px 16px 16px;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    line-height: 17px;
  }

  /* The footer is moved in here on phones (see placeFooter). It is the last
     child of the drawer's flex column, so it settles at the bottom on its own
     once the project list has taken the space it needs.

     Stacked, it ran to three lines while the right half of the drawer sat
     empty. Two columns instead: the brand wraps onto its own two lines on the
     left, and the links use the space beside it rather than a third row. */
  .menu-foot .footer {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 16px 16px;
    /* .footer wraps by default. Left wrapping, the brand's one-line basis
       (~250px) plus the links (~110px) overflows the 288px drawer, so the
       links were pushed onto a third row before the brand ever shrank. Held
       on one line, the brand shrinks and wraps internally instead. */
    flex-wrap: nowrap;
  }

  /* The brand takes what is left and wraps within it; the links keep their
     natural width, hard against the right edge. */
  .menu-foot .footer-brand { flex: 1 1 auto; min-width: 0; }
  .menu-foot .footer-links { flex: 0 0 auto; justify-content: flex-end; }

  /* The separators are inline items between the links, so once the rows wrap
     they get orphaned at the end of a line: "All rights reserved. |" and
     "... API Documentation |" with the last link alone underneath. Dropping
     them and widening the gap keeps the items readable as distinct without
     leaving a pipe dangling at a line break. They are aria-hidden decoration,
     so nothing is lost for screen readers. */
  .footer-brand, .footer-links { gap: 3px 16px; }
  .footer-sep { display: none; }
  .opt-grid { grid-template-columns: repeat(3, 1fr); }

  /* Three columns suit the eleven aspect ratios, but they strand 4K alone on
     a second row. Resolution has exactly four options and they fit across, as
     they already do on desktop. */
  #resolutionGrid { grid-template-columns: repeat(4, 1fr); }

  /* Nine controls plus a wordmark do not fit a phone, and with 44px touch
     targets they fit even less. Rather than hide any of them, the bar
     scrolls: everything stays reachable at any width. The scrollbar is
     suppressed because this reads as a toolbar, not as content. */
  .topnav {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .topnav::-webkit-scrollbar { display: none; }
  .topnav > * { flex-shrink: 0; }

  /* The one genuinely duplicated control goes first: New project is right
     there in the projects drawer, which is one tap away. */
  .new-project-top { display: none; }

  .brand { font-size: 20px; }

  /* The pinned composer owns the bottom of the screen, so the toast has to
     sit above it. It used to overlap the Generate button; harmless while
     toasts expired on their own, but sticky error toasts stay put and would
     have covered the button they are telling you to press. */
  .toast {
    bottom: calc(var(--composer-h, 215px) + 12px);
    left: 12px;
    right: 12px;
    transform: none;
    max-width: none;
  }

  /* The design brief asks for the prompt pinned to the bottom on mobile. */
  .composer-wrap {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 45;
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
    background: rgba(19, 19, 19, 0.94);
    backdrop-filter: blur(16px);
    border-top: 1px solid var(--hairline);
  }

  .composer textarea { min-height: 64px; }

  /* Keep the pinned bar from covering the canvas. */
  /* Reserves room for the fixed composer so the footer can be scrolled clear
     of it. Sized to the composer's real height rather than split across two
     rules that had to be added together to work. */
  /* --composer-h is measured and published by a ResizeObserver in app.js. The
     literal is the fallback for the first paint, before the observer has run
     and on the rare browser without ResizeObserver. */
  .canvas { padding-bottom: calc(var(--composer-h, 215px) + 16px); }
  .batch-grid { grid-template-columns: repeat(2, 1fr); }
}

/* The scroll above kept every control reachable, but nothing advertised that
   the bar scrolled, so at common phone widths Help and API key simply sat off
   the right edge: 430px of content in a 360px bar. Tightening the spacing and
   dropping the touch targets from 44px to 40px brings the row to 348px, so all
   six destinations are visible down to 360px. The scroll stays as the fallback
   for anything narrower than that. */
@media (max-width: 430px) {
  .topnav { padding-left: 8px; padding-right: 8px; gap: 2px; }
  .topnav .icon-btn { width: 40px; height: 40px; }
}

/* ===================== v1.10 additions ===================== */

/* References beyond the current model's cap: kept, visible, not sent. */

/* Backup row in the settings modal. */
.backup-row { margin-top: 10px; }
.backup-btns { display: flex; align-items: center; gap: 8px; }

/* ===== Reference attachments, in the composer =====================
   They used to be a filename list in the sidebar, a long way from both the
   prompt they apply to and the composer, which was already accepting drops
   and pastes with no visible sign that it would. */

.ref-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 14px 0;
}

.ref-chip {
  position: relative;
  width: 52px;
  height: 52px;
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  overflow: hidden;
  background: var(--surface-container-low);
  cursor: help;
}

.ref-chip img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Past the model's cap: kept and visible, but never sent. */
.ref-chip.is-inactive { opacity: .4; }
.ref-chip.is-inactive::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    45deg, transparent, transparent 5px,
    rgba(0, 0, 0, .35) 5px, rgba(0, 0, 0, .35) 10px);
}

.ref-chip-badge {
  position: absolute;
  left: 3px;
  bottom: 3px;
  font-family: var(--mono);
  font-size: 8px;
  letter-spacing: .04em;
  color: #fff;
  background: rgba(0, 0, 0, .62);
  border-radius: var(--r-full);
  padding: 1px 5px;
}

.ref-chip-remove {
  appearance: none;
  position: absolute;
  top: 2px;
  right: 2px;
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: var(--r-full);
  background: rgba(0, 0, 0, .62);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity .16s ease, background .16s ease;
}

.ref-chip:hover .ref-chip-remove,
.ref-chip-remove:focus-visible { opacity: 1; }
.ref-chip-remove:hover { background: var(--error); color: #1a0d0c; }
.ref-chip-remove .material-symbols-outlined { font-size: 13px; }

/* Count rides on the attach button, so the strip needs no header. */
.attach-count {
  position: absolute;
  top: 1px;
  right: 1px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  border-radius: var(--r-full);
  background: var(--primary);
  color: var(--on-primary);
  font-family: var(--mono);
  font-size: 9px;
  line-height: 14px;
  text-align: center;
}

.attach-count.is-max { background: var(--tertiary); }
#attachBtn { position: relative; }

/* Attachments are touch targets on a phone too. */
@media (pointer: coarse) {
  .ref-chip-remove { opacity: 1; width: 22px; height: 22px; }
}

/* ===== Spend breakdown ===== */

.spend-rows {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 7px 16px;
  margin: 4px 0 16px;
  font-size: 13px;
}

.spend-rows dt { margin: 0; color: var(--on-surface-variant); }

.spend-rows dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--on-surface);
  text-align: right;
}

.spend-rows dt.spend-head {
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--hairline-soft);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  opacity: .7;
}

.spend-rows dt.spend-head + dd {
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--hairline-soft);
}

/* ===================== v1.12: session thread panel =====================
   The current session as a conversation: user prompt bubble on the right,
   the resulting image(s) replying on the left, one pair per turn. Replaces
   the old under-canvas filmstrip. */

.thread-panel {
  flex: 0 0 300px;
  width: 300px;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--surface-container-lowest);
  border-left: 1px solid var(--hairline);
  z-index: 40;
}

.thread-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 10px 12px 16px;
  border-bottom: 1px solid var(--hairline-soft);
}

.thread-title { flex: 1; font-weight: 600; font-size: 13px; }

.thread-list {
  flex: 1;
  overflow-y: auto;
  padding: 14px 12px 20px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.thread-empty {
  margin: 8px 4px;
  color: var(--on-surface-variant);
  font-size: 12px;
  line-height: 1.5;
}

.turn { display: flex; flex-direction: column; gap: 6px; }

/* User bubble: the prompt, right-aligned like a sent chat message, with a
   copy button sitting beside it. */
.turn-user-row {
  align-self: flex-end;
  max-width: 92%;
  display: flex;
  align-items: flex-end;
  gap: 6px;
}

.turn-user {
  min-width: 0;
  background: rgba(208, 188, 255, 0.10);
  border: 1px solid rgba(208, 188, 255, 0.18);
  border-radius: 12px 12px 3px 12px;
  padding: 8px 10px;
}

.turn-copy {
  appearance: none;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--r-md);
  background: none;
  color: var(--on-surface-variant);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0.45;
  transition: opacity .16s ease, background .16s ease;
}

.turn-copy:hover {
  opacity: 1;
  color: var(--on-surface);
  background: rgba(255, 255, 255, 0.06);
}

.turn-copy .material-symbols-outlined { font-size: 15px; }

/* Copy and delete stack in the gutter beside the bubble. Side by side they
   would take 54px of a 92%-wide row and squeeze the prompt they belong to. */
.turn-actions {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Destructive, so it does not share the neutral hover of Copy above it. */
.turn-del:hover {
  color: var(--error);
  background: rgba(255, 180, 171, 0.10);
}

.turn-prompt {
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--on-surface);
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.turn-edit-tag {
  display: block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--primary);
  margin-bottom: 4px;
}

/* Reply: the image(s) that came back, left-aligned. */
.turn-reply {
  align-self: flex-start;
  width: 84%;
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px;
}

.turn-reply.is-multi { grid-template-columns: 1fr 1fr; }

.turn-shot {
  appearance: none;
  position: relative;
  display: block;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 10px;
  background: var(--surface-container-low);
  overflow: hidden;
  cursor: pointer;
  line-height: 0;
  transition: border-color .16s ease;
}

.turn-shot:hover { border-color: var(--outline-variant); }

.turn-shot[aria-current="true"] {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary), 0 0 12px rgba(208, 188, 255, 0.25);
}

/* The tile is a <button>, so its delete has to be a sibling rather than a
   child: nested buttons are invalid, and the inner one eats the outer's
   click. The wrapper is what the grid lays out now. */
.turn-shot-wrap { position: relative; }
.turn-shot-wrap > .turn-shot { width: 100%; }

.turn-shot-del {
  appearance: none;
  position: absolute;
  top: 5px;
  right: 5px;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: rgba(14, 14, 14, 0.78);
  backdrop-filter: blur(10px);
  color: var(--on-surface);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity .16s ease, color .16s ease, border-color .16s ease;
}

.turn-shot-del .material-symbols-outlined { font-size: 14px; }

/* Revealed on hover, and by keyboard focus, which hover alone would strand. */
.turn-shot-wrap:hover .turn-shot-del,
.turn-shot-del:focus-visible { opacity: 1; }

.turn-shot-del:hover {
  color: var(--error);
  border-color: rgba(255, 180, 171, 0.45);
}

.turn-shot img { width: 100%; display: block; }
.turn-reply.is-multi .turn-shot img { aspect-ratio: 1 / 1; object-fit: cover; }

.turn-shot-n {
  position: absolute;
  left: 6px;
  bottom: 6px;
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1;
  letter-spacing: 0.06em;
  color: #fff;
  background: rgba(0, 0, 0, 0.55);
  border-radius: var(--r-full);
  padding: 3px 7px;
}

.turn-meta {
  align-self: flex-start;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.03em;
  color: var(--on-surface-variant);
}

/* ===================== v1.14: focus, undo, guidance =====================

   1. A single visible focus ring for keyboard users, everywhere.
      :focus-visible never fires on pointer clicks, so this stays invisible
      during mouse use, so there is no reason to suppress it anywhere. */

:focus-visible {
  outline: 2px solid var(--secondary);
  outline-offset: 2px;
}

/* Form controls each set `outline: none` in their own higher-specificity
   rules, so the ring has to be restated to beat them. */
.composer textarea:focus-visible,
#promptOut:focus-visible,
#customStyle:focus-visible,
.field select:focus-visible,
.modal-card input[type="password"]:focus-visible,
.modal-card input[type="text"]:focus-visible {
  outline: 2px solid var(--secondary);
  outline-offset: 2px;
}

/* Inside a scrolling list an offset ring gets clipped; inset it instead. */
.thread-list :focus-visible,
.history-list :focus-visible,
.library-list :focus-visible {
  outline-offset: -2px;
}

/* 2. Toast with an action (undo). Text-only toasts keep the centred
      block layout; only this variant switches to a row. */

.toast.has-action {
  display: flex;
  align-items: center;
  gap: 14px;
  text-align: left;
}

.toast-action {
  appearance: none;
  flex: 0 0 auto;
  border: 0;
  background: none;
  color: var(--primary);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 2px 4px;
  border-radius: var(--r);
  cursor: pointer;
}

.toast-action:hover { color: var(--on-surface); }

/* Only on sticky toasts, which have no timer to close them. */
.toast-dismiss {
  appearance: none;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  margin: -4px -8px -4px 0;
  border: 0;
  border-radius: var(--r-full);
  background: none;
  color: var(--on-surface-variant);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}

.toast-dismiss:hover { color: var(--on-surface); background: rgba(255, 255, 255, 0.08); }
.toast.is-error .toast-action { color: var(--error); }

/* 3. Example prompts on the empty canvas: the fastest path to a first
      image for someone who doesn't know what to type. */

.empty-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-top: 18px;
}

.chip-prompt {
  appearance: none;
  border: 1px solid var(--hairline);
  background: var(--surface-container-low);
  color: var(--on-surface-variant);
  border-radius: var(--r-full);
  padding: 7px 14px;
  font-family: var(--sans);
  font-size: 12px;
  line-height: 1.3;
  cursor: pointer;
  transition: color .16s ease, border-color .16s ease, background .16s ease;
}

.chip-prompt:hover {
  color: var(--on-surface);
  border-color: var(--outline-variant);
  background: var(--surface-container);
}

/* Way back into the last project, since nothing auto-reopens now. */
.resume-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  padding: 6px 14px 6px 6px;
  border: 1px solid var(--outline-variant);
  border-radius: var(--r-full);
  background: var(--surface-container-low);
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  max-width: 100%;
  transition: border-color .16s ease, background .16s ease;
}

.resume-btn:hover { border-color: var(--primary); background: var(--surface-container); }

.resume-thumb {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: var(--r-full);
  object-fit: cover;
  display: block;
}

/* One line, ellipsised, so a long opening prompt cannot wrap the button
   into an awkward two-line block. */
.resume-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.resume-meta {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--on-surface-variant);
  opacity: .75;
  white-space: nowrap;
}

.empty-hint {
  display: block;
  margin-top: 16px;
  font-size: 12px;
  color: var(--on-surface-variant);
  opacity: .7;
}

/* 4. Elapsed seconds while a generation runs. A 4K render can take 40s,
      and a moving number is the difference between "working" and "hung". */

.loading-time {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
  opacity: .65;
}

/* 5. Keyboard shortcut list in the Help modal. */

.shortcuts {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 14px;
  align-items: center;
}

.shortcuts dt { margin: 0; }

.shortcuts dd {
  margin: 0;
  font-size: 13px;
  color: var(--on-surface-variant);
}

kbd {
  display: inline-block;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1;
  color: var(--on-surface);
  background: var(--surface-container-high);
  border: 1px solid var(--hairline);
  border-bottom-width: 2px;
  border-radius: var(--r);
  padding: 4px 6px;
  white-space: nowrap;
}

/* 7. Confirm dialog. Reuses .modal / .modal-card / .btn-* wholesale; the
      only new piece is the emphasised line that carries the figure the
      question is really about, such as the price of a run. */

/* Text field inside the shared confirm/prompt dialog. */
.confirm-input {
  width: 100%;
  margin: 4px 0 4px;
  padding: 11px 12px;
  border: 1px solid var(--outline-variant);
  border-radius: var(--r-lg);
  background: var(--surface-container-low);
  color: var(--on-surface);
  font-family: var(--sans);
  font-size: 14px;
  outline: none;
}

.confirm-input:focus { border-color: var(--primary); }
.confirm-input:focus-visible { outline: 2px solid var(--secondary); outline-offset: 2px; }

.confirm-detail {
  margin: 0 0 6px;
  font-family: var(--mono);
  font-size: 26px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--primary);
}

/* 8. Terms and Privacy. Long-form prose inside the standard modal card,
      which already scrolls at 85vh. */

.footer-sep { opacity: .35; }

.modal-legal { max-width: 680px; }

.legal-date {
  margin: 0 0 18px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
  opacity: .7;
}

.legal { font-size: 13px; line-height: 21px; color: var(--on-surface-variant); }

.legal p { margin: 0 0 12px; }

.legal h3 {
  font-family: var(--display);
  font-size: 14px;
  font-weight: 600;
  color: var(--on-surface);
  margin: 22px 0 8px;
}

.legal b { color: var(--on-surface); font-weight: 600; }

.legal ul { margin: 0 0 12px; padding-left: 20px; }
.legal li { margin-bottom: 7px; }

.legal a { color: var(--secondary); }

.legal code {
  font-family: var(--mono);
  font-size: 11px;
  background: var(--surface-container-high);
  border-radius: var(--r);
  padding: 1px 5px;
}

/* The opening paragraph carries the whole point, so it gets some weight. */
.legal-lede {
  color: var(--on-surface);
  border-left: 2px solid var(--primary);
  padding-left: 14px;
  margin-bottom: 18px !important;
}

/* 6. Touch targets: comfortable sizes once there is no mouse. */

@media (pointer: coarse) {
  .icon-btn, .ghost-icon { width: 44px; height: 44px; }
  .segmented button { min-height: 40px; }
  .turn-copy { width: 36px; height: 36px; opacity: 1; }
  /* No hover to reveal it with, so it is simply always there. */
  .turn-shot-del { width: 30px; height: 30px; opacity: 1; }
  .chip-prompt { padding: 10px 16px; }

  /* Footer links are 15px of text otherwise, which is a poor thumb target. */
  .footer-links a, .powered-link { display: inline-block; padding: 9px 0; }
}

/* Below the desktop breakpoint the shell stacks vertically, so the panel
   becomes a fixed drawer sliding over from the right instead of a column. */
@media (max-width: 1279px) {
  .thread-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(340px, 92vw);
    z-index: 70;
    box-shadow: -18px 0 50px rgba(0, 0, 0, 0.45);
  }
}

/* ============================================================
   MARKUP DIALOG (sketch + text over the image being edited)
   ============================================================ */

/* Wider than the standard card: the image is the interface here, and a
   440px column would show a postage stamp of a 16:9 render. */
.markup-card {
  background: var(--surface-container);
  border: 1px solid rgba(73, 68, 84, 0.5);
  border-radius: var(--r-xl);
  padding: 20px;
  width: 100%;
  max-width: 900px;
  max-height: 92vh;
  overflow-y: auto;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.markup-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.markup-head h2 { font-family: var(--display); margin: 0 0 6px; font-size: 20px; font-weight: 500; }
.markup-head .modal-note { margin: 0; max-width: 62ch; }

/* The <img> lays the stage out; the canvas is stretched over it, so the
   drawing surface and the picture can never drift out of register. */
.markup-stage {
  position: relative;
  align-self: center;
  line-height: 0;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface-container-lowest);
}

.markup-stage img {
  display: block;
  max-width: 100%;
  max-height: min(56vh, 620px);
  width: auto;
  height: auto;
}

#markupCanvas {
  position: absolute;
  left: 0;
  top: 0;
  cursor: crosshair;
  /* Without this a drag on a touchscreen scrolls the dialog instead of
     drawing, and the stroke never starts. */
  touch-action: none;
}

#markupCanvas.is-text { cursor: text; }

/* A ring the size of the hit tolerance, so the eraser shows its own reach.
   Double-stroked white over black to stay visible on any photograph. */
#markupCanvas.is-erase {
  cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><circle cx="12" cy="12" r="8.5" fill="none" stroke="black" stroke-width="3"/><circle cx="12" cy="12" r="8.5" fill="none" stroke="white" stroke-width="1.6"/></svg>') 12 12, cell;
}

/* Colour and weight do nothing while the eraser is selected; dimmed rather
   than disabled, so picking one switches back to marking without a detour. */
.markup-swatches.is-idle { opacity: .45; }

/* Parked at the click point and styled like the label it will become, so
   what you type is what you get. */
.markup-text-input {
  position: absolute;
  z-index: 2;
  min-width: 120px;
  max-width: 70%;
  padding: 2px 6px;
  border: 1px dashed rgba(255, 255, 255, 0.5);
  border-radius: var(--r);
  background: rgba(0, 0, 0, 0.55);
  font-family: var(--sans);
  font-weight: 600;
  line-height: 1.2;
  outline: none;
}

.markup-text-input::placeholder { color: rgba(255, 255, 255, 0.45); font-weight: 400; }

/* ---- Colour swatches ---- */

.markup-swatches { display: flex; justify-content: center; flex-wrap: wrap; gap: 12px; }

.markup-swatch {
  appearance: none;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: var(--r-full);
  cursor: pointer;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.12) inset;
  transition: transform .15s ease, border-color .15s ease;
}

.markup-swatch:hover { transform: scale(1.1); }

/* The ring sits outside the swatch so it reads on the black one too. */
.markup-swatch[aria-checked="true"] {
  border-color: var(--surface-container);
  box-shadow: 0 0 0 2px var(--on-surface);
  transform: scale(1.1);
}

/* ---- Tools ---- */

.markup-tools { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; }

.markup-tool {
  appearance: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 70px;
  padding: 8px 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--surface-container-high);
  color: var(--on-surface-variant);
  font-family: var(--sans);
  font-size: 12px;
  cursor: pointer;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}

.markup-tool:hover { color: var(--on-surface); border-color: var(--hairline-strong); }

.markup-tool[aria-checked="true"] {
  background: rgba(208, 188, 255, 0.16);
  border-color: var(--primary);
  color: var(--primary);
}

/* Inline SVG rather than the icon font: the subset in fonts/ carries only
   the glyphs the app already used, and a sketch squiggle is cheaper to draw
   here than to add there. */
.markup-icon { width: 22px; height: 22px; display: block; }
.edit-markup .markup-icon { width: 16px; height: 16px; }

/* Stacked so the pill lands on the tools' icon row and its label on their
   label row, which is what makes the toolbar read as one row of controls
   rather than four buttons and a stray widget. */
.markup-size-field {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Tuned so the label lands on the tools' label row: their icon block is
     22px on an 8px top pad, the pill is 26px, so the pill starts 4px
     higher and both labels end up at the same offset. */
  gap: 4px;
  padding: 4px 0 8px;
  margin-left: 4px;
}

.markup-size { width: 132px; }

.markup-size-label {
  font-family: var(--sans);
  font-size: 12px;
  color: var(--on-surface-variant);
}

/* Dimmed with the swatches while the eraser is selected: neither the
   colour nor the weight means anything to it. */
.markup-size-field.is-idle { opacity: .45; }

/* ---- Footer ---- */

.markup-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.markup-actions-left { display: flex; align-items: center; gap: 4px; }

.markup-count {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--on-surface-variant);
  opacity: .8;
  margin-right: 8px;
}

.btn-ghost[disabled], .btn-solid[disabled] { opacity: .4; cursor: not-allowed; pointer-events: none; }

/* ---- The trigger in the edit banner ---- */

.edit-markup {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-full);
  padding: 6px 12px;
  background: none;
  color: var(--on-surface-variant);
  font-family: var(--sans);
  font-size: 12px;
  cursor: pointer;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}

.edit-markup:hover { background: var(--surface-container-high); color: var(--on-surface); border-color: var(--hairline-strong); }
.edit-markup .material-symbols-outlined { font-size: 16px; }

/* Marks attached: the button carries the count, so the composer says what
   is going out without opening the dialog again. */
.edit-markup.has-markup { border-color: var(--primary); color: var(--primary); }

.markup-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--r-full);
  background: var(--primary);
  color: var(--on-primary);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 600;
}

@media (max-width: 640px) {
  .markup-card { padding: 14px; gap: 10px; }
  .markup-stage img { max-height: 44vh; }
  .markup-swatches { gap: 8px; }
  .markup-swatch { width: 26px; height: 26px; }
  .markup-actions { justify-content: center; }
  .markup-actions-left { width: 100%; justify-content: center; }
}
