/*
 * eeMGe design system.
 *
 * The palette is lifted from the cut-paper robin collage: a warm linen ground,
 * layered forest/leaf/olive greens, warm bark brown, and a single high-chroma
 * amber that the eye lands on first (the birds' faces). That amber is reserved
 * for the one action a screen wants you to take -- everything else stays in the
 * muted range so the hierarchy does the work instead of the colour.
 *
 * Sections:
 *   1. Fonts
 *   2. Tokens (light + dark)
 *   3. Reset and base
 *   4. Canvas -- the layered background and paper grain
 *   5. Layout
 *   6. Navigation
 *   7. Typography
 *   8. Buttons and links
 *   9. Cards, tiles and sections
 *   9b. Dashboard -- stats, panels, chart
 *   9c. Exit survey -- the way out of the demo
 *  10. Flow -- hero, stepper, summary
 * 10b. Timeline -- one order's seven stages
 *  11. Forms
 *  12. Tables
 * 12b. Pagination
 *  13. Flash and status
 *  14. Utilities (kept for existing views)
 *  15. Responsive
 */

/* ---------------------------------------------------------------- 1. Fonts */

@font-face {
  font-family: 'Inter-Variable';
  src: url("/assets/Inter-Variable-c6876acd.ttf") format('truetype');
  font-display: swap;
}

/* Merienda was referenced throughout the old stylesheet but never declared, so
   every rule asking for it silently fell back to the default serif. */
@font-face {
  font-family: 'Merienda-Variable';
  src: url("/assets/Merienda-Variable-612fa39b.ttf") format('truetype');
  font-display: swap;
}

/* --------------------------------------------------------------- 2. Tokens */

:root {
  /* Surfaces -- warm linen, the collage's ground */
  --canvas: #e7d6c6;
  --raised: #f6ede3;
  --raised-soft: #f0e3d6;
  --sunken: #dcc7b4;

  /* Ink */
  --ink: #241b14;
  --ink-soft: #5b4a3d;
  --ink-faint: #8a7666;
  --line: rgba(36, 27, 20, 0.16);
  --line-strong: rgba(36, 27, 20, 0.3);

  /* Collage accents */
  --forest: #1f4632;
  --leaf: #2f8f2c;
  --olive: #77873a;
  --bark: #4a2f1d;
  --amber: #d98f22;
  --amber-ink: #2a1a05;
  --rose: #c48376;
  --slate-blue: #4f7d99;
  --cream: #e9e3c2;

  /* Categorical chart series, in fixed order and never cycled. Taken whole
     from the data-visualisation palette rather than picked by eye, and run
     through its validator against --raised: every adjacent pair clears the
     colour-blind separation floor (worst 9.1 protan) and the normal-vision
     floor (worst 19.6) in light, 8.4 and 19.3 in dark.
     Re-run the validator before changing any of them.

     Four of the light steps sit under 3:1 against the surface, which the
     validator allows only where something else carries the identity -- here
     every multi-series chart draws a legend, which is that something. */
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --series-4: #eda100;
  --series-5: #e87ba4;
  --series-6: #008300;
  --series-7: #4a3aa7;
  --series-8: #e34948;

  /* Chart series. Not reused from the UI accents: --slate-blue fails the
     chroma floor as a chart mark (it reads gray), and green-against-brown is
     indistinguishable to a deuteranope. These two were run through the
     validator against --raised and pass every check, worst adjacent pair
     dE 21.1 deutan. Re-run before changing either. */
  --chart-in: #2f8f2c;
  --chart-out: #2b6ea8;

  /* Progress ramp. One hue, four steps, deepening as a meter fills -- a
     sequential scale rather than a traffic light, so an order that has only
     just come in is not painted as a problem. Run through the palette
     validator against --raised (ordinal: monotone L, adjacent dL >= 0.06,
     light end clears 2:1); re-run before changing a step. */
  --progress-1: #6bb35e;
  --progress-2: #43992f;
  --progress-3: #2b7c28;
  --progress-4: #1f4632;
  --progress-track: #dfe7d4;

  /* Semantic */
  --accent: var(--forest);
  --accent-contrast: #f6ede3;
  --focus: var(--slate-blue);

  /* Shape -- cut-paper corners, one soft edge to break the symmetry */
  --radius-sm: 0.5rem;
  --radius: 0.9rem;
  --radius-lg: 1.4rem;
  --leaf-corner: var(--radius-lg) var(--radius-lg) var(--radius-lg) 0.4rem;

  /* Space */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.5rem;
  --s6: 2rem;
  --s7: 3rem;
  --s8: 4.5rem;

  --shadow: 0 1px 2px rgba(36, 27, 20, 0.06), 0 8px 24px -12px rgba(36, 27, 20, 0.28);
  --shadow-lift: 0 2px 4px rgba(36, 27, 20, 0.08), 0 18px 40px -18px rgba(36, 27, 20, 0.4);

  --grain-opacity: 0.4;
  --wash-opacity: 1;

  --content-width: 60rem;
  --reading-width: 34rem;
}

/* Dark is the same collage after dusk: the forest greens become the ground and
   the linen becomes the ink. Accents are lifted so they keep their contrast. */
:root.dark-mode,
body.dark-mode {
  --canvas: #10170f;
  --raised: #1b2a1d;
  --raised-soft: #182319;
  --sunken: #0b100a;

  --ink: #f0e5d6;
  --ink-soft: #bfb2a1;
  --ink-faint: #8e8474;
  --line: rgba(240, 229, 214, 0.16);
  --line-strong: rgba(240, 229, 214, 0.32);

  --forest: #63a97b;
  --leaf: #6cc45f;
  --olive: #a8b45e;
  --bark: #b3835b;
  --amber: #e9a63c;
  --amber-ink: #241704;
  --rose: #dda294;
  --slate-blue: #79a8c2;
  --cream: #efe9cb;

  --chart-in: #4faa42;
  --chart-out: #4a90d9;

  /* The same eight hues stepped for the dark surface, not a flip. */
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
  --series-6: #008300;
  --series-7: #9085e9;
  --series-8: #e66767;

  /* The ramp is re-picked for the dark surface, not flipped: more progress is
     a brighter green here, where light-on-dark is what reads as "further". */
  --progress-1: #3f7a3c;
  --progress-2: #4f9a45;
  --progress-3: #6cc45f;
  --progress-4: #9adb85;
  --progress-track: #2b3d2a;

  --accent: var(--leaf);
  --accent-contrast: #0d140c;
  --focus: var(--slate-blue);

  --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 8px 24px -12px rgba(0, 0, 0, 0.8);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.55), 0 18px 40px -18px rgba(0, 0, 0, 0.9);

  --grain-opacity: 0.22;
  --wash-opacity: 0.85;
}

