/* ════════════════════════════════════════════════════════════════════
   MOCK-UP STUDIO — extensions to crnl for layouts crnl doesn't ship.
   crnl itself is vendored in crnl/ (scripts/sync-crnl.mjs). This file is
   unlayered, so it wins over every crnl layer without !important.

   Anything with a crnl class lives in the HTML with no custom rules:
   .top-bar, .input-field, .input-select, .btn, .btn-circle, .card-closed,
   .surface-card, .scale-700, .card-text-pair, .card-grid, text styles.

   What's here is ONLY what crnl has no component for:
     app shell / two-column layout, filter rail, selection ("N selected")
     bar, web modal (tokens only in crnl), toast, empty state, spinner,
     multi-select card state, drag-drop overlay.

   Everything below composes crnl tokens only — no hardcoded colours,
   spacing, radius or shadow. Note crnl type classes set their own
   color, so anything on a dark surface needs it set explicitly.
   ════════════════════════════════════════════════════════════════════ */

/* JS overwrites these with measured values (syncTopBarHeight); these
   fallbacks keep the shell sane on first paint and if JS never runs. */
/* Embedded in the docs site (?embed=1): the site supplies the header. */
[data-embedded="1"] .top-bar { display: none; }

:root {
  --topbar-h: 64px;
  --shell-h: calc(100vh - 128px);
  /* Headlines in Inter Black. crnl's base display face is not vendored
     (crnl/ skips the 145 display faces); weight travels with the face. */
  --display-font: 'Inter', system-ui, -apple-system, sans-serif;
  --display-weight: 900;
}

body {
  margin: 0;
  background: var(--bg-base);
  color: var(--text-primary);
  font-family: 'Inter', sans-serif;
  min-height: 100vh;
}

[hidden] { display: none !important; }

/* Small icon mark used in the top-bar brand slot — sized to match the
   logo slot height so the row keeps its 64px baseline without an image. */
.brand-mark {
  color: var(--brand-core);
  font-size: var(--icon-size-400);
  width: 48px;
  height: 48px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ────────────────────────────────────────────────────────────────────
   Views — only the active one is shown
   ──────────────────────────────────────────────────────────────────── */

/* ════════════════════════════════════════════════════════════════════
   THE SHELL — identical in both steps. Only the rail payload and the
   canvas contents swap, so nothing structural moves between step 1 and
   step 2 (previously the rail jumped 1260px and the heading 240px).
   ════════════════════════════════════════════════════════════════════ */

.flow-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-300);
  height: 64px;
  padding: 0 var(--spacing-300);
  background: var(--bg-base);
  border-bottom: 1px solid var(--border-default);
}

.stepper {
  display: flex;
  align-items: center;
  gap: var(--spacing-150);
  min-width: 0;
}

.stepper-step {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: var(--spacing-100);
  cursor: pointer;
  font-family: 'Inter', sans-serif;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
}

.stepper-step:disabled { cursor: default; opacity: 0.5; }
.stepper-step.is-current { color: var(--text-primary); font-weight: 600; }

.stepper-num {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  background: var(--neutral-100);
  color: var(--text-secondary);
  flex-shrink: 0;
}

.stepper-step.is-current .stepper-num,
.stepper-step.is-done .stepper-num {
  background: var(--interactive-transactional);
  color: var(--interactive-transactional-text);
}

.stepper-line {
  width: 40px;
  height: 1px;
  background: var(--border-default);
  flex-shrink: 0;
}

.flow-bar-actions {
  display: flex;
  align-items: center;
  gap: var(--spacing-150);
  flex-shrink: 0;
}

.shell {
  display: flex;
  align-items: stretch;
  height: var(--shell-h);
}

.rail {
  width: 240px;
  flex-shrink: 0;
  background: var(--bg-surface);
  padding: var(--spacing-300);
  overflow-y: auto;
}

.rail::-webkit-scrollbar { display: none; }
.rail { -ms-overflow-style: none; scrollbar-width: none; }

.rail-payload {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-300);
}

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.sidebar-clear-all {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Inter', sans-serif;
  font-size: 12px;
  color: var(--interactive-transactional);
}

.sidebar-section {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-100);
}

.sidebar-section-title {
  margin: 0;
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.sidebar-list {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-25);
}

