/* GENERATED FILE. Do not edit.
 * Source: design/theme/*.css. Rebuild: scripts/build-theme.sh
 * scripts/check-theme-current.sh fails the build if this has been edited here.
 */
/*
 * The shared shell for every BackMyTown surface a person signs in to.
 *
 * ONE SOURCE. This file is copied into each app by scripts/build-theme.mjs and
 * scripts/check-theme-current.sh fails the build if a copy has drifted. Edit
 * here, never in an app's copy. Three apps had independently grown the same
 * 30-line navy-and-system-ui sheet, and they had already diverged.
 *
 * NEUTRAL SHELL, ONE ACCENT. Everything here is greyscale except a single
 * --accent, which each page sets per tenant from the town's own brand read.
 * That is the whole white-label mechanism and it is deliberate: a shopkeeper
 * was invited by InvestReigate, not by us, so the dashboard has to read as
 * their town's. The accent is validated as a hex triplet on write AND on read
 * (services/tenant_brand_colour) because it lands inside a declaration here,
 * where punctuation could close the rule and write CSS of its own.
 *
 * LIGHT ONLY, AND SAID SO. The old sheets declared `color-scheme: light dark`
 * and then defined only light colours, giving inputs a transparent background
 * over an unknown ground: a shop's price list came out unreadable in dark
 * mode. Doing dark properly needs a second contrast decision for every accent,
 * because ink_on() picks text for the accent assuming a light page behind it.
 * One honest light theme beats two half-done ones.
 */

/* ------------------------------------------------------------ tokens --- */

:root {
  color-scheme: light;

  /* The town's colour. Overridden per page; this default is a plain slate so
     a town with no accent set looks PLAIN rather than looking like us. */
  --accent: #33404d;
  --accent-ink: #ffffff;
  --accent-soft: #eef1f4;
  --accent-line: #c4ccd4;

  --page: #f6f6f3;
  --surface: #ffffff;
  --sunk: #f1f1ed;
  --line: #e3e3dd;
  --line-strong: #cbcbc3;

  --ink: #15171c;
  --ink-soft: #565a63;
  --ink-faint: #8b8f96;

  --ok: #0d6a4a;
  --ok-soft: #e8f4ee;
  --warn: #8a5a05;
  --warn-soft: #fdf3e1;
  --bad: #a3282f;
  --bad-soft: #fbebec;

  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;
  --s8: 64px;

  --r1: 6px;
  --r2: 10px;
  --r3: 16px;
  --pill: 999px;

  --shadow-1: 0 1px 2px rgb(21 23 28 / 6%), 0 1px 1px rgb(21 23 28 / 4%);
  --shadow-2: 0 4px 16px rgb(21 23 28 / 8%), 0 1px 3px rgb(21 23 28 / 6%);

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --wrap: 960px;
}

/* A tinted accent, where the browser can mix one. Declared after the flat
   fallbacks above, so a browser that cannot parse color-mix keeps those. */
@supports (color: color-mix(in srgb, red 10%, white)) {
  :root {
    --accent-soft: color-mix(in srgb, var(--accent) 9%, var(--surface));
    --accent-line: color-mix(in srgb, var(--accent) 28%, var(--surface));
  }
}

/* -------------------------------------------------------------- base --- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.55;
}

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

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

a:hover {
  text-decoration-thickness: 2px;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--s6) 0;
}

/* ---------------------------------------------------------- headings --- */

h1,
h2,
h3 {
  color: var(--ink);
  font-weight: 620;
  letter-spacing: -0.015em;
  line-height: 1.2;
  text-wrap: balance;
  margin: 0;
}

h1 {
  font-size: 1.75rem;
}

h2 {
  font-size: 1.2rem;
}

h3 {
  font-size: 1rem;
}

p {
  margin: 0;
}

/* Vertical rhythm by flow, so no page has to remember its own margins. */
main > * + *,
.stack > * + * {
  margin-top: var(--s5);
}

.stack-tight > * + * {
  margin-top: var(--s3);
}
/* ------------------------------------------------------------ layout --- */

/*
 * `hidden` has to win. It is only the browser's own `display: none`, so any
 * class that sets a display (.actions, .row, .choice) showed the element anyway:
 * "Add the ticked offers" stood on the import page before anything had been read.
 */
