/* App Screenshots — a light table.
 *
 * The world is a designer's light table: warm paper, ink, hairline rules, and the artwork as the only
 * saturated thing on screen. Chrome recedes to structure so nothing competes with the screenshot the
 * user is judging. Reason and palette are in DESIGN.md.
 */

:root {
  /* Ground and chrome */
  --paper: #faf9f7;
  --surface: #ffffff;
  --rule: #e6e3de;
  --rule-strong: #d5d1ca;

  /* Ink */
  --ink: #191817;
  --ink-muted: #6b6862;

  /* State only. Never decoration. */
  --accent: #1f4bd8;
  --accent-soft: #eef1fc;
  --warn: #b4430f;
  --warn-soft: #fdf1ea;
  --pass: #1b6b4a;

  /* Depth is reserved for things genuinely above the page: artwork, drag ghosts. */
  --lift: 0 1px 2px rgba(25, 24, 23, 0.06), 0 8px 24px rgba(25, 24, 23, 0.09);
  --lift-high: 0 2px 4px rgba(25, 24, 23, 0.08), 0 18px 44px rgba(25, 24, 23, 0.16);

  --sidebar: 236px;
  --inspector: 312px;

  --topbar: 52px;
  --stage-pad: 32px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 13px/1.5 Inter, -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
  /* Numbers are measurements in this product; digits that shift width read as sloppy. */
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

/* ── Browser surfaces carry the design ────────────────────────────────────────────
 * The parts nobody draws and everybody meets. Themed from the palette, because browser
 * defaults belong to no design system. */

::selection { background: var(--accent-soft); color: var(--ink); }

input, textarea, select { caret-color: var(--accent); }

* { scrollbar-width: thin; scrollbar-color: var(--rule-strong) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--rule-strong);
  border: 3px solid var(--paper);
  border-radius: 6px;
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-muted); }

:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

a { color: var(--accent); text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* ── Type ─────────────────────────────────────────────────────────────────────── */

h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.011em; }

.label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.measure { color: var(--ink-muted); }

/* ── Shell ────────────────────────────────────────────────────────────────────── */

.app { display: flex; flex-direction: column; height: 100%; }

.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  height: var(--topbar);
  padding: 0 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
  flex: none;
}
.topbar .wordmark { font-size: 15px; font-weight: 600; letter-spacing: -0.015em; }
.topbar .divider { width: 1px; height: 22px; background: var(--rule); }
.topbar .project { font-size: 13px; color: var(--ink); }
.topbar .spacer { flex: 1; }
.topbar .user { font-size: 12px; color: var(--ink-muted); }

/* The size being produced, in Apple's own words, always visible: it is the fact that
 * determines whether the export is even valid. */
.readout {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 5px 10px;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 6px;
}
.readout .size { font-size: 12px; font-weight: 600; letter-spacing: -0.01em; }
.readout .slot { font-size: 11px; color: var(--ink-muted); }

.workbench {
  display: grid;
  grid-template-columns: var(--sidebar) 1fr var(--inspector);
  /* The row must not grow with its contents.
   *
   * Left to `auto`, the row became as tall as the inspector — which a 3D device makes very tall — and
   * the stage stretched to match. The artwork is centred in the stage, so it was centred in a box
   * taller than the window: pushed 100px down and cut off at the bottom, with no scrollbar to say so.
   * The columns scroll inside themselves, which is what those `overflow-y` rules were always for. */
  grid-template-rows: minmax(0, 1fr);
  flex: 1;
  min-height: 0;
}

/* ── Sidebar: PANELS over LAYERS ──────────────────────────────────────────────────
 * Two lists, because they are two ideas: a panel is a position in a set, a layer is
 * something drawn on it. */

.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--rule);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.sidebar .group { padding: 14px 12px; border-bottom: 1px solid var(--rule); }
.sidebar .group:last-child { border-bottom: none; flex: 1; }
.sidebar .group > .label { margin-bottom: 9px; display: block; }

.stack { display: flex; flex-direction: column; gap: 3px; }