/* Mirrors .filter-item on the docs site (docs/site/src/site.css:255) */
.sidebar-item {
  appearance: none;
  background: transparent;
  border: 0;
  border-left: 2px solid transparent;
  text-align: left;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-primary);
  padding: 5px var(--spacing-100);
  border-radius: var(--border-radius-50);
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--spacing-100);
  transition: background 120ms ease, color 120ms ease;
}

.sidebar-item:hover { background: var(--neutral-50); }

.sidebar-item.is-active {
  color: var(--interactive-transactional);
  border-left-color: var(--interactive-transactional);
  font-weight: 600;
  background: var(--neutral-50);
}

.sidebar-item.is-active .sidebar-item-count {
  color: var(--interactive-transactional);
}

.sidebar-item-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-item-count {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.canvas { flex: 1; min-width: 0; display: flex; }

.view { display: none; flex: 1; min-width: 0; flex-direction: column; }
.view.is-active { display: flex; }

.canvas-header {
  flex-shrink: 0;
  padding: var(--spacing-400) var(--spacing-300) var(--spacing-300);
}

.canvas-header h1 {
  margin: 0;
  max-width: 760px;
  text-wrap: balance;
}

/* The one scrollport in either step. Step 1 scrolls a long grid; step 2
   centres a short one via margin:auto, which collapses to 0 on overflow. */
.canvas-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  padding: 0 var(--spacing-300) var(--spacing-400);
}

.canvas-inner { flex: 1; min-width: 0; }

/* ────────────────────────────────────────────────────────────────────
   Step 1 — the two entry paths
   ──────────────────────────────────────────────────────────────────── */

.entry-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--spacing-card);
  margin-bottom: var(--spacing-400);
}

/* The card IS the control, so it carries surface + scale itself
   (the crnl tile rule: no inner button -> surface-card scale-700). */
.entry-card {
  appearance: none;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  border-radius: var(--border-radius-200);
  padding: var(--spacing-300);
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-200);
}

.entry-card-icon {
  font-size: var(--icon-size-400);   /* 32px — anchors the card next to labelBold40 */
  color: var(--interactive-transactional);
  flex-shrink: 0;
}

.entry-card .card-text-pair { min-width: 0; }

/* Occupies the search card's cell while active. */
.entry-search {
  grid-column: 2;
  align-self: stretch;
  display: flex;
  align-items: center;
}

.entry-search .input-field { width: 100%; }

/* Chrome renders its own clear affordance on input[type=search], which
   would sit next to crnl's .input-clear. Suppress the native one. */
.entry-search input[type="search"]::-webkit-search-cancel-button,
.entry-search input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}

.empty-inline { margin: var(--spacing-300) 0 0; }

.grid-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--spacing-200);
}

/* ────────────────────────────────────────────────────────────────────
   Step 2 — the composite grid
   ──────────────────────────────────────────────────────────────────── */

.studio-grid {
  /* Scale the composites so a row fits the visible canvas instead of
     running past it. 402/874 is the bezel aspect; the subtracted px are
     the step heading + canvas padding + the card's own chrome. */
  --card-w: clamp(150px, calc((var(--shell-h) - 380px) * 402 / 874), 280px);
  width: 100%;
  display: grid;
  /* auto-FIT, not auto-fill: fill leaves empty tracks in place, so
     centring would centre the empties too and the composites would sit
     left of centre. auto-fit collapses them to zero width. */
  grid-template-columns: repeat(auto-fit, var(--card-w));
  gap: var(--spacing-300);
  justify-content: center;
  /* margin:auto centres when there is spare room and collapses to 0 when
     the grid is taller than the canvas, anchoring it top-left. */
  margin-block: auto;
}

/* ────────────────────────────────────────────────────────────────────
   Library grid — screenshot cards
   ──────────────────────────────────────────────────────────────────── */

.library-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--spacing-card);
}

/* The screenshot IS the card — no surface panel, no permanent label
   strip. Metadata rides in on hover/focus over a scrim, so a dense grid
   stays calm. Interactivity is crnl .scale-700 (applied in app.js). */
.lib-card {
  position: relative;
  overflow: hidden;
  text-align: left;
  appearance: none;
  font: inherit;
  padding: 0;
  /* Defines the screen's outline against the page. box-sizing is
     border-box globally, so 1px -> 2px on select doesn't reflow the grid. */
  border: 1px solid var(--neutral-200);
  background: transparent;
  border-radius: var(--border-radius-200);
  cursor: pointer;
  display: block;
}