[hidden] {
  display: none !important;
}

.page {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 var(--s5);
}

header.bar {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  box-shadow: var(--shadow-1);
  margin-bottom: var(--s7);
}

header.bar .page {
  display: flex;
  align-items: center;
  gap: var(--s4);
  min-height: 60px;
  flex-wrap: wrap;
  padding-block: var(--s3);
}

header.bar .brand {
  font-weight: 640;
  font-size: 1.05rem;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.01em;
  /* The town's colour, as a mark rather than as a wash of it. */
  border-left: 3px solid var(--accent);
  padding-left: var(--s3);
}

header.bar .for-business {
  color: var(--ink-faint);
  font-size: 0.82rem;
}

header.bar nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--s4);
  font-size: 0.9rem;
  flex-wrap: wrap;
}

header.bar nav a {
  color: var(--ink-soft);
  text-decoration: none;
  padding: var(--s1) 0;
  border-bottom: 2px solid transparent;
}

header.bar nav a:hover {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

header.bar nav a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--accent);
  font-weight: 560;
}

main {
  display: block;
  padding-bottom: var(--s8);
}

footer.foot {
  margin-top: var(--s8);
  border-top: 1px solid var(--line);
  padding: var(--s5) 0 var(--s7);
  color: var(--ink-faint);
  font-size: 0.82rem;
}

footer.foot a {
  color: var(--ink-soft);
}

/* The page's own title block: one heading, one line under it, then air. */
.title {
  margin-bottom: var(--s6);
}

.title p {
  color: var(--ink-soft);
  margin-top: var(--s2);
  max-width: 62ch;
}

/* ------------------------------------------------- cards and sections --- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r3);
  padding: var(--s5);
  box-shadow: var(--shadow-1);
}

.card > * + * {
  margin-top: var(--s4);
}

.card > h2 + p,
.card > h3 + p {
  margin-top: var(--s2);
}

/* A card's own heading strip, separated rather than just bolder. */
.card-head {
  border-bottom: 1px solid var(--line);
  margin: calc(var(--s5) * -1) calc(var(--s5) * -1) 0;
  padding: var(--s4) var(--s5);
  background: var(--sunk);
  border-radius: var(--r3) var(--r3) 0 0;
}

.card-head + * {
  margin-top: var(--s5);
}

.card-head p {
  color: var(--ink-soft);
  font-size: 0.88rem;
  margin-top: var(--s1);
}

