/* Coincide — the booking screens: cards, time buttons, forms, deposit panel.
   NO RAW COLOUR VALUES HERE. Every colour is a var() supplied by the page's
   theme — the guest screens load theme-guest.css, the vendor's pages and the
   office load theme-staff.css, and this same file dresses both. That is why a
   colour written literally here would be a bug: it would look right on one
   face and wrong on the other. */

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

.section-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.25;
  color: var(--c-ink);
}

.eyebrow {
  margin: 0 0 var(--space-1);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-ink-faint);
}

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

.stack--tight { gap: var(--space-2); }
.stack--loose { gap: var(--space-8); }

/* --- Event header ------------------------------------------------------ */

.event-facts {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
}

.event-facts div {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: var(--space-1) var(--space-3);
  align-items: baseline;
}

.event-facts dt {
  color: var(--c-ink-faint);
  font-size: var(--step--1);
}

.event-facts dd {
  margin: 0;
  color: var(--c-ink);
}

/* --- Cards ------------------------------------------------------------- */

.card-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-4);
}

.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
  background: var(--c-surface-raised);
  border: 1px solid var(--c-line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
}

.card__vendor {
  margin: 0;
  font-size: var(--step--1);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-brand);
}

.card__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.25;
  color: var(--c-ink);
}

.card__text {
  margin: 0;
  color: var(--c-ink-muted);
  text-wrap: pretty;
}

/* --- Money and meta ---------------------------------------------------- */

.facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.fact {
  padding: var(--space-1) var(--space-3);
  background: var(--c-surface-sunken);
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
  color: var(--c-ink-muted);
}

.fact strong {
  color: var(--c-ink);
  font-weight: 600;
}

.money-line {
  margin: 0;
  font-size: var(--step--1);
  color: var(--c-ink-muted);
}

.money-line strong { color: var(--c-ink); font-weight: 600; }

/* --- Buttons ----------------------------------------------------------- */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--hit);
  padding: var(--space-3) var(--space-6);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: var(--c-brand);
  color: var(--c-ink-inverse);
  font: inherit;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.button:hover { background: var(--c-brand-strong); color: var(--c-ink-inverse); }

.button[disabled],
.button[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.55;
}

.button--quiet {
  background: transparent;
  border-color: var(--c-line-strong);
  color: var(--c-ink);
}

.button--quiet:hover {
  background: var(--c-surface-sunken);
  color: var(--c-ink);
}

.button--block { width: 100%; }

.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.button-row .button { flex: 1 1 12rem; }

/* --- The list of times ------------------------------------------------- */

.slot-day {
  margin: 0 0 var(--space-2);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--c-ink);
}

.slot-grid {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
  display: grid;
  /* THREE ACROSS, always — her call. minmax(0,1fr) rather than a bare 1fr so
     a wide label can never push a column past its third of the row. An odd
     last row is expected and fine. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}

.slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  width: 100%;
  /* Condensed from 60px to the app-wide tap minimum — and NOT below it. This
     is the only target for the tap it takes; a guest is picking it standing
     up, on a phone, in a room. */
  min-height: var(--hit);
  padding: 4px 2px;
  background: var(--c-surface-raised);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--radius-md);
  color: var(--c-ink);
  font: inherit;
  cursor: pointer;
}

.slot:hover:not([disabled]) {
  border-color: var(--c-brand);
  background: var(--c-surface-sunken);
}

.slot__time { font-weight: 600; }

.slot__state {
  font-size: var(--step--1);
  color: var(--c-ink-faint);
}

/* A full time stays on the page, plainly marked. It is not hidden: a guest who
   sees "7:00 — taken" trusts the page. A guest whose 7:00 quietly vanished
   does not. The word "Taken" carries the meaning; the styling only supports
   it, so this reads the same to anyone who cannot see colour. */
.slot[disabled] {
  background: var(--c-surface-sunken);
  border-style: dashed;
  border-color: var(--c-line);
  color: var(--c-ink-faint);
  cursor: not-allowed;
}

.slot[disabled] .slot__time { text-decoration: line-through; font-weight: 500; }

.slot--chosen,
.slot[aria-pressed="true"] {
  border-color: var(--c-brand);
  border-width: 2px;
  background: var(--c-surface-sunken);
}

/* --- Forms ------------------------------------------------------------- */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.field label {
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--c-ink);
}

.field .hint {
  font-size: var(--step--1);
  color: var(--c-ink-muted);
}

/* Boxes of every kind share one look, in one rule. A textarea that does not
   quite match the inputs above it is the sort of thing nobody can name but
   everybody notices. */
.field input,
.field textarea {
  min-height: var(--hit);
  padding: var(--space-2) var(--space-3);
  background: var(--c-surface-raised);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--radius-md);
  color: var(--c-ink);
  font: inherit;
}

/* Only up and down. Sideways dragging can push a textarea wider than the
   column it sits in. */
.field textarea {
  resize: vertical;
  line-height: 1.5;
}

.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"] {
  border-color: var(--c-danger);
  border-width: 2px;
}

.field .field-error {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--c-danger);
}

.required-mark {
  color: var(--c-ink-muted);
  font-weight: 400;
}

/* --- Messages ---------------------------------------------------------- */

.message {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--c-line);
  border-left: 3px solid var(--c-ink-faint);
  border-radius: var(--radius-md);
  background: var(--c-surface-raised);
  font-size: var(--step--1);
  color: var(--c-ink);
}