/* Visitors who have never touched the toggle get their system preference. The
   server writes .light-mode once a choice exists, which switches this off. */
@media (prefers-color-scheme: dark) {
  body:not(.light-mode) {
    --canvas: #10170f;
    --raised: #1b2a1d;
    --raised-soft: #182319;
    --sunken: #0b100a;

    --ink: #f0e5d6;
    --ink-soft: #bfb2a1;
    --ink-faint: #8e8474;
    --line: rgba(240, 229, 214, 0.16);
    --line-strong: rgba(240, 229, 214, 0.32);

    --forest: #63a97b;
    --leaf: #6cc45f;
    --olive: #a8b45e;
    --bark: #b3835b;
    --amber: #e9a63c;
    --amber-ink: #241704;
    --rose: #dda294;
    --slate-blue: #79a8c2;
    --cream: #efe9cb;

    --chart-in: #4faa42;
    --chart-out: #4a90d9;

    /* The same eight hues stepped for the dark surface, not a flip. */
    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
    --series-6: #008300;
    --series-7: #9085e9;
    --series-8: #e66767;

    /* The ramp is re-picked for the dark surface, not flipped: more progress is
       a brighter green here, where light-on-dark is what reads as "further". */
    --progress-1: #3f7a3c;
    --progress-2: #4f9a45;
    --progress-3: #6cc45f;
    --progress-4: #9adb85;
    --progress-track: #2b3d2a;

    --accent: var(--leaf);
    --accent-contrast: #0d140c;

    --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 8px 24px -12px rgba(0, 0, 0, 0.8);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.55), 0 18px 40px -18px rgba(0, 0, 0, 0.9);

    --grain-opacity: 0.22;
    --wash-opacity: 0.85;
  }
}

/* -------------------------------------------------------- 3. Reset and base */

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

* {
  margin: 0;
  padding: 0;
}

/* Put back the one margin the reset above should not have taken. The UA
   stylesheet centres a [popover] in the top layer with `inset: 0; margin:
   auto`, and `* { margin: 0 }` beats it -- which pins every popover to the
   top-left corner of the screen instead. Both /variations and the demo's exit
   survey depend on this. */
[popover] {
  margin: auto;
}

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

body {
  min-height: 100vh;
  font-family: 'Inter-Variable', system-ui, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--ink);
  background-color: var(--canvas);
  transition: background-color 0.5s ease, color 0.5s ease;
}

img {
  max-width: 100%;
  height: auto;
}

::selection {
  background: var(--amber);
  color: var(--amber-ink);
}

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

/* ------------------------------------------------------------- 4. Canvas */

/* Two fixed layers stand in for the collage: soft colour masses roughly where
   the greens sit in the artwork, and the canvas grain over the top. Both are
   pure CSS so they cost nothing and follow the theme. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
}

body::before {
  opacity: var(--wash-opacity);
  background:
    radial-gradient(58% 46% at 76% 18%, color-mix(in srgb, var(--forest) 26%, transparent), transparent 68%),
    radial-gradient(46% 38% at 88% 62%, color-mix(in srgb, var(--olive) 22%, transparent), transparent 70%),
    radial-gradient(50% 42% at 12% 86%, color-mix(in srgb, var(--leaf) 18%, transparent), transparent 72%),
    radial-gradient(38% 30% at 6% 12%, color-mix(in srgb, var(--rose) 16%, transparent), transparent 70%);
}

body::after {
  opacity: var(--grain-opacity);
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
}

body.dark-mode::after,
body:not(.light-mode)::after {
  mix-blend-mode: normal;
}

@media (prefers-color-scheme: light) {
  body:not(.dark-mode)::after {
    mix-blend-mode: multiply;
  }
}

/* ------------------------------------------------------------- 5. Layout */

.yield {
  display: block;
}

.cap {
  width: 100%;
  max-width: var(--content-width);
  margin: 0 auto;
  padding: calc(var(--s8) + var(--s2)) var(--s5) var(--s8);
}

.no-cap {
  width: 100%;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}

.stack-tight {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 50;
}

.skip-link:focus {
  left: var(--s4);
  top: var(--s4);
  padding: var(--s2) var(--s4);
  background: var(--raised);
  border-radius: var(--radius-sm);
}

/* --------------------------------------------------------- 6. Navigation */

.top-links {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 10;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s5);
  background: color-mix(in srgb, var(--canvas) 82%, transparent);
  backdrop-filter: blur(12px) saturate(1.2);
  border-bottom: 1px solid var(--line);
}

.links {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: 0;
}

.links-1 {
  justify-self: start;
}

.links-2 {
  justify-self: center;
  margin: 0;
}

.links-3 {
  justify-self: end;
  margin: 0;
}

.root-name {
  font-family: 'Merienda-Variable', 'Inter-Variable', serif;
  font-size: 1.35rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--ink);
  margin: 0;
  padding: var(--s1) var(--s2);
}

.root-name:hover {
  color: var(--accent);
}

.nav-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  margin: 0;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.nav-button:hover {
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  border-color: var(--line);
  color: var(--ink);
}

.nav-link {
  margin: 0;
  padding: var(--s1) var(--s3);
  border-radius: 999px;
  font-size: 0.95rem;
  color: var(--ink-soft);
}

.nav-link:hover {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}

.link-icon {
  height: 1.4rem;
  width: auto;
}

/* --------------------------------------------------------- 7. Typography */

h1, h2, h3, h4 {
  font-family: 'Merienda-Variable', 'Inter-Variable', serif;
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink);
  text-wrap: balance;
}

h1 {
  font-size: clamp(1.75rem, 1.2rem + 2.2vw, 2.6rem);
}

h2 {
  font-size: clamp(1.35rem, 1.1rem + 1vw, 1.7rem);
}

h3 {
  font-size: 1.15rem;
}

p {
  text-wrap: pretty;
}

.lede {
  font-size: 1.1rem;
  color: var(--ink-soft);
  max-width: var(--reading-width);
}

.eyebrow {
  font-family: 'Inter-Variable', sans-serif;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

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

.form-heading {
  font-family: 'Merienda-Variable', 'Inter-Variable', serif;
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.1rem);
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink);
  margin-bottom: var(--s5);
}

/* The animated wave-through-text effect the app was built around, kept as an
   opt-in flourish rather than the default for every link and button. */
.wave-text {
  background-image: url("/assets/wave5-a05157fb.svg");
  background-size: auto 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: color-mix(in srgb, var(--forest) 55%, transparent);
  animation: scroll-bg 15s linear infinite;
}

