/* =====================================================================
 * storefront.css — the storefront (VM1) theme (P4).
 *
 * Replaces base.html's throwaway inline <style>. This is the SINGLE
 * themeable seam: EVERY color, font-family, radius, and box metric the
 * storefront renders comes from the :root token block below, modeled on
 * export-template/app/source/css/variables.css. The UI builder re-themes the
 * whole store by changing token VALUES here — never by rewriting templates or
 * hunting hex literals scattered through the markup.
 *
 * DISCIPLINE (Senior/QA-enforced):
 *   - NO hard-coded color anywhere outside :root. A grep for #[0-9a-fA-F]{3,8}
 *     outside the :root block must come back empty.
 *   - NO hard-coded font-family outside :root — always var(--font-family-*).
 *   - The BT card fields' Layer-B `style` object (checkout.html) reads THESE
 *     SAME tokens at runtime via getComputedStyle(:root) — it hand-duplicates
 *     no color (see checkout.html §4).
 *
 * CSP: served under the storefront's tight base policy — style-src 'self'
 * (this file) + font-src 'self' (the @font-face woff2 below). ZERO CSP change
 * (see security.py build_csp; base already allows 'self' for both). No inline
 * <style>, no external font host.
 * ===================================================================== */

/* ---------------------------------------------------------------------
 * Self-hosted Poppins (OPTION A — no third-party font request from our page).
 *
 * WEIGHT PARITY + DRIFT NOTE: the export-template loads Poppins 200-700 +
 * italics from Google (base/master.html). P4 renders only body text (400) and
 * headings/labels/buttons (600), so we self-host a TRIMMED subset: 400 + 600,
 * normal style only. A self-hosted subset can DRIFT from the export-template's
 * Google-hosted Poppins version; keeping the subset minimal shrinks that
 * surface. If the design later needs medium (500), add a matching @font-face +
 * woff2 here — do not reach for a Google host (that would require a CSP change).
 *
 * LICENSE: Poppins is OFL-1.1 (permits self-hosting/redistribution). The
 * license text ships alongside the fonts at static/fonts/OFL.txt.
 *
 * The card fields (BT iframes) get Poppins from BT's OWN context via the
 * checkout's fonts:[css2-url] — that fetch is governed by BT's CSP, not ours,
 * so it needs nothing from our policy (see spec §2).
 * --------------------------------------------------------------------- */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/poppins-latin-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/poppins-latin-600.woff2') format('woff2');
}

/* =====================================================================
 * :root — the ONLY place a raw color / font-family / box metric is written.
 * ===================================================================== */
:root {
  /* Typography — mirrors export-template variables.css (Poppins + system
     fallbacks so the store is still legible before/if the woff2 loads). */
  --font-family-base: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-family-heading: var(--font-family-base);
  --font-size-base: 1rem;
  --font-size-sm: 0.875rem;
  --font-size-lg: 1.25rem;
  --font-size-xl: 1.6rem;
  --line-height-base: 1.5;
  --font-weight-normal: 400;
  --font-weight-semibold: 600;

  /* Color tokens — every color in the member comes from here. The three raw
     hex literals the throwaway base.html carried (#b00020, #005a2b, #555) are
     folded into --color-error / --color-success / --color-text-muted. */
  --color-text: #1a1a1a;
  --color-text-muted: #555555;          /* was the raw #555 in base.html */
  --color-bg: #ffffff;
  --color-surface: #fafafa;
  --color-border: #cccccc;
  --color-focus: #2b6cb0;
  --color-error: #b00020;               /* was the raw #b00020 in base.html */
  --color-success: #005a2b;             /* was the raw #005a2b in base.html */
  --color-primary: #1a1a1a;
  --color-primary-text: #ffffff;
  --color-primary-hover: #333333;
  --color-link: #2b6cb0;

  /* Card-input inner tokens (also read by the BT Layer-B style object). */
  --color-input-placeholder: #8a8a8a;
  --color-input-disabled: #8a8a8a;

  /* Status badge tints. */
  --color-badge-bg: #eef1f4;
  --color-badge-text: #33404d;

  /* Box tokens — ALSO the Layer-A card-wrapper source (§4): .field-box reads
     border/radius/background/padding from these, so the BT wrapper matches our
     own inputs exactly. */
  --radius: 6px;
  --border-width: 1px;
  --input-padding: 0.55rem;
  --space: 0.6rem;
  --space-lg: 1.25rem;
  --page-width: 60rem;
  --form-width: 26rem;
}