.grid {
  display: grid;
  gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.figures {
  display: flex;
  gap: var(--s7);
  flex-wrap: wrap;
}

.figure {
  font-size: 1.9rem;
  font-weight: 640;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  display: block;
}
/* ------------------------------------------------------------- forms --- */

fieldset {
  border: 0;
  border-top: 1px solid var(--line);
  padding: var(--s5) 0 0;
  margin: var(--s6) 0 0;
  min-width: 0;
}

fieldset:first-of-type {
  border-top: 0;
  padding-top: 0;
  margin-top: 0;
}

legend {
  font-weight: 620;
  font-size: 0.95rem;
  color: var(--ink);
  padding: 0;
  margin-bottom: var(--s1);
}

legend + .hint {
  margin-bottom: var(--s4);
}

.field {
  margin-top: var(--s4);
  min-width: 0;
}

.field > label {
  display: block;
  font-size: 0.85rem;
  font-weight: 560;
  color: var(--ink);
  margin-bottom: var(--s1);
}

.optional {
  color: var(--ink-faint);
  font-weight: 400;
}

input,
select,
textarea {
  width: 100%;
  padding: 10px var(--s3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r1);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.95rem;
}

input:hover,
select:hover,
textarea:hover {
  border-color: var(--ink-faint);
}

input:focus,
select:focus,
textarea:focus {
  outline: 0;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

input[aria-invalid="true"] {
  border-color: var(--bad);
}

select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
    linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
  background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: var(--s7);
}

/*
 * A placeholder must never be mistakable for a value. Both at once, because
 * either alone is not enough: the old sheet inherited the input's own colour,
 * so an add-offer form that had thrown away everything a shopkeeper typed
 * looked exactly like one holding the numbers it had just refused.
 */
::placeholder {
  color: var(--ink-faint);
  font-style: italic;
  opacity: 1;
}

.hint {
  color: var(--ink-soft);
  font-size: 0.85rem;
  margin-top: var(--s2);
  max-width: 62ch;
}

.hint-inline {
  color: var(--ink-faint);
  font-size: 0.8rem;
  margin-top: var(--s1);
}

/* The long explanations, folded away. The words are worth keeping (several
   carry data-protection or pricing-law guidance) and worth not making every
   shopkeeper read every time. */
details.more {
  margin-top: var(--s2);
}

details.more > summary {
  cursor: pointer;
  color: var(--accent);
  font-size: 0.82rem;
  width: fit-content;
  list-style: none;
}

details.more > summary::-webkit-details-marker {
  display: none;
}

details.more > summary::before {
  content: "+ ";
  font-weight: 600;
}

details.more[open] > summary::before {
  content: "\2212 ";
}

details.more > summary:hover {
  text-decoration: underline;
}

details.more > .hint {
  margin-top: var(--s2);
  padding-left: var(--s3);
  border-left: 2px solid var(--line);
}
/* ------------------------------------------- the controls you pick with --- */

/*
 * Split from the field styles beside it for the 200-line rule, and the seam
 * is a real one: above is how a value is typed, here is how one is chosen.
 */

/*
 * A tickbox or radio sits BESIDE its words, on the first line of them. The old
 * sheet made every label `display: block`, which dropped the control onto its
 * own line above the text and left it floating, centred and unattached.
 */
.choice {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s3);
  align-items: start;
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r2);
  background: var(--surface);
  cursor: pointer;
  margin-top: var(--s2);
}

.choice:hover {
  border-color: var(--accent-line);
}

.choice:has(:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}

/*
 * Outside a .choice as well: `input { width: 100% }` stretches a bare tickbox
 * across the page and centres the box on a line of its own, above its words.
 */
input[type="checkbox"],
input[type="radio"] {
  width: auto;
}

.choice input[type="checkbox"],
.choice input[type="radio"] {
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  accent-color: var(--accent);
  flex: none;
}

/* An offer's own bulk-select box, top-right of its card and clear of the
   withdraw/restore button in the edit disclosure well below it, so a shop
   can never press one for the other. */
.offer-pick {
  display: flex;
  justify-content: flex-end;
}

.offer-pick input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--accent);
}

.choice .choice-label {
  font-weight: 560;
  color: var(--ink);
  display: block;
}

.choice .choice-note {
  color: var(--ink-soft);
  font-size: 0.86rem;
  display: block;
  margin-top: 2px;
}

input[type="file"] {
  padding: var(--s2);
  background: var(--sunk);
  border-style: dashed;
  font-size: 0.85rem;
  cursor: pointer;
}

input[type="file"]::file-selector-button {
  font: inherit;
  font-size: 0.85rem;
  font-weight: 560;
  margin-right: var(--s3);
  padding: 6px var(--s3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r1);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}

input[type="file"]::file-selector-button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Money. The symbol belongs in the box, not in the label. */
.money {
  position: relative;
}

.money > input {
  padding-left: var(--s6);
}

.money::before {
  content: "£";
  position: absolute;
  left: var(--s3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--ink-soft);
  font-size: 0.95rem;
  pointer-events: none;
}

.row {
  display: flex;
  gap: var(--s4);
  flex-wrap: wrap;
  align-items: flex-start;
}

.row > .field {
  flex: 1 1 180px;
  margin-top: 0;
}

/*
 * A number and its unit, side by side: "3 | days". Both are controls and every
 * control is `width: 100%`, so in a plain .row each wrapped onto a line of its
 * own and read as two unrelated questions.
 */
.with-unit {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s2);
}

.with-unit > select {
  width: auto;
  min-width: 7rem;
}

.actions {
  display: flex;
  gap: var(--s3);
  align-items: center;
  flex-wrap: wrap;
  margin-top: var(--s5);
}
/* ----------------------------------------------------------- buttons --- */

button,
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: 10px var(--s5);
  border: 1px solid var(--accent);
  border-radius: var(--r1);
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-size: 0.92rem;
  font-weight: 560;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}