@keyframes scroll-bg {
  0% { background-position: 100% center; }
  100% { background-position: 0% center; }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --------------------------------------------------- 8. Buttons and links */

a {
  color: var(--accent);
  text-decoration: none;
  text-underline-offset: 0.2em;
}

a:hover {
  text-decoration: underline;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

/* Shared shape for anything that behaves like a button. */
.btn,
.link,
input[type="submit"],
button.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--raised);
  color: var(--ink);
  font-family: 'Inter-Variable', sans-serif;
  font-size: 0.98rem;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}

.btn:hover,
.link:hover,
input[type="submit"]:hover,
button.button:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: var(--shadow);
}

.btn:active,
.link:active,
input[type="submit"]:active {
  transform: translateY(0);
}

/* The one amber action per screen. */
.btn-primary,
.actions input[type="submit"] {
  background: var(--amber);
  border-color: transparent;
  color: var(--amber-ink);
  font-weight: 600;
}

.btn-primary:hover,
.actions input[type="submit"]:hover {
  background: color-mix(in srgb, var(--amber) 88%, var(--ink));
}

.btn-lg {
  padding: 0.85rem 1.6rem;
  font-size: 1.05rem;
}

.btn-ghost {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--ink-soft);
}

.btn-ghost:hover {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.btn-quiet {
  background: transparent;
  border-color: transparent;
  color: var(--ink-soft);
  padding-left: var(--s3);
  padding-right: var(--s3);
}

.btn-quiet:hover {
  color: var(--ink);
  box-shadow: none;
}

.btn-danger {
  background: transparent;
  border-color: color-mix(in srgb, var(--rose) 70%, transparent);
  color: color-mix(in srgb, var(--rose) 85%, var(--ink));
}

/* ------------------------------------------- 9. Cards, tiles and sections */

/* .tile is the app's universal wrapper -- indexes, shows and forms all reach
   for it -- so it has to read as a card without any markup changes. */
.tile {
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--leaf-corner);
  background: color-mix(in srgb, var(--raised) 88%, transparent);
  color: var(--ink);
  box-shadow: var(--shadow);
  transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.2s ease;
}

/* Selects and other controls pick up .tile in several forms; they should not
   inherit card padding and shadow. */
select.tile,
input.tile,
textarea.tile {
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius-sm);
  box-shadow: none;
  background: var(--raised);
}

table.tile {
  padding: var(--s2) var(--s4);
}

.card {
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--leaf-corner);
  background: color-mix(in srgb, var(--raised) 90%, transparent);
  box-shadow: var(--shadow);
}

/* A grid of secondary destinations -- calm, uniform, never competing with the
   hero above it. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--s3);
}

.action-card {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 82%, transparent);
  color: var(--ink);
  font-size: 0.98rem;
  line-height: 1.3;
  transition: transform 0.15s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.action-card:hover {
  text-decoration: none;
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
}

.action-card img {
  width: 2rem;
  height: 2rem;
  object-fit: contain;
  flex: none;
}

.action-card-label {
  display: block;
}

.action-card-note {
  display: block;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

/* The price list as a till. Same card as the grid above, but a button rather
   than a link, and stacked so the name and the figure read as one label -- a
   clerk aims at these fifty times a day and reads them at arm's length. */
.pos-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s1);
  width: 100%;
  min-height: 5rem;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 82%, transparent);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.pos-tile:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
}

/* The press is worth showing: at a counter the question after a tap is always
   "did that register?", and the bill below is the other half of the answer. */
.pos-tile:active {
  transform: translateY(0);
  background: color-mix(in srgb, var(--amber) 22%, var(--raised));
}

/* Big enough to be the thing the eye lands on: the point of the picture is
   that the label underneath does not have to be read. */
.pos-icon {
  font-size: 1.9rem;
  line-height: 1;
}

.pos-name {
  font-weight: 600;
}

/* What is about to go up, while the taps are still being counted. */
.pos-tally {
  margin-left: var(--s1);
  padding: 0 var(--s2);
  border-radius: 999px;
  background: var(--amber);
  color: var(--amber-ink);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

.pos-price {
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

.pos-unit {
  font-size: 0.8rem;
  color: var(--ink-faint);
}

/* What has been rung up. Three columns, so the figures line up down the bill
   and the buttons sit in one place rather than wherever the name ends. */
.bill {
  display: grid;
  gap: var(--s1);
}

.bill-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line);
}

.bill-row:last-child {
  border-bottom: none;
}

.bill-count {
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.bill-amount {
  font-variant-numeric: tabular-nums;
  text-align: right;
  min-width: 5rem;
}

.bill-actions {
  display: flex;
  gap: var(--s1);
  min-width: 5rem;
  justify-content: flex-end;
}

/* Square, thumb-sized, and quiet: these are corrections, not the point of the
   screen. */
.bill-button {
  min-width: 2.5rem;
  padding: 0.35rem 0;
  justify-content: center;
  font-size: 1.1rem;
  line-height: 1;
}

.bill-total {
  border-top: 1px solid var(--line-strong);
  border-bottom: none;
  padding-top: var(--s3);
}

/* Role cards on the demo's Start screen. Same card as the home grid, but each
   one is a form rather than a link -- choosing a role signs you in -- so the
   button reset has to be undone and the contents laid out top-down to make
   room for a sentence about the person. */
.role-card {
  width: 100%;
  align-items: flex-start;
  text-align: left;
  font-family: inherit;
  cursor: pointer;
}

.role-card:hover {
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
}

.role-card-blurb {
  display: block;
  margin-top: var(--s2);
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* The role you are already using. Marked rather than removed: seeing the full
   cast is part of the point, and a missing card reads as a bug. */
.role-card-current {
  border-color: var(--leaf);
  background: color-mix(in srgb, var(--leaf) 10%, var(--raised));
}

/* The three plans. Wider than the destination grid because each card carries a
   list rather than a label, and they are meant to be read side by side. */
.tier-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--s4);
  align-items: start;
}

.tier {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 82%, transparent);
}

/* The middle plan is the one most shops land on, so it is the one the eye
   should reach first. */
.tier-featured {
  border-color: var(--line-strong);
  background: color-mix(in srgb, var(--raised) 100%, transparent);
  box-shadow: var(--shadow);
}

.tier-name {
  margin: 0;
  font-size: 1.5rem;
}

.tier-tagline {
  margin: 0;
  font-size: 1.05rem;
  color: var(--ink);
}

