/* Stock tool. Mobile first - most of the real use is on a phone, often
   one-handed at an event, so tap targets stay large and nothing important
   hides behind a hover. */

/* White with a muted pink, at Flo's request. The ground is as close to white
   as it can be while still looking chosen rather than unset - a whisper of
   pink in it, so the white cards on top read as cards rather than as nothing.
   Nothing here is a bright pink: the accent is a dusty rose and it is the only
   saturated thing on the page.
   Every pair is contrast-checked rather than picked by eye. The accent carries
   white button text at 4.68:1, and the faint text that labels item numbers and
   quantities sits at 4.87:1 on white - which matters on a phone held at arm's
   length in whatever light an event happens to have. */
:root {
  --bg: #fdfbfc;
  --surface: #ffffff;
  --surface-2: #f8f2f4;
  --ink: #2c2428;
  --ink-soft: #6b5c62;
  --ink-faint: #7d6d74;
  --rule: #ecdfe4;
  --accent: #a85d75;
  --accent-ink: #ffffff;
  --accent-soft: #fbeef2;
  --good: #3f6b4f;
  --good-soft: #e9f2ec;
  --warn: #8a6524;
  --warn-soft: #f9f0dd;
  /* Deliberately redder than the accent rather than a deeper pink: "Remove"
     and "Cancel order" must not read as just another brand-coloured button. */
  --bad: #973a33;
  --bad-soft: #fae9e7;
  --radius: 12px;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #191316;
    --surface: #211a1e;
    --surface-2: #2a2125;
    --ink: #f3ebee;
    --ink-soft: #c3b2b9;
    --ink-faint: #94838b;
    --rule: #3a2e34;
    /* Lifted and softened for a dark ground - the light-mode rose would be
       muddy here, and this still reads as the same colour family. */
    --accent: #e2a0b4;
    --accent-ink: #21161a;
    --accent-soft: #33222a;
    --good: #8fb899;
    --good-soft: #1f2a22;
    --warn: #d9b169;
    --warn-soft: #2b2417;
    --bad: #e0918a;
    --bad-soft: #2f1e1d;
  }
}

/* The STOREFRONT pins itself light (data-theme="light" on <html> in
   shop_layout.php) - it is a shopfront, and Flo's branding should not invert
   itself because a customer's phone happens to be in dark mode. Her own admin
   carries no such attribute and still follows the device.

   color-scheme has to be stated too, not just the custom properties: without
   it the UA still paints its OWN widgets dark on a dark-mode phone - text
   inputs, select dropdowns, scrollbars, autofill - so the page would be white
   with black form fields sitting in it. */
:root[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; }

/* The browser's own [hidden] rule is an attribute selector, so ANY author
   class rule that sets display beats it - which meant the QR overlay, styled
   `display: grid`, sat permanently open over every invoice page. Restate it
   with !important so `el.hidden = true` reliably hides things. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
}

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

/* ---- top bar -------------------------------------------------------- */

.bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
  position: sticky;
  top: 0;
  z-index: 20;
}
.bar-brand {
  font-weight: 700;
  text-decoration: none;
  color: var(--ink);
  margin-right: auto;
}
.bar-nav { display: flex; align-items: center; gap: 14px; font-size: 15px; }
.bar-nav a { text-decoration: none; }
.bar-nav a:hover { text-decoration: underline; }
.inline { display: inline; margin: 0; }
.linkish {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--accent); font: inherit; font-size: 15px;
}
.linkish:hover { text-decoration: underline; }

/* ---- layout --------------------------------------------------------- */

.wrap {
  max-width: 62rem;
  margin: 0 auto;
  padding: 20px 16px 72px;
}
.wrap.narrow { max-width: 30rem; }

h1 { font-size: 1.5rem; margin: 0 0 4px; text-wrap: balance; }
h2 { font-size: 1.12rem; margin: 28px 0 10px; }
.sub { color: var(--ink-soft); margin: 0 0 20px; }