button:hover,
.btn:hover {
  filter: brightness(1.12);
}

button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: none;
}

button.secondary,
.btn.secondary {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--line-strong);
}

button.secondary:hover,
.btn.secondary:hover {
  border-color: var(--accent);
  color: var(--accent);
  filter: none;
}

button.danger {
  background: var(--surface);
  color: var(--bad);
  border-color: var(--bad);
}

button.danger:hover {
  background: var(--bad-soft);
  filter: none;
}

button.link {
  background: none;
  border: 0;
  color: var(--accent);
  padding: 0;
  text-decoration: underline;
  font-weight: 400;
}

/* The one-thumb button behind a counter: marking an order collected. */
button.bigbtn {
  width: 100%;
  padding: 15px;
  font-size: 1rem;
  border-radius: var(--r2);
}

form.inline {
  display: inline;
}

/* ------------------------------------------------------------ tables --- */

table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.92rem;
}

th,
td {
  text-align: left;
  padding: var(--s4);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

thead th {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-weight: 620;
  background: var(--sunk);
  border-bottom-color: var(--line-strong);
  white-space: nowrap;
}

tbody tr:last-child td {
  border-bottom: 0;
}

td.num,
th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* A short fact that reads as one thing: "just now", "21h old". Wrapped, it read as two. */
td.brief {
  white-space: nowrap;
}

/* Withdraw is its own form posting to its own action, so it cannot live inside
   the edit form (HTML forbids a nested one and browsers silently re-point the
   button at the outer action). It gets its own row, which is better anyway:
   a destructive action should not sit flush against Save. */
.actions.withdraw {
  margin-top: var(--s3);
}
/* --------------------------------------------- pills, flashes, states --- */

.pill {
  display: inline-block;
  font-size: 0.73rem;
  font-weight: 620;
  letter-spacing: 0.02em;
  padding: 3px var(--s3);
  border-radius: var(--pill);
  background: var(--sunk);
  color: var(--ink-soft);
  white-space: nowrap;
}

.pill.live {
  background: var(--ok-soft);
  color: var(--ok);
}

.pill.draft {
  background: var(--warn-soft);
  color: var(--warn);
}

.pill.off {
  background: var(--bad-soft);
  color: var(--bad);
}

.flash {
  padding: var(--s4) var(--s5);
  border-radius: var(--r2);
  margin-bottom: var(--s5);
  font-size: 0.93rem;
  border: 1px solid transparent;
}

.flash.ok {
  background: var(--ok-soft);
  color: var(--ok);
  border-color: var(--ok);
}

.flash.bad {
  background: var(--bad-soft);
  color: var(--bad);
  border-color: var(--bad);
}

.note {
  color: var(--ink-soft);
  font-size: 0.86rem;
}

.empty {
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r3);
  padding: var(--s7) var(--s5);
  text-align: center;
  color: var(--ink-soft);
}

.out {
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: var(--r2);
  padding: var(--s4);
  font-size: 0.9rem;
  white-space: pre-wrap;
}

img.shot {
  display: block;
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: var(--r1);
  border: 1px solid var(--line);
  background: var(--sunk);
}

/* A small spinner for a wait with no other way to show progress: a photo
   being read, or a row being created. Shown beside a disabled button rather
   than replacing its label, so the label stays readable while it works. */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.spinner {
  display: inline-block;
  width: 1em;
  height: 1em;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  vertical-align: -0.2em;
  animation: spin 0.7s linear infinite;
}

.spinner[hidden] {
  display: none;
}
/* ------------------------------------------------ the counter surfaces --- */

/* The production summary: "38 kebabs across 4 orders", read at a glance
   before anything else on the Collections page. */
.summary {
  font-size: 1.15rem;
  color: var(--ink);
}

/* An order waiting to be handed over. Roomier than an ordinary card, because
   it is used one-handed with a queue in front of you. */
.card.collect {
  padding: var(--s5);
}

/* The weekly opening grid. Tighter than an ordinary table: it is seven rows
   of two time boxes, and ordinary table padding makes it a page of its own. */
table.hours th,
table.hours td {
  padding: var(--s2) var(--s3);
}