.tier-inherits {
  margin: var(--s2) 0 0;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.tier-features {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  font-size: 0.92rem;
  line-height: 1.4;
}

.tier-features li {
  padding-left: var(--s4);
  position: relative;
  color: var(--ink-soft);
}

.tier-features li::before {
  content: "\2713";
  position: absolute;
  left: 0;
  color: var(--leaf);
}

/* Pushes the call to action to the bottom of every card, so three lists of
   different lengths still line their buttons up. */
.tier .btn {
  margin-top: auto;
  align-self: flex-start;
}

/* The trades, on /variations. They reuse .tier-grid and .tier-name so the page
   reads as a sibling of /pricing rather than a new kind of screen. The card is
   a button because the whole face opens the popover; the reset below is what
   stops it looking like one. */
.variation-card {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 82%, transparent);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.variation-card:hover,
.variation-card:focus-visible {
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
}

/* The seven stages, in order. Numbered because the order is the argument: the
   reader is meant to recognise their own job moving through the same slots the
   laundry does. Wraps rather than scrolls -- on a phone this becomes a column
   of short lines, which is still legible. */
.variation-stages {
  margin: var(--s2) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s3);
  font-size: 0.86rem;
  line-height: 1.4;
  color: var(--ink-soft);
}

.variation-stages li {
  position: relative;
  padding-right: var(--s3);
}

.variation-stages li:not(:last-child)::after {
  content: "\2192";
  position: absolute;
  right: 0;
  color: var(--ink-faint);
}

.variation-more {
  margin-top: auto;
  padding-top: var(--s2);
  font-size: 0.9rem;
  color: var(--accent);
}

/* The detail, in the top layer. Closed is the default for [popover], so the
   content sits in the DOM -- and in the page source -- whether or not it has
   been opened. */
/* One centred panel in the top layer, used by anything that wants the reader's
   whole attention for a moment -- a variation's detail, a payment being taken.
   Native [popover] rather than a dialog and a controller: the browser already
   gives Esc, click-outside and the top layer for nothing. */
.sheet {
  max-width: min(34rem, calc(100vw - 2rem));
  /* A sheet holding a list has to stop somewhere short of the screen and
     scroll: the payable picker can be handed two hundred unsettled orders. */
  max-height: 90vh;
  overflow-y: auto;
  padding: var(--s5);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--raised);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}

.sheet::backdrop {
  background: rgba(36, 27, 20, 0.45);
}

.sheet-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s4);
}

/* The till takes the screen it is given. A popover sizes to its content, so
   this asks for the room rather than only capping it -- left to itself the
   grid settles into two columns and a clerk scrolls a small box on a large
   monitor to find the bill. The sheet itself never scrolls: its two columns
   do, independently, so the bill stays put while the basket is worked through. */
.sheet-wide {
  width: min(78rem, 94vw);
  max-width: 94vw;
  /* As tall as the price list needs, up to nearly the whole screen. A fixed
     height would leave a shop with eight items staring at half a screen of
     nothing; past the cap the two columns scroll rather than the sheet. */
  height: fit-content;
  max-height: 90vh;
  overflow: hidden;
}

/* The display goes on the open state and nowhere else. A closed popover is
   hidden by a user-agent rule, and any author rule setting display on the
   element beats it whatever its specificity -- so a bare `display: flex` here
   leaves the till laid out invisibly over the page, swallowing clicks meant
   for the buttons underneath. */
.sheet-wide:popover-open {
  display: flex;
  flex-direction: column;
}

.till-body {
  display: grid;
  grid-template-columns: 2fr minmax(15rem, 1fr);
  gap: var(--s5);
  flex: 1;
  /* Without this a grid child refuses to shrink below its content, and the
     columns push the sheet's own scrollbar back. */
  min-height: 0;
}

.till-items,
.till-bill {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  min-height: 0;
  overflow-y: auto;
}

/* On a phone it becomes a bottom sheet, which is what a thumb can reach --
   the same move the exit survey makes at the same width. */
@media (max-width: 34rem) {
  .sheet {
    width: 100vw;
    max-width: 100vw;
    max-height: 90vh;
    margin: auto 0 0;
    overflow-y: auto;
    border-width: 1px 0 0;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    padding: var(--s4);
  }

  /* One column and one scroll: two scrolling panes inside a bottom sheet is a
     trap on a phone. The panes give up their own heights entirely -- left
     squashed by the flex parent while overflowing visibly, the tiles paint
     straight over the bill underneath them. */
  .sheet-wide {
    width: 100vw;
    height: auto;
  }

  .till-body {
    grid-template-columns: 1fr;
    flex: none;
    min-height: auto;
  }

  .till-items,
  .till-bill {
    min-height: auto;
    overflow-y: visible;
  }
}

.variation-facts {
  margin: var(--s4) 0 var(--s4);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

.variation-facts dt {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
}

.variation-facts dd {
  margin: var(--s1) 0 0;
  font-size: 0.95rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* -------------------------------------------------- 9c. Exit survey */

/* The way out of the demo, under the role cards on /start. It has to be findable
   without competing with the four cards above it -- somebody who is still
   looking should not be offered the door in amber. So: a quiet text button, and
   the survey itself in the top layer where it has the screen to itself.

   Built on the same native [popover] as .sheet. The one difference is
   that this one holds a form, so it is sized for fields rather than prose and
   scrolls when a phone keyboard takes half the viewport. */

.demo-exit {
  margin: var(--s4) 0 0;
}

/* A button that reads as a link. Used for the trigger and for the decline, both
   of which are real actions that must not look like the primary one. */
.link-quiet,
.xs-decline input[type="submit"] {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--ink-faint);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.link-quiet:hover,
.link-quiet:focus-visible,
.xs-decline input[type="submit"]:hover {
  color: var(--ink);
  box-shadow: none;
}

.exit-survey {
  width: min(32rem, calc(100vw - 2rem));
  max-height: min(88vh, 46rem);
  overflow-y: auto;
  padding: var(--s5);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--raised);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
}

.exit-survey::backdrop {
  background: rgba(36, 27, 20, 0.45);
}

.exit-survey-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s4);
}

/* [hidden] is what the step machine toggles, and .xs-step sets display, so the
   attribute needs to win. Without !important every step renders at once the
   moment the second rule loads -- which is also, deliberately, what happens
   with scripting off, since then nothing sets [hidden] in the first place. */
.xs-step {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

.xs-step[hidden] {
  display: none !important;
}

.xs-title {
  margin: 0;
  font-size: 1.15rem;
  line-height: 1.3;
}

.xs-lede {
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-faint);
}

/* The verdict. Two equal buttons, deliberately large: this is the one question
   that always gets asked, and the whole survey turns on it being easy. */
.xs-choice {
  display: flex;
  gap: var(--s3);
  margin-top: var(--s2);
}

.xs-choice-btn {
  flex: 1;
  justify-content: center;
  padding: var(--s4) var(--s3);
  font-size: 1.02rem;
}