.message--ok      { background: var(--c-ok-wash);     border-left-color: var(--c-ok); }
.message--warn    { background: var(--c-warn-wash);   border-left-color: var(--c-warn); }
.message--problem { background: var(--c-danger-wash); border-left-color: var(--c-danger); }

.message strong { font-weight: 600; }

/* A status word always appears as a word, never as a colour alone. */
.status-word {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--c-line-strong);
  border-radius: 999px;
  font-size: var(--step--1);
  font-weight: 600;
}

.status-word--ok      { border-color: var(--c-ok);     color: var(--c-ok); }
.status-word--warn    { border-color: var(--c-warn);   color: var(--c-warn); }
.status-word--problem { border-color: var(--c-danger); color: var(--c-danger); }

/* --- The deposit panel ------------------------------------------------- */

.deposit {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--c-surface-raised);
  border: 2px solid var(--c-brand);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}

.deposit__amount {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1.1;
  color: var(--c-ink);
}

.handles {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

.handle {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--c-surface-sunken);
  border-radius: var(--radius-md);
}

.handle__where {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-ink-muted);
}

/* Monospace, and for a real reason: a handle is TRANSCRIBED — read off this
   screen and typed into Venmo, character by character. A typewriter face is
   the one that keeps l and 1 and I apart, and 0 apart from O. Getting that
   wrong sends somebody's deposit to a stranger with a similar name. */
.handle__who {
  font-family: var(--font-mono);
  font-size: var(--step-0);
  color: var(--c-ink);
  overflow-wrap: anywhere;
}

/* A payment link is the exception, because nobody reads it out — they tap it.
   None of the reasoning above applies, and monospace on a sentence like "Open
   the payment page" just makes prose look like code. */
.handle--link .handle__who {
  font-family: var(--font-body);
}

/* And it is a thing a guest presses, standing up, at an event, on a phone —
   so it obeys the same minimum as every other target in this app. As a plain
   line of text it was about half that: the smallest tap target in the whole
   booking flow was the one that takes the money. */
.handle--link .handle__who a {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit);
  font-weight: 600;
}

/* --- The vendor's board ------------------------------------------------- */
/* Built for somebody standing at a table, on a phone, with a queue in front of
   them. Every target is at least --hit; nothing needs a steady hand. */

.tally {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: var(--space-3);
}

.tally__item {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  background: var(--c-surface-raised);
  border: 1px solid var(--c-line);
  border-radius: var(--radius-md);
}

.tally__count {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 600;
  line-height: 1;
}

/* Colour is never the only signal — the label underneath always says it too. */
.tally__count--ok      { color: var(--c-ok); }
.tally__count--warn    { color: var(--c-warn); }
.tally__count--problem { color: var(--c-danger); }

.tally__label {
  font-size: var(--step--1);
  color: var(--c-ink-muted);
}

/* ===========================================================================
   THE BOARD — the night on one screen.
   Replaces .slot-block / .slot-head, which gave every time of the evening its
   own padded card: ten cards, seven of them saying only "Nobody yet.", and a
   474px tile per guest carrying four full-width buttons. Measured at 3,615px
   on a phone for three bookings.

   The shape now is a LIST, not a stack of cards. A time is one line. An empty
   time is a thin quiet line. The actions live inside the line you tap, except
   the one the night is actually made of.
   =========================================================================== */

/* --- The header strip ---------------------------------------------------- */

.board-head { display: grid; gap: var(--space-1); }

.board-head__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.1;
}

/* Counts on the left, the two tools on the right, one line where there is room
   and two where there is not. */
.board-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

/* The tally stops being two boxes and becomes two figures. Same numbers, same
   words under them — a sixth of the height. */
.board-bar .tally {
  display: flex;
  grid-template-columns: none;
  gap: var(--space-4);
}

.board-bar .tally__item {
  flex-direction: row;
  align-items: baseline;
  gap: var(--space-1);
  padding: 0;
  background: none;
  border: 0;
}

.board-bar .tally__count { font-size: var(--step-1); }

.board-bar__tools { display: flex; gap: var(--space-2); }

.board-bar__tools .button {
  min-height: 38px;
  padding: 6px 12px;
  font-size: var(--step--1);
}

/* --- Standing advice, folded ---------------------------------------------- */

.board-notes {
  border: 2px solid var(--c-line);
  padding: var(--space-2) var(--space-3);
}

.board-notes > summary {
  cursor: pointer;
  font-size: var(--step--1);
  font-weight: 700;
}

.board-notes[open] > summary { margin-bottom: var(--space-2); }
.board-notes .fine + .fine { margin-top: var(--space-2); }

/* --- The night ----------------------------------------------------------- */

.night { list-style: none; margin: 0; padding: 0; }

.night__day {
  margin: var(--space-4) 0 var(--space-1);
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--c-ink-faint);
}

.night__day:first-child { margin-top: 0; }

.night__slot {
  display: grid;
  /* Wide enough for "5:00 PM" set in the mono face without it wrapping or
     eating into the name beside it. */
  grid-template-columns: 4.5rem minmax(0, 1fr);
  gap: var(--space-2);
  align-items: start;
  padding: var(--space-1) 0;
  border-top: 2px solid var(--c-line);
}

.night__when {
  margin: 0;
  padding-top: 12px; /* level with the first line of the row beside it */
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 700;
  white-space: nowrap;
}