/* =====================================================================
 * Base elements — the account/* templates inherit ALL of their restyle from
 * here (they use semantic <label>/<input>/<button>/.msg markup), so P4 does
 * not rewrite those templates: changing a token here re-themes them too.
 * ===================================================================== */
* { box-sizing: border-box; }

body {
  font-family: var(--font-family-base);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: var(--color-text);
  background: var(--color-bg);
  max-width: var(--page-width);
  margin: 2.5rem auto;
  padding: 0 1.25rem;
}

/* Site header (brand). base.html renders exactly one <h1> per page (the brand);
   page titles below are <h2> — headings stay in order, one <h1>/page. */
header { margin-bottom: var(--space-lg); border-bottom: var(--border-width) solid var(--color-border); padding-bottom: var(--space); }
h1 { font-family: var(--font-family-heading); font-size: var(--font-size-xl); font-weight: var(--font-weight-semibold); margin: 0; }
h1 a { color: var(--color-text); text-decoration: none; }
h1 a:hover { color: var(--color-primary-hover); }
h2 { font-family: var(--font-family-heading); font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin: 0 0 var(--space) 0; }
h3 { font-family: var(--font-family-heading); font-size: var(--font-size-base); font-weight: var(--font-weight-semibold); margin: 0 0 0.3rem 0; }

a { color: var(--color-link); }

/* Forms shared by the account/* pages and P4 pages. Auth forms are naturally
   narrow — cap them so they don't stretch to the full page width. */
form { max-width: var(--form-width); }
label { display: block; margin: var(--space) 0; font-size: var(--font-size-sm); color: var(--color-text); }
input, select {
  display: block; width: 100%; margin-top: 0.2rem;
  padding: var(--input-padding);
  font-family: var(--font-family-base); font-size: var(--font-size-base);
  color: var(--color-text); background: var(--color-bg);
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius);
}
input:focus, select:focus { outline: none; border-color: var(--color-focus); box-shadow: 0 0 0 3px var(--color-focus); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-focus) 25%, transparent); }

button, .button {
  display: inline-block;
  padding: 0.55rem 1.1rem; margin-top: var(--space);
  font-family: var(--font-family-base); font-size: var(--font-size-base); font-weight: var(--font-weight-semibold);
  color: var(--color-primary-text); background: var(--color-primary);
  border: var(--border-width) solid var(--color-primary); border-radius: var(--radius);
  text-decoration: none; cursor: pointer;
}
button:hover, .button:hover { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }
button:disabled { opacity: 0.55; cursor: not-allowed; }
button.secondary, .button.secondary { color: var(--color-text); background: var(--color-bg); border-color: var(--color-border); }
button.secondary:hover, .button.secondary:hover { background: var(--color-surface); }

/* Messages (info/error) — the .msg/.msg.error/.msg.info the auth pages use. */
.msg { min-height: 1.2rem; margin: var(--space) 0; font-size: var(--font-size-sm); }
.msg.error { color: var(--color-error); }
.msg.info { color: var(--color-success); }

nav { margin: var(--space-lg) 0; }
nav a { margin-right: 0.9rem; font-size: var(--font-size-sm); }

dl { margin: var(--space) 0; }
dt { font-weight: var(--font-weight-semibold); font-size: var(--font-size-sm); color: var(--color-text-muted); }
dd { margin: 0 0 var(--space) 0; }

.muted { color: var(--color-text-muted); }
.price { font-weight: var(--font-weight-semibold); }
.empty-state { color: var(--color-text-muted); padding: var(--space-lg) 0; }

/* Status badge — driven by the shared JS status label-map (base.html). */
.badge {
  display: inline-block; padding: 0.1rem 0.5rem; border-radius: var(--radius);
  font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold);
  background: var(--color-badge-bg); color: var(--color-badge-text);
}

/* =====================================================================
 * Catalog (GET /) — flat responsive product grid.
 * ===================================================================== */
/* The list-reset box metrics (list-style/padding/margin) live HERE, not as an
   inline style on the <ul> — same token/discipline as the rest of the unit. */