table.hours input[type="time"] {
  width: auto;
  min-width: 7.5rem;
}

@media (max-width: 640px) {
  .card.collect {
    padding: var(--s4);
  }
}

/* --------------------------------------------------- media and panels --- */

/*
 * A thumbnail, the thing it describes, and a figure on the right. The
 * catalogue used to be a three-column table whose last column held a whole
 * edit form, so every offer showed nine input boxes whether or not anybody
 * was editing it: a shop with six offers met fifty-four. Now the row states
 * the facts and the form is a disclosure under it.
 */
.media {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--s4);
  align-items: start;
}

.media > .media-body {
  min-width: 0;
}

.media > .media-body > * + * {
  margin-top: var(--s1);
}

.media > .media-aside {
  text-align: right;
}

/* The per-kg line and the saving line are separate facts and must not run
   together, which they did the moment the old <br/>-separated cell became a
   stack of spans. */
.media > .media-aside > .note {
  display: block;
  margin-top: var(--s1);
}

.tags {
  display: flex;
  gap: var(--s2);
  flex-wrap: wrap;
  margin-top: var(--s2);
}

/* One session of a class: a sunk block rather than a bordered fieldset, so a
   form carrying four of them reads as four blocks instead of four pages. The
   legend repeats on every one deliberately, because a screen reader meets
   each group on its own and "When it runs" is what it is. */
fieldset.session {
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: var(--r2);
  padding: var(--s3) var(--s4) var(--s4);
  margin-top: var(--s3);
}

fieldset.session > legend {
  font-size: 0.8rem;
  color: var(--ink-soft);
  font-weight: 560;
}

fieldset.session .field {
  margin-top: var(--s2);
}

/* A disclosure that reads as a section of the card it sits in. */
details.panel {
  border-top: 1px solid var(--line);
  margin-top: var(--s4);
  padding-top: var(--s4);
}

details.panel > summary {
  cursor: pointer;
  font-size: 0.88rem;
  font-weight: 560;
  color: var(--accent);
  width: fit-content;
  list-style: none;
}

details.panel > summary::-webkit-details-marker {
  display: none;
}

details.panel > summary::after {
  content: " \203A";
  display: inline-block;
  transform: rotate(90deg);
  margin-left: var(--s1);
}

details.panel[open] > summary::after {
  transform: rotate(-90deg);
}

details.panel > summary:hover {
  text-decoration: underline;
}

details.panel > *:not(summary) {
  margin-top: var(--s4);
}

@media (max-width: 640px) {
  .media {
    grid-template-columns: auto 1fr;
  }

  .media > .media-aside {
    grid-column: 1 / -1;
    text-align: left;
  }
}
/* ------------------------------------------------- the photo review sheet --- */

/*
 * The source photograph beside what was read off it, which is the spec's one
 * rule for this screen: a reviewer must always see the ticket next to the
 * suggestion. Shared, because the operator console and the shop's own
 * dashboard both render it now.
 */
.import-photo {
  display: flex;
  gap: var(--s5);
  align-items: flex-start;
  flex-wrap: wrap;
}

.import-photo img {
  max-width: 320px;
  width: 100%;
  border-radius: var(--r2);
  border: 1px solid var(--line);
}

.import-items {
  flex: 1 1 380px;
  min-width: 0;
}

.import-items h3 {
  margin-top: var(--s5);
}

.import-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  align-items: center;
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--line);
}

.import-row input,
.import-row select {
  width: auto;
}

.import-row .choice {
  margin-top: 0;
  padding: var(--s2) var(--s3);
}

.import-row .money > input {
  width: 8rem;
}

.import-row input.f-name {
  flex: 1 1 200px;
}

.import-row input.f-desc {
  flex: 1 1 180px;
}

.import-row input.f-count,
.import-row input.f-grams {
  width: 7.5rem;
}

.import-row .result {
  margin-left: auto;
}

.import-row .result.ok {
  color: var(--ok);
  font-weight: 600;
}

.import-row .result.bad {
  color: var(--bad);
}

/* Once a row is on sale there is nothing left to do with it here: it is
   greyed out and its fields locked, so a long sheet reads at a glance as
   done / not done rather than making you re-read every "added" line. */