/* An empty time: short and quiet, but ruled like every other row.

   These had a 1px rule to read lighter than the booked ones. The list does not
   sit on whole pixels — measured, every row lands on a .92 offset — so a 1px
   line straddles two device pixels and the browser keeps some and drops others.
   The empty half of the evening came out visibly half-ruled. A 2px line always
   survives that, and the quiet is carried by the dimmed time and the small grey
   "open" instead, which is what was doing the work anyway. */
.night__slot--open {
  align-items: center;
  padding: 0;
}

.night__slot--open .night__when {
  padding-top: 0;
  opacity: 0.5;
  font-weight: 600;
}

/* Whole pixels on purpose. At a fractional row height the 1px rules above these
   rows landed on half-pixels and the browser dropped every other one, so the
   empty part of the evening came out with a striped, half-ruled look. */
.night__free {
  margin: 0;
  padding: 8px 0;
  font-size: var(--step--1);
  line-height: 20px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--c-ink-faint);
}

.night__body { min-width: 0; }

.night__cap {
  margin: 0 0 var(--space-1);
  font-size: var(--step--1);
  color: var(--c-ink-faint);
}

.night__bookings {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.night__line {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* The whole line is the tap target — name, design and status all open the row.
   44px tall even though it draws shorter, because it is a control. */
.night__head {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* The affordance. Without it a line that opens looks exactly like a line that
   does not. */
.night__head::after {
  content: "";
  flex: none;
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid var(--c-ink-faint);
  transition: transform 120ms ease;
}

.night__head[aria-expanded="true"]::after { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .night__head::after { transition: none; }
}

.night__who {
  display: grid;
  gap: 1px;
  min-width: 0;
  flex: 1 1 auto;
}

/* The name owns its line; the money and the design share the one under it.
   Both truncate rather than wrap — a long name must not turn one row into
   three and undo the whole point of the list. */
.night__name {
  font-weight: 700;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.night__meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}

/* The money holds its width and the placement gives way, because the amount
   owed is what she is scanning this column for. */
.night__choice {
  flex: 0 1 auto;
  min-width: 0;
  font-size: var(--step--1);
  line-height: 1.25;
  color: var(--c-ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* DROPPED FROM THE CROWDED ROWS ONLY. A row carrying the time, the name, the
   money, an unread mark, a caret AND a Mark paid button has about forty pixels
   left at 375px — enough to render "I…", which is clutter pretending to be
   information. A row without that button has room, so it keeps it: this is
   scoped to the rows with the problem rather than to a screen width, which had
   the side effect of hiding it on paid rows that could well afford it.

   It reads in full inside the opened row either way (.night__asked), so nothing
   is lost here. Where :has() is unsupported the line simply stays and truncates
   as before — the old behaviour, not a broken one. */
.night__booking:has(.night__paid) .night__choice { display: none; }

@media (min-width: 30rem) {
  .night__booking:has(.night__paid) .night__choice { display: block; }
}

/* The same fact, opened out: what she is actually about to make. */
.night__asked {
  margin: 0;
  font-weight: 700;
}

/* Colour is never the only signal — the mark always says the money or the word
   as well, which is the rule the status words on this app already follow. */
.night__mark {
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.night__mark--ok      { color: var(--c-ok); }
.night__mark--warn    { color: var(--c-warn); }
.night__mark--problem { color: var(--c-danger); }

.night__new {
  flex: none;
  padding: 2px 6px;
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--c-ink-inverse);
  background: var(--c-brand);
}

/* Kept narrow so the placement beside it is not squeezed into an ellipsis. */
.night__paid {
  flex: none;
  min-height: 36px;
  padding: 6px 9px;
  font-size: 0.6875rem;
  letter-spacing: 0.01em;
}

.night__detail {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-2) 0 var(--space-3);
}

/* The opened row's own buttons sit in a wrapped line rather than a stack. The
   app-wide `flex: 1 1 12rem` gives each button a 192px basis, which on a phone
   is one button per line — three lines of full-width chrome, which is the
   thing this whole screen was rebuilt to get away from. Only one row is ever
   open, so these can be small without being hard to hit: still 38px tall. */
.night__detail .button-row .button {
  flex: 0 1 auto;
  min-height: 38px;
  padding: 7px 12px;
  font-size: var(--step--1);
}

.booking-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

.booking {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  background: var(--c-surface-raised);
  border: 1px solid var(--c-line);
  border-radius: var(--radius-md);
}

.booking__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.booking__name {
  margin: 0;
  font-size: var(--step-0);
  font-weight: 600;
  color: var(--c-ink);
}

/* The guest's phone, and the only screen it appears on. A real link, so it
   dials — the vendor is holding the phone they would ring from. */
.booking__reach {
  margin: 0;
}

/* --- The cancelled-appointments dialog ---------------------------------- */
/* A real <dialog>. The browser traps focus, closes it on Escape and paints the
   backdrop; none of that is worth reimplementing, and hand-rolled versions of
   it are where keyboard users get stranded. */

dialog {
  width: min(34rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 3rem);
  overflow-y: auto;
  padding: var(--space-6);
  background: var(--c-surface);
  color: var(--c-ink);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}

dialog::backdrop {
  background: var(--c-backdrop);
}

/* The "move them to another time" picker. A real select, so it uses the phone's
   own native wheel rather than a list this app would have to reinvent badly. */
.move-select {
  min-height: var(--hit);
  padding: var(--space-2) var(--space-3);
  background: var(--c-surface-raised);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--radius-md);
  color: var(--c-ink);
  font: inherit;
  width: 100%;
}

.booking__reach a {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit);
  font-family: var(--font-mono);
  font-weight: 600;
}

/* --- Summary of what is being booked ----------------------------------- */

.summary {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-4) var(--space-6);
  background: var(--c-surface-sunken);
  border-radius: var(--radius-md);
}

/* Two columns, so the labels line up and a long value wraps inside its own
   column instead of dropping below and breaking the rhythm of the list. */
.summary div {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: var(--space-1) var(--space-3);
  align-items: baseline;
}

.summary dt {
  color: var(--c-ink-faint);
  font-size: var(--step--1);
}

.summary dd { margin: 0; color: var(--c-ink); }

/* --- Steps ------------------------------------------------------------- */

.step-count {
  margin: 0;
  font-size: var(--step--1);
  color: var(--c-ink-faint);
}

/* --- Loading ----------------------------------------------------------- */

.loading {
  margin: 0;
  color: var(--c-ink-muted);
  font-size: var(--step--1);
}

/* --- Small print ------------------------------------------------------- */

.fine {
  margin: 0;
  font-size: var(--step--1);
  color: var(--c-ink-muted);
  text-wrap: pretty;
}

.back-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit);
  font-size: var(--step--1);
}