.row {
  display: grid;
  /* Four columns: the affordance, the name, a fact, and the actions that appear on hover. The last
   * column collapses to nothing while the actions are hidden, so the row does not shift. */
  grid-template-columns: 18px 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid transparent;
  cursor: pointer;
  font-size: 12px;
  background: none;
  text-align: left;
  width: 100%;
  color: inherit;
}
.row:hover { background: var(--paper); }
.row[aria-selected='true'] { background: var(--accent-soft); border-color: #c9d3f7; }
.row .ordinal { color: var(--ink-muted); font-size: 11px; text-align: right; }
.row .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row .meta { color: var(--ink-muted); font-size: 11px; }

/* What you can do to a row shows on the row, not in a menu somewhere else. */
.row .actions { display: none; grid-auto-flow: column; gap: 2px; }
.row:hover .actions,
.row:focus-within .actions { display: grid; }
.row:hover .ordinal,
.row:hover .meta,
.row:focus-within .ordinal,
.row:focus-within .meta { display: none; }
.row .actions .icon-button { width: 20px; height: 20px; }

/* Dragging a panel to reorder it. */
.row.dragging { opacity: 0.4; }
.row.drop-before { box-shadow: inset 0 2px 0 var(--accent); }
.row.drop-after { box-shadow: inset 0 -2px 0 var(--accent); }
.row .grip { color: var(--rule-strong); cursor: grab; display: flex; }
.row:hover .grip { color: var(--ink-muted); }

.icon-button {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: var(--ink-muted);
  cursor: pointer;
}
.icon-button:hover { background: var(--paper); border-color: var(--rule); color: var(--ink); }
.icon-button[aria-pressed='false'] { color: var(--rule-strong); }

/* ── Stage ────────────────────────────────────────────────────────────────────── */

.stage {
  display: grid;
  place-items: center;
  padding: var(--stage-pad);
  overflow: auto;
  background: var(--paper);
  position: relative;
}

.plate {
  position: relative;
  /* No background: a full-bleed screenshot inside a white card shows a white ring, which reads as a
   * frame nobody designed. The plate exists only to carry the radius and the lift. */
  background: none;
  border-radius: 10px;
  box-shadow: var(--lift);
  /* The one authored moment: the artwork settling when the panel changes. */
  animation: settle 260ms var(--ease) both;
}
@keyframes settle {
  from { opacity: 0; transform: translateY(6px) scale(0.995); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .plate { animation: none; }
}

/* The artwork is fitted here rather than measured in JavaScript.
 *
 * These are the layout's own numbers, so the fit cannot disagree with the columns. A canvas is a
 * replaced element: when both maxima apply, the browser scales it down and keeps its ratio, which is
 * exactly the "show me the whole panel" behaviour a screenshot editor needs. */
.plate canvas {
  display: block;
  width: auto;
  height: auto;
  max-width: calc(100vw - var(--sidebar) - var(--inspector) - var(--stage-pad) * 2);
  max-height: calc(100vh - var(--topbar) - var(--stage-pad) * 2);
  border-radius: 10px;
}
/* The selection outline sits over the artwork, wherever the artwork is.
 *
 * This rule used to be scoped to `.plate`, so on the board the overlay was an ordinary block child of
 * the card: it took up flow space at its own intrinsic height and left every card 185px taller than the
 * picture in it, with a dead band underneath. The overlay belongs to whatever positioned box it is in,
 * which is the only thing it needs to know. */
.overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
}

.stage .empty {
  display: grid;
  gap: 10px;
  justify-items: center;
  text-align: center;
  max-width: 42ch;
  padding: 40px;
  border: 1px dashed var(--rule-strong);
  border-radius: 10px;
  color: var(--ink-muted);
}
.stage .empty strong { color: var(--ink); font-weight: 600; font-size: 15px; }

/* ── Inspector ────────────────────────────────────────────────────────────────── */

.inspector {
  background: var(--surface);
  border-left: 1px solid var(--rule);
  overflow-y: auto;
}
.inspector .group { padding: 14px 14px 16px; border-bottom: 1px solid var(--rule); }
.inspector .group > .label { display: block; margin-bottom: 11px; }
.inspector .group:last-child { border-bottom: none; }

.field { display: grid; gap: 5px; margin-bottom: 11px; }
.field:last-child { margin-bottom: 0; }
.field > .name { font-size: 11px; color: var(--ink-muted); }

.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

input[type='text'],
input[type='email'],
input[type='password'],
input[type='number'],
textarea,
select {
  width: 100%;
  padding: 7px 9px;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  transition: border-color 120ms var(--ease);
}
input:hover, textarea:hover, select:hover { border-color: var(--ink-muted); }
input:focus, textarea:focus, select:focus { border-color: var(--accent); }
input:disabled, textarea:disabled, select:disabled {
  background: var(--paper);
  color: var(--ink-muted);
  cursor: not-allowed;
}
textarea { resize: vertical; min-height: 62px; line-height: 1.45; }

input[type='color'] {
  width: 100%;
  height: 30px;
  padding: 2px;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: 6px;
  cursor: pointer;
}

.swatches { display: flex; gap: 6px; flex-wrap: wrap; }
.swatch {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  border: 1px solid var(--rule-strong);
  cursor: pointer;
  padding: 0;
}
.swatch[aria-pressed='true'] { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── Buttons ──────────────────────────────────────────────────────────────────── */

button {
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: 7px;
  padding: 7px 13px;
  cursor: pointer;
  transition: background 120ms var(--ease), border-color 120ms var(--ease);
}
button:hover:not(:disabled) { border-color: var(--ink-muted); }
button:active:not(:disabled) { background: var(--paper); }
button:disabled { color: var(--ink-muted); background: var(--paper); cursor: not-allowed; }

button.primary {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 600;
}
button.primary:hover:not(:disabled) { background: #1a41bd; border-color: #1a41bd; }
button.quiet { background: none; border-color: transparent; color: var(--ink-muted); }
button.quiet:hover:not(:disabled) { background: var(--paper); border-color: var(--rule); color: var(--ink); }

.button-row { display: flex; gap: 8px; align-items: center; }

/* ── Messages ─────────────────────────────────────────────────────────────────── */

.note {
  font-size: 12px;
  padding: 9px 11px;
  border-radius: 7px;
  line-height: 1.45;
}
.note.warn { background: var(--warn-soft); color: var(--warn); }
.note.info { background: var(--paper); color: var(--ink-muted); }

.status { font-size: 12px; color: var(--ink-muted); min-height: 17px; }
/* Busy state is carried by the words and a colour, not by a dot pretending to be a live feed. The
 * text says "Uploading 2 of 5" or "Rendering"; a pulsing 7px ornament adds nothing and is exactly the
 * kind of decoration that makes a tool feel like it is performing work rather than doing it. */
.status.busy { color: var(--accent); font-weight: 600; }

.toast {
  position: fixed;
  bottom: 18px;
  left: 18px;
  background: var(--ink);
  color: var(--paper);
  padding: 10px 16px;
  border-radius: 8px;
  font-size: 12.5px;
  box-shadow: var(--lift-high);
  animation: settle 200ms var(--ease) both;
  max-width: 60ch;
}
.toast.bad { background: var(--warn); }

/* ── Auth and project list ────────────────────────────────────────────────────── */

.centered { max-width: 380px; margin: 12vh auto; padding: 0 20px; }
.centered h1 { font-size: 20px; margin-bottom: 6px; }
.centered .lede { color: var(--ink-muted); margin: 0 0 26px; }

.panel-form {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 20px;
  box-shadow: var(--lift);
}

.catalogue { max-width: 860px; margin: 40px auto; padding: 0 20px; }
.catalogue header { display: flex; align-items: baseline; gap: 12px; margin-bottom: 20px; }
.catalogue h1 { font-size: 20px; }

.projects { border-top: 1px solid var(--rule); }
.project-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 18px;
  align-items: baseline;
  width: 100%;
  padding: 15px 4px;
  border: none;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}
.project-row:hover { background: var(--paper); }
.project-row .name { font-size: 15px; font-weight: 600; letter-spacing: -0.012em; }
.project-row .facts { color: var(--ink-muted); font-size: 12px; }

/* ── Narrow ─────────────────────────────────────────────────────────────────────
 * A three-column workbench cannot hold at 390px, and a pixel editor is not a phone tool. It still
 * has to be readable rather than broken: the lists stack above and below the artwork, which is
 * workable on a small screen even if nobody will do real work that way. */

@media (max-width: 1000px) {
  .workbench { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr) auto; }
  .sidebar { border-right: none; border-bottom: 1px solid var(--rule); }
  .inspector { border-left: none; border-top: 1px solid var(--rule); }
  .plate canvas {
    max-width: calc(100vw - var(--stage-pad) * 2);
    max-height: none;
  }
}

@media (max-width: 700px) {
  .topbar { flex-wrap: wrap; height: auto; min-height: var(--topbar); padding: 10px 12px; gap: 8px; }
  .topbar .user, .topbar .divider, .topbar .project { display: none; }
  /* The name is the one thing that must not break across lines; the size keeps its digits because
   * they are the fact that makes an export valid, and drops the device name, which the inspector
   * states in full a few pixels below. */
  .topbar .wordmark { flex: 1 1 100%; white-space: nowrap; }
  .readout { margin-left: auto; }
  .readout .slot { display: none; }
  :root { --stage-pad: 16px; }
  .catalogue { margin-top: 24px; }
}

/* ── Rotation and tier controls ─────────────────────────────────────────────────
 * Rotation is the one control here that is continuous rather than discrete, so it gets a slider:
 * a number field is exact but unusable for finding an angle, and a slider is the reverse. Both are
 * present — the readout is the exact number, the slider is the way to find it. */

.control { display: grid; gap: 3px; margin-bottom: 9px; }
.control:last-child { margin-bottom: 0; }
.control-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.control-head .name { font-size: 11px; color: var(--ink-muted); }
.control-head .value { font-size: 11px; font-weight: 600; }

input[type='range'] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 18px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
}
input[type='range']::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 2px;
  background: var(--rule-strong);
}
input[type='range']::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -5.5px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface);
  box-shadow: 0 1px 3px rgba(25, 24, 23, 0.3);
}
input[type='range']:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--accent); outline-offset: 2px; }
input[type='range']::-moz-range-track { height: 3px; border-radius: 2px; background: var(--rule-strong); }
input[type='range']::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--surface);
}