.import-row.added {
  opacity: 0.55;
}

/* The one sentence that says a weight is our estimate: its own line under the
   row it is about, so it cannot be read as belonging to the next one. */
.import-row .weight-note {
  flex-basis: 100%;
}

/* The goods, cut out of the photograph, beside the rows they would go on:
   small, because it is a proposal to glance at, not the picture itself. */
.import-crop {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex-wrap: wrap;
  margin: var(--s2) 0 var(--s3);
}

.import-crop img {
  max-width: 120px;
  max-height: 120px;
  width: auto;
  border-radius: var(--r2);
  border: 1px solid var(--line);
}

.import-crop .choice {
  margin: 0;
  padding: var(--s2) var(--s3);
}

.import-crop .note {
  flex-basis: 100%;
}

/* A photograph offered whole is the picture itself, so it gets more room than a cut. */
.import-crop.whole img {
  max-width: 200px;
  max-height: 200px;
}

/* The photographs gathered for one read, on the shop's import page: what the
   reading will be shown, each with a way to take it out again. */
.gathered-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  margin-top: var(--s3);
}

.gathered-strip:empty {
  display: none;
}

figure.gathered {
  position: relative;
  margin: 0;
}

figure.gathered img {
  width: 96px;
  height: 96px;
  object-fit: cover;
  border-radius: var(--r2);
  border: 1px solid var(--line);
  display: block;
}

figure.gathered .remove {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: 50%;
  line-height: 1;
  font-size: 1rem;
}

/* The button that actually adds anything sits after however long the sheet
   runs, which on eight photographs is a long way down. Fixed to the
   viewport, like the header and footer bars are, so it stays reachable
   while you are still ticking rows rather than found only by scrolling
   past every one of them. */
.import-actionbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  background: var(--surface);
  border-top: 1px solid var(--line);
  box-shadow: 0 -2px 8px rgba(21, 23, 28, 0.08);
}

.import-actionbar .page {
  display: flex;
  align-items: center;
  gap: var(--s4);
  flex-wrap: wrap;
  padding-block: var(--s4);
}

/* Room for the bar above, only while it is actually showing, so it never
   sits over the sheet's last row or the note underneath it. */
body:has(.import-actionbar:not([hidden])) main {
  padding-bottom: var(--s8);
}
/* ------------------------------------------------------------- prose --- */

/*
 * The legal documents. They were served as an escaped <pre> of their own
 * markdown, outside the site layout entirely, on the page where a shopkeeper
 * is asked to accept them.
 */
.prose {
  max-width: 68ch;
  color: var(--ink-soft);
  line-height: 1.7;
}

.prose > * + * {
  margin-top: 1.05em;
}

.prose h1,
.prose h2,
.prose h3 {
  color: var(--ink);
}

.prose h2 {
  font-size: 1.25rem;
  margin-top: 2.2em;
  padding-top: 1.2em;
  border-top: 1px solid var(--line);
}

.prose h3 {
  font-size: 1.02rem;
  margin-top: 1.7em;
}

.prose strong {
  color: var(--ink);
  font-weight: 620;
}

.prose ul,
.prose ol {
  padding-left: 1.4em;
}

.prose ul {
  list-style: disc;
}

.prose ol {
  list-style: decimal;
}

.prose li + li {
  margin-top: 0.4em;
}

.prose code {
  font-family: var(--mono);
  font-size: 0.9em;
  background: var(--sunk);
  padding: 1px 5px;
  border-radius: var(--r1);
}

/* -------------------------------------------------------- responsive --- */

@media (max-width: 640px) {
  :root {
    --wrap: 100%;
  }

  .page {
    padding: 0 var(--s4);
  }

  header.bar {
    margin-bottom: var(--s5);
  }

  header.bar .page {
    padding-block: var(--s3);
  }

  header.bar nav {
    margin-left: 0;
    width: 100%;
    gap: var(--s4);
  }

  h1 {
    font-size: 1.45rem;
  }

  .card {
    padding: var(--s4);
    border-radius: var(--r2);
  }

  .card-head {
    margin: calc(var(--s4) * -1) calc(var(--s4) * -1) 0;
    padding: var(--s3) var(--s4);
    border-radius: var(--r2) var(--r2) 0 0;
  }

  .figures {
    gap: var(--s5);
  }

  .row > .field {
    flex-basis: 100%;
  }

  /* Behind a counter, one handed. */
  .actions button,
  .actions .btn {
    flex: 1 1 auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}
/* ---------------------------------------------------- optional parts --- */

/*
 * An optional part of a form, put as a question somebody can say no to by
 * walking past it. Closed it is one line; open, it is the same fields it always
 * was. The add-offer form showed seventeen boxes under a line saying only two
 * sections were needed, which was true and did not look it.
 */
details.optional {
  margin-top: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r2);
  background: var(--surface);
}