.contact-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
}

.contact-list a {
  display: inline-flex;
  align-items: center;
  min-height: var(--hit);
  gap: var(--space-1);
}

/* An icon drawn beside a word, sized off the word rather than off the page —
   so it grows with the text when somebody has bumped their font size up, which
   a fixed pixel size would not. It never shrinks below its box in a flex row,
   and it sits on the text baseline rather than hanging under it. */
.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  vertical-align: -0.15em;
}

/* Only for screen readers — used for labels that would be repetitive on
   screen but are needed out of context. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- The door list ------------------------------------------------------ */
/* One line per person, readable at arm's length by somebody holding it in one
   hand. The columns are fixed rather than fluid so that names line up down the
   page — a list you scan for a name is much faster when the names all start in
   the same place. */

.door-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.door-row {
  display: grid;
  grid-template-columns: 5.5rem 1fr auto auto;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--c-line);
}

.door-row__time {
  font-variant-numeric: tabular-nums;
  color: var(--c-ink-muted);
  font-size: var(--step--1);
}

.door-row__name {
  font-weight: 600;
  color: var(--c-ink);
  /* A long name wraps rather than pushing the money off the edge. */
  overflow-wrap: anywhere;
}

/* What this guest chose, under their name. Its own line rather than beside the
   name: on paper the vendor is scanning a column of names, and a design title
   sitting inline turns that column into prose. */
.door-row__choice {
  display: block;
  font-weight: 400;
  font-size: var(--step--1);
  color: var(--c-ink-muted);
  overflow-wrap: anywhere;
}

.door-row__owed {
  font-variant-numeric: tabular-nums;
  font-size: var(--step--1);
  color: var(--c-ink-muted);
  text-align: right;
}

/* The same fact on the vendor's board, where it is the thing the appointment
   IS — so it is not shrunk down to fine print the way the paper version is. */
.booking__choice {
  margin: 0 0 var(--space-1);
  font-weight: 600;
  color: var(--c-ink);
  overflow-wrap: anywhere;
}

/* A box to tick with a pen, on the printed version only. The app records no
   attendance at all — that was removed because ticking a box mid-appointment is
   work the night does not pay back — but paper is exactly where a note like that
   belongs, and it costs nobody a tap. On screen it would look like a checkbox
   that does nothing, so it is not there. */
.door-row__tick {
  display: none;
}

/* --- Printing ----------------------------------------------------------- */
/* THE ONLY PLACE THE PAPER TOKENS ARE USED. Paper has no dark mode: --c-ink and
   --c-surface flip with the phone's appearance setting, so printing with them
   would hand a vendor in dark mode a black page. */

.no-print {
  /* Nothing here: on screen these elements are perfectly normal. The rule
     exists so the print block below has one thing to hide. */
}

@media print {
  /* Anything that can only be tapped, and anything that is advice for whoever
     is holding the phone rather than the paper. */
  .no-print,
  .skip,
  .foot,
  .loading {
    display: none !important;
  }

  html,
  body {
    background: var(--c-paper);
    color: var(--c-paper-ink);
  }

  .page {
    max-width: none;
    margin: 0;
    padding: 0;
  }

  .lede,
  .section-title,
  .door-row__name {
    color: var(--c-paper-ink);
  }

  .eyebrow,
  .fine,
  .door-row__time,
  .door-row__choice,
  .door-row__owed {
    color: var(--c-paper-ink);
  }

  .door-row {
    border-bottom: 1px solid var(--c-paper-line);
    /* Never split a person across two pages. */
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .door-row__tick {
    display: block;
    width: 1.1rem;
    height: 1.1rem;
    border: 1px solid var(--c-paper-ink);
  }

  /* A heading should not be the last thing on a page with its list overleaf. */
  .section-title {
    break-after: avoid;
    page-break-after: avoid;
  }

  /* The one message that must survive onto the paper: it is the instruction
     that rescues a guest whose phone is dead, and paper is where somebody will
     be looking when that happens. */
  .message--warn {
    border: 1px solid var(--c-paper-line);
    background: var(--c-paper);
    color: var(--c-paper-ink);
  }
}

/* --- The office ---------------------------------------------------------- */
/* The operator's own screen. Plain on purpose — one competent person uses it,
   and it needs the right fields in the right order, not a look. Same tokens as
   everything else, so a re-skin reaches it too. */

.office-host { margin-bottom: var(--space-12); }

.office-event {
  border-top: 2px solid var(--c-line-strong);
  padding-top: var(--space-4);
  margin-top: var(--space-8);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.office-card {
  border: 1px solid var(--c-line);
  border-radius: var(--radius-md);
  background: var(--c-surface-raised);
  padding: var(--space-4);
  margin-top: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.office-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.office-card__title { margin: 0; font-weight: 600; color: var(--c-ink); }

/* Two columns where there is room, one on a phone. */
.office-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-3);
}

.office-field { display: flex; flex-direction: column; gap: var(--space-1); }

.office-input {
  min-height: var(--hit);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--c-line-strong);
  border-radius: var(--radius-sm);
  background: var(--c-surface);
  color: var(--c-ink);
  font: inherit;
  width: 100%;
}