.xs-options {
  display: flex;
  flex-direction: column;
}

/* A full reset, not a tweak. .form-label -- which every other label in the app
   wears -- is uppercase, letter-spaced and block, all of which are wrong for a
   row you tick. These labels are their own thing. */
label.xs-option {
  display: flex;
  align-items: flex-start;
  gap: var(--s2);
  margin: 0;
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.92rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  cursor: pointer;
}

label.xs-option input {
  margin-top: 0.2rem;
  flex: none;
}

label.xs-option-inline {
  display: inline-flex;
  align-items: center;
  padding: var(--s1) 0;
  margin-right: var(--s4);
  border-bottom: 0;
}

.xs-channels {
  display: flex;
  flex-wrap: wrap;
  padding-bottom: var(--s2);
  border-bottom: 1px solid var(--line);
}

/* The base textarea rule gives this its colours, border and radius. All this
   has to do is beat that rule's 6rem min-height: two rows is the right size for
   an afterthought, and a box that big reads as homework. */
.xs-note {
  min-height: 3rem;
  font-size: 0.9rem;
}

.xs-field {
  margin: 0;
}

.xs-actions {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-top: var(--s2);
}

/* Send is disabled until there is an address or a number worth having. Saying
   so in the styling matters more than usual here: the visitor is on their way
   out and will not hunt for the reason a button did nothing. */
.exit-survey .btn[disabled],
.exit-survey input[type="submit"][disabled] {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

.xs-decline {
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}

/* Off-screen rather than display:none, which some bots know to skip. */
.xs-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* On a phone this becomes a sheet against the bottom edge, where the thumb
   already is and where the keyboard pushes from. */
@media (max-width: 34rem) {
  .exit-survey {
    width: 100vw;
    max-width: 100vw;
    max-height: 90vh;
    margin: auto 0 0;
    border-width: 1px 0 0;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    padding: var(--s4);
  }

  .xs-choice {
    flex-direction: column;
  }
}

/* ----------------------------------------------------- 9b. Dashboard */

/* Home is the working surface of whoever is looking. These are the pieces it
   is built from -- figures, panels of real work, and the owner's one chart.
   Nothing here is amber: that is reserved for the single primary action on a
   screen, which on this page is still the hero's button. */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--s3);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 82%, transparent);
  color: var(--ink);
  transition: transform 0.15s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.stat:hover {
  text-decoration: none;
  transform: translateY(-2px);
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
}

/* The figure is the point of the tile, so it carries the weight and the label
   sits under it rather than over it. */
.stat-value {
  font-size: 1.45rem;
  font-weight: 600;
  line-height: 1.15;
}

.stat-label {
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.stat-note {
  font-size: 0.78rem;
  color: var(--ink-faint);
}

.panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--s4);
  align-items: start;
}

/* The dashboard's panels are independent cards of wildly different heights: a
   board of eight stages beside a list of two things needing attention. On a
   grid they align to rows, so the short panel leaves a hole under it and the
   next one waits for the tallest in its row to finish -- which is how a screen
   ends up with a column of payments below the board and nothing at all to the
   right of it.

   Columns let each card start where the last one ended. The trade is reading
   order: down a column, then across. These panels are independent of one
   another, so there is no order to lose. */
.panel-masonry {
  columns: 3 17rem;
  column-gap: var(--s4);
}

/* Two columns rather than three, for cards that carry a chart: at 17rem a
   line chart is a legend with a squiggle under it. */
.masonry-wide {
  columns: 2 22rem;
}

.panel-masonry > * {
  /* A card broken across a column boundary is unreadable. */
  break-inside: avoid;
  margin-bottom: var(--s4);
}

.panel {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 82%, transparent);
}

.panel-head {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.panel-title {
  margin: 0;
  font-size: 1.05rem;
}

.panel-title a {
  color: inherit;
}

.panel-note {
  margin: 0;
  font-size: 0.82rem;
  color: var(--ink-faint);
}

/* One line of work: what it is on the left, what you can do about it on the
   right. Wraps rather than truncates -- an address the rider cannot read is
   worse than a taller row. */
.dash-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s3);
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
}

.dash-row:last-child {
  padding-bottom: 0;
  border-bottom: none;
}

.dash-row-main {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  min-width: 0;
}

.dash-row-title {
  font-weight: 600;
  font-size: 0.95rem;
}

.dash-row-note {
  font-size: 0.82rem;
  color: var(--ink-soft);
  line-height: 1.35;
}

.dash-row-action {
  flex: none;
}

/* Chartkick sizes the canvas itself; the tile just gives it a surface and
   stops a wide axis pushing the page sideways. */
.chart-tile {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  overflow-x: auto;
}

@media (max-width: 620px) {
  .stat-grid {
    grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  }

  .stat-value {
    font-size: 1.2rem;
  }

  /* The action drops under the detail rather than squeezing it. */
  .dash-row {
    flex-direction: column;
    align-items: stretch;
  }
}

/* Progressive disclosure. Settings has two dozen destinations; they live in
   collapsed groups so a screen only ever shows what was asked for. */
.section {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 55%, transparent);
  overflow: hidden;
}

.section > summary {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s4) var(--s5);
  cursor: pointer;
  list-style: none;
  font-family: 'Merienda-Variable', 'Inter-Variable', serif;
  font-size: 1.1rem;
  color: var(--ink);
}

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

.section > summary::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  margin-left: auto;
  border-right: 2px solid var(--ink-faint);
  border-bottom: 2px solid var(--ink-faint);
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}

.section[open] > summary::after {
  transform: rotate(-135deg);
}

.section > summary:hover {
  background: color-mix(in srgb, var(--ink) 5%, transparent);
}

.section-count {
  font-family: 'Inter-Variable', sans-serif;
  font-size: 0.8rem;
  color: var(--ink-faint);
}

.section-body {
  padding: 0 var(--s5) var(--s5);
}

/* ---------------------------------------------- 10. Flow: hero and stepper */

/* One screen, one obvious next move. */
.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  padding: var(--s7) var(--s6);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) var(--s6);
  background:
    linear-gradient(150deg,
      color-mix(in srgb, var(--raised) 92%, transparent),
      color-mix(in srgb, var(--raised) 72%, transparent));
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}

/* Two cut-paper leaves overlapping in the corner, the way the collage layers
   its shapes -- one forest, one olive, neither quite symmetrical. */
.hero::before,
.hero::after {
  content: "";
  position: absolute;
  pointer-events: none;
}

.hero::before {
  right: 4.5rem;
  bottom: -5rem;
  width: 12rem;
  height: 12rem;
  border-radius: 38% 62% 60% 40% / 56% 38% 62% 44%;
  background: color-mix(in srgb, var(--olive) 26%, transparent);
  transform: rotate(-12deg);
}