details.optional > summary {
  cursor: pointer;
  list-style: none;
  padding: var(--s3) var(--s4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s3);
  align-items: baseline;
  font-size: 0.92rem;
  font-weight: 560;
  color: var(--ink);
}

details.optional > summary::-webkit-details-marker {
  display: none;
}

details.optional > summary::before {
  content: "+";
  color: var(--accent);
  font-weight: 640;
  width: 1ch;
}

details.optional[open] > summary::before {
  content: "\2212";
}

details.optional > summary:hover {
  color: var(--accent);
}

details.optional[open] > summary {
  border-bottom: 1px solid var(--line);
}

details.optional .optional-note {
  font-weight: 400;
  font-size: 0.84rem;
  color: var(--ink-soft);
}

details.optional > *:not(summary) {
  margin: var(--s4);
}
/* -------------------------------------------------------- checklist --- */

/*
 * What is done and what is left, in order. The mark is the list's own, so a
 * step reads as ticked or waiting before a word of it is read.
 */
.checklist {
  list-style: none;
  margin: 0;
  padding: 0;
}

.checklist > li {
  display: grid;
  grid-template-columns: 1.6rem minmax(0, 1fr);
  gap: var(--s2);
  padding: var(--s3) 0;
  border-top: 1px solid var(--line);
}

.checklist > li:first-child {
  border-top: 0;
}

.checklist > li::before {
  content: "\25CB";
  color: var(--ink-faint);
}

.checklist > li.done::before {
  content: "\2713";
  color: var(--accent);
  font-weight: 640;
}

.checklist > li.done {
  color: var(--ink-soft);
}
/* Category groups on the Offers page: headings, drag handles, the state a
   dragged section or card carries while it moves. Its own file at the
   200-line limit, the same reason the theme is nine files rather than one. */

/* The row atop each offer card: the drag handle (when the page is grouped)
   and the bulk-select checkbox. `justify-content: flex-end` alone is what a
   shop with no categories has always seen, unchanged: one child, pushed
   right. The handle's own `margin-right: auto` is what makes room for it at
   the LEFT once it exists, without moving the checkbox off its old spot. */
.card-top {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s3);
}

.card-top .offer-drag-handle {
  margin-right: auto;
}

.drag-handle {
  cursor: grab;
  color: var(--ink-soft);
  font-size: 1.1rem;
  line-height: 1;
  user-select: none;
  touch-action: none;
}

.drag-handle:active {
  cursor: grabbing;
}

.offer-group + .offer-group {
  margin-top: var(--s5);
}

.offer-group-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s3);
}

.offer-group-head h3 {
  margin: 0;
}

.offer-group-cards > * + * {
  margin-top: var(--s4);
}

/* The section or card currently being dragged, so a shop can see which one
   it is mid-drag rather than only where it will land. */
.dragging {
  opacity: 0.5;
}

/* The shop preview: one offer per card, photograph, words, then price. */
.preview-offer {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--s4);
  align-items: start;
}

.preview-offer h3 {
  margin: 0 0 var(--s2);
}

.preview-offer p {
  margin: 0 0 var(--s2);
}

.preview-offer .price {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--s2);
  text-align: right;
}

@media (max-width: 560px) {
  .preview-offer {
    grid-template-columns: auto 1fr;
  }

  .preview-offer .price {
    grid-column: 1 / -1;
    align-items: flex-start;
    text-align: left;
  }
}