.office-input:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 1px;
}

.office-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-2) 0;
  border-top: 1px dashed var(--c-line);
  /* See the note on .office-code. */
  min-width: 0;
}

/* A private link is for copying, not reading — it may be long, and it must
   never push the page sideways. */
/* ⚠️ min-width: 0 IS WHAT MAKES THE OFFICE FIT ON A PHONE, and its absence is
   what stopped it. These boxes hold private links — one long unbreakable run of
   characters each — and a flex item defaults to min-width: auto, meaning it
   refuses to shrink below its own content. overflow-x and max-width: 100% were
   already here and could not help, because the box never got narrow enough to
   need to scroll: it pushed its parent wider instead.

   With eighteen of them on the page, <main> measured 592px inside a 320px
   phone, and every two-column field grid stayed two columns — the auto-fit
   maths was running against a container that had been forced wide. Hiding the
   links dropped it to 340px, which is how this was found. One property here
   fixes the whole screen. */
.office-code {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--c-ink-muted);
  background: var(--c-surface-sunken);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  overflow-x: auto;
  max-width: 100%;
  min-width: 0;
  white-space: nowrap;
}

.office-slots summary { cursor: pointer; min-height: var(--hit); display: flex; align-items: center; }

.office-maker {
  border: 1px dashed var(--c-line-strong);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-top: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.office-maker summary {
  cursor: pointer;
  min-height: var(--hit);
  display: flex;
  align-items: center;
  font-weight: 600;
  color: var(--c-ink);
}

/* --- Option menus — the vendor's question ------------------------------- */
/* A guest picks with a thumb, on a phone, in a room. Big targets, and the
   chosen one has to be obvious at arm's length — colour is never the only
   signal, so the selected choice also carries a check mark and aria-checked. */

.choices {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--space-3);
}

.choices--text { grid-template-columns: 1fr; }

.choice {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  width: 100%;
  min-height: var(--hit);
  padding: var(--space-3);
  text-align: left;
  background: var(--c-surface-raised);
  border: 2px solid var(--c-line);
  border-radius: var(--radius-md);
  color: var(--c-ink);
  font: inherit;
  cursor: pointer;
}

.choice[aria-checked="true"] {
  border-color: var(--c-brand);
  background: var(--c-surface-sunken);
}

.choice__img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--c-surface-sunken);
}

.choice__name { font-weight: 600; }
.choice__meta { font-size: var(--step--1); color: var(--c-ink-muted); }

/* The tick that means "this one", for anyone who cannot see the border. */
.choice__tick { font-size: var(--step--1); color: var(--c-brand); min-height: 1.2em; }