.lib-card.is-selected {
  border: 2px solid var(--color-interactive);
}

/* Screenshots are opaque, so there is no transparency to indicate here.
   This is purely the placeholder behind a lazy-loading image — a flat
   neutral keeps the grid calm while 430 thumbnails stream in. */
.lib-card-thumb {
  aspect-ratio: 393 / 852;
  /* Grid rows stretch every card to the tallest in the row, and auto-fill
     hands columns uneven fractional widths — so aspect-ratio alone left a
     ~2px dead strip at the bottom of shorter cards, inside the border.
     Filling the stretched height closes it; object-fit:cover absorbs it. */
  height: 100%;
  /* No radius of its own: `inherit` would copy the card's OUTER 16px,
     but this sits in the padding box, which curves at 16px minus the
     border width (15 unselected / 14 selected) — the mismatch left a
     wedge at each corner. The card's overflow:hidden already clips to
     the correct concentric curve. */
  background: var(--neutral-100);
  overflow: hidden;
  position: relative;
}

.lib-card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Hover/focus overlay. White text on a dark scrim, so not .text-secondary. */
.lib-card-meta {
  position: absolute;
  inset: 0;
  padding: var(--spacing-300) var(--spacing-150) var(--spacing-150);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 1px;
  /* Light wash across the whole screen, deepening at the bottom so the
     label stays legible over bright screenshots. */
  background:
    linear-gradient(to top, var(--black-900) 0%, var(--black-500) 38%, transparent 72%),
    var(--black-300);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 140ms ease-out, transform 140ms ease-out;
  pointer-events: none;
}

.lib-card:hover .lib-card-meta,
.lib-card:focus-visible .lib-card-meta,
.lib-card.is-selected .lib-card-meta {
  opacity: 1;
  transform: none;
}

.lib-card-meta-row {
  display: flex;
  align-items: flex-end;
  gap: var(--spacing-100);
}

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

.lib-card-title,
.lib-card-project {
  font-family: 'Inter', sans-serif;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}

.lib-card-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--white-1000);
}

.lib-card-project {
  font-size: 11px;
  font-weight: 400;
  color: var(--white-700);
}

/* No hover on touch — metadata would otherwise be unreachable. */
@media (hover: none) {
  .lib-card-meta { opacity: 1; transform: none; }
}

.lib-card-check {
  position: absolute;
  top: var(--spacing-100);
  right: var(--spacing-100);
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--interactive-primary);
  color: var(--interactive-primary-text);
  display: none;
  align-items: center;
  justify-content: center;
}

.lib-card.is-selected .lib-card-check { display: flex; }

/* 16px, not 20 — it sits inside a 28px circle and 20 crowds it. */
.lib-card-check .material-symbols-rounded { font-size: var(--icon-size-100); }

.lib-card-delete {
  position: absolute;
  top: var(--spacing-100);
  left: var(--spacing-100);
  appearance: none;
  border: 0;
  background: var(--black-700);
  color: var(--white-1000);
  border-radius: 50%;
  width: 28px;
  height: 28px;
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0.6;
}

.lib-card-delete:hover { opacity: 1; }
.lib-card-delete .material-symbols-rounded { font-size: var(--icon-size-100); }
.lib-card:hover .lib-card-delete[data-deletable="true"] { display: flex; }

.empty-state {
  text-align: center;
  padding: var(--spacing-1000) var(--spacing-400);
  max-width: 480px;
  margin: var(--spacing-600) auto 0;
}

.empty-icon {
  font-size: var(--icon-size-700);
  color: var(--text-secondary);
  margin-bottom: var(--spacing-200);
}

.empty-state p + p { margin-top: var(--spacing-150); }
.secondary { color: var(--text-secondary); }

/* ────────────────────────────────────────────────────────────────────
   Studio — shared preset header + cards
   ──────────────────────────────────────────────────────────────────── */


/* .studio-card composes .card-closed .surface-card. Adds preview frame
   + controls layout on top of crnl card chrome. */
.studio-card {
  padding: var(--spacing-200);
  display: flex;
  flex-direction: column;
  gap: var(--spacing-200);
}