/* "Only you can see this": a banner, not loose text, so nobody mistakes the preview for the page. */
.flash.preview-standing {
  background: var(--surface);
  border-color: var(--line-strong);
  border-left: 4px solid var(--accent);
}
/* The Offers page's view controls, folding groups, and the one-row bulk bar
   (28 September 2026). Its own file for the same 200-line reason as the rest. */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------- view controls --- */

.offers-view .actions {
  margin-top: 0;
}

.view-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s3);
}

.view-group-filter {
  flex: 1 1 12rem;
  margin: 0;
}

/* ------------------------------------------------------ folding groups --- */

/* The handles are buttons so the arrow keys can reach them; they keep the
   look they had as bare glyphs. */
button.drag-handle {
  padding: var(--s1) var(--s2);
  border: 0;
  background: none;
  color: var(--ink-soft);
  font-size: 1.1rem;
  font-weight: 400;
}

button.drag-handle:hover {
  color: var(--accent);
  filter: none;
}

.offer-group-head h3 {
  flex: 1 1 auto;
}

/* The group's name is the fold button: a whole-width target, reading as a
   heading with a chevron that turns when it folds. */
.group-toggle {
  width: 100%;
  justify-content: flex-start;
  gap: var(--s3);
  padding: var(--s2) 0;
  border: 0;
  background: none;
  color: var(--ink);
  font-size: inherit;
  font-weight: inherit;
  text-align: left;
}

.group-toggle:hover {
  color: var(--accent);
  filter: none;
}

.group-toggle::after {
  content: "";
  width: 7px;
  height: 7px;
  margin-left: auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

.group-toggle[aria-expanded="false"]::after {
  transform: rotate(-45deg);
}

.group-count {
  color: var(--ink-faint);
  font-size: 0.85rem;
  font-weight: 400;
}

/* A folded group is its heading alone, so the gap below it closes up. */
.offer-group:has(.offer-group-cards[hidden]) .offer-group-head {
  margin-bottom: 0;
}

.offer-group:has(.offer-group-cards[hidden]) + .offer-group {
  margin-top: var(--s2);
}

/* ------------------------------------------------ the one-row bulk bar --- */

/* One row at any width (Rob, 28 September 2026): the menu and, when shown,
   the new group's name share what is left beside the button, and shrink
   rather than wrap. */
.bulk-row {
  display: flex;
  align-items: center;
  gap: var(--s2);
  width: 100%;
}

.bulk-row select,
.bulk-row input {
  flex: 1 1 0;
  min-width: 0;
  width: auto;
  margin: 0;
}

.bulk-row button {
  flex: 0 0 auto;
  white-space: nowrap;
}

#bulk-status:empty {
  display: none;
}

#bulk-status {
  flex-basis: 100%;
  margin: 0;
}

#bulk-actionbar .page {
  gap: var(--s2);
  padding-block: var(--s3);
}

@media (max-width: 560px) {
  #bulk-actionbar .page {
    padding-inline: var(--s4);
  }
}
/* Offer photographs at showcase size, and the full-screen view a tap opens
   (28 September 2026). The shop preview and the town's shop page share it. */

/* The photograph is a button so a keyboard can reach it; it keeps the look
   of the picture alone, with a pointer saying it does something. */
button.photo-zoom {
  display: block;
  flex: none;
  align-self: start;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}

button.photo-zoom:hover {
  filter: none;
}

button.photo-zoom:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r1);
}

img.shot.showcase {
  width: 120px;
  height: 120px;
}

.photo-zoom-dialog {
  max-width: none;
  max-height: none;
  padding: var(--s3);
  border: 0;
  border-radius: var(--r2);
  background: var(--surface);
  cursor: zoom-out;
}

.photo-zoom-dialog::backdrop {
  background: rgba(21, 23, 28, 0.8);
}

/* Never wider than the screen, never taller than most of it, and never more
   than twice the picture's own width, or 320px for a tiny one (set by the
   script before it opens): a photograph cut from a price board is small, and
   blown up further it looks worse than it does in the card. */
.photo-zoom-dialog img {
  display: block;
  width: min(88vw, var(--zoom-max, 88vw));
  max-height: 78vh;
  height: auto;
  object-fit: contain;
  border-radius: var(--r1);
}

.photo-zoom-close {
  display: block;
  margin: 0 0 var(--s3) auto;
}