.office-menus { display: flex; flex-direction: column; gap: var(--space-3); }
.office-choice {
  border-top: 1px dashed var(--c-line);
  padding-top: var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* --- "Thinking of running your own?" ------------------------------------ */
/* The one piece of selling anywhere inside the product, on the vendor's own
   page, at the bottom, after they have finished what they came to do. Quiet
   on purpose: a tool that pitches at you mid-task is a tool you trust less. */
.own-event {
  margin-top: var(--space-12);
  padding-top: var(--space-6);
  border-top: 1px solid var(--c-line);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.office-invite {
  border-top: 1px dashed var(--c-line);
  border-bottom: 1px dashed var(--c-line);
  padding: var(--space-3) 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* --- Messages — one booking's conversation ------------------------------- */
/* Used by BOTH faces: the guest's booking page (theme-guest) and the vendor's
   board (theme-staff). So there is not a single colour written here — every
   one is a token, and each theme fills them in with its own. That is the whole
   contract at the top of base.css, and this is the first block in the file
   that two different themes render. */

.thread {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.thread__item {
  padding: var(--space-2);
  border: 1px solid var(--c-line);
  border-radius: var(--radius-md);
  background: var(--c-surface-raised);
  /* Not a chat bubble pinned to one side. Two people are reading this on a
     phone in a loud room; a full-width block in reading order is easier to
     follow than a shape you have to decode first. */
}

/* Your own words, set back a little so the two voices are told apart without
   relying on colour alone — the same rule the status words follow. */
.thread__item--mine {
  background: var(--c-surface-sunken);
  border-color: var(--c-line-strong);
}

.thread__who {
  margin: 0 0 var(--space-1);
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
  font-size: var(--step--1);
}

.thread__name { font-weight: 600; color: var(--c-ink); }
.thread__at { color: var(--c-ink-faint); }

.thread__body {
  margin: 0;
  color: var(--c-ink);
  /* Somebody typed line breaks; show them. And break a pasted address rather
     than letting it push the page sideways on a phone. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* The unread mark on a vendor's board tile. A word, not only a dot — a colour
   alone is not a message. */
.thread-flag {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--c-ink-inverse);
  background: var(--c-accent);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-1);
}

/* --- The poster event page (guest home) ---------------------------------- */
/* The browsable layout the guest home page draws: date strip, host row, hero
   photo with caption band, lineup rows, amenity tiles, the who's-here
   directory and the sticky reserve bar. Structure only — every colour is a
   token, so a theme decides how all of it looks. */

.poster-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  /* CENTRED, and it is not cosmetic. On the maker's page the left item is a
     back LINK — an inline-flex carrying the 44px tap minimum, its text
     centred in that box — while the right item is a bare paragraph whose text
     sits at the top of the same stretched line. Left to default alignment the
     two read as stacked even though they share a line. */
  align-items: center;
  gap: var(--space-2) var(--space-4);
  padding-bottom: var(--space-3);
}

.poster-strip p { margin: 0; }

.poster-title {
  margin: 0;
  max-width: var(--measure);
  font-family: var(--font-display);
  /* Bigger than the shared display step — this is the poster's whole voice,
     and at --step-3 it sat level with the section headings below it. */
  font-size: clamp(2.75rem, 2rem + 5.5vw, 4.75rem);
  line-height: 0.95;
  letter-spacing: -0.03em;
  color: var(--c-ink);
  text-wrap: balance;
}

.host-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.host-row__logo {
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--c-ink);
  object-fit: cover;
}

.host-row__lines p { margin: 0; }
.host-row__name { color: var(--c-ink); }
.host-row__where { color: var(--c-ink-faint); }

.hero {
  position: relative;
  margin: 0;
}

.hero__img {
  display: block;
  width: 100%;
  max-height: 24rem;
  object-fit: cover;
  border: 1px solid var(--c-line);
}

.hero__band {
  position: absolute;
  right: 0;
  bottom: var(--space-4);
  padding: var(--space-2) var(--space-4);
  background: var(--c-brand);
  color: var(--c-ink-inverse);
  font-family: var(--font-display);
  font-weight: 700;
}

.lineup-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
}

.lineup-head p { margin: 0; color: var(--c-accent); }

.lineup {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
}

.lineup > li {
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--c-line);
}

.lineup > li:first-child { border-top: 1px solid var(--c-line); }

.lineup-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--space-2) var(--space-3);
  align-items: start;
}

.lineup-row__img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border: 1px solid var(--c-line-strong);
}

.lineup-row__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.lineup-row__body p { margin: 0; }

.lineup-row__title {
  margin: 0;
  font-family: var(--font-display);
  /* The prototype's row scale, measured off the approved capture: a dense
     browse line, roughly half the size of a section heading. */
  font-size: 0.9375rem;
  line-height: 1.25;
  color: var(--c-ink);
}

.lineup-row__meta { color: var(--c-ink-faint); }
.lineup-row__text { color: var(--c-ink-muted); font-size: var(--step--1); }

.lineup-row__spots { color: var(--c-accent); font-weight: 600; }
.lineup-row__spots--full { color: var(--c-ink-faint); }

/* The action is a SMALL block on the row's right edge at every width — the
   prototype's compact RSVP, measured at ~28px tall, never a full-width bar.
   ⚠️ Deliberately below the app-wide 44px tap minimum: her explicit call,
   matching the approved design exactly — and the whole ROW is tappable too
   (photo and title link to the same page), so the effective target is big. */
.lineup-row__btn {
  align-self: center;
  min-height: 0;
  padding: 6px 12px;
  flex: none;
  white-space: nowrap;
}

.tile-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: var(--space-4);
}

.tile {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--c-ink);
  background: var(--c-surface-raised);
}

/* The picture takes any height the grid row gives beyond the caption. Without
   the grow, a one-line caption left a stray strip of empty tile below itself
   while a two-line neighbour set the row's height. */
.tile__img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  flex: 1 1 auto;
  min-height: 0;
}

.tile__blank {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--c-surface-raised);
  flex: 1 1 auto;
  min-height: 0;
}

/* And the caption always sits on the floor of the tile. */
.tile__bar { margin-top: auto; }

.tile__bar {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--c-ink);
  color: var(--c-ink-inverse);
}

.who-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-4);
}

.who-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.who-row__logo {
  width: 52px;
  height: 52px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--c-ink);
  object-fit: cover;
}

.who-row__logo--blank {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--c-surface-raised);
  color: var(--c-ink);
  font-family: var(--font-display);
  font-weight: 700;
}

.who-row p { margin: 0; }
.who-row__name { color: var(--c-ink); font-weight: 700; }
.who-row__what { color: var(--c-ink-muted); font-size: var(--step--1); }

/* The reserve bar: pinned to the bottom of the screen while the page is
   browsed, settling into place at the end of the content. */
.reserve-bar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--c-brand);
  color: var(--c-ink-inverse);
  border: 1px solid var(--c-ink);
}

.reserve-bar__tally {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
}

/* --- The schedule chart -------------------------------------------------- */
/* A bar per offering along the event's hours. Bars are positioned by
   percentage from home.js; colours come from the theme. */

