/* ============================================================================
   components.css — every component in the storefront.
   ============================================================================

   House rule: no literal color, size, font or radius appears below this line.
   Everything is var(--token). If a value is needed that tokens.css does not
   provide, the token is missing — add it there rather than hard-coding here,
   or the client's design will not be able to reach it.

   State lives in the markup as data-* attributes, never as a color class.
   `data-verdict="warn"` says what a thing IS; this file decides what that
   looks like. So a brand that wants a different amber changes one token and
   touches no HTML and no JavaScript.
   ========================================================================= */

/* ── Reset, kept to what actually matters ───────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-base);
  -webkit-font-smoothing: antialiased;
}

:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}

/* ── Layout ────────────────────────────────────────────────────────────── */
.page { max-width: var(--page-max); margin: 0 auto; padding: var(--space-6) var(--space-5); }
.stack > * + * { margin-top: var(--space-4); }
.prose { max-width: var(--measure); }

.card {
  background: var(--surface);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  box-shadow: var(--shadow-sm);
}

/* ── Type ──────────────────────────────────────────────────────────────── */
h1, h2, h3 { margin: 0; line-height: var(--leading-tight); font-weight: var(--weight-bold); }
h1 { font-size: var(--text-xl); }
h2 { font-size: var(--text-lg); }
h3 { font-size: var(--text-md); }

/* Micro-label above a group. Uppercase needs the extra tracking or it sets
   as an unreadable block. */
.label {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--ink-muted);
}

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

/* Measurements, prices and dpi. Tabular figures so columns of numbers line up
   — a misread dimension is the exact failure preflight exists to prevent. */
.numeric {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.95em;
}

/* ── Verdict badge ─────────────────────────────────────────────────────── */
.badge {
  display: inline-block;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  background: var(--verdict-unknown-bg);
  color: var(--verdict-unknown);
}
.badge[data-verdict="pass"] { background: var(--verdict-pass-bg); color: var(--verdict-pass); }
.badge[data-verdict="warn"] { background: var(--verdict-warn-bg); color: var(--verdict-warn); }
.badge[data-verdict="fail"] { background: var(--verdict-fail-bg); color: var(--verdict-fail); }

/* ── Verdict banner ────────────────────────────────────────────────────── */
.verdict {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  border: var(--border-width) solid var(--verdict-unknown-line);
  border-left-width: var(--space-1);
  border-radius: var(--radius-md);
  background: var(--verdict-unknown-bg);
}
.verdict[data-verdict="pass"] { background: var(--verdict-pass-bg); border-color: var(--verdict-pass-line); }
.verdict[data-verdict="warn"] { background: var(--verdict-warn-bg); border-color: var(--verdict-warn-line); }
.verdict[data-verdict="fail"] { background: var(--verdict-fail-bg); border-color: var(--verdict-fail-line); }
.verdict h3 { font-size: var(--text-base); }
.verdict p  { margin: var(--space-1) 0 0; font-size: var(--text-sm); }
.verdict[data-verdict="pass"] h3 { color: var(--verdict-pass); }
.verdict[data-verdict="warn"] h3 { color: var(--verdict-warn); }
.verdict[data-verdict="fail"] h3 { color: var(--verdict-fail); }

/* ── Check row ─────────────────────────────────────────────────────────── */
.check-list { border-top: var(--border-width) solid var(--line); }
.check-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: var(--border-width) solid var(--line);
}
.check-row__name { font-weight: var(--weight-medium); }
.check-row__detail,
.check-row__message { grid-column: 2; font-size: var(--text-sm); }
.check-row__detail  { color: var(--ink-muted); }
.check-row__message { color: var(--ink); }

/* ── Guide overlay ─────────────────────────────────────────────────────── */
.stage {
  background: var(--surface);
  border: var(--border-width) solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  text-align: center;
  overflow: auto;
}
.stage canvas { max-width: 100%; height: auto; box-shadow: var(--shadow-md); }

.legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-4);
  font-size: var(--text-xs);
  color: var(--ink-muted);
}
.legend span { display: inline-flex; align-items: center; gap: var(--space-2); }
.legend i {
  width: var(--space-5);
  border-top: var(--rule-width) solid currentColor;
  display: inline-block;
}
.legend i[data-guide="bleed"] { color: var(--guide-bleed); border-top-style: dashed; }
.legend i[data-guide="trim"]  { color: var(--guide-trim); }
.legend i[data-guide="safe"]  { color: var(--guide-safe); border-top-style: dashed; }
.legend i[data-guide="fold"]  { color: var(--guide-fold); border-top-style: dotted; }
.legend i[data-guide="zone"]  { border-top: none; height: var(--space-3); background: var(--guide-zone); }

/* ── Requirements panel ──────────────────────────────────────────────────
   Shown before any upload. This is the screen that attacks the rejection rate,
   so it reads as guidance, not as a warning. */
.requirements { background: var(--surface-sunken); border-radius: var(--radius-md); padding: var(--space-4); }
.requirements dl { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2) var(--space-4); margin: 0; }
.requirements dt { color: var(--ink-muted); font-size: var(--text-sm); }
.requirements dd { margin: 0; font-weight: var(--weight-medium); }