/* Two mutually exclusive renderers. A switch, not a checkbox: neither is "off". */
.segmented {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  border: 1px solid var(--rule-strong);
  border-radius: 7px;
  overflow: hidden;
}
.segmented button {
  padding: 6px 9px;
  font-size: 12px;
  border: none;
  border-radius: 0;
  background: var(--surface);
}
.segmented button + button { border-left: 1px solid var(--rule-strong); }
.segmented button[aria-pressed='true'] { background: var(--accent); color: #fff; font-weight: 600; }
.segmented button[aria-pressed='true']:hover { background: var(--accent); }

/* ── Board: the whole set, side by side ──────────────────────────────────────────
 * The strip scrolls sideways because that is how a store listing is read — in order, one after
 * another — and because a set is judged as a sequence, not as ten separate pictures. */

.board { padding: 22px; overflow: auto; background: var(--paper); }
.board-strip { display: flex; gap: 20px; align-items: flex-start; }
.board-card { margin: 0; flex: 0 0 auto; width: 296px; cursor: pointer; }

/* The card is the artwork's frame: it carries the radius, the lift and the selection ring, and the
 * editor's canvas and overlay sit inside it. */
.board-surface {
  position: relative;
  border-radius: 8px;
  background: var(--surface);
  box-shadow: var(--lift);
}
.board-surface canvas { display: block; width: 100%; height: auto; border-radius: 8px; }
.board-card.selected .board-surface { outline: 2px solid var(--accent); outline-offset: 2px; }
.board-card figcaption {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 9px;
  font-size: 12px;
  color: var(--ink-muted);
}
.board-card .ordinal { color: var(--ink); font-weight: 600; }
.board .empty { max-width: 42ch; }

@media (max-width: 700px) {
  .board { padding: var(--stage-pad); }
  .board-card { width: 232px; }
}

/* The board keeps the columns, so it needs the same row clamp the workbench has. */
.workbench > .board { min-height: 0; }

/* ── The element library ─────────────────────────────────────────────────────────
 * Tiles rather than a list, because these are shapes: the way to choose a laurel is to see it.
 * Each preview keeps the element's own proportions, so what is on the tile is what gets added. */

.element-library { display: grid; gap: 12px; margin-top: 9px; }
.element-group { display: grid; gap: 6px; }
.element-group > .name { font-size: 11px; color: var(--ink-muted); }
.element-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 6px; }