.chart {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.chart__axis {
  display: flex;
  justify-content: space-between;
  padding-top: var(--space-2);
  color: var(--c-ink-faint);
}

.chart__bars {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  /* A bar shifted to its hour keeps its full-row box, and the empty
     remainder pokes past the viewport and widens the page. The rail clips —
     nothing visible lives out there. */
  overflow-x: clip;
}

.chart__bar {
  position: relative;
  display: flex;
  align-items: center;
}

.chart__bar > span {
  position: relative;
  left: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 0;
  min-height: 24px;
  max-width: 100%;
  padding: 3px 10px;
  color: var(--c-ink-inverse);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* All-night strips stretch; one-off blocks hug their words at their start. */
.chart__bar { margin-left: 0; }
.chart__bar > span { width: 100%; box-sizing: border-box; }

/* --- The maker page's photo (and its placeholder) ------------------------- */

.detail-photo { margin: 0; }

.detail-photo__img {
  display: block;
  width: 100%;
  max-height: 20rem;
  object-fit: cover;
  border: 1px solid var(--c-line-strong);
}

.detail-ph {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 5 / 2;
  background: var(--c-surface-raised);
  border: 1px dashed var(--c-line-strong);
}

.detail-ph__initial {
  font-family: var(--font-display);
  font-size: var(--step-3);
  color: var(--c-ink-faint);
}

/* --- Stat chips (Length / Price / Deposit / Spots) ------------------------ */

.fact--stat {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-2) var(--space-3);
}

.fact__label { color: var(--c-ink-faint); }
.fact__value { color: var(--c-ink); font-weight: 700; }
.fact--open .fact__value { color: var(--c-accent); }

/* --- Lineup row picture link and placeholder ------------------------------ */

.lineup-row__pic { display: block; line-height: 0; }

.lineup-row__ph {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  background: var(--c-surface-raised);
  border: 1px dashed var(--c-line-strong);
  font-family: var(--font-display);
  font-size: var(--step-1);
  color: var(--c-ink-faint);
  line-height: 1;
}

.lineup-row__titlelink {
  color: inherit;
  text-decoration: none;
}
.lineup-row__titlelink:hover { color: var(--c-accent); }

/* --- The maker page, condensed (her call, Aug 30) ------------------------- */
/* Tighter section rhythm than the browse page, a one-line maker row with a
   small mark, compact stat chips, and a shorter photo — the approved detail
   pages read dense, not airy. */

.page--detail { gap: var(--space-6); }

.page--detail .detail-photo__img { max-height: 14rem; }

.page--detail .host-row__logo {
  width: 38px;
  height: 38px;
}

/* Name and one-liner flow as a single wrapping line beside the mark. */
.page--detail .host-row__lines {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-2);
}

.fact--stat {
  padding: 4px 10px;
  gap: 1px;
}

/* "Have a look" — the flash-sheet preview. */
.preview-link {
  font-weight: 700;
  color: var(--c-accent);
}

/* The photo (and its placeholder) runs FLUSH to the screen edge — her call,
   matching the approved page's full-bleed opener. It escapes the page's own
   side padding and drops its side borders at the edges. */
.page--detail .detail-photo,
.page--detail .detail-ph {
  margin-left: calc(-1 * var(--space-6));
  margin-right: calc(-1 * var(--space-6));
  width: auto;
}

.page--detail .detail-photo__img,
.page--detail .detail-ph {
  border-left: 0;
  border-right: 0;
}

/* Stat chips are ONE line now — value only, no stacked label. */
.fact--stat {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  padding: 6px 12px;
  white-space: nowrap;
}

/* --- The RSVP screen (approved step 2) ----------------------------------- */

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

.hold-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  margin-left: calc(-1 * var(--space-6));
  margin-right: calc(-1 * var(--space-6));
  padding: var(--space-3) var(--space-6);
  background: var(--c-brand);
  color: var(--c-ink-inverse);
}

.hold-bar p { margin: 0; }

.rsvp-table {
  margin: 0;
  border: 2px solid var(--c-ink);
  background: var(--c-surface-raised);
}

/* ===========================================================================
   THE RSVP SCREEN IS DELIBERATELY TIGHTER THAN THE REST OF THE APP.
   Measured Aug 31: this one screen ran 992px on a 375px phone — taller than
   anything else on the site — and read as clunky next to the compact vendor
   cards. Every rule below trades VERTICAL space only. The horizontal insets
   stay at --space-4 because they are what makes these read as poster boxes
   rather than a plain form, and squeezing them would save no height at all.
   Tap targets (inputs, the submit button, the back link) are untouched: they
   are at the 44px floor and shrinking them would be an accessibility bug, not
   a design choice.
   =========================================================================== */

.rsvp-table__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  /* 8px, not 12px. Three rows of chrome, so every pixel here counts thrice. */
  padding: var(--space-2) var(--space-4);
  /* The page's 1.6 is a reading line-height; these are single-line labels
     with a figure opposite, and the top row only wraps because the offering's
     name is long. */
  line-height: 1.35;
}

.rsvp-table__row + .rsvp-table__row {
  border-top: 1px solid var(--c-line-strong);
}

.rsvp-table dt { font-weight: 700; color: var(--c-ink); }
.rsvp-table dd { margin: 0; font-weight: 700; color: var(--c-ink); white-space: nowrap; }