/* No transparency checkerboard: the export is *meant* to be transparent,
   so flagging it is noise. Letting the card surface show through is the
   honest preview of how the PNG will sit on any background. */
.studio-card-preview {
  /* One shared height across the grid (set by syncPreviewHeights) so every
     card lines up. Falls back to the composite's own ratio before that
     resolves, and to portrait before the first render. */
  height: var(--preview-h, auto);
  aspect-ratio: var(--preview-ratio, 402 / 874);
  background: transparent;
  border-radius: var(--border-radius-200);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.studio-card-preview img {
  max-width: 100%;
  max-height: 100%;
  display: block;
}

.studio-card-spinner {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}

.studio-card-spinner .material-symbols-rounded {
  font-size: var(--icon-size-400);
  animation: spin 1s linear infinite;
}

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

/* Cards scale down to ~150px wide, where a select and a button side by
   side leaves the select with no room to show its value. Stack them. */
.studio-card-controls {
  margin-top: auto;   /* pin to the bottom so controls line up across a row
                         even when a title wraps to two lines */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--spacing-100);
}

.studio-card-controls .input-field { flex: 1; min-width: 0; }

/* ────────────────────────────────────────────────────────────────────
   Upload modal — uses native <dialog>, styled with tokens
   ──────────────────────────────────────────────────────────────────── */

.modal {
  margin: auto;
  background: var(--bg-sheet);
  color: var(--text-primary);
  border: 1px solid var(--border-default);
  border-radius: var(--border-radius-200);
  padding: 0;
  max-width: 560px;
  width: calc(100% - var(--spacing-400));
  box-shadow: var(--shadow-modal);
}

.modal::backdrop {
  background: var(--black-500);
  backdrop-filter: blur(4px);
}

.modal-form { display: flex; flex-direction: column; }
.modal-header { padding: var(--spacing-300) var(--spacing-400) 0; }
.modal-header p { margin: 0; }
.modal-header p + p { margin-top: var(--spacing-50); }

.modal-body {
  padding: var(--spacing-300) var(--spacing-400);
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: var(--spacing-300);
  align-items: start;
}

.modal-preview {
  aspect-ratio: 393 / 852;
  background: var(--neutral-100);
  border-radius: var(--border-radius-200);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.modal-fields {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-200);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--spacing-100);
  padding: var(--spacing-200) var(--spacing-400) var(--spacing-300);
  border-top: var(--border-default);
}

/* ────────────────────────────────────────────────────────────────────
   Drag-drop overlay + toast
   ──────────────────────────────────────────────────────────────────── */

.drop-overlay {
  position: fixed;
  inset: 0;
  background: var(--black-700);
  backdrop-filter: blur(8px);
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.drop-overlay-inner {
  border: 2px dashed var(--white-1000);
  border-radius: var(--border-radius-200);
  padding: var(--spacing-600) var(--spacing-1000);
  text-align: center;
  color: var(--white-1000);
}

.drop-overlay-icon {
  font-size: var(--icon-size-700);
  margin-bottom: var(--spacing-200);
}

.toast {
  position: fixed;
  bottom: var(--spacing-400);
  right: var(--spacing-400);
  background: var(--bg-sheet);
  border: 1px solid var(--border-default);
  border-radius: var(--border-radius-200);
  padding: var(--spacing-150) var(--spacing-300);
  box-shadow: var(--shadow-sheet);
  z-index: 11;
  max-width: 320px;
}

/* ────────────────────────────────────────────────────────────────────
   Responsive — matches the docs site: the rail drops away below 1100px
   rather than squeezing the grid.
   ──────────────────────────────────────────────────────────────────── */

@media (max-width: 1099px) {
  /* Rail folds above the canvas rather than vanishing, so filters and
     frame settings stay reachable. The shell stops being viewport-locked
     and the page scrolls instead. */
  .shell { flex-direction: column; height: auto; }
  .rail { width: auto; overflow: visible; }
  .rail-payload { flex-direction: row; flex-wrap: wrap; align-items: flex-end; }
  .rail-payload .sidebar-header { width: 100%; }
  .canvas-body { overflow: visible; }
  .flow-bar { padding: 0 var(--spacing-200); }
  .stepper-label { display: none; }
}

@media (max-width: 767px) {
  .entry-pair { grid-template-columns: 1fr; }
  .entry-search { grid-column: 1; }
}