.catalog { list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: var(--space-lg); margin: var(--space-lg) 0 0 0; padding: 0; }
.product-card {
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius);
  background: var(--color-surface); padding: var(--space-lg);
  display: flex; flex-direction: column; gap: 0.4rem;
}
.product-card h3 { margin: 0; }
.product-card a.card-link { color: var(--color-text); text-decoration: none; }
.product-card a.card-link:hover h3 { color: var(--color-primary-hover); }
.product-card .price { font-size: var(--font-size-lg); }
.product-card .delivery-hint { font-size: var(--font-size-sm); color: var(--color-text-muted); }
.product-card .oos { color: var(--color-error); font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); }

/* =====================================================================
 * Product detail (GET /p/<id>) + checkout order summary.
 * ===================================================================== */
.product-detail { max-width: 40rem; }
.product-detail .price { font-size: var(--font-size-xl); display: block; margin: var(--space) 0; }
.product-detail .description { color: var(--color-text); margin: var(--space) 0; white-space: pre-wrap; }
.availability { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); }
.availability.in { color: var(--color-success); }
.availability.out { color: var(--color-error); }

.order-summary {
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius);
  background: var(--color-surface); padding: var(--space-lg); margin: var(--space-lg) 0;
  max-width: 30rem;
}
.order-summary .row { display: flex; justify-content: space-between; margin: 0.3rem 0; }
.order-summary .row.total { font-weight: var(--font-weight-semibold); border-top: var(--border-width) solid var(--color-border); padding-top: 0.4rem; margin-top: 0.4rem; }

/* =====================================================================
 * §4 Layer-A — the BT card-field OUTER wrapper. Border, radius, background,
 * padding, and the focus ring live HERE (the BT inner input cannot take them;
 * emitting them into the Layer-B style object is silently ignored → drift).
 * Sourced from the SAME tokens as our own `input` above, so the wrapper matches.
 * ===================================================================== */
.checkout-form { max-width: 30rem; }
.field { margin: var(--space) 0; }
.field > label { margin-bottom: 0.2rem; }
.field-box {
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  padding: var(--input-padding);
}
/* BT emits focus/blur events; checkout.js toggles .is-focused on the wrapper. */
.field-box.is-focused { border-color: var(--color-focus); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-focus) 25%, transparent); }
.field-box.is-error { border-color: var(--color-error); }
/* Per-field aria-live error slot (the reason for split elements — one error
   node per field, never one generic "check details"). */
.field-error { min-height: 1.1rem; margin-top: 0.2rem; font-size: var(--font-size-sm); color: var(--color-error); }

/* =====================================================================
 * Order history (GET /account/orders) + receipt (GET /account/orders/<id>).
 * ===================================================================== */
.order-table { width: 100%; border-collapse: collapse; margin-top: var(--space-lg); }
.order-table th, .order-table td { text-align: left; padding: var(--space); border-bottom: var(--border-width) solid var(--color-border); font-size: var(--font-size-sm); }
.order-table th { color: var(--color-text-muted); font-weight: var(--font-weight-semibold); }
.order-table .order-id { font-family: var(--font-family-base); font-weight: var(--font-weight-semibold); }

.receipt { max-width: 40rem; }
.receipt .order-id-line { font-size: var(--font-size-lg); font-weight: var(--font-weight-semibold); margin: var(--space) 0; }
.receipt dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.2rem var(--space-lg); }
.receipt dt { margin: 0; }
.receipt dd { margin: 0; }

/* =====================================================================
 * Return / confirmation (GET /order/<order_number>) — the go/no-go panel.
 * The outcome tint is chosen by a SINGLE class the JS sets from the derived
 * outcome; no per-outcome hex outside :root.
 * ===================================================================== */
.outcome-panel {
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius);
  background: var(--color-surface); padding: var(--space-lg); margin: var(--space-lg) 0;
}
.outcome-panel.is-good { border-color: var(--color-success); }
.outcome-panel.is-pending { border-color: var(--color-focus); }
.outcome-panel.is-declined { border-color: var(--color-error); }
.outcome-panel h2 { margin-top: 0; }
.outcome-panel .outcome-detail { color: var(--color-text); margin: var(--space) 0 0 0; }
.outcome-panel .order-id-line { font-weight: var(--font-weight-semibold); margin-top: var(--space); }