.element-tile {
  display: grid;
  justify-items: center;
  gap: 5px;
  padding: 8px 4px 6px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 7px;
  cursor: pointer;
}
.element-tile:hover { border-color: var(--ink-muted); background: var(--paper); }
.element-tile canvas { display: block; }
.element-tile .name {
  font-size: 10px;
  line-height: 1.2;
  color: var(--ink-muted);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

/* ── The set view: upload, then every template ───────────────────────────────────
 * The screen a person lands on with a folder of screenshots. The upload control is first
 * because it is the only thing to do until files are in, and the gallery below it answers
 * "which layout" by showing all of them rather than describing them. */

.set-view { padding: 26px 28px 60px; overflow-y: auto; max-width: 1500px; margin: 0 auto; }

.upload-panel { display: grid; gap: 12px; margin-bottom: 34px; }

.drop {
  display: grid;
  gap: 6px;
  justify-items: center;
  padding: 34px 20px;
  text-align: center;
  background: var(--surface);
  border: 1px dashed var(--rule-strong);
  border-radius: 12px;
  cursor: pointer;
  transition: border-color 120ms var(--ease), background 120ms var(--ease);
}
.drop.over { border-color: var(--accent); background: var(--accent-soft); }
.drop-title { font-size: 15px; font-weight: 600; letter-spacing: -0.012em; }
.drop-hint { font-size: 12px; color: var(--ink-muted); }

.uploads { display: grid; gap: 6px; }
.upload-list { border-top: 1px solid var(--rule); }
.upload-row {
  display: grid;
  grid-template-columns: 20px 1fr auto 1fr;
  gap: 12px;
  align-items: baseline;
  padding: 8px 2px;
  border-bottom: 1px solid var(--rule);
  font-size: 12px;
}
.upload-row .mark { font-weight: 600; }
.upload-row.added .mark { color: var(--pass); }
.upload-row.rejected .mark { color: var(--warn); }
.upload-row.rejected .name { color: var(--warn); }
.upload-row .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upload-row .facts { color: var(--ink-muted); font-size: 11px; }
.upload-row .why { color: var(--ink-muted); }

.gallery-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 16px; }
.gallery-head h2 { font-size: 16px; }

.gallery { display: grid; gap: 22px; }
.gallery-card {
  display: grid;
  grid-template-columns: 1fr 260px;
  gap: 22px;
  align-items: start;
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 12px;
}
.gallery-strip { display: flex; gap: 12px; overflow-x: auto; }
.gallery-strip canvas {
  display: block;
  /* Width, not height.
   *
   * An iPhone panel is 1320×2868, so pinning the height to 300px pinned the width to 138px — the
   * screenshots were drawn at a tenth of their size and nothing on them could be read, which is the one
   * thing a template gallery exists to let somebody do. */
  width: 320px;
  height: auto;
  border-radius: 6px;
  box-shadow: var(--lift);
  background: var(--paper);
  flex: 0 0 auto;
}
.gallery-meta { display: grid; gap: 8px; align-content: start; }
.gallery-meta h3 { font-size: 14px; }
.gallery-meta .why { font-size: 12px; color: var(--ink-muted); margin: 0; line-height: 1.45; }

@media (max-width: 900px) {
  .gallery-card { grid-template-columns: 1fr; }
  .gallery-strip canvas { width: 240px; }
  .set-view { padding: var(--stage-pad); }
}