.row-between {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 16px;
}

/* ---- flashes -------------------------------------------------------- */

.flash {
  padding: 11px 14px; border-radius: var(--radius); margin: 0 0 16px;
  border-left: 4px solid var(--good); background: var(--good-soft); color: var(--ink);
}
.flash-error { border-left-color: var(--bad); background: var(--bad-soft); }
.flash-warn  { border-left-color: var(--warn); background: var(--warn-soft); }

/* ---- cards / panels ------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 16px;
  margin: 0 0 14px;
}

.tiles { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 560px) { .tiles { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 860px) { .tiles.three { grid-template-columns: repeat(3, 1fr); } }

.tile-num { font-size: 26px; font-weight: 700; color: var(--accent); display: block; line-height: 1.15; }
.tile-label { color: var(--ink-faint); font-size: 13px; text-transform: uppercase; letter-spacing: .06em; }

/* ---- forms ---------------------------------------------------------- */

label { display: block; font-size: 14px; color: var(--ink-soft); margin: 0 0 4px; }
.field { margin: 0 0 14px; }

input[type=text], input[type=email], input[type=password], input[type=number],
input[type=search], select, textarea {
  width: 100%;
  padding: 11px 12px;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
  min-height: 44px;           /* comfortable on a phone */
}
textarea { min-height: 88px; resize: vertical; }
input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 11px 16px; min-height: 44px;
  font: inherit; font-weight: 600;
  border: 1px solid var(--accent); border-radius: 10px;
  background: var(--accent); color: var(--accent-ink);
  cursor: pointer; text-decoration: none;
}
.btn:hover { filter: brightness(1.06); }
.btn-quiet { background: var(--surface); color: var(--ink); border-color: var(--rule); }
.btn-small { padding: 6px 10px; min-height: 36px; font-size: 14px; }
.btn-danger { background: var(--bad-soft); color: var(--bad); border-color: var(--bad); }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

.hint { color: var(--ink-faint); font-size: 13px; margin: 4px 0 0; }

/* A checkbox with its words beside it. The bare `label` rule above is written
   for the caption ABOVE a field, so a label wrapping a checkbox needs the
   block display and the bottom margin taken back off. */
.check {
  display: flex; gap: 8px; align-items: center; cursor: pointer;
  margin: 0; color: var(--ink);
}
.check input { width: auto; min-height: 0; }

/* ---- her photo ------------------------------------------------------- */

/* Always square in the file (see FLO_UPLOAD_FOLDERS), so the radius alone
   makes it round. object-fit is the guard for anything stored before that. */
.avatar {
  width: 40px; height: 40px; border-radius: 50%; object-fit: cover;
  border: 1px solid var(--rule); background: var(--surface-2); flex: 0 0 auto;
}
.avatar-lg { width: 84px; height: 84px; }
/* No photo yet: her initial, so the row keeps its shape rather than jumping
   when she adds one. */
.avatar-empty {
  display: flex; align-items: center; justify-content: center;
  color: var(--accent); background: var(--accent-soft);
  border-color: var(--accent-soft); font-weight: 700; font-size: 2rem;
}