.hero::after {
  right: -3rem;
  bottom: -4rem;
  width: 15rem;
  height: 15rem;
  border-radius: 68% 32% 55% 45% / 44% 60% 40% 56%;
  background: color-mix(in srgb, var(--forest) 24%, transparent);
}

.hero > * {
  position: relative;
  z-index: 1;
}

.hero-title {
  max-width: 22ch;
}

/* With a dashboard under it the hero is a header, not the screen. It keeps the
   one primary action -- that rule has not been repealed -- but stops taking
   the whole first fold and drops the cut-paper decoration, which was scaled
   for a page that ended at the fold. */
.hero-compact {
  padding: var(--s5) var(--s6);
  gap: var(--s3);
  box-shadow: var(--shadow);
}

.hero-compact::before,
.hero-compact::after {
  display: none;
}

.hero-compact .hero-title {
  font-size: 1.5rem;
  max-width: 34ch;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s2);
}

/* Live status inside the hero: the request currently in flight. */
.hero-status {
  display: flex;
  align-items: center;
  gap: var(--s4);
  flex-wrap: wrap;
}

.hero-status-text {
  min-width: 12rem;
}

.stepper {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  list-style: none;
  margin-bottom: var(--s5);
}

.stepper li {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) var(--s3) var(--s1) var(--s1);
  border-radius: 999px;
  font-size: 0.9rem;
  color: var(--ink-faint);
}

.stepper .step-index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  font-size: 0.8rem;
  font-weight: 600;
}

.stepper .is-current {
  color: var(--ink);
  background: color-mix(in srgb, var(--amber) 20%, transparent);
}

.stepper .is-current .step-index {
  background: var(--amber);
  border-color: transparent;
  color: var(--amber-ink);
}

.stepper .is-done {
  color: var(--ink-soft);
}

.stepper .is-done .step-index {
  background: color-mix(in srgb, var(--forest) 75%, transparent);
  border-color: transparent;
  color: var(--accent-contrast);
}

.stepper a {
  color: inherit;
}

/* --------------------------------------------------------- 10b. Timeline */

/* The seven stages of one order, read down rather than across. The .stepper
   above is a wizard: three steps, all of them the reader's own work. This is a
   record: seven stages, most of them somebody else's. It borrows the state
   words -- is-done, is-current -- so those mean one thing everywhere. */
.timeline {
  display: grid;
  list-style: none;
}

.timeline li {
  position: relative;
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  align-items: start;
  gap: var(--s3);
  padding-bottom: var(--s4);
  color: var(--ink-faint);
}

.timeline li:last-child {
  padding-bottom: 0;
}

/* The rail is drawn from each node down to the next rather than as one line
   behind the column, so nothing hangs below the last stage. */
.timeline li::before {
  content: "";
  position: absolute;
  left: calc(0.8rem - 1px);
  top: 1.6rem;
  bottom: 0;
  width: 2px;
  background: var(--line);
}

.timeline li:last-child::before {
  display: none;
}

.timeline .step-index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--raised);
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
}

/* Line-height matches the node, so a label sits on its own marker's centre. */
.timeline-label {
  font-size: 0.95rem;
  line-height: 1.6rem;
}

/* Every stage says its state in words as well as in colour -- "Done", "Now" --
   because a rail of green circles is a rail of grey circles to some readers. */
.timeline-note {
  display: block;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--ink-faint);
}

.timeline .is-done {
  color: var(--ink-soft);
}

.timeline .is-done .step-index {
  background: color-mix(in srgb, var(--forest) 75%, transparent);
  border-color: transparent;
  color: var(--accent-contrast);
}

/* Finished stages join up in the accent while the rail ahead stays the plain
   line, so how far an order has got is legible before a word is read. */
.timeline .is-done::before {
  background: color-mix(in srgb, var(--forest) 55%, transparent);
}

.timeline .is-current {
  color: var(--ink);
  font-weight: 600;
}

.timeline .is-current .step-index {
  background: var(--amber);
  border-color: transparent;
  color: var(--amber-ink);
}

/* Three kinds of thing to pay for, side by side, one of them on. A segmented
   control rather than a dropdown: with three options a dropdown hides two of
   them behind a click and says nothing about what the choice is between.

   The radio stays in the DOM and stays focusable -- it is moved out of sight
   rather than display:none'd -- so the keyboard tabs into the group and a
   screen reader announces it as the radio group it is. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
}

.tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: var(--s2) var(--s4);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--raised);
  color: var(--ink-soft);
  font-size: 0.95rem;
  font-weight: 500;
  cursor: pointer;
}

.tab input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.tab:hover {
  border-color: var(--ink-faint);
}

.tab:disabled {
  cursor: default;
}

/* button_to wraps its button in a form of its own; display: contents lets the
   button sit in the row as though the form were not there. */
.tabs form {
  display: contents;
}

/* A tab that is a button rather than a radio says which one is on with a
   class, and takes itself out of the tab order once it is -- pressing the
   state you are already in is not a thing to offer. */
.tab.is-on,
.tab:has(input:checked) {
  background: var(--amber);
  border-color: transparent;
  color: var(--amber-ink);
  font-weight: 600;
}

.tab:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* The button that stands in for a select: it says what is chosen, and opens
   the sheet that chooses it. */
.picker {
  width: 100%;
  justify-content: flex-start;
}

/* Wider than the method grid: these carry a name and a line of description,
   and at the payment grid's 7.5rem "Detergent and supplies" wraps to four
   lines. */
.type-grid {
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
}

/* The picture on a choice card. Big enough to be what the eye lands on, since
   the point of it is that the label underneath need not be read. */
.choice-icon {
  margin-right: var(--s2);
  font-size: 1.25rem;
  line-height: 1;
}

/* The same radio cards, laid out across rather than down: three payment
   methods side by side fit where three stacked rows would push the button that
   records the payment off the bottom of a phone. */
.method-grid {
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}

/* Radio cards -- picking a service should feel like picking a thing, not
   filling a dropdown. */
.choice-list {
  display: grid;
  gap: var(--s3);
}

/* The radio stays a real, visible radio -- the card is the hit area, not a
   replacement for the control. */
.choice {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--raised) 70%, transparent);
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}

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

.choice input[type="radio"] {
  width: 1.1rem;
  height: 1.1rem;
  margin-top: 0.25rem;
  accent-color: var(--amber);
  cursor: pointer;
}

.choice-body {
  display: block;
  min-width: 0;
}

.choice-title {
  display: block;
  font-weight: 600;
}

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