/* ── Forms ─────────────────────────────────────────────────────────────── */
.field { display: block; }
.field > .label { display: block; margin-bottom: var(--space-2); }

.select, .input {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
}
.input::placeholder { color: var(--ink-subtle); }
.field__hint { margin-top: var(--space-2); font-size: var(--text-sm); color: var(--ink-muted); }

/* ── Buttons ───────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-sm);
  font: inherit;
  font-weight: var(--weight-medium);
  cursor: pointer;
  background: var(--brand);
  color: var(--brand-ink);
  transition: background var(--duration) var(--ease);
}
.btn:hover:not(:disabled) { background: var(--brand-hover); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn[data-variant="secondary"] {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line-strong);
}
.btn[data-variant="secondary"]:hover:not(:disabled) { background: var(--surface-sunken); }
.btn[data-variant="quiet"] {
  background: none;
  color: var(--ink-muted);
  padding-inline: var(--space-2);
}
.btn[data-variant="quiet"]:hover:not(:disabled) { color: var(--ink); background: none; }

/* ── Dropzone ──────────────────────────────────────────────────────────── */
.dropzone {
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
  padding: var(--space-7) var(--space-5);
  text-align: center;
  transition: border-color var(--duration) var(--ease), background var(--duration) var(--ease);
}
.dropzone[data-state="dragging"] { border-color: var(--brand); background: var(--surface); }
.dropzone[data-state="busy"]     { border-style: solid; }
.dropzone[data-state="done"]     { border-style: solid; border-color: var(--line); background: var(--surface); }
.dropzone__hint { font-size: var(--text-sm); color: var(--ink-muted); margin-top: var(--space-2); }

.progress {
  height: var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
  overflow: hidden;
  margin-top: var(--space-4);
}
.progress__bar { height: 100%; background: var(--brand); transition: width var(--duration) var(--ease); }

/* ── Product card ──────────────────────────────────────────────────────── */
.product-card { display: flex; flex-direction: column; gap: var(--space-3); }
.product-card__thumb {
  aspect-ratio: 4 / 3;
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  color: var(--ink-subtle);
  font-size: var(--text-xs);
}
/* The image sits inside the 4:3 well. `contain`, not `cover`: a yard sign and a
   bumper sticker have very different shapes, and cropping to fill would cut the
   piece the customer is buying. Letterboxing against the sunken surface is the
   honest result. */
.product-card__thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: inherit;
}
.product-card__price { font-size: var(--text-md); font-weight: var(--weight-bold); }
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr));
  gap: var(--space-5);
}

/* ── Cart line ─────────────────────────────────────────────────────────── */
.cart-line {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: var(--border-width) solid var(--line);
  align-items: start;
}
.cart-line__thumb {
  width: var(--space-8);
  aspect-ratio: 3 / 4;
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  font-size: var(--text-xs);
  color: var(--ink-subtle);
}
.cart-line__meta { font-size: var(--text-sm); color: var(--ink-muted); }
.cart-line__total { text-align: right; font-weight: var(--weight-medium); }
.cart-line[data-unavailable="true"] { opacity: 0.6; }

/* ── Order summary ─────────────────────────────────────────────────────── */
.summary__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) 0;
}
.summary__row[data-total="true"] {
  border-top: var(--border-width) solid var(--line);
  margin-top: var(--space-2);
  padding-top: var(--space-4);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
}

/* ── Notices and empty states ────────────────────────────────────────────
   An empty screen is an invitation to act, so it gets an action, not a shrug. */
.notice {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  border: var(--border-width) solid var(--verdict-unknown-line);
  background: var(--verdict-unknown-bg);
  font-size: var(--text-sm);
}
.notice[data-verdict="warn"] { border-color: var(--verdict-warn-line); background: var(--verdict-warn-bg); color: var(--verdict-warn); }
.notice[data-verdict="fail"] { border-color: var(--verdict-fail-line); background: var(--verdict-fail-bg); color: var(--verdict-fail); }

.empty {
  text-align: center;
  padding: var(--space-7) var(--space-5);
  color: var(--ink-muted);
}
.empty h3 { color: var(--ink); margin-bottom: var(--space-2); }

/* ── Steps ─────────────────────────────────────────────────────────────── */
.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.steps li {
  counter-increment: step;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-3);
  padding: var(--space-3) 0;
}
.steps li::before {
  content: counter(step);
  width: var(--space-6);
  height: var(--space-6);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}
.steps li[data-state="done"]::before { background: var(--verdict-pass-bg); color: var(--verdict-pass); }
.steps li[data-state="current"]::before { background: var(--brand); color: var(--brand-ink); }
.steps__note { font-size: var(--text-sm); color: var(--ink-muted); }

/* The one place a literal is unavoidable: CSS custom properties cannot be used
   in a media query condition. Breakpoints therefore live here rather than in
   tokens.css. Everything inside the query still uses tokens. */
@media (max-width: 640px) {
  .page { padding: var(--space-4) var(--space-3); }
  .cart-line { grid-template-columns: auto 1fr; }
  .cart-line__total { grid-column: 2; text-align: left; }
}