.photo-edit { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
/* The file input and the remove tickbox share the space beside the photo, and
   need to be able to shrink - a bare file input on a narrow phone is wide
   enough to push the layout sideways. */
.photo-edit-controls { flex: 1 1 12rem; min-width: 0; display: grid; gap: 8px; }
.photo-edit-controls input[type=file] { max-width: 100%; font-size: 14px; }

/* ---- item rows ------------------------------------------------------ */

.items { display: grid; gap: 10px; }

.item {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 12px;
  align-items: start;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 12px;
}
.item-thumb {
  width: 56px; height: 56px; object-fit: contain;
  background: var(--surface-2); border-radius: 8px;
}
.item-body { min-width: 0; }
.item-name { font-weight: 600; margin: 0 0 2px; overflow-wrap: anywhere; }
.item-meta { color: var(--ink-faint); font-size: 13px; font-family: var(--mono); }
.item-controls {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 10px;
}
.item-controls input[type=number] { width: 5.5rem; }
.item-controls input[type=text] { width: 7rem; }

.price-now { font-weight: 700; }
.price-was { color: var(--ink-faint); text-decoration: line-through; font-size: 14px; margin-left: 6px; }

.pill {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
  background: var(--surface-2); color: var(--ink-soft);
}
.pill-sale { background: var(--bad-soft); color: var(--bad); }
.pill-out { background: var(--warn-soft); color: var(--warn); }
.pill-disc { background: var(--surface-2); color: var(--ink-faint); }
.pill-ok { background: var(--good-soft); color: var(--good); }
/* An order that arrived on its own, rather than one she raised at a table.
   It needs to stand out in the list: nobody told her it happened. */
.pill-online { background: var(--accent-soft); color: var(--accent); }

/* ---- whole-row links ------------------------------------------------
   An invoice number like "1" is a tiny tap target on a phone, so the whole
   card is the link. The anchor stays a real anchor (keyboard focus, open in
   new tab, screen readers all behave) and its ::after simply covers the card.
   Only used on rows that contain no other controls. */

.item-link { position: relative; }
.item-link:hover { border-color: var(--accent); }
.item-link:has(a:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.item-link .stretch { text-decoration: none; color: var(--ink); }
.item-link .stretch::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.item-link .stretch:focus-visible { outline: none; }
/* Anything genuinely interactive inside the row must sit above the overlay. */
.item-link .above { position: relative; z-index: 1; }

/* ---- invoice sharing / QR ------------------------------------------- */

.share { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.share-qr { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.share-text { flex: 1; min-width: 15rem; }

/* The code is always drawn on white with a quiet zone, whatever the theme -
   a dark-mode QR on a dark card is a scan that fails. */
.qr-box {
  width: 148px; height: 148px; padding: 8px;
  background: #fff; border-radius: 10px; border: 1px solid var(--rule);
  display: grid; place-items: center;
}
.qr-box svg { display: block; width: 100%; height: 100%; }

.qr-overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0, 0, 0, .72);
  display: grid; place-items: center; padding: 20px;
}
.qr-overlay-inner {
  background: #fff; color: #111; border-radius: 16px; padding: 22px;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  max-width: min(92vw, 30rem);
}
/* As large as the screen sensibly allows: held out across a table, a bigger
   code is the difference between a scan that works first time and one that
   does not. */
.qr-box.big {
  width: min(78vw, 60vh, 24rem);
  height: min(78vw, 60vh, 24rem);
  border: 0; padding: 0;
}
.qr-overlay-meta { margin: 0; color: #444; font-size: 15px; }

@media (max-width: 520px) {
  .share { gap: 16px; }
  .share-qr { width: 100%; }
}

/* ---- scanner --------------------------------------------------------- */

.scan-stage {
  position: relative;
  background: #111;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
}
.scan-stage video {
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .2s;
}
.scan-stage video.is-live { opacity: 1; }

/* A guide box, so she knows roughly where to hold the barcode. */
.scan-frame {
  position: absolute; inset: 18% 10%;
  border: 2px solid rgba(255,255,255,.85);
  border-radius: 10px;
  box-shadow: 0 0 0 100vmax rgba(0,0,0,.28);
  pointer-events: none;
}
.scan-status {
  position: absolute; bottom: 10px; left: 10px; right: 10px;
  margin: 0; text-align: center; color: #fff; font-size: 14px;
  text-shadow: 0 1px 3px rgba(0,0,0,.8);
}

/* Feedback is big and colour-coded: at an event it is read at a glance,
   over someone's shoulder, in bad light. */
.scan-feedback {
  min-height: 2.8rem;
  margin: 10px 0 4px;
  padding: 11px 14px;
  border-radius: var(--radius);
  font-weight: 600;
  background: var(--surface-2);
  color: var(--ink-soft);
}
.scan-feedback.is-ok   { background: var(--good-soft); color: var(--good); }
.scan-feedback.is-warn { background: var(--warn-soft); color: var(--warn); }
.scan-feedback.is-bad  { background: var(--bad-soft);  color: var(--bad); }

/* A scan has to be confirmable without looking at the screen. iOS Safari has
   no vibration API, so the picture itself flashes. */
@keyframes flashOk  { from { box-shadow: inset 0 0 0 9999px rgba(63,107,79,.75); } to { box-shadow: none; } }
@keyframes flashBad { from { box-shadow: inset 0 0 0 9999px rgba(140,58,50,.7); }  to { box-shadow: none; } }
.scan-stage.flash-ok  { animation: flashOk .45s ease-out; }
.scan-stage.flash-bad { animation: flashBad .45s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .scan-stage.flash-ok, .scan-stage.flash-bad { animation-duration: .01s; }
}

/* ---- the public storefront ------------------------------------------
   Customers arrive from a link on a phone, usually with no idea what this
   is, so it leads with the seller and the product rather than any chrome. */

.shop-head { margin: 0 0 24px; }
/* Her photo and her business name, on one line above the list name. */
.shop-who { display: flex; gap: 10px; align-items: center; margin: 0 0 6px; }
.shop-seller {
  margin: 0; color: var(--ink-faint); font-size: 14px;
  text-transform: uppercase; letter-spacing: .08em;
}
.shop-head h1 { font-size: 1.6rem; margin: 0 0 6px; }
.shop-deal {
  display: inline-block; margin: 0 0 8px; padding: 3px 10px; border-radius: 999px;
  background: var(--bad-soft); color: var(--bad); font-weight: 600; font-size: 14px;
}
.shop-intro { color: var(--ink-soft); margin: 6px 0 0; }
.shop-group { margin: 30px 0 12px; font-size: 1.05rem; color: var(--accent); }

/* Licensed collection. Quiet on purpose - it is useful information, not a
   sales badge, and it sits directly under a product name that is already
   doing the shouting. */
.shop-licensed {
  margin: 2px 0 0; font-size: .72rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--accent);
}
.pill-licensed { background: var(--accent-soft); color: var(--accent); }

/* Search. Sits above the category pills because it overrides them: a search
   crosses every category, so the two are alternatives, not filters that stack. */
.shop-search { display: flex; gap: 8px; margin: 0 0 14px; }
.shop-search input {
  flex: 1; min-width: 0; padding: 10px 12px; min-height: 44px;
  border: 1px solid var(--rule); border-radius: 10px;
  background: var(--surface); color: var(--ink); font: inherit; font-size: 16px;
}
.shop-found { margin: -6px 0 14px; color: var(--ink-soft); font-size: .9rem; }

/* Category links.
 *
 * WRAPS by default. A single scrolling row was cut off on desktop with no way
 * to reach the rest: the scrollbar was hidden, and a mouse has no sideways
 * gesture, so a third of the categories simply could not be got at. Hidden and
 * unreachable is far worse than a header one line taller. */
.shop-cats {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 18px;
}

/* On a phone the row scrolls sideways instead, because wrapping ten pills
   there costs four lines and pushes the products off the screen - and a touch
   screen can actually scroll it. Bleeds to the screen edges so a scrolled row
   does not appear to stop in mid-air, with the gutter restored as padding. */
@media (max-width: 639px) {
  .shop-cats {
    flex-wrap: nowrap;
    overflow-x: auto; scrollbar-width: none;
    padding: 2px 16px 6px; margin-left: -16px; margin-right: -16px;
    scroll-padding-left: 16px;
  }
  .shop-cats::-webkit-scrollbar { display: none; }
}
.shop-cat {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; min-height: 38px;
  border: 1px solid var(--rule); border-radius: 999px;
  background: var(--surface); color: var(--ink);
  font-size: .88rem; font-weight: 600; text-decoration: none;
  white-space: nowrap;
}
.shop-cat:hover { border-color: var(--accent); }
.shop-cat.is-on {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
.shop-cat-n { font-weight: 400; opacity: .7; font-size: .82rem; }

/* The second row sits visually under the first - smaller and quieter, so the
   two levels read as a hierarchy rather than as one long wrapped row. */
.shop-subs { margin-top: -10px; }
@media (max-width: 639px) { .shop-subs { margin-top: -14px; } }
.shop-sub {
  padding: 6px 12px; min-height: 34px;
  font-size: .82rem; font-weight: 500;
  background: var(--surface-2); border-color: transparent;
}
.shop-sub.is-on {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}

/* Two per row on a phone. A single column made customers scroll past very
   little, and the products are recognised by their picture more than their
   name, so smaller tiles cost nothing. */
.shop-grid { display: grid; gap: 12px; grid-template-columns: repeat(2, 1fr); }
@media (min-width: 640px) { .shop-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 980px) { .shop-grid { grid-template-columns: repeat(4, 1fr); } }

.shop-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--radius); overflow: hidden;
  /* Claiming returns to #item-N. Without this the card lands flush against
     the top edge with no context above it, which reads as a different page
     rather than the same one. */
  scroll-margin-top: 84px;
}
.shop-img {
  width: 100%; aspect-ratio: 1 / 1; object-fit: contain;
  background: var(--surface-2); display: block;
}
.shop-body { padding: 12px; display: flex; flex-direction: column; flex: 1; gap: 5px; }
/* Reserve two lines for the name whether or not it needs them, so the price,
   the Details toggle and the Claim button sit on the same line across every
   tile in a row. Without this a one-line name pulls everything below it up and
   the row reads as ragged. min-height, not height: a three-line name on a
   narrow phone still gets its third line rather than being clipped. */
.shop-name {
  margin: 0; font-size: .95rem; line-height: 1.3; overflow-wrap: anywhere;
  min-height: calc(2 * 1.3em);
}
.shop-price { margin: 0; font-size: 1.05rem; }

/* At two-up the tile is roughly 160px across, so the blurb is noise rather
   than information - the picture and the name carry it. */
@media (max-width: 520px) {
  .shop-body { padding: 10px; }
  .shop-name { font-size: .88rem; }
  .shop-price { font-size: 1rem; }
  .shop-more summary { font-size: 12.5px; }
  .shop-claim { padding: 9px 10px; min-height: 40px; font-size: .9rem; }
}
.shop-rrp {
  color: var(--ink-faint); text-decoration: line-through;
  font-size: .85rem; margin-left: 6px; font-weight: 400;
}
.shop-desc { margin: 0; color: var(--ink-soft); font-size: 14px; }

/* A native <details> rather than a scripted panel: it works with no
   JavaScript, keyboard and screen readers already understand it, and it
   keeps working if she is offline. */
.shop-more { margin: 2px 0 0; }
.shop-more summary {
  cursor: pointer; color: var(--accent); font-size: 13px; font-weight: 600;
  list-style: none; padding: 4px 0; min-height: 28px; display: inline-flex; align-items: center;
}
.shop-more summary::-webkit-details-marker { display: none; }

/* A chevron drawn with borders rather than a character: no unicode escape
   to get wrong, no font to be missing, and it rotates rather than swapping
   glyphs, which makes the open state obvious at a glance. */
.shop-more summary::after {
  content: "";
  width: 6px; height: 6px;
  margin-left: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .15s ease;
}
.shop-more[open] summary::after {
  transform: rotate(-135deg) translate(-1px, -1px);
}
@media (prefers-reduced-motion: reduce) {
  .shop-more summary::after { transition: none; }
}
.shop-more p {
  margin: 4px 0 0; color: var(--ink-soft); font-size: 13px; line-height: 1.5;
}
.shop-spec { color: var(--ink-faint); font-size: 12px; }
.shop-have { margin: 0; color: var(--good); font-size: 14px; font-weight: 600; }
.shop-gone { margin: 0; color: var(--ink-faint); font-size: 14px; }
/* The button is wrapped in a form, so it is the FORM that has to take the
   spare space - margin-top:auto on the button itself was a no-op, because the
   button is not the flex child here. This is what actually bottom-aligns the
   buttons when one tile's name runs longer than its neighbour's. */
.shop-buy, .shop-gone { margin-top: auto; }
.shop-claim { width: 100%; }

/* The running basket follows her customer down the page - on a phone the
   checkout button must never be something they have to go hunting for. */
.shop-bar {
  position: sticky; bottom: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin: 24px -16px 0; padding: 12px 16px;
  background: var(--surface); border-top: 1px solid var(--rule);
  box-shadow: 0 -6px 20px rgba(0,0,0,.07);
}
.shop-total { text-align: right; font-size: 1.2rem; margin: 0 0 18px; }
.shop-done { text-align: center; padding: 30px 0; }
.shop-done h1 { font-size: 1.6rem; }
.shop-foot {
  max-width: 62rem; margin: 30px auto 0; padding: 18px 16px 40px;
  border-top: 1px solid var(--rule); color: var(--ink-faint);
  font-size: 13px; text-align: center;
}
.shop-foot p { margin: 0 0 4px; }

/* ---- empty state ---------------------------------------------------- */

.empty {
  text-align: center; padding: 40px 20px; color: var(--ink-soft);
  border: 1px dashed var(--rule); border-radius: var(--radius);
}
.empty p { margin: 0 0 14px; }

/* ---- tables (desktop only; they stack on a phone) ------------------- */

.scroll-x { overflow-x: auto; }

/* ---- pagination ----------------------------------------------------- */

.pager { display: flex; gap: 10px; align-items: center; justify-content: center; margin: 20px 0 0; }
.pager span { color: var(--ink-faint); font-size: 14px; }

/* ---- search bar ----------------------------------------------------- */

.searchbar { display: flex; gap: 8px; margin: 0 0 16px; }
.searchbar input { flex: 1; }
.searchbar select { width: auto; min-width: 9rem; }
@media (max-width: 520px) {
  .searchbar { flex-wrap: wrap; }
  .searchbar select, .searchbar .btn { flex: 1; }
}

/* ---- the shopfront index at the root -------------------------------- */

/* A whole card is the target, not just the heading - on a phone the heading
   alone is a small thing to hit accurately while holding a drink. */
.shop-listcard { display: block; text-decoration: none; color: inherit; }
.shop-listcard:hover { border-color: var(--accent); }
.shop-listcard h2 { margin: 0; font-size: 1.15rem; color: var(--accent); }
.shop-listmeta { margin: 4px 0 0; color: var(--ink-faint); font-size: 14px; }
.shop-listintro { margin: 8px 0 0; color: var(--ink-soft); font-size: 15px; }

/* Her sign-in link on the shopfront index, top corner. Deliberately the
   quietest thing on the page: it is for exactly one person, and every other
   visitor should read past it without pausing. There was briefly a second one
   at the foot as well, which on a page with one list put both on screen at
   once and just looked like a mistake. */
.shop-admin { margin: 0 0 10px; text-align: right; font-size: 13px; }
.shop-admin a {
  color: var(--ink-faint); text-decoration: none;
  padding: 6px 10px; border-radius: 999px; border: 1px solid var(--rule);
  display: inline-block; min-height: 0;
}
.shop-admin a:hover { color: var(--accent); border-color: var(--accent); }

/* The shopfront index header: her photo beside her business name, which is
   the heading here rather than an eyebrow over a second name. Stacks on a
   phone so a long trading name is not squeezed into half the width. */
.shop-front { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.shop-front > div { flex: 1 1 14rem; min-width: 0; }
.shop-front h1 { margin: 0; }
.shop-front .shop-intro { margin-top: 4px; }
