/* PFD Chef Portal. Every colour and size lives here, at the top. Screens only
   ever use these names, so the look changes in one place. Values read off
   PFD's own store: Questrial, near-black, white, light grey cards, and the
   amber off the bar that runs across the top of their site. */
:root {
  --ink: #111111;        --ink-soft: #4a4a4a;   --ink-faint: #949494;
  --bg: #ffffff;         --bg-soft: #f4f4f4;    --card: #f9f9f9;   --line: #e1e1e1;
  --nav: #0b0b0b;        --nav-ink: #ffffff;
  /* Amber, sampled off PFD's own store. Rare on purpose: a strip, a pill, a
     chip, the cart badge. Everything else stays ink on white. */
  --amber: #f2a341;      --amber-soft: #fff3e2;  --amber-line: #f0d2a4;
  --amber-deep: #8a4b00;                          /* the same hue, dark enough to read on white */
  --accent: #111111;     --accent-ink: #ffffff;   /* prices and totals are ink, so the amber stays loud */
  --good: #1f7a3a;       --warn: #b45309;       --danger: #b40c1c; --danger-bg: #fcedee;
  --font: "Questrial", "Helvetica Neue", Arial, sans-serif;
  --radius: 14px;        --gutter: 16px;        --max: 960px;
  --ease: cubic-bezier(.2,.7,.2,1);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 16px/1.45 var(--font); letter-spacing: .01em;
  -webkit-font-smoothing: antialiased;
}
img { display: block; }
a { color: inherit; }

/* The amber strip, the way PFD run one across the top of their store. */
.strip {
  background: var(--amber); color: #2a1800;
  font-size: 13px; letter-spacing: .02em; text-align: center;
  padding: 9px max(var(--gutter), calc((100% - var(--max)) / 2));
}
.strip span { display: inline-block; max-width: 100%; }

/* The black nav. A faint grain so it reads as material, not flat paint. */
.nav {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px max(var(--gutter), calc((100% - var(--max)) / 2)); background: var(--nav); color: var(--nav-ink);
  background-image:
    radial-gradient(120% 140% at 0% 0%, rgba(255,255,255,.06), transparent 60%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .05 0'/></filter><rect width='120' height='120' filter='url(%23n)'/></svg>");
  box-shadow: 0 1px 0 rgba(255,255,255,.06), 0 8px 24px rgba(0,0,0,.18);
}
.brand { flex: none; }
.brand img { height: 56px; width: auto; }
@media (min-width: 640px) { .brand img { height: 68px; } }
.nav-right { display: flex; align-items: center; gap: 12px; min-width: 0; }
.nav-right .nav-label { max-width: 38vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-right .link { white-space: nowrap; }
.nav-label { font-size: 12px; letter-spacing: .18em; text-transform: uppercase; color: rgba(255,255,255,.7); }
/* The cart in the nav: a pressable target with the count sitting on it. */
.cart {
  position: relative; display: grid; place-items: center; flex: none;
  width: 42px; height: 42px; border-radius: 999px;
  color: var(--nav-ink); text-decoration: none;
  transition: background .2s var(--ease);
}
.cart svg { width: 22px; height: 22px; }
@media (min-width: 640px) { .cart svg { width: 25px; height: 25px; } }
.cart:hover { background: rgba(255,255,255,.12); }
.cart:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.cart:active { transform: scale(.94); }
.badge {
  position: absolute; top: 3px; right: 2px;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
  display: grid; place-items: center; font-size: 11px; line-height: 1;
  background: var(--amber); color: #2a1800; box-shadow: 0 0 0 2px var(--nav);
}
@media (prefers-reduced-motion: reduce) { .cart { transition: none; } .cart:active { transform: none; } }

.wrap { max-width: var(--max); margin: 0 auto; padding: 24px var(--gutter) 48px; }
body.wide { --max: 1240px; }

/* Heading block: small tracked eyebrow, a large quiet title, a hairline under. */
.head { padding-bottom: 18px; border-bottom: 1px solid var(--line); margin-bottom: 18px; }
.eyebrow { margin: 0 0 6px; font-size: 12px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-soft); }
.eyebrow::before { content: ""; display: inline-block; width: 18px; height: 2px; margin: 0 8px 4px 0; background: var(--amber); vertical-align: middle; }
h1 { margin: 0 0 6px; font-size: clamp(28px, 6vw, 40px); font-weight: 400; letter-spacing: -.01em; line-height: 1.1; }
.sub { margin: 0; color: var(--ink-soft); font-size: 16px; }
.ago { display: inline-block; margin-left: 8px; color: var(--ink-faint); font-size: 13px; }

/* Cards. One column on a phone, two from 640px. */
/* 🔴 align-items: start, or two cards in a row stretch to the taller of them.
   A long product name makes one card tall, its neighbour is stretched to match,
   and the stretch beats the photo's aspect-ratio: the picture grows into a
   column again, which is the thing this layout exists to stop. Each card is now
   its own height. */
.grid { display: grid; gap: 14px; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 640px) { .grid { grid-template-columns: 1fr 1fr; gap: 18px; } }

.card {
  position: relative;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; display: grid; grid-template-columns: 1fr;
  opacity: 0; transform: translateY(10px);
  animation: rise .55s var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * 60ms);
  transition: box-shadow .25s var(--ease), transform .25s var(--ease);
}
.card:hover { box-shadow: 0 10px 30px rgba(0,0,0,.08); }
@keyframes rise { to { opacity: 1; transform: none; } }