/* Small responsive guard: on narrow screens the summary/receipt go full width. */
@media (max-width: 30rem) {
  .order-summary, .checkout-form, .receipt, .product-detail { max-width: 100%; }
  .receipt dl { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------------
   CATEGORY FILTER + CHIPS (CCM-093 S1)

   The filter nav sits above the catalogue; the chips also appear on a product
   page, linking back to the catalogue with that category preselected. Both use
   the same .cat-chip so a shopper learns one affordance.

   ★ [hidden] IS GIVEN AN EXPLICIT RULE. .product-card sets `display`, which
   BEATS the browser's default `[hidden] { display: none }` — so without this the
   filter would set the attribute and nothing would visibly happen. The same trap
   the admin's `.hidden` class documents.
--------------------------------------------------------------------------- */
.product-card[hidden] { display: none !important; }

.cat-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 1.25rem;
}
.cat-chip {
  display: inline-block;
  padding: 0.3rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  font-size: 0.9rem;
  text-decoration: none;
  color: inherit;
}
.cat-chip:hover { border-color: var(--color-primary); }
.cat-chip[aria-current="true"] {
  border-color: var(--color-primary);
  font-weight: var(--font-weight-semibold);
}
.cat-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 1rem; }

/* ---------------------------------------------------------------------------
   PRODUCT IMAGES (CCM-093 S2)

   ★ Sized by max-width/height with object-fit, NOT by fixed dimensions: images
   are stored as-received (no re-encode this version), so the storefront receives
   whatever aspect ratio the merchant uploaded and must not distort it.

   A product with no image renders NO <img> at all, so there is nothing here to
   reserve space for — the card is simply a text card.
--------------------------------------------------------------------------- */
.product-thumb {
  display: block;
  width: 100%;
  max-height: 220px;
  object-fit: contain;
  margin: 0 0 0.75rem;
  background: var(--color-surface);
}
.product-hero {
  display: block;
  max-width: 100%;
  max-height: 420px;
  object-fit: contain;
  margin: 0 0 1rem;
  background: var(--color-surface);
}

/* -- account nav in the header (CCM-139) ------------------------------------
   The header is a flex row so the brand and the account links sit on one line.
   `nav.acct` is scoped so it does not inherit the page-level `nav` margins,
   which are sized for in-page navigation rather than header chrome.
   -------------------------------------------------------------------------- */
header { display: flex; align-items: baseline; justify-content: space-between;
         gap: var(--space); flex-wrap: wrap; }
nav.acct { margin: 0; }
nav.acct a { margin-right: 0; margin-left: 0.9rem; }

/* ---------------------------------------------------------------------------
   RESPONSIVE (CCM-143 item 4)

   This surface started in far better shape than the admin — the catalogue grid is
   `auto-fill minmax(14rem, 1fr)`, the header already wraps, and the base font is
   1rem so mobile Safari never zooms on focus. The gaps were narrower:

   ★ THE ORDER TABLES OVERFLOWED. `.order-table` is `width: 100%` with four
     columns and `--space` padding, which does not fit a phone: the widest cell
     wins and the whole PAGE gains a horizontal scrollbar, so body text is cut off
     too. The table becomes its own scroll container — a page that scrolls
     sideways is a broken page; a table that does is a table.

   ★ 2.5rem OF DEAD SPACE ABOVE THE FOLD. The body's desktop vertical margin costs
     a phone most of a thumb's worth of first screen.

   ★ ONE BREAKPOINT AT 30rem ONLY. That is a phone boundary; a tablet in portrait
     (~768px) got the full desktop treatment. The layout is fluid enough that this
     was not broken, but the reading measure and the spacing were tuned for a
     60rem page, so the tablet block below tightens spacing without changing
     structure.
   --------------------------------------------------------------------------- */

/* Tablet and below. */
@media (max-width: 48rem) {
  body { margin: 1.5rem auto; }
  /* The table is the scroll container, never the page. `display: block` makes the
     table box scroll its own overflow; max-width bounds it to the viewport. */
  .order-table { display: block; max-width: 100%; overflow-x: auto; }
  .order-table th, .order-table td { white-space: nowrap; }
}

/* Phone. The existing 30rem block above still applies; this adds to it. */
@media (max-width: 30rem) {
  body { margin: 1rem auto; padding: 0 0.9rem; }
  h1 { font-size: var(--font-size-lg); }
  /* Full-width, thumb-sized actions: a button that shares a row with another on a
     360px screen gives both of them too small a target. */
  button, .button { width: 100%; min-height: 44px; }
  /* Except where they are deliberately a row of links (the return page's nav). */
  nav .button { width: auto; }
  /* Account links stack under the brand rather than crowding it. */
  nav.acct a { margin-left: 0; margin-right: 0.9rem; }
}