.rsvp-form .field input {
  background: var(--c-surface-raised);
}

.name-row {
  display: grid;
  /* minmax(0, …): an input's intrinsic minimum would otherwise size these
     columns wider than half a phone. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-3);
}

.name-row input { width: 100%; min-width: 0; }

.pay-box {
  border: 2px solid var(--c-ink);
  background: var(--c-surface-raised);
}

.pay-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  line-height: 1.35;
}

.pay-row + .pay-row { border-top: 1px solid var(--c-line-strong); }

.pay-row p { margin: 0; }
.pay-row__name { font-weight: 700; color: var(--c-ink); }
.pay-row__handle { color: var(--c-ink-muted); }

.pay-row__btn {
  min-height: 0;
  padding: 8px 14px;
  flex: none;
  white-space: nowrap;
}

/* The single biggest block on the screen — 217px of the 992px, and none of it
   information a guest hasn't got. So the WORDS all stay and only the leading
   gives: a 1.6 line-height under an uppercase display face is set for reading
   paragraphs, and this is a four-line notice. */
.rsvp-warn {
  padding: var(--space-3);
  border: 2px solid var(--c-danger);
  background: var(--c-danger-wash);
}

.rsvp-warn p { margin: 0; }

.rsvp-warn__head {
  font-weight: 800;
  color: var(--c-danger);
  line-height: 1.2;
}

.rsvp-warn__body {
  margin-top: var(--space-1);
  color: var(--c-ink-muted);
  font-size: 0.9375rem;
  line-height: 1.45;
}

.agree-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
  /* Fine print, sized like it. The label is still the tap target — the whole
     row is clickable, so this does not shrink anything a finger aims at. */
  font-size: 0.9375rem;
  line-height: 1.4;
}

.agree-row input {
  width: 28px;
  height: 28px;
  flex: none;
  margin: 0;
  accent-color: var(--c-ink);
}

/* The rhythm BETWEEN the blocks, not inside them. Four gaps at 16px on a
   screen this tall is 64px of nothing; 12px still separates the boxes without
   letting them drift apart. */
#step-rsvp { gap: var(--space-3); }

/* "YOUR INFO" labels the form UNDER it, so it must sit nearer to that form
   than to the summary table above — otherwise it reads as a caption on the
   table. The stack's gap is even, so the margin does the leaning. */
#step-rsvp .eyebrow {
  margin-top: var(--space-1);
  margin-bottom: 0;
}

/* The back link's paragraph brought the browser's default 1em margins into a
   stack that was already spacing its children — 32px of double-counted air at
   the very bottom. The link itself keeps its 44px target. */
#step-rsvp > p:last-child { margin: 0; }

/* ===========================================================================
   THE FLASH SHEET POPUP — a tall picture in a window you can move around.
   The dialog is a column: a fixed bar of controls, then the picture box
   taking every remaining pixel. Zoom is the image's width; the box scrolls.
   =========================================================================== */

.sheet-dialog {
  width: min(94vw, 34rem);
  /* A CAP, not a size: a sheet shorter than the window gets a frame that ends
     where it ends, instead of a cream apron below it. */
  max-height: min(92dvh, 60rem);
  padding: 0;
  margin: auto; /* the browser centres an open dialog; said anyway */
}

.sheet-dialog[open] {
  display: flex;
  flex-direction: column;
}

.sheet-dialog__bar {
  flex: none;
  display: flex;
  gap: var(--space-2);
  justify-content: flex-end;
  padding: var(--space-2) var(--space-3);
}

/* Square, thumb-sized, and the glyph is all they say — the aria-label
   carries the words. */
.sheet-dialog__zoom {
  min-width: 44px;
  min-height: 44px;
  padding: 0 12px;
  font-size: 1.25rem;
  line-height: 1;
}

.sheet-dialog__pan {
  flex: 1 1 auto;
  min-height: 0; /* lets the box shrink inside the flex column and scroll */
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain; /* zoomed panning must not scroll the page */
}

.sheet-dialog__img {
  display: block;
  width: 100%;
  height: auto;
}

/* ===========================================================================
   THE LOADING SCREEN — the party page's first moment (her call, Aug 31).
   A hard-edged square in the poster's own ink, one rust edge chasing round.
   Deliberately not a circle: nothing on the guest face is round.
   =========================================================================== */

.load-screen {
  display: grid;
  justify-items: center;
  gap: var(--space-4);
  padding: var(--space-16) 0;
}

.load-screen__spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--c-ink);
  border-top-color: var(--c-brand);
  animation: load-turn 0.9s linear infinite;
}

@keyframes load-turn {
  to { transform: rotate(360deg); }
}

.load-screen__word {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-ink-faint);
}

@media (prefers-reduced-motion: reduce) {
  /* Still, and whole: a stopped spinner with a missing edge reads broken. */
  .load-screen__spinner { animation: none; border-top-color: var(--c-ink); }
}

/* A chip that is also a door — the venue chip opens directions. The anchor
   fills the chip so the whole box is the target, not just the words. */
.fact--link { padding: 0; }

.fact--link .fact__link {
  display: block;
  padding: 6px 12px;
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: from-font;
}

/* The hero's looping film sits exactly where the still does. */
.hero__video {
  display: block;
  width: 100%;
  max-height: 24rem;
  object-fit: cover;
  border: 1px solid var(--c-line);
  background: var(--c-surface-sunken);
}