/* The whole card opens the pack page. An overlay rather than wrapping the card
   in an <a>, because the card contains a form and a form inside a link is
   invalid and swallows the button's click. Everything a chef can actually press
   is lifted above it below. */
.card-hit { position: absolute; inset: 0; z-index: 1; }
.card .card-media, .card .name-link, .card .add, .card .qty,
.card .btn, .card .ghost-btn { position: relative; z-index: 2; }

/* 🔴 The photograph sits across the top, not down the side.
   As a side column it was a grid item stretching to the card's height, and the
   cards are tall because the real product names run to three and four lines in
   a narrow column. So object-fit cropped every picture into a tall strip: a
   whole roast seen through a letterbox.
   Across the top it gets the card's full width, the ratio is fixed rather than
   inherited from whatever the text below happens to need, and the name gets the
   width back so it wraps once instead of four times. */
.card-media {
  background: linear-gradient(160deg, #ececec, #f6f6f6);
  display: grid; place-items: center; color: var(--ink-faint); font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase; text-align: center; padding: 8px;
  border-bottom: 1px solid var(--line);
  aspect-ratio: 3 / 2;
}
/* 🔴 Absolutely positioned, not just sized.
   In flow, height:100% of a parent whose own height comes from aspect-ratio is
   indeterminate, so the image falls back to its natural size and PUSHES the
   panel taller than the ratio. It only showed on the portrait shots: most of
   the catalogue is landscape and fitted inside the box by luck. Out of flow it
   cannot affect the panel's height at all, and object-fit crops it to the box. */
.card-media { overflow: hidden; min-height: 0; }
.card-media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.card-body { padding: 14px 14px 12px; min-width: 0; }
.card-top { display: flex; flex-wrap: wrap; align-items: start; gap: 6px 10px; }
.card-top .name { flex: 1 1 100%; }
@media (min-width: 640px) { .card-top { flex-wrap: nowrap; justify-content: space-between; } .card-top .name { flex: 1 1 auto; } }
.name { margin: 0; font-size: 19px; font-weight: 400; line-height: 1.2; }
.pill {
  flex: none; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  padding: 4px 9px; border-radius: 999px; background: var(--amber-soft);
  border: 1px solid var(--amber-line); color: var(--amber-deep);
}
.meta { margin: 6px 0 10px; font-size: 14px; color: var(--ink-soft); }
.meta b { font-weight: 400; color: var(--ink); }

/* The price block is the thing to remember: big offer, struck retail beside it. */
.price { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.offer { font-size: 26px; color: var(--accent); letter-spacing: -.01em; }
.offer small { font-size: 13px; color: var(--ink-soft); margin-left: 2px; }
.retail { color: var(--ink-faint); font-size: 14px; }
/* The sale badge, on the photo panel where every shop puts one. Out of the
   price row, so a long price can never push it onto a line of its own. */
.card-media, .pack-media { position: relative; }
.onsale {
  position: absolute; top: 8px; left: 8px; font-style: normal;
  font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
  background: var(--amber); color: #2a1800; padding: 3px 7px; border-radius: 999px;
}
.onsale.big { top: 12px; left: 12px; font-size: 12px; padding: 5px 10px; }
.approx { flex-basis: 100%; font-size: 12px; color: var(--ink-soft); }

.add { display: flex; gap: 10px; align-items: stretch; }
.qty { display: flex; align-items: center; gap: 6px; border: 1px solid var(--line); border-radius: 10px; padding: 0 10px; background: var(--bg); }
.qty span { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); }
.qty input { width: 44px; border: 0; background: transparent; font: inherit; font-size: 16px; text-align: center; padding: 10px 0; }
.btn {
  flex: 1; min-height: 44px; border: 0; border-radius: 10px; cursor: pointer;
  background: var(--ink); color: var(--nav-ink); font: inherit; font-size: 15px; letter-spacing: .02em;
  transition: transform .12s var(--ease), background .2s var(--ease);
}
.btn:hover { background: #2a2a2a; }
.btn:active { transform: scale(.97); }
.btn:focus-visible, .qty input:focus-visible { outline: 2px solid var(--amber-deep); outline-offset: 2px; }
.btn:hover { background: #2a2a2a; box-shadow: 0 6px 18px rgba(242,163,65,.28); }

.empty { grid-column: 1 / -1; text-align: center; color: var(--ink-soft); padding: 48px 0; }

@media (prefers-reduced-motion: reduce) {
  .card { animation: none; opacity: 1; transform: none; transition: none; }
  .btn { transition: none; }
}

/* A card that has just left the list fades out before app.js removes it. */
.card.gone { opacity: 0; transform: translateY(6px); transition: opacity .3s var(--ease), transform .3s var(--ease); animation: none; }

/* One-line status above the heading, for "the cart arrives next" and later notices. */
.notice {
  margin: 0 0 16px; padding: 12px 14px; border-radius: 10px;
  background: var(--bg-soft); border: 1px solid var(--line); color: var(--ink-soft); font-size: 14px;
}
.soldout { margin: 0; padding: 12px 0 2px; color: var(--ink-faint); letter-spacing: .12em; text-transform: uppercase; font-size: 12px; }

/* Sign-in, setup, forgot and reset. One narrow column, generous spacing. */
.auth { max-width: 420px; margin: 8vh auto 0; }
.auth h1 { margin-bottom: 4px; }
.stack { display: grid; gap: 14px; margin-top: 22px; }
.field { display: grid; gap: 6px; }
.field span { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-soft); }
.field input {
  font: inherit; font-size: 16px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg); color: var(--ink); transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.field input:focus { outline: 0; border-color: var(--amber-deep); box-shadow: 0 0 0 3px rgba(242,163,65,.35); }
.btn.wide { width: 100%; min-height: 48px; font-size: 16px; margin-top: 6px; }
.fine { margin-top: 18px; font-size: 13px; color: var(--ink-soft); }
.fine a { color: var(--amber-deep); }
.notice.error { background: var(--danger-bg); border-color: #f1b8bd; color: var(--danger); }
.inline { display: inline; }
.link { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; letter-spacing: .18em;
  text-transform: uppercase; color: rgba(255,255,255,.7); cursor: pointer; }
.link:hover { color: #fff; }
.auth, .notice { animation: rise .5s var(--ease) both; }

/* The cart: one line per held pack, its clock, and the totals. */
.lines { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.line {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card);
  animation: rise .45s var(--ease) both;
}
.line-main { display: grid; gap: 3px; min-width: 0; }
.line-name { font-size: 17px; }
.line-meta { font-size: 14px; color: var(--ink-soft); }
.line-hold { font-size: 12px; color: var(--good); letter-spacing: .02em; }
.line-hold.warn { color: var(--warn); }
.line-hold.warn .clock { animation: pulse 1s ease-in-out infinite; }
.line.expired { opacity: .55; border-style: dashed; }
.line.expired .line-hold { color: var(--danger); }
@keyframes pulse { 50% { opacity: .35; } }
.ghost { background: none; border: 0; font: inherit; font-size: 13px; color: var(--danger); cursor: pointer; padding: 8px; }
.total { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line); display: grid; gap: 12px; }
.total > div { display: flex; justify-content: space-between; align-items: baseline; }
.total strong { font-size: 26px; color: var(--accent); font-weight: 400; }
.btn:disabled { opacity: .4; cursor: not-allowed; }
a.btn, .ghost-btn { text-decoration: none; }
.ghost-btn { display: grid; place-items: center; background: var(--bg); color: var(--ink); border: 1px solid var(--ink); text-decoration: none; }
.ghost-btn:hover { background: var(--bg-soft); }

/* Order placed: the tick, the number, the receipt. */
.placed { max-width: 460px; margin: 24px auto 0; text-align: center; animation: rise .5s var(--ease) both; }
.tick {
  width: 64px; height: 64px; margin: 0 auto 18px; border-radius: 50%; display: grid; place-items: center;
  background: var(--good); color: #fff; font-size: 15px; letter-spacing: .1em;
  animation: pop .5s var(--ease) both;
}
@keyframes pop { from { transform: scale(.6); opacity: 0; } }
.placed h1 { font-size: 40px; margin: 0 0 8px; }
.receipt { list-style: none; margin: 22px 0; padding: 0; text-align: left; border-top: 1px solid var(--line); }
.receipt li { display: flex; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--line); font-size: 15px; }
.receipt li span:last-child { white-space: nowrap; }
.receipt .sum { font-size: 17px; color: var(--accent); }
.placed .stack { margin-top: 8px; }
@media (prefers-reduced-motion: reduce) { .line, .placed, .tick, .line-hold.warn .clock { animation: none; } }

/* Admin. Same skin, wider tables, a sub-nav under the black bar. */
.subnav { display: flex; gap: 4px; flex-wrap: wrap; margin: -8px 0 22px; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.subnav a { padding: 8px 12px; border-radius: 999px; text-decoration: none; font-size: 13px; color: var(--ink-soft); }
.subnav a.on { background: var(--ink); color: #fff; box-shadow: inset 0 -2px 0 var(--amber); }
.subnav a:hover:not(.on) { background: var(--bg-soft); }
/* The admin tiles.
   They were grey boxes on a grey page at equal weight, so the eye had nowhere
   to land. Now: white cards lifted off the page, an icon each so a tile is
   found by shape, and colour that MEANS something. Amber is live money and live
   stock, green is fine, red is a problem. The first tile is the money, and it
   is dressed as the answer to the question every admin opens this page with. */
.tiles { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); margin-bottom: 14px; }
.tiles.two { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.tile {
  position: relative; background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px 18px 16px; display: grid; gap: 6px; align-content: start;
  box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 6px 20px rgba(0,0,0,.04);
  animation: rise .5s var(--ease) both;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease);
}
.tile:hover { transform: translateY(-2px); box-shadow: 0 2px 4px rgba(0,0,0,.05), 0 12px 28px rgba(0,0,0,.07); }
.tiles .tile:nth-child(2) { animation-delay: 60ms; }
.tiles .tile:nth-child(3) { animation-delay: 120ms; }
.tiles .tile:nth-child(4) { animation-delay: 180ms; }
/* The small tracked label is the eyebrow; the dash before it is dropped inside
   a tile because the icon already says "here is a heading". */
.tile .eyebrow::before { display: none; }
.tile strong { font-size: 30px; font-weight: 400; line-height: 1.1; color: var(--ink); letter-spacing: -.01em; }
.tile small { color: var(--ink-soft); font-size: 13px; }
.tile-ico {
  position: absolute; top: 16px; right: 16px; width: 36px; height: 36px; border-radius: 10px;
  display: grid; place-items: center; background: var(--bg-soft); color: var(--ink-soft);
}
/* The money. */
.tile.hero { background: var(--ink); border-color: var(--ink); color: #fff; }
.tile.hero .eyebrow, .tile.hero small { color: rgba(255,255,255,.7); }
.tile.hero strong { color: #fff; font-size: 34px; }
.tile.hero .tile-ico { background: var(--amber); color: #2a1800; }
/* Live stock in somebody's cart. */
.tile.warm { border-color: var(--amber-line); background: var(--amber-soft); }
.tile.warm .tile-ico { background: var(--amber); color: #2a1800; }
.tile.warm strong { color: var(--amber-deep); }
/* Nothing wrong. */
.tile.good .tile-ico { background: #e6f2ea; color: var(--good); }
.tile.good strong { color: var(--good); font-size: 24px; }
/* Something wrong. */
.tile.bad { background: var(--danger-bg); border-color: #f1b8bd; }
.tile.bad strong { color: var(--danger); }
.tile.bad .tile-ico { background: #f6d3d6; color: var(--danger); }
@media (prefers-reduced-motion: reduce) { .tile, .tile:hover { animation: none; transform: none; transition: none; } }
.byrest { list-style: none; margin: 4px 0 0; padding: 0; font-size: 13px; }
.byrest li { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; border-bottom: 1px solid var(--line); }
.light { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 8px; vertical-align: middle; }
.light.ok { background: var(--good); box-shadow: 0 0 0 4px rgba(31,122,58,.15); } .light.bad { background: var(--danger); }
.tablewrap {
  overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 6px 20px rgba(0,0,0,.04);
}
.grid-table tbody tr { transition: background .15s var(--ease); }
.grid-table tbody tr:hover { background: #fcfaf6; }
.grid-table td b { font-weight: 400; color: var(--ink); }

/* A live pack: its photograph, its name, and its stock as a picture. */
.item { display: flex; align-items: center; gap: 12px; min-width: 220px; }
.item .thumb {
  flex: none; width: 52px; height: 52px; border-radius: 10px; overflow: hidden;
  background: linear-gradient(160deg, #ececec, #f6f6f6);
}
.item .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.item-name { display: block; color: var(--ink); line-height: 1.3; }
.item small { display: block; margin-top: 2px; }

/* The stock bar. Sold green, held amber, left grey, as shares of what was
   pulled. Answers "how is this pack doing" before a single number is read. */
.stockcell { min-width: 200px; }
.stockbar {
  display: flex; height: 8px; border-radius: 999px; overflow: hidden;
  background: #ececec; margin: 6px 0 7px;
}
.stockbar i { display: block; height: 100%; }
.stockbar .sold { background: var(--good); }
.stockbar .held { background: var(--amber); }
.grid-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.grid-table th { text-align: left; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft); padding: 12px 14px; border-bottom: 1px solid var(--line); background: var(--bg); white-space: nowrap; }
.grid-table td { padding: 12px 14px; border-bottom: 1px solid var(--line); vertical-align: top; }
.grid-table tr:last-child td { border-bottom: 0; }
.grid-table small { color: var(--ink-soft); }
.grid-table tr.off td { color: var(--ink-faint); }
/* The note on a hidden pack. `.grid-table small` is more specific than a bare
   .warn-text, so without this the one line explaining a missing pack is grey. */
.grid-table .warn-text { color: var(--warn); }
.grid-table td.empty { text-align: center; color: var(--ink-soft); padding: 32px; }
.actions { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.small { font: inherit; font-size: 12px; padding: 6px 10px; border-radius: 8px; border: 1px solid var(--ink); background: var(--bg); color: var(--ink); cursor: pointer; white-space: nowrap; }
.small:hover { background: var(--ink); color: #fff; } .small:disabled { opacity: .35; cursor: not-allowed; }
.ok-text { color: var(--good); } .warn-text { color: var(--warn); } .bad-text { color: var(--danger); }
.split { display: grid; gap: 18px; grid-template-columns: 1fr; }
@media (min-width: 900px) { .split { grid-template-columns: 3fr 2fr; align-items: start; } }
.panel { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px; }
.panel.narrow { max-width: 560px; }
.panel h2 { margin: 0 0 4px; }
.field select { font: inherit; font-size: 16px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg); }
/* Setting a password on a row: a box and a button, in the column that already
   stacks the other per-row actions. */
.setpw { display: flex; gap: 6px; }
.setpw input {
  font: inherit; font-size: 12px; padding: 6px 8px; width: 132px; min-width: 0;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--ink);
}
.setpw input:focus { outline: 0; border-color: var(--amber-deep); box-shadow: 0 0 0 3px rgba(242,163,65,.35); }
a.nav-label { text-decoration: none; }

/* The single pack page. Same vocabulary as a card, given the room a phone
   screen can spare: photo panel on top, facts underneath, spec last. */
.crumb { margin: 0 0 14px; font-size: 13px; color: var(--ink-soft); }
.crumb a { color: var(--ink-soft); text-decoration: none; }
.crumb a:hover { color: var(--ink); }
.name-link { text-decoration: none; }
.name-link:hover { text-decoration: underline; }
/* A phone has no hover, so the name carries its own "there is more here" mark. */
.name-link::after { content: " \203A"; color: var(--ink-faint); }
a.card-media { text-decoration: none; }
.pack {
  display: grid; border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: var(--card); animation: rise .5s var(--ease) both;
}
/* Same ratio as a card, for the same reason. On a phone this sits above the
   details, so it is simply a wide photograph. */
.pack-media {
  position: relative; overflow: hidden;
  background: linear-gradient(160deg, #ececec, #f6f6f6); display: grid; place-items: center;
  aspect-ratio: 3 / 2; color: var(--ink-faint); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; border-bottom: 1px solid var(--line);
}
.pack-body { padding: 20px 18px 22px; min-width: 0; }
.pack-spec { padding: 4px 18px 22px; min-width: 0; }
/* Two columns of facts on a wide screen: eight rows in one column is a long
   thin list under a wide photograph, and the page ends up scrolling for no
   reason. */
@media (min-width: 720px) { .pack-spec .spec { columns: 2; column-gap: 40px; } }
.pack-name { margin: 0; font-size: clamp(22px, 5vw, 30px); font-weight: 400; line-height: 1.15; flex: 1 1 auto; }
.approx-line { margin: -4px 0 12px; font-size: 13px; color: var(--ink-soft); }
.pack .fine { margin-top: 12px; }
.spec-h { margin: 26px 0 8px; font-size: 12px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft); font-weight: 400; }
.spec-h::before { content: ""; display: inline-block; width: 18px; height: 2px; margin: 0 8px 4px 0; background: var(--amber); vertical-align: middle; }
.spec { margin: 0; border-top: 1px solid var(--line); }
.spec > div { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.spec dt { color: var(--ink-soft); font-size: 14px; }
.spec dd { margin: 0; text-align: right; font-size: 14px; }
@media (min-width: 720px) {
  /* Near enough half each. The photograph was 340px against a spec table that
     runs to eight rows, so it was the narrower half of the page stretched to
     the taller half's height, so the photograph was a letterbox. */
  .pack { grid-template-columns: 1fr 1fr; align-items: start; }
  /* 🔴 align-self: start, and a ratio rather than min-height: 100%.
     Stretching to the row is exactly what made it vertical, and the row is as
     tall as the details beside it, which is not a fact about the photograph.
     4:3 here rather than the card's 3:2: at half the page it is already large,
     and a little more height reads as a product shot rather than a banner. */
  /* Top-aligned. The details column is the taller of the two and always will
     be (eight spec rows against one photograph), so there is space left over
     whatever happens. Aligned to the top it reads as a photograph with the page
     continuing beside it; centred it floats, with dead space above AND below
     and the sale badge stranded in the middle of the page.
     The divider lives on the body so it runs the card's full height rather than
     stopping wherever the picture happens to end. */
  .pack-media { align-self: start; aspect-ratio: 4 / 3; border-bottom: 0; }
  .pack-body { border-left: 1px solid var(--line); }
  /* Full width, under both. */
  .pack-spec { grid-column: 1 / -1; border-top: 1px solid var(--line); }
}
@media (prefers-reduced-motion: reduce) { .pack { animation: none; } }
/* On a wide screen the button does not need the whole column. */
@media (min-width: 720px) { .pack .add, .pack .btn.wide { max-width: 420px; } }

/* The admin's date range. A plain GET form, so the range lives in the URL and a
   filtered view can be bookmarked or sent to somebody. */
.filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  padding: 12px 14px; margin: 0 0 18px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
}
.filters label {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft);
}
.filters input[type="date"] {
  font: inherit; font-size: 15px; color: var(--ink); text-transform: none; letter-spacing: 0;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg);
}
.filters input[type="date"]:focus-visible { outline: 2px solid var(--amber-deep); outline-offset: 1px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
.chip {
  font-size: 12px; letter-spacing: .06em; text-decoration: none;
  padding: 7px 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink-soft);
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.chip:hover { border-color: var(--amber-line); color: var(--ink); }
/* The range you are looking at, so the answer to "what am I seeing" is on screen. */
.chip.on { background: var(--amber-soft); border-color: var(--amber-line); color: var(--amber-deep); }
@media (max-width: 560px) { .chips { margin-left: 0; width: 100%; } }

/* A photograph fills its panel and crops rather than letterboxing: the packs
   are photographed at several aspect ratios and a row of different-shaped
   pictures reads as a broken page. */
/* Out of flow, or a portrait source pushes the panel past its ratio. Same trap
   as the cards; it only shows on the portrait shots in the catalogue. */
.pack-media img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}

/* The Shopify export picker on the admin's Photos screen. */
.filelabel {
  display: block; padding: 18px; border: 1px dashed var(--line); border-radius: var(--radius);
  background: var(--bg); font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-soft);
}
.filelabel input { display: block; margin-top: 12px; font: inherit; text-transform: none; letter-spacing: 0; }
.filelabel.busy { opacity: .55; }
.filelabel.busy::after { content: " reading…"; text-transform: none; letter-spacing: 0; }

/* The saving, the way the client's own page shows it: a third number under
   was and now, in green because it is the good news. */
.save { flex-basis: 100%; font-size: 14px; color: var(--good); letter-spacing: .01em; }
.save small { font-size: 12px; color: var(--good); opacity: .8; margin-left: 1px; }

/* Search above the list. It was a white box on a white page with a hairline
   border, which reads as a divider rather than something to type into. A
   magnifying glass, a real border, and a lifted surface say "search" before
   anyone reads the placeholder. The icon is an inline SVG background, so it
   costs no request and cannot fail to load. */
.search { display: block; margin: 0 0 20px; position: relative; }
/* An author display rule beats the browser's own [hidden] rule, so without
   this the attribute the script sets would hide nothing. */
.search[hidden] { display: none; }
.search input {
  width: 100%; font: inherit; font-size: 17px; color: var(--ink);
  padding: 16px 18px 16px 52px;
  border: 1.5px solid #cfcfcf; border-radius: 999px;
  background: #fff url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23111' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><line x1='21' y1='21' x2='16.2' y2='16.2'/></svg>") no-repeat 20px center;
  box-shadow: 0 2px 10px rgba(0,0,0,.05);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.search input::placeholder { color: #6b6b6b; }
.search input:hover { border-color: #a8a8a8; }
/* Amber on focus, the one colour this site keeps for "look here". */
.search input:focus {
  outline: none; border-color: var(--amber);
  box-shadow: 0 0 0 4px var(--amber-soft), 0 2px 10px rgba(0,0,0,.05);
}
/* The browser's own clear-x sits on top of our padding; keep it tidy. */
.search input::-webkit-search-cancel-button { cursor: pointer; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.card[hidden] { display: none; }