.choice:has(input:checked) {
  border-color: var(--amber);
  background: color-mix(in srgb, var(--amber) 14%, var(--raised));
  box-shadow: var(--shadow);
}

.choice:has(input:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

.choice-tag {
  display: inline-block;
  margin-left: var(--s2);
  padding: 0 var(--s2);
  border-radius: 999px;
  background: color-mix(in srgb, var(--olive) 25%, transparent);
  font-size: 0.75rem;
  color: var(--ink-soft);
  vertical-align: middle;
}

/* Step 3 review list. */
.summary-list {
  display: grid;
  gap: var(--s3);
  margin: 0;
}

.summary-list div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s4);
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
}

.summary-list dt {
  color: var(--ink-faint);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  flex: none;
}

.summary-list dd {
  text-align: right;
}

.flow-actions {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s6);
}

.flow-actions .btn-quiet {
  margin-right: auto;
}

/* ------------------------------------------------------------- 11. Forms */

.form-container {
  width: 100%;
  max-width: var(--reading-width);
}

/* A form with a picker in it is not a reading column: the tiles want room to
   be a grid, and the fields beside them rather than a scroll below them. */
.form-wide {
  max-width: min(58rem, 100%);
}

/* What is being recorded on the left, the details of it on the right -- the
   shape of every till and counter system, which is why a clerk can use one
   without being taught. It stacks below the breakpoint, picker first, because
   that is the order the questions come in. */
.form-split {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: var(--s5);
  align-items: start;
}

/* Grid children refuse to shrink below their content without this, and the
   tiles push the fields off the edge. */
.form-split > * {
  min-width: 0;
}

.centered-content {
  display: flex;
  justify-content: center;
}

.field {
  margin-bottom: var(--s4);
}

.form-label,
label {
  display: block;
  margin-bottom: var(--s1);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--ink-soft);
}

.form-input,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="number"],
input[type="date"],
input[type="datetime-local"],
input[type="search"],
select,
textarea {
  width: 100%;
  padding: 0.65rem 0.8rem;
  font: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: var(--raised);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

textarea {
  min-height: 6rem;
  resize: vertical;
}

.form-input:focus,
input:focus,
select:focus,
textarea:focus {
  border-color: var(--focus);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus) 25%, transparent);
}

input[type="checkbox"],
input[type="radio"] {
  width: auto;
  accent-color: var(--forest);
}

.checkbox-field,
.form-radio-group {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.checkbox-field label,
.form-radio-label,
.form-checkbox-label {
  display: inline;
  margin: 0;
  font-weight: 400;
  color: var(--ink-soft);
}

.form-checkbox {
  width: 1rem;
  height: 1rem;
}

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

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

.form-errors {
  padding: var(--s4);
  margin-bottom: var(--s5);
  border: 1px solid color-mix(in srgb, var(--rose) 60%, transparent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--rose) 16%, transparent);
  color: var(--ink);
}

.form-errors ul {
  margin: var(--s2) 0 0 var(--s5);
}

.links-container {
  margin-top: var(--s5);
  text-align: center;
}

.auth-links {
  background: color-mix(in srgb, var(--raised) 88%, transparent);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4);
  margin-top: var(--s5);
}

.auth-link {
  display: block;
  margin-bottom: var(--s2);
  font-size: 0.92rem;
  color: var(--accent);
}

.auth-omniauth {
  margin-top: var(--s3);
}

.collection-select {
  margin-top: var(--s1);
}

/* ------------------------------------------------------------ 12. Tables */

table {
  width: 100%;
  border-collapse: collapse;
}

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

th {
  position: sticky;
  top: 0;
  z-index: 4;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  background: var(--canvas);
}

tr:last-child td {
  border-bottom: none;
}

/* A timestamp reads as one thing with two lines: when it happened, and -- in
   the smallest type on the page, underscored so it reads as a footnote rather
   than a second date -- what year that was. */
.stamp {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  line-height: 1.2;
}

.stamp-when,
.elapsed {
  white-space: nowrap;
}

.stamp-year {
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 1px;
}

/* A row that opens something. The anchor lives in the first cell and its
   ::after is stretched over the row, so the hit target is the whole row while
   the thing being clicked is still an ordinary link -- focusable, middle
   clickable, openable in a new tab. The row is the positioned ancestor that
   overlay resolves against; the cells must stay unpositioned or each would
   trap the overlay in its own box. */
.linked-row {
  position: relative;
}

.linked-row:hover {
  background: var(--raised-soft);
}

/* The focus ring belongs to the row, because the row is what the link covers.
   :focus-visible so it appears for the keyboard and not on every mouse click. */
.linked-row:has(.row-link:focus-visible) {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}

.row-link {
  color: inherit;
  text-decoration: none;
}

.row-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* A ratio against a limit, sized so a column of them can be compared by eye
   alone. The fill's colour is the same reading as its length, one step deeper
   per band, and the number sits outside the bar in ink -- a bar too short to
   hold its own label is exactly the bar you most want labelled. */
.meter {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 7rem;
}

.meter-track {
  flex: 1;
  height: 0.45rem;
  border-radius: 999px;
  background: var(--progress-track);
  overflow: hidden;
}

.meter-fill {
  display: block;
  width: var(--fill, 0%);
  height: 100%;
  border-radius: 999px;
  background: var(--progress-1);
}

.meter-fill[data-band="2"] { background: var(--progress-2); }
.meter-fill[data-band="3"] { background: var(--progress-3); }
.meter-fill[data-band="4"] { background: var(--progress-4); }

/* Fixed width and tabular figures, so "7%" and "100%" take the same room and
   every track in the column starts and ends on the same two lines -- a ragged
   right edge would leave the bars comparing their labels rather than their
   values. */
.meter-value {
  flex: none;
  min-width: 2.6rem;
  text-align: right;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* Wide tables scroll inside themselves rather than pushing the page sideways. */
.table-scroll {
  width: 100%;
  overflow-x: auto;
}

/* --------------------------------------------------------- 12b. Pagination */

/* Pagy ships markup and no styling, so the nav arrived as a row of bare links
   pressed against the bottom of the table. It is the end of a page: give it
   room above, room below, and hit targets a thumb can find. */
.pagy {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--s2);
  margin: var(--s5) auto var(--s7);
  padding: 0 var(--s4);
}

.pagy a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0 var(--s2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--raised);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
}

.pagy a:hover {
  background: var(--raised-soft);
  border-color: var(--line-strong);
  text-decoration: none;
}

/* The page you are on, and the arrows with nowhere to go: pagy marks both
   aria-disabled, so the current page is picked out first and the rest of that
   set recedes. */
.pagy a.current {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
  font-weight: 600;
}

.pagy a[aria-disabled="true"]:not(.current) {
  background: transparent;
  color: var(--ink-faint);
  cursor: default;
}

.pagy a.gap {
  border-color: transparent;
  background: transparent;
  color: var(--ink-faint);
}

/* -------------------------------------------------- 13. Flash and status */

#flash-messages {
  position: relative;
  z-index: 5;
  max-width: var(--content-width);
  margin: calc(var(--s8) + var(--s2)) auto 0;
  padding: 0 var(--s5);
}

#flash-messages:empty {
  display: none;
  margin: 0;
}

#flash-messages + .yield .cap {
  padding-top: var(--s5);
}

.notice, .alert {
  position: relative;
  padding: var(--s3) var(--s4);
  margin-bottom: var(--s3);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: opacity 0.5s ease-out;
}

#flash-messages .notice {
  background: color-mix(in srgb, var(--leaf) 18%, var(--raised));
  border-color: color-mix(in srgb, var(--leaf) 40%, transparent);
  color: var(--ink);
}

#flash-messages .alert {
  background: color-mix(in srgb, var(--rose) 22%, var(--raised));
  border-color: color-mix(in srgb, var(--rose) 55%, transparent);
  color: var(--ink);
}

.notice2 {
  color: var(--forest);
  font-size: 1.2rem;
  text-align: center;
  margin: var(--s4);
}

.flash-close {
  position: absolute;
  right: var(--s3);
  top: var(--s2);
  cursor: pointer;
  font-weight: 700;
}

.progress-circle {
  --percentage: 0%;
  width: 3.25rem;
  height: 3.25rem;
  flex: none;
  border-radius: 50%;
  background: conic-gradient(var(--forest) calc(var(--percentage) * 1%), color-mix(in srgb, var(--ink) 12%, transparent) 0%);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

/* Punch the middle out so it reads as a ring. */
.progress-circle::before {
  content: "";
  position: absolute;
  inset: 0.42rem;
  border-radius: 50%;
  background: var(--raised);
}

.progress-circle span {
  position: relative;
  z-index: 1;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink);
}

.call-button {
  display: inline-block;
  margin-left: var(--s2);
  padding: 0.15rem var(--s2);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--forest) 85%, transparent);
  color: var(--accent-contrast);
  font-size: 0.85rem;
  text-decoration: none;
}

.call-button:hover {
  text-decoration: none;
  background: var(--forest);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  padding: 0.1rem var(--s3);
  border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  font-size: 0.8rem;
  color: var(--ink-soft);
}

/* Delivery states. Tinted rather than loud: a push that failed is worth
   noticing in a list, not worth shouting over the message it belongs to. */
.chip-good {
  background: color-mix(in srgb, var(--leaf) 16%, transparent);
  color: var(--forest);
}

.chip-warn {
  background: color-mix(in srgb, var(--amber) 20%, transparent);
  color: var(--bark);
}

.chip-bad {
  background: color-mix(in srgb, var(--rose) 26%, transparent);
  color: var(--bark);
}

/* Notification opt-in is an aside, not the first thing on the page. */
.push-prompt {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--s3);
}

/* ---------------------------------------------------------- 14. Utilities */

.hidden { display: none; }

/* The [hidden] attribute is only "display: none" in the UA stylesheet, so any
   author rule that sets display wins over it -- .btn's inline-flex is why a
   <button hidden> was still on screen. This puts the attribute back in charge. */
[hidden] { display: none !important; }

.centered-text { text-align: center; }
.right-text { text-align: right; }
.no-border { border: none; }
.black { color: var(--ink); }
.bottom { bottom: 0; }
.line-text-1 { line-height: 1.35; }
.link-spacer { margin-top: var(--s6); }
.negative-bottom { margin-bottom: calc(var(--s5) * -1); }
.round-margin-half { margin: var(--s2); }
.round-margin { margin: var(--s4); }
.round-padding-half { padding: var(--s2); }

.row-wrap {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--s3);
}

.row-stretch {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  gap: var(--s3);
}

.column-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s1);
}

.small-cell {
  font-size: 0.9rem;
  max-width: 45ch;
}

.location-description {
  padding: var(--s1);
  max-width: 60ch;
}

.service-description {
  max-width: 40ch;
  white-space: normal;
  word-wrap: break-word;
  padding: var(--s1) 0;
  color: var(--ink-soft);
  font-size: 0.92rem;
}

.base-icon {
  height: 3.25rem;
  width: auto;
  margin-bottom: var(--s2);
}

.tiny-icon {
  height: 1.1rem;
  width: auto;
  vertical-align: -0.15rem;
  flex: none;
}

.zone-link.active {
  font-weight: 600;
  color: var(--ink);
}

/* --------------------------------------------------------- 15. Responsive */

@media (max-width: 900px) {
  .cap {
    padding-left: var(--s4);
    padding-right: var(--s4);
  }

  #flash-messages {
    padding-left: var(--s4);
    padding-right: var(--s4);
  }
}

@media (max-width: 620px) {
  body {
    font-size: 1rem;
  }

  .form-split {
    grid-template-columns: 1fr;
  }

  /* Below this width a three-column table can only be read by dragging it
     sideways, so each row stops being a row and becomes a card: the cells
     stack, and each one carries the label its column header used to give it.
     The header itself goes, having nothing left to head. */
  .stacked-table thead {
    display: none;
  }

  .stacked-table,
  .stacked-table tbody,
  .stacked-table tr,
  .stacked-table td {
    display: block;
  }

  .stacked-table tr {
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--line);
  }

  .stacked-table tr:last-child {
    border-bottom: none;
  }

  /* Label left, value right, on one line each -- the shape a phone reads down.
     The cells keep their own borders off: the card edge is the divider now. */
  .stacked-table td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    padding: var(--s1) 0;
    border-bottom: none;
  }

  .stacked-table td::before {
    content: attr(data-label);
    flex: none;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-faint);
  }

  /* The bar takes the room the value side gives it, rather than sitting at its
     minimum width with the card's right half empty. */
  .stacked-table .meter {
    flex: 1;
  }

  .top-links {
    padding: var(--s2) var(--s3);
  }

  .links-3 .nav-link {
    padding: var(--s1) var(--s2);
  }

  .hero {
    padding: var(--s6) var(--s4);
    border-bottom-left-radius: var(--s5);
  }

  .hero::after {
    width: 10rem;
    height: 10rem;
  }

  .card-grid {
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  }

  .section > summary,
  .section-body {
    padding-left: var(--s4);
    padding-right: var(--s4);
  }

  .summary-list div {
    flex-direction: column;
    gap: var(--s1);
  }

  .summary-list dd {
    text-align: left;
  }

  .flow-actions {
    flex-wrap: wrap;
  }
}
