:root {
  color-scheme: light;

  /* Corner softening. One token so the whole site moves together, and one
     matching shadow so a card lifts off the paper without announcing it. */
  --radius: 7px;
  --shadow: 0 1px 2px rgba(20, 20, 20, .04), 0 2px 8px rgba(20, 20, 20, .05);

  /* gold — headings and display type */
  --accent: #8f7220;
  --gold-soft: #b08b2c;

  /* baby blue — accents only, never text */
  --blue: #e2f2fa;
  --blue-mid: #b7dcee;
  --blue-strong: #7fbcd9;

  --ink: #141414;          /* body text */
  --muted: #4f5155;
  --line: #cfe2ec;
  --bg: #ffffff;
  --panel: #f6fbfd;
  --page: #ffffff;

  --ok: #2f6b45;
  --err: #a5372a;

  --serif: Georgia, 'Times New Roman', serif;
  --sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
}

* { box-sizing: border-box; }

/* display values on .field / .lang-stack would otherwise beat the attribute */
[hidden] { display: none !important; }

html, body { margin: 0; padding: 0; min-height: 100%; }

/* Reserve the scrollbar's width on every page, whether or not that page is
   long enough to need one. Without this, moving from a short tab to a tall
   one makes the scrollbar appear and shunts the whole layout sideways.
   This is the native property for it, not a workaround: the scrollbar still
   behaves normally, its space is simply always allowed for. */
html { scrollbar-gutter: stable; }

body {
  background-color: var(--page);
  color: var(--ink);
  font-family: var(--sans);
  -webkit-text-size-adjust: 100%;
}

/* Invitation feel: a faint botanical wallpaper over a real paper grain.
   The grain is a still frame of the Paper Design paper-texture shader,
   rendered once at build time (see build/make-paper.html) rather than run
   as WebGL on every guest's phone. The shader is a static texture, so
   there is nothing to animate and nothing is lost by baking it. */
/* Three layers behind the page, back to front:
     1. #paper-bg  live Paper Design paper-texture shader (paper-bg.js)
     2. #wallpaper faint baby blue botanical trellis, tiled
     3. #petals    blossom drifting on the wind (petals.js)
   The baked paper.webp underneath is only a fallback for devices without
   WebGL; once the shader mounts it is dropped. */
body:not(.admin) {
  background-image: url('/paper.webp');
  background-repeat: repeat;
  background-size: 512px 512px;
}

.has-paper-shader body:not(.admin) { background-image: none; }

#paper-bg, #wallpaper, #petals {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

/* The flowers go down first, then the paper multiplies over them, so the
   grain and tooth fall ON the print rather than hiding behind it. That is
   what makes it read as ink on card rather than a picture of flowers. */
#wallpaper { z-index: 0; }

#paper-bg {
  z-index: 1;
  mix-blend-mode: multiply;
  overflow: hidden;
}

/* ShaderMount sizes its canvas in device pixels. Left unconstrained that is
   twice the viewport width on a phone, which overflows the document and
   shoves the flex-centred card off centre. */
#paper-bg canvas {
  display: block;
  width: 100% !important;
  height: 100% !important;
}

/* Lift page content above the background layers. The fixed elements are
   excluded: overriding their position drops them into the flex row as real
   items, and the 26px admin hotspot then pushes the card off centre. */
/* .pv-head is excluded too. Every :not(#id) in this selector carries id
   specificity, so the rule beats .pv-head's own z-index and flattened the
   sticky header to the same layer as the page body. Being later in the DOM,
   .pv-main then painted over the open mobile menu and swallowed taps on its
   links: the menu looked fine and simply did not work. */
body:not(.admin) > *:not(#paper-bg):not(#wallpaper):not(#admin-hotspot):not(#admin-modal):not(.pv-head) {
  position: relative;
  z-index: 2;
}

/* --- wallpaper -----------------------------------------------------------
   A half-drop repeat: the tile carries column two offset by half the pattern,
   which is how wallpaper is hung and what stops the eye locking onto a grid.
   Held at low opacity so text stays comfortable to read over it. */
#wallpaper {
  background-image: url('/wallpaper.webp');
  background-repeat: repeat;
  background-size: 1500px 750px;
  opacity: .22;
}

@media (max-width: 40rem) {
  /* a large tile on a small screen means barely one repeat is ever in view */
  #wallpaper { background-size: 980px 490px; opacity: .18; }
}

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

/* --- hidden admin hotspot ------------------------------------------------ */
#admin-hotspot {
  position: fixed;
  top: 0; left: 0;
  width: 44px; height: 44px;      /* a real touch target, still invisible */
  z-index: 90;
  background: transparent;
  cursor: default;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;     /* no double-tap zoom stealing the gesture */
}

/* --- display type -------------------------------------------------------- */
h1 {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.1rem, 8vw, 3.4rem);
  line-height: 1.12;
  color: var(--accent);
}

/* always beside the names, never stacked — on any device, on every page.
   Set in the same face, size and colour as the names, not as a separate mark. */
.amp {
  display: inline;
  font: inherit;
  color: inherit;
  margin: 0;
}

.eyebrow {
  margin: 0 0 1.4rem;
  font-size: .78rem;
  letter-spacing: .14em;
  color: var(--muted);
}

.date {
  margin: 1.2rem 0 0;
  font-size: .95rem;
  letter-spacing: .05em;
  color: var(--ink);
}

.rule {
  width: 3rem;
  height: 2px;
  background: var(--blue-strong);
  margin: 2rem auto;
}

/* --- gate ---------------------------------------------------------------- */
body:not(.site) {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
  min-height: 100vh;
  min-height: 100dvh;
}

.card {
  width: 100%;
  max-width: 34rem;
  text-align: center;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, .74);
  padding: clamp(2rem, 7vw, 3.5rem) clamp(1.25rem, 6vw, 3rem);
}

.blurb {
  font-size: 1.02rem;
  line-height: 1.7;
  color: var(--ink);
}

/* --- forms --------------------------------------------------------------- */
input[type=text], input[type=password], input[type=search], textarea, select {
  width: 100%;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .7rem .85rem;
}

input::placeholder, textarea::placeholder { color: #7b7d82; }

input:focus-visible, textarea:focus-visible,
select:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

#code-form {
  display: flex;
  gap: .5rem;
  margin: 1.6rem auto 0;
  max-width: 22rem;
}

#code {
  text-align: center;
  letter-spacing: .3em;
  text-transform: uppercase;
  font-size: 1.15rem;
}

/* arrived by invite link: no field to fill in, just one wide centred button */
#code-form.code-supplied { max-width: 17rem; }

#code-form.code-supplied #code-submit {
  width: 100%;
  min-height: 3.2rem;
  font-size: 1rem;
}

button {
  font-family: var(--sans);
  font-size: .92rem;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: .7rem 1.3rem;
  cursor: pointer;
  white-space: nowrap;
}

button:hover:not(:disabled) { background: var(--gold-soft); border-color: var(--gold-soft); }
button:disabled { opacity: .5; cursor: default; }

button.ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--blue-strong);
}

button.ghost:hover:not(:disabled) { background: var(--blue); }
button.small { font-size: .82rem; padding: .45rem .9rem; }

.error {
  margin: 1rem 0 0;
  min-height: 1.2em;
  font-size: .88rem;
  color: var(--err);
}

/* --- modal --------------------------------------------------------------- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 20, 26, .5);
  padding: 1.25rem;
}

.modal-box {
  width: 100%;
  max-width: 20rem;
  background: var(--bg);
  border: 1px solid var(--line);
  padding: 1.6rem;
  display: flex;
  flex-direction: column;
  gap: .7rem;
}

.modal-box h2 {
  margin: 0 0 .2rem;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.15rem;
  color: var(--accent);
}

.modal-actions { display: flex; gap: .5rem; justify-content: flex-end; }

/* --- guest site ----------------------------------------------------------
   Mobile first: compact details block at the top, everything scrolls.
   From 62rem the details pane pins and only the content moves. */
.layout { display: grid; }

.details {
  background: rgba(255, 255, 255, .5);
  border-bottom: 2px solid var(--blue-mid);
  padding: 1.5rem 1.25rem 1.35rem;
}

.details-inner {
  max-width: 23rem;
  margin: 0 auto;
  text-align: center;
}

.details h1 { font-size: clamp(1.75rem, 6.5vw, 2.3rem); }

/* sits in the slot where "Save the date" is on the gate */
.eyebrow.greeting {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0;
  color: var(--accent);
  min-height: 1.4em;
}

/* quiet, and centred wherever it appears */
#logout {
  display: block;
  width: fit-content;
  margin: 1.5rem auto 0;
  font-size: .78rem;
  padding: .4rem .8rem;
}

/* --- schedule (left pane) ------------------------------------------------ */
.schedule {
  list-style: none;
  margin: 1.15rem 0 0;
  padding: 1.05rem 0 0;
  border-top: 1px solid var(--line);
  display: grid;
  gap: .8rem;
  text-align: left;
}

.schedule li {
  display: grid;
  grid-template-columns: 4.3rem 1fr;
  gap: .8rem;
  align-items: baseline;
}

.time {
  font-size: .88rem;
  letter-spacing: .02em;
  color: var(--accent);
  white-space: nowrap;
}

.what-title {
  display: block;
  font-size: .95rem;
  color: var(--ink);
}

.where {
  display: block;
  margin-top: .1rem;
  font-size: .85rem;
  line-height: 1.45;
  color: var(--muted);
}

a.where {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--blue-strong);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

a.where:hover { text-decoration-color: var(--accent); }

/* --- content ------------------------------------------------------------- */
.content { padding: 0 1.25rem 3rem; }

.panel {
  border-bottom: 1px solid var(--line);
  padding: 1.7rem 0;
}

.panel:last-child { border-bottom: 0; }

.panel h2 {
  margin: 0 0 .85rem;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.3rem;
  color: var(--accent);
}

.panel p { margin: 0 0 .8rem; line-height: 1.7; }
.panel p:last-child { margin-bottom: 0; }

.section-note { color: var(--muted); }

.pending {
  display: inline-block;
  background: var(--blue);
  border: 1px solid var(--blue-mid);
  padding: .3rem .8rem;
  font-size: .82rem;
  color: var(--ink);
}

/* --- rsvp ---------------------------------------------------------------- */
.guest {
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, .68);
  padding: 1.2rem;
  margin-bottom: .9rem;
}

.guest-name {
  margin: 0 0 .9rem !important;
  font-family: var(--serif);
  font-size: 1.2rem;
  line-height: 1.2;
  color: var(--accent);
}

.guest input[type=text] { min-height: 3rem; }

/* two equal columns spanning the card, so both options are the same size and
   line up flush with the dietary field below — regardless of label length */
.choices {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .6rem;
  margin-bottom: 1.1rem;
}

.choices input[type=radio] { position: absolute; opacity: 0; pointer-events: none; }

.choices label {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 3rem;
  padding: .6rem .8rem;
  font-size: .9rem;
  line-height: 1.3;
  color: var(--ink);
  border: 1px solid var(--blue-mid);
  background: var(--bg);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}

.choices label:hover { background: var(--blue); border-color: var(--blue-strong); }

.choices input:checked + label {
  background: var(--blue-mid);
  border-color: var(--blue-strong);
  box-shadow: inset 0 0 0 1px var(--blue-strong);
  font-weight: 600;
}

.choices input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }

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

.field { display: block; }

.field > span {
  display: block;
  font-size: .85rem;
  color: var(--muted);
  margin-bottom: .4rem;
}

.field.dietary { margin: 0; }

/* looks exactly like a .field label, behaves like a disclosure control */
.dietary-toggle {
  display: block;
  width: auto;
  margin: 0 0 .4rem;
  padding: 0;
  background: none;
  border: 0;
  font-family: var(--sans);
  font-size: .85rem;
  line-height: inherit;
  color: var(--muted);
  text-align: left;
  cursor: pointer;
}

.dietary-toggle:hover {
  background: none;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

#rsvp-form .field:not(.dietary) { margin-top: 1.3rem; }

.form-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.3rem;
  flex-wrap: wrap;
}

.saved { font-size: .88rem; }
.saved.ok { color: var(--ok); }
.saved.err { color: var(--err); }

/* --- desktop split ------------------------------------------------------- */
@media (min-width: 62rem) {
  .layout { grid-template-columns: minmax(21rem, 36%) 1fr; }

  .details {
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;              /* short viewports keep everything reachable */
    display: flex;
    align-items: center;
    justify-content: center;
    border-bottom: 0;
    border-right: 2px solid var(--blue-mid);
    padding: 2.5rem 2rem;
  }

  .details h1 { font-size: clamp(2.3rem, 3.4vw, 3.1rem); }
  .details .date { margin-top: 1.3rem; }

  .schedule { margin-top: 1.6rem; padding-top: 1.4rem; gap: 1.05rem; }

  /* Vertically centred against the viewport, matching the details column,
     while staying hard left horizontally. min-height rather than height, so a
     page taller than the screen simply grows downward instead of having its
     top cropped by the centring. */
  .content {
    padding: 4rem 3rem 5rem;
    max-width: 44rem;
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .panel { padding: 2.1rem 0; }
}

/* --- admin ---------------------------------------------------------------
   Same design language, green rather than blue so it is obvious at a glance
   which side you are on. Cards stay white against the green page for
   contrast. */
body.admin {
  --blue: #e4f3ea;
  --blue-mid: #a9d6bb;
  --blue-strong: #4f9a71;
  --line: #bcd9c8;
  --panel: #ffffff;
  --page: #f0f8f3;
}

/* sign-in page at /admin */
body.admin-gate {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: 2rem 1.25rem;
  background: var(--page);
}

.admin-card { max-width: 21rem; text-align: left; background: var(--bg); }
.admin-card h2 {
  margin: 0 0 1.1rem;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.3rem;
  color: var(--accent);
}
.admin-card form { display: flex; flex-direction: column; gap: .7rem; }
.admin-card button { min-height: 2.7rem; }

/* --- admin tabs ---------------------------------------------------------- */
/* the stats scroll away above them, the tabs stay reachable */
.tabs {
  position: sticky;
  top: 0;
  z-index: 19;
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

.tabs button {
  background: none;
  border: 0;
  border-bottom: 3px solid transparent;
  color: var(--muted);
  font-size: .95rem;
  padding: .85rem .5rem;
  min-height: 3rem;
}

.tabs button:hover { background: var(--blue); color: var(--ink); }

.tabs button[aria-selected="true"] {
  color: var(--accent);
  border-bottom-color: var(--blue-strong);
  background: none;
}

/* --- admin overview: one line per group ---------------------------------- */
.filters {
  display: flex;
  gap: .4rem;
  margin-bottom: .9rem;
}

.filter {
  flex: 1 1 auto;
  background: var(--bg);
  color: var(--muted);
  border: 1px solid var(--line);
  font-size: .85rem;
  padding: .5rem .7rem;
  min-height: 2.5rem;
}

.filter:hover { background: var(--blue); color: var(--ink); }

.filter.is-on {
  background: var(--blue);
  border-color: var(--blue-strong);
  color: var(--ink);
  font-weight: 600;
}

.ov-group { margin-bottom: .4rem; }

.ov-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  padding: .7rem .8rem;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  text-align: left;
  font-size: 1rem;
  cursor: pointer;
}

.ov-row:hover:not(:disabled) { background: var(--blue); border-color: var(--blue-mid); }

.ov-chevron {
  flex: 0 0 auto;
  font-size: .95rem;
  line-height: 1;
  color: var(--muted);
  transition: transform .15s ease;
}

.ov-group.is-open .ov-chevron { transform: rotate(180deg); }
.ov-group.is-open .ov-row { border-bottom-color: transparent; }

.ov-detail {
  border: 1px solid var(--line);
  border-top: 0;
  background: var(--bg);
  padding: .3rem .8rem .7rem;
}

.ov-guest {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
  padding: .45rem 0;
  border-bottom: 1px dashed var(--line);
}

.ov-guest:last-of-type { border-bottom: 0; }
.ov-guest-name { font-size: .92rem; }

.ov-tag {
  flex: 0 0 auto;
  font-size: .78rem;
  white-space: nowrap;
}

.ov-tag.yes { color: var(--ok); }
.ov-tag.no { color: var(--err); }
.ov-tag.none { color: var(--muted); }

.ov-diet {
  margin: 0 0 .35rem !important;
  font-size: .8rem;
  color: var(--accent);
}

.ov-meta {
  margin: .6rem 0 0 !important;
  font-size: .78rem;
  color: var(--muted);
}

.ov-message {
  margin: .5rem 0 0 !important;
  font-family: var(--serif);
  font-style: italic;
  font-size: .9rem;
  line-height: 1.5;
  color: var(--ink);
}

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

.ov-name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--serif);
  font-size: 1.02rem;
  color: var(--accent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ov-counts {
  display: flex;
  gap: .35rem;
  flex: 0 0 auto;
}

.tally {
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
  padding: .12rem .4rem;
  border: 1px solid var(--line);
  white-space: nowrap;
}

.tally.yes { color: var(--ok); border-color: var(--blue-strong); background: var(--blue); }
.tally.no { color: var(--err); }
.tally.none { color: var(--muted); border-style: dashed; }

.status-pill {
  flex: 0 0 auto;
  font-size: .75rem;
  padding: .2rem .5rem;
  border: 1px solid var(--line);
  white-space: nowrap;
}

.status-pill.ok { color: var(--ok); border-color: var(--blue-strong); background: var(--blue); }
.status-pill.waiting { color: var(--muted); }

.dietary-row {
  display: grid;
  gap: .1rem;
  padding: .65rem 0;
  border-bottom: 1px solid var(--line);
}

.dietary-row:last-child { border-bottom: 0; }
.dietary-name { color: var(--ink); }
.dietary-need { color: var(--accent); }
.dietary-household { font-size: .8rem; color: var(--muted); }

/* --- admin search -------------------------------------------------------- */
.search-field { margin-bottom: 1rem; }
.search-field input { min-height: 3rem; }

body.admin main {
  max-width: 60rem;
  margin: 0 auto;
  padding: 1.25rem 1.25rem 3rem;
}

body.admin .panel { border-bottom: 0; padding: 1.6rem 0 .6rem; }

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: .8rem;
  padding: 0 0 1.1rem;
}

.stat {
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1.1rem .8rem;
  text-align: center;
}

.stat-value {
  margin: 0 !important;
  font-family: var(--serif);
  font-size: 1.85rem;
  line-height: 1;
  color: var(--accent);
}

.stat-label {
  margin: .45rem 0 0 !important;
  font-size: .8rem;
  color: var(--muted);
}

.table-wrap { overflow-x: auto; }

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

th, td {
  text-align: left;
  padding: .6rem .7rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

th { font-size: .82rem; color: var(--muted); font-weight: 600; }

td.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
td.ok { color: var(--ok); }
td.muted { color: var(--muted); }

/* --- admin: guest management --------------------------------------------- */
.checkbox {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .9rem;
  cursor: pointer;
}

.checkbox input { width: 1rem; height: 1rem; accent-color: var(--accent); }

.create-form {
  display: grid;
  gap: 1rem;
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 1.2rem;
}

.create-row { display: flex; flex-wrap: wrap; align-items: end; gap: 1rem; }

.field.inline { display: flex; align-items: center; gap: .5rem; }
.field.inline > span { margin: 0; }

.household-card {
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1.15rem;
  margin-bottom: .9rem;
}

.household-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  margin-bottom: .8rem;
}

.code-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
}

.invite-link {
  flex: 1 1 100%;
  margin: 0 !important;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .78rem;
  line-height: 1.4;
  color: var(--muted);
  word-break: break-all;
  user-select: all;          /* one click selects the whole link */
}

button.copy-failed { color: var(--err); border-color: var(--err); }

/* editable fields look editable — visible affordance, not invisible text */
.household-name-input {
  flex: 1 1 14rem;
  font-family: var(--serif);
  font-size: 1.15rem;
  color: var(--accent);
  border: 1px solid var(--line);
  padding: .4rem .55rem;
  width: auto;
}

.code-chip {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1rem;
  letter-spacing: .1em;
  background: var(--blue);
  border: 1px solid var(--blue-mid);
  padding: .38rem .65rem;
  color: var(--ink);
  white-space: nowrap;
}

.household-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  padding-bottom: .8rem;
  border-bottom: 1px solid var(--line);
}

.household-controls select { padding: .38rem .5rem; font-size: .9rem; width: auto; }

.seats { font-size: .85rem; color: var(--muted); }
.seats.ok { color: var(--ok); font-weight: 600; }

.admin-guests { padding-top: .55rem; }

.admin-guest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  padding: .3rem 0;
}

.guest-name-input {
  flex: 1 1 12rem;
  border: 1px solid var(--line);
  padding: .45rem .55rem;
  width: auto;
}

/* --- phones --------------------------------------------------------------
   The admin panel is a working tool on a small screen: keep it tight. Every
   control still clears a 36px tap target, but nothing gets a line of its own
   unless it has to. */
@media (max-width: 32rem) {
  button.small { min-height: 2.15rem; padding: .35rem .6rem; font-size: .78rem; }
  .checkbox input { width: 1.05rem; height: 1.05rem; }

  body.admin main { padding: 0 .8rem 2rem; }
  body.admin .panel { padding: 1rem 0 .4rem; }
  body.admin .panel h2 { font-size: .95rem; margin-bottom: .6rem; }

  .stats { gap: .45rem; padding: 0 0 .7rem; }
  .stat { padding: .6rem .4rem; }
  .stat-value { font-size: 1.35rem; }
  .stat-label { font-size: .7rem; margin-top: .25rem !important; }

  .tabs button { min-height: 2.5rem; padding: .55rem .4rem; font-size: .88rem; }

  /* group card */
  .household-card { padding: .7rem .7rem .6rem; margin-bottom: .6rem; }
  .household-head { gap: .35rem; margin-bottom: .5rem; }

  .household-name-input {
    flex: 1 1 100%;
    width: 100%;
    font-size: 1rem;
    padding: .3rem .4rem;
  }

  .code-row { width: 100%; gap: .35rem; }
  .code-row .code-chip {
    flex: 1 1 auto;
    text-align: center;
    font-size: .82rem;
    letter-spacing: .06em;
    padding: .28rem .4rem;
  }
  .code-row button { flex: 0 0 auto; }

  .invite-link { font-size: .66rem; margin-top: .25rem !important; opacity: .8; }

  .household-controls { gap: .4rem .7rem; padding-bottom: .5rem; }
  .household-controls .field.inline { flex: 1 1 auto; gap: .35rem; }
  .household-controls .field.inline > span { font-size: .78rem; }
  .household-controls select { flex: 1 1 auto; padding: .3rem .4rem; font-size: .85rem; }
  .seats { font-size: .78rem; }

  /* name across the top, then gender, status and Remove together on one line */
  .admin-guests { padding-top: .3rem; }
  .admin-guest {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem .5rem;
    padding: .3rem 0;
  }
  .admin-guest .guest-name-input {
    flex: 1 1 100%;
    width: 100%;
    padding: .3rem .4rem;
    font-size: .92rem;
  }
  .admin-guest .gender-select { flex: 0 0 auto; padding: .25rem .2rem; font-size: .8rem; }
  .admin-guest .guest-status { flex: 0 1 auto; font-size: .76rem; }
  .admin-guest button { flex: 0 0 auto; margin-left: auto; }

  .add-guest { margin-top: .45rem; padding-top: .45rem; gap: .35rem; }
  .add-guest input { min-height: 2.15rem; font-size: .85rem; padding: .3rem .45rem; }

  .household-foot { margin-top: .6rem; padding-top: .5rem; gap: .35rem; }
  .household-foot button { flex: 1 1 auto; }

  .household-message { font-size: .85rem; padding: .45rem .6rem; margin-top: .5rem !important; }

  /* add-a-group form */
  .create-form { padding: .8rem; gap: .6rem; }
  .create-row { gap: .5rem; }
  .create-row .field { flex: 1 1 7rem; }
  .create-row button { flex: 1 1 5rem; min-height: 2.4rem; }
  .field > span { font-size: .78rem; margin-bottom: .25rem; }
  .search-field { margin-bottom: .6rem; }
  .search-field input { min-height: 2.5rem; }

  /* overview */
  .filters { gap: .3rem; margin-bottom: .6rem; }
  .filter { min-height: 2.2rem; font-size: .8rem; padding: .35rem .4rem; }
  .ov-row { padding: .5rem .55rem; gap: .4rem; }
  .ov-name { font-size: .95rem; }
  .tally { font-size: .76rem; padding: .1rem .3rem; }
  .status-pill { font-size: .7rem; padding: .15rem .35rem; }
  .ov-detail { padding: .25rem .55rem .5rem; }
  .ov-guest { padding: .3rem 0; }
  .ov-guest-name { font-size: .85rem; }
}

/* --- admin ---------------------------------------------------------------
   Same design language, green rather than blue so it is obvious at a glance
   which side you are on. Cards stay white against the green page for
   contrast. */
body.admin {
  --blue: #e4f3ea;
  --blue-mid: #a9d6bb;
  --blue-strong: #4f9a71;
  --line: #bcd9c8;
  --panel: #ffffff;
  --page: #f0f8f3;
}

/* --- admin tabs ---------------------------------------------------------- */
/* the stats scroll away above them, the tabs stay reachable */
.tabs {
  position: sticky;
  top: 0;
  z-index: 19;
  display: grid;
  grid-template-columns: 1fr 1fr;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

.tabs button {
  background: none;
  border: 0;
  border-bottom: 3px solid transparent;
  color: var(--muted);
  font-size: .95rem;
  padding: .85rem .5rem;
  min-height: 3rem;
}

.tabs button:hover { background: var(--blue); color: var(--ink); }

.tabs button[aria-selected="true"] {
  color: var(--accent);
  border-bottom-color: var(--blue-strong);
  background: none;
}

/* --- admin overview: one line per group ---------------------------------- */
.filters {
  display: flex;
  gap: .4rem;
  margin-bottom: .9rem;
}

.filter {
  flex: 1 1 auto;
  background: var(--bg);
  color: var(--muted);
  border: 1px solid var(--line);
  font-size: .85rem;
  padding: .5rem .7rem;
  min-height: 2.5rem;
}

.filter:hover { background: var(--blue); color: var(--ink); }

.filter.is-on {
  background: var(--blue);
  border-color: var(--blue-strong);
  color: var(--ink);
  font-weight: 600;
}

.ov-group { margin-bottom: .4rem; }

.ov-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  padding: .7rem .8rem;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  text-align: left;
  font-size: 1rem;
  cursor: pointer;
}

.ov-row:hover:not(:disabled) { background: var(--blue); border-color: var(--blue-mid); }

.ov-chevron {
  flex: 0 0 auto;
  font-size: .95rem;
  line-height: 1;
  color: var(--muted);
  transition: transform .15s ease;
}

.ov-group.is-open .ov-chevron { transform: rotate(180deg); }
.ov-group.is-open .ov-row { border-bottom-color: transparent; }

.ov-detail {
  border: 1px solid var(--line);
  border-top: 0;
  background: var(--bg);
  padding: .3rem .8rem .7rem;
}

.ov-guest {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
  padding: .45rem 0;
  border-bottom: 1px dashed var(--line);
}

.ov-guest:last-of-type { border-bottom: 0; }
.ov-guest-name { font-size: .92rem; }

.ov-tag {
  flex: 0 0 auto;
  font-size: .78rem;
  white-space: nowrap;
}

.ov-tag.yes { color: var(--ok); }
.ov-tag.no { color: var(--err); }
.ov-tag.none { color: var(--muted); }

.ov-diet {
  margin: 0 0 .35rem !important;
  font-size: .8rem;
  color: var(--accent);
}

.ov-meta {
  margin: .6rem 0 0 !important;
  font-size: .78rem;
  color: var(--muted);
}

.ov-message {
  margin: .5rem 0 0 !important;
  font-family: var(--serif);
  font-style: italic;
  font-size: .9rem;
  line-height: 1.5;
  color: var(--ink);
}

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

.ov-name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--serif);
  font-size: 1.02rem;
  color: var(--accent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ov-counts {
  display: flex;
  gap: .35rem;
  flex: 0 0 auto;
}

.tally {
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
  padding: .12rem .4rem;
  border: 1px solid var(--line);
  white-space: nowrap;
}

.tally.yes { color: var(--ok); border-color: var(--blue-strong); background: var(--blue); }
.tally.no { color: var(--err); }
.tally.none { color: var(--muted); border-style: dashed; }

.status-pill {
  flex: 0 0 auto;
  font-size: .75rem;
  padding: .2rem .5rem;
  border: 1px solid var(--line);
  white-space: nowrap;
}

.status-pill.ok { color: var(--ok); border-color: var(--blue-strong); background: var(--blue); }
.status-pill.waiting { color: var(--muted); }

.dietary-row {
  display: grid;
  gap: .1rem;
  padding: .65rem 0;
  border-bottom: 1px solid var(--line);
}

.dietary-row:last-child { border-bottom: 0; }
.dietary-name { color: var(--ink); }
.dietary-need { color: var(--accent); }
.dietary-household { font-size: .8rem; color: var(--muted); }

/* --- admin search -------------------------------------------------------- */
.search-field { margin-bottom: 1rem; }
.search-field input { min-height: 3rem; }

body.admin main {
  max-width: 60rem;
  margin: 0 auto;
  padding: 1.25rem 1.25rem 3rem;
}

body.admin .panel { border-bottom: 0; padding: 1.6rem 0 .6rem; }

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: .8rem;
  padding: 0 0 1.1rem;
}

.stat {
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1.1rem .8rem;
  text-align: center;
}

.stat-value {
  margin: 0 !important;
  font-family: var(--serif);
  font-size: 1.85rem;
  line-height: 1;
  color: var(--accent);
}

.stat-label {
  margin: .45rem 0 0 !important;
  font-size: .8rem;
  color: var(--muted);
}

.table-wrap { overflow-x: auto; }

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

th, td {
  text-align: left;
  padding: .6rem .7rem;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

th { font-size: .82rem; color: var(--muted); font-weight: 600; }

td.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
td.ok { color: var(--ok); }
td.muted { color: var(--muted); }

/* --- admin: guest management --------------------------------------------- */
.checkbox {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .9rem;
  cursor: pointer;
}

.checkbox input { width: 1rem; height: 1rem; accent-color: var(--accent); }

.create-form {
  display: grid;
  gap: 1rem;
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 1.2rem;
}

.create-row { display: flex; flex-wrap: wrap; align-items: end; gap: 1rem; }

.field.inline { display: flex; align-items: center; gap: .5rem; }
.field.inline > span { margin: 0; }

.household-card {
  border: 1px solid var(--line);
  background: var(--panel);
  padding: 1.15rem;
  margin-bottom: .9rem;
}

.household-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  margin-bottom: .8rem;
}

.code-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
}

.invite-link {
  flex: 1 1 100%;
  margin: 0 !important;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .78rem;
  line-height: 1.4;
  color: var(--muted);
  word-break: break-all;
  user-select: all;          /* one click selects the whole link */
}

button.copy-failed { color: var(--err); border-color: var(--err); }

/* editable fields look editable — visible affordance, not invisible text */
.household-name-input {
  flex: 1 1 14rem;
  font-family: var(--serif);
  font-size: 1.15rem;
  color: var(--accent);
  border: 1px solid var(--line);
  padding: .4rem .55rem;
  width: auto;
}

.code-chip {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1rem;
  letter-spacing: .1em;
  background: var(--blue);
  border: 1px solid var(--blue-mid);
  padding: .38rem .65rem;
  color: var(--ink);
  white-space: nowrap;
}

.household-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  padding-bottom: .8rem;
  border-bottom: 1px solid var(--line);
}

.household-controls select { padding: .38rem .5rem; font-size: .9rem; width: auto; }

.seats { font-size: .85rem; color: var(--muted); }
.seats.ok { color: var(--ok); font-weight: 600; }

.admin-guests { padding-top: .55rem; }

.admin-guest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
  padding: .3rem 0;
}

.guest-name-input {
  flex: 1 1 12rem;
  border: 1px solid var(--line);
  padding: .45rem .55rem;
  width: auto;
}



.gender-select {
  width: auto;
  flex: 0 0 auto;
  padding: .4rem .35rem;
  font-size: .85rem;
}

.guest-status { font-size: .8rem; white-space: nowrap; }
.guest-status.ok { color: var(--ok); font-weight: 600; }
.guest-status.muted { color: var(--muted); }

.add-guest {
  display: flex;
  gap: .5rem;
  margin-top: .6rem;
  padding-top: .6rem;
  border-top: 1px dashed var(--line);
}

.add-guest input { flex: 1 1 auto; font-size: .9rem; padding: .5rem .55rem; }

.household-message {
  margin: .8rem 0 0 !important;
  font-family: var(--serif);
  font-style: italic;
  color: var(--ink);
  background: var(--blue);
  border: 1px solid var(--blue-mid);
  padding: .6rem .8rem;
  line-height: 1.6;
}

.household-foot {
  display: flex;
  gap: .5rem;
  margin-top: .9rem;
  padding-top: .8rem;
  border-top: 1px solid var(--line);
}

button.danger { color: var(--err); border-color: var(--err); }
button.danger:hover:not(:disabled) { background: #fdf1ef; border-color: var(--err); }

/* --- admin guest list: one editable grid ---------------------------------
   A dense sheet rather than a stack of cards: every group and every person is
   one row, all fields visible at once, edited in place.

   Two things have to be beaten for this to work. The global control rule is
   input[type=text] (0,1,1), so cell controls are written as .sheet td input
   (0,1,2) or they inherit 1rem type and .7rem padding and each row becomes a
   box. And body.admin main is capped for reading prose, which a sheet is not,
   so the list panel is widened below. */

body.admin main { max-width: 92rem; }

.sheet-scroll {
  overflow-x: auto;
  border: 1px solid var(--blue-mid);
  background: var(--bg);
}

.sheet {
  border-collapse: collapse;
  width: 100%;
  font-size: .85rem;
  font-variant-numeric: tabular-nums;
}

.sheet th,
.sheet td {
  border-bottom: 1px solid var(--line);
  border-right: 1px solid var(--line);
  padding: 0 .3rem;
  text-align: left;
  vertical-align: middle;
  height: 1.85rem;           /* a spreadsheet row, not a form field */
}
.sheet th:last-child,
.sheet td:last-child { border-right: 0; }

.sheet thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--blue);
  border-bottom: 1px solid var(--blue-strong);
  font-weight: 600;
  font-size: .78rem;
  letter-spacing: .04em;
  white-space: nowrap;
  height: 2rem;
  padding: 0 .45rem;
}

/* --- column widths: the sheet should fill the width, not drift ----------- */
.sheet .col-name    { width: 26%; min-width: 13rem; }
.sheet .col-lang    { width: 9rem; }
.sheet .col-gender  { width: 6.5rem; }
.sheet .col-reply   { width: 11rem; }
.sheet .col-diet    { width: auto; min-width: 10rem; }
.sheet .col-seats   { width: 4rem; text-align: right; }
.sheet .col-code    { width: 13rem; white-space: nowrap; }
.sheet .col-actions { width: 13.5rem; white-space: nowrap; }
.sheet .col-seats,
.sheet thead .col-seats { text-align: right; padding-right: .5rem; }

/* --- rows ---------------------------------------------------------------- */
.sheet tbody tr[hidden] { display: none; }
.sheet tbody tr:hover > td { background: var(--blue); }

/* a group opens a band; its people belong to it until the next one */
.sheet .row-group > td {
  background: #eef7f1;
  border-top: 1px solid var(--blue-strong);
  font-weight: 600;
}
.sheet .row-person > td { background: var(--bg); }
.sheet .row-person:nth-of-type(even) > td { background: #fbfdfc; }

/* --- cell controls ------------------------------------------------------- */
/* written against .sheet td so they outrank the global input[type=text] */
.sheet td input[type=text],
.sheet td select {
  width: 100%;
  border: 1px solid transparent;
  background: transparent;
  padding: .1rem .25rem;
  font-family: var(--sans);
  font-size: .85rem;
  line-height: 1.4;
  color: var(--ink);
  min-height: 0;
  height: 1.5rem;
  border-radius: var(--radius);
}
.sheet .row-group td input[type=text] { font-weight: 600; }
.sheet td input[type=text].indent { padding-left: 1.15rem; }

/* flat until worth touching, then obviously editable */
.sheet td input[type=text]:hover,
.sheet td select:hover {
  border-color: var(--blue-mid);
  background: var(--bg);
}
.sheet td input[type=text]:focus,
.sheet td select:focus {
  border-color: var(--blue-strong);
  background: var(--bg);
  outline: none;
}
.sheet td select { padding-right: 0; }

/* a saved edit confirms itself: a typed value and a stored one look the same */
@keyframes cell-saved-fade {
  from { background: var(--blue-mid); }
  to   { background: transparent; }
}
.sheet td.cell-saved { animation: cell-saved-fade 1.3s ease-out; }
.sheet td.cell-failed { background: #fdf1ef; }

/* --- read-only cells ----------------------------------------------------- */
.sheet-pill {
  display: inline-block;
  padding: 0 .35rem;
  font-size: .76rem;
  border: 1px solid var(--line);
  background: var(--bg);
  white-space: nowrap;
}
.sheet-pill.ok { color: var(--ok); border-color: var(--ok); }
.sheet-pill.waiting { color: var(--muted); }

.sheet-tally { font-size: .74rem; color: var(--muted); margin-left: .35rem; }

.sheet-tag { font-size: .8rem; white-space: nowrap; }
.sheet-tag.yes { color: var(--ok); }
.sheet-tag.no { color: var(--err); }
.sheet-tag.none { color: var(--muted); }

.sheet-num { color: var(--muted); }

.sheet-diet {
  display: block;
  font-size: .8rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.code-chip.small {
  font-size: .78rem;
  padding: 0 .3rem;
  letter-spacing: .06em;
  margin-right: .25rem;
  border: 1px solid var(--blue-mid);
}

/* buttons have to sit inside a 1.85rem row without stretching it */
.sheet button.ghost.tiny {
  padding: 0 .35rem;
  font-size: .74rem;
  min-height: 0;
  height: 1.4rem;
  line-height: 1.4rem;
  margin-right: .2rem;
  vertical-align: middle;
}

/* --- inline add, only while adding --------------------------------------- */
.sheet .row-add > td { background: var(--bg); }
.sheet-add { display: flex; gap: .25rem; align-items: center; }
.sheet-add input[type=text] {
  border-color: var(--blue-strong) !important;
  background: var(--bg) !important;
}

.sheet .row-message > td {
  background: var(--bg);
  height: auto;
  padding: .2rem .5rem .3rem 1.15rem;
  white-space: normal;
}
.sheet-message-label {
  font-size: .72rem;
  color: var(--muted);
  letter-spacing: .05em;
  margin-right: .4rem;
}
.sheet-message { font-style: italic; }

/* the name column anchors the sheet when the fields scroll sideways */
@media (min-width: 62rem) {
  .sheet .col-name { position: sticky; left: 0; z-index: 1; }
  .sheet thead .col-name { z-index: 3; }
  .sheet .row-group > td.col-name { background: #eef7f1; }
  .sheet .row-person > td.col-name { background: var(--bg); }
  .sheet .row-person:nth-of-type(even) > td.col-name { background: #fbfdfc; }
  .sheet tbody tr:hover > td.col-name { background: var(--blue); }
}

@media (max-width: 62rem) {
  .sheet { min-width: 58rem; }   /* narrower than this and columns collide */
}

/* the add form sat as a tall stack above the sheet, pushing the data down.
   One inline row instead, so the list starts near the top of the screen. */
@media (min-width: 62rem) {
  body.admin .create-form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .6rem;
  }
  body.admin .create-form .field { flex: 1 1 14rem; margin: 0; gap: .2rem; }
  body.admin .create-form .field > span { font-size: .78rem; color: var(--muted); }
  body.admin .create-form .field input,
  body.admin .create-form .field textarea,
  body.admin .create-form select {
    padding: .35rem .5rem;
    font-size: .9rem;
    min-height: 0;
  }
  body.admin .create-form textarea { height: 3.4rem; resize: vertical; }
  body.admin .create-form .create-row {
    display: flex;
    align-items: flex-end;
    gap: .6rem;
    flex: 0 0 auto;
  }
  body.admin .create-form .create-row .field { flex: 0 0 9rem; }
  body.admin .create-form button[type=submit] { padding: .4rem 1.1rem; }
  body.admin .create-form .error { flex: 1 0 100%; margin: 0; }

  /* the two panels are one workspace, not two documents */
  body.admin #panel-list .panel { padding: .9rem 0 .5rem; }
  body.admin #panel-list .panel h2 { margin-bottom: .5rem; }
  body.admin .search-field { margin-bottom: .6rem; }
  body.admin .search-field input { min-height: 2.2rem; padding: .35rem .6rem; }
}

/* --- corner softening -----------------------------------------------------
   Every bordered box takes the same barely-there radius from one token. Kept
   as a single list rather than a universal selector so table cells, dividers
   and the paper background stay square, which is where a radius would show
   as a mistake rather than as a finish. */
button,
.card,
.choices label,
.code-chip,
.create-form,
.filter,
.guest,
.household-card,
.household-message,
.modal-box,
.ov-detail,
.ov-row,
.pending,
.sheet-pill,
.sheet-scroll,
.sheet-tag,
.stat,
.status-pill,
.tally,
.tabs button,
.panel .field input,
.panel .field textarea,
.saved,
.dietary-row,
.admin-card,
.checkbox,
.sheet-add input[type=text],
.sheet td input[type=text],
.sheet td select {
  border-radius: var(--radius);
}

/* the container clips, so the sheet's square corners do not poke through its
   rounded ones. Horizontal scrolling still has to work, hence auto not hidden */
.sheet-scroll { overflow-x: auto; }

/* --- location links ------------------------------------------------------
   A small pin marks the schedule lines that open a map. Only a.where gets it:
   the last line ("time to be confirmed") is a span.where, not a link, and must
   stay unmarked.

   Drawn as a mask rather than a coloured background image so the pin takes
   currentColor and follows the link through its hover state for free. */
a.where::before {
  content: "";
  display: inline-block;
  width: .82em;
  height: .82em;
  margin-right: .28em;
  vertical-align: -.11em;
  background-color: currentColor;
  -webkit-mask: var(--pin) no-repeat center / contain;
  mask: var(--pin) no-repeat center / contain;
}

:root {
  --arrow: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M4%2011.25h13.19l-4.72-4.72L13.53%205.5%2020%2011.97l-6.47%206.47-1.06-1.06%204.72-4.63H4z'/%3E%3C/svg%3E");
  --chev: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M7.41%208.59%2012%2013.17l4.59-4.58L18%2010l-6%206-6-6z'/%3E%3C/svg%3E");
  --pin: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M12%202C8.13%202%205%205.13%205%209c0%205.25%207%2013%207%2013s7-7.75%207-13c0-3.87-3.13-7-7-7zm0%209.5c-1.38%200-2.5-1.12-2.5-2.5s1.12-2.5%202.5-2.5%202.5%201.12%202.5%202.5-1.12%202.5-2.5%202.5z'/%3E%3C/svg%3E");
}

/* secondary copy on the reply confirmation: quieter than .blurb, so the
   thank-you leads and the line under it sits back */
.replied-note {
  margin: .55rem 0 0;
  font-size: .9rem;
  line-height: 1.65;
  color: var(--muted);
}

/* --- change a reply -------------------------------------------------------
   An anchor rather than a button: it navigates, and it must keep working if
   the page's JavaScript does not. Styled as a quiet ghost button so it reads
   as a way back without competing with the reply itself. */
.change-reply { margin: 1.8rem 0 0; }

.button-link {
  display: inline-block;
  font-family: var(--sans);
  font-size: .88rem;
  color: var(--ink);
  background: transparent;
  border: 1px solid var(--blue-strong);
  border-radius: var(--radius);
  padding: .5rem 1.1rem;
  text-decoration: none;
}

.button-link:hover { background: var(--blue); }

/* Spanish and French put "hacia medianoche" / "vers minuit" in the time column
   itself, so their note underneath is empty. Collapse it rather than leaving
   an empty line pushing the row taller than the others. */
.where:empty { display: none; }

/* --- schedule alignment ---------------------------------------------------
   Each li used to be its own two-column grid, so the columns only lined up
   while every time happened to fit the same fixed width. Spanish and French
   put "hacia medianoche" and "vers minuit" in that column and it broke.

   One grid for the whole list instead, with the rows contributing their cells
   directly. The time column now sizes itself to the longest time in whichever
   language is being served, and every row lines up by construction. */
.schedule {
  grid-template-columns: minmax(4.3rem, max-content) 1fr;
  column-gap: .8rem;
  align-items: baseline;
}

.schedule li {
  display: contents;
}

/* ==========================================================================
   Preview site (/preview): welcome, the day, venues, gifts, questions.

   Shares the paper and wallpaper layers, the gold display type and the blue
   rules with the invite site, so it reads as the same wedding. What differs is
   the shape: this one is a normal multi-section site with a header and a menu,
   not a two-column invitation, because it is meant to be browsed.
   ========================================================================== */

body.pv {
  /* One typeface for the whole preview. The site had a serif for display type
     and a sans for everything else, which read as a mix rather than a system.
     Repointing the token covers all 21 places that asked for the serif, so
     there is no call site left behind to drift back.
     Scoped to .pv: the invite site keeps the type it already had. */
  --serif: var(--sans);

  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}


/* --- header --------------------------------------------------------------- */
.pv-head {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(255, 255, 255, .88);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}

.pv-head-inner {
  max-width: 68rem;
  margin: 0 auto;
  padding: .7rem 1.25rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.pv-brand { text-decoration: none; margin-right: auto; }
.pv-brand-names {
  display: block;
  font-family: var(--serif);
  font-size: 1.3rem;
  color: var(--accent);
  line-height: 1.1;
}
.pv-brand-date {
  display: block;
  font-size: .7rem;
  letter-spacing: .06em;
  color: var(--muted);
  margin-top: .1rem;
}

.pv-nav { display: flex; align-items: center; gap: 1.4rem; }
.pv-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: 1.2rem;
}

.pv-navlink {
  color: var(--ink);
  text-decoration: none;
  font-size: .92rem;
  padding: .3rem 0;
  border-bottom: 2px solid transparent;
}
.pv-navlink:hover { border-bottom-color: var(--blue-mid); }
.pv-navlink[aria-current="page"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.pv-lang { display: flex; align-items: center; gap: .4rem; }
.pv-lang > span { font-size: .78rem; color: var(--muted); white-space: nowrap; }
.pv-lang select {
  width: auto;
  padding: .3rem 1.6rem .3rem .5rem;
  font-size: .85rem;
}

/* the burger is desktop-hidden rather than mobile-added, so the nav is the
   default and the menu is the exception */
.pv-burger { display: none; }

/* --- body ----------------------------------------------------------------- */
.pv-main {
  flex: 1 0 auto;
  width: 100%;
  /* wide enough for the day page to stand the map at the timeline's full
     height beside it. Every other section keeps its own narrower measure and
     is centred, so the headings still line up from tab to tab. */
  max-width: 86rem;
  margin: 0 auto;
  /* one token for the top inset, so anything that needs to match it (the
     centred welcome page) cannot fall out of step with it */
  --main-pad: 2.6rem;
  padding: var(--main-pad) 1.25rem 4rem;
  outline: none;
}

/* Every tab is the same block: same width, same centre, same starting point.
   Four sections with four different max-widths meant the heading jumped left
   and right as you moved between them. Card contents stay left aligned. */
.pv-section {
  width: 100%;
  max-width: 44rem;
  margin: 0 auto;
  text-align: center;
}

.pv-day-card,
.pv-faq,
.pv-card,
.pv-face,
.pv-account-list { text-align: left; }

.pv-title { margin: .2rem 0 0; }
.pv-date {
  margin: .9rem 0 0;
  font-size: .95rem;
  letter-spacing: .04em;
  color: var(--ink);
}
.pv-lede {
  font-size: 1.02rem;
  line-height: 1.75;
  color: var(--ink);
  max-width: 38rem;
  margin-left: auto;
  margin-right: auto;
}
.pv-home { text-align: center; }
/* the measure is capped for reading, so it has to be centred explicitly
   or it sits left inside a wider section */
.pv-lede { margin-left: auto; margin-right: auto; }

.pv-h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.6rem, 4vw, 2.1rem);
  color: var(--accent);
  margin: 0 0 .6rem;
}
.pv-h3 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.35rem;
  color: var(--accent);
  margin: 0 0 .2rem;
}

/* --- home cards ----------------------------------------------------------- */
.pv-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .9rem;
  margin-top: 2.2rem;
  text-align: left;
}

.pv-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .9rem;
  padding: 1.15rem 1.2rem;
  background: rgba(255, 255, 255, .74);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color .15s, transform .15s;
}
.pv-card:hover {
  border-color: var(--blue-strong);
  transform: translateY(-2px);
}
.pv-card-title {
  display: block;
  font-family: var(--serif);
  font-size: 1.15rem;
  color: var(--accent);
}

.pv-card-arrow {
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
  background-color: var(--accent);
  -webkit-mask: var(--arrow) no-repeat center / contain;
  mask: var(--arrow) no-repeat center / contain;
  transition: transform .18s ease;
}
.pv-card:hover .pv-card-arrow { transform: translateX(3px); }

@media (prefers-reduced-motion: reduce) {
  .pv-card, .pv-card-arrow { transition: none; }
}


/* --- venues --------------------------------------------------------------- */
.pv-place {
  padding: 1.6rem 0;
  border-top: 1px solid var(--line);
}
.pv-place:first-of-type { border-top: 0; padding-top: .6rem; }
.pv-where {
  margin: 0 0 .8rem;
  font-size: .85rem;
  letter-spacing: .05em;
  color: var(--muted);
}

.pv-maplink {
  display: inline-block;
  margin-top: .2rem;
  color: var(--accent);
  font-size: .9rem;
  text-decoration: underline;
  text-decoration-color: var(--blue-strong);
  text-underline-offset: 3px;
}
.pv-maplink::before {
  content: "";
  display: inline-block;
  width: .82em;
  height: .82em;
  margin-right: .28em;
  vertical-align: -.11em;
  background-color: currentColor;
  -webkit-mask: var(--pin) no-repeat center / contain;
  mask: var(--pin) no-repeat center / contain;
}

.pv-shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: .9rem;
  margin-top: 1.2rem;
}

.pv-figure { margin: 0; }
.pv-figure img {
  display: block;
  width: 100%;
  height: 18rem;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--blue);
}

/* required by CC BY-SA: never style this to nothing */
.pv-credit {
  margin-top: .3rem;
  font-size: .72rem;
  color: var(--muted);
}
.pv-credit a { color: var(--muted); }

/* --- questions: one card each -------------------------------------------
   The same card as everywhere else, expanding downwards when opened. No rules
   between them: the card edges already separate one from the next. */
.pv-faq {
  background: rgba(255, 255, 255, .78);
  border: var(--card-border, 1px) solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0 1.1rem;
  margin-bottom: .7rem;
}
.pv-faq:last-of-type { margin-bottom: 0; }

.pv-faq-q {
  cursor: pointer;
  padding: .85rem 1.9rem .85rem 0;
  font-size: .98rem;
  font-weight: 600;          /* the question carries the weight */
  color: var(--ink);
  position: relative;
  list-style: none;
}
.pv-faq-q::-webkit-details-marker { display: none; }
.pv-faq-q::after {
  content: "+";
  position: absolute;
  right: 0;
  top: .72rem;
  color: var(--accent);
  font-size: 1.15rem;
  font-weight: 400;
}
.pv-faq[open] .pv-faq-q::after { content: "\2212"; }

.pv-faq-a {
  margin: 0 0 .95rem;
  font-weight: 400;          /* the answer does not */
  color: var(--ink);
  line-height: 1.7;
}

/* --- footer --------------------------------------------------------------- */
.pv-foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--line);
  padding: 1.4rem 1.25rem 2rem;
  text-align: center;
}
.pv-foot p {
  margin: 0;
  font-size: .8rem;
  letter-spacing: .05em;
  color: var(--muted);
}

/* --- phones --------------------------------------------------------------- */
@media (max-width: 52rem) {
  .pv-burger {
    display: flex;
    align-items: center;
    gap: .5rem;
    background: transparent;
    color: var(--ink);
    border: 1px solid var(--line);
    padding: .45rem .7rem;
    min-height: 2.4rem;
  }
  /* the base button rule turns every button gold on hover, which on this one
     leaves dark text on a gold field. Three classes to outrank it. */
  .pv-burger:hover:not(:disabled),
  .pv-menu-open .pv-burger {
    background: var(--blue);
    border-color: var(--blue-strong);
    color: var(--ink);
  }

  .pv-burger-label { font-size: .85rem; }
  .pv-burger-bars {
    display: inline-flex;
    flex-direction: column;
    justify-content: space-between;
    width: 1.05rem;
    height: .72rem;
  }
  .pv-burger-bars i {
    display: block;
    height: 2px;
    background: var(--accent);
    transition: transform .18s, opacity .18s;
  }

  /* the bars become a cross while the menu is open, so the button says what
     pressing it again will do */
  .pv-menu-open .pv-burger-bars i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
  .pv-menu-open .pv-burger-bars i:nth-child(2) { opacity: 0; }
  .pv-menu-open .pv-burger-bars i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

  .pv-nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: #fff;
    border-bottom: 1px solid var(--line);
    padding: .4rem 1.25rem 1rem;
    box-shadow: 0 12px 24px rgba(20, 20, 20, .07);

    /* collapsed by default and genuinely inert: display:none keeps the links
       out of the tab order, which visibility or opacity alone would not */
    display: none;
  }
  .pv-menu-open .pv-nav { display: flex; }

  .pv-nav ul { flex-direction: column; gap: 0; }
  .pv-navlink {
    display: block;
    padding: .8rem 0;
    border-bottom: 1px solid var(--line);
    font-size: 1rem;
  }
  .pv-navlink:hover { border-bottom-color: var(--line); }
  .pv-navlink[aria-current="page"] { border-bottom-color: var(--accent); }

  /* The language control used to sit inside this collapsed menu, where a
     fixed 11rem basis was right. It now lives in the header bar, and that
     basis was pinning the select to 176px: flex-basis beats width, so every
     width rule written for it afterwards was silently ignored and the button
     was pushed onto a second row. The control sizes itself now. */
  .pv-head-inner .pv-lang select { flex: 0 1 auto; }

  .pv-head-inner { position: relative; }
  .pv-main { --main-pad: 1.8rem; padding: var(--main-pad) 1.15rem 3rem; }
  .pv-figure img { height: 13rem; }
  .pv-cards { grid-template-columns: 1fr; margin-top: 1.6rem; }
}

/* link through to the preview site from the admin panel */
.pv-open {
  display: flex;
  align-items: center;
  gap: .7rem;
  flex-wrap: wrap;
  margin: 0 0 1rem;
}
.pv-open-note { font-size: .78rem; color: var(--muted); }

/* --- language control in the bar -----------------------------------------
   Moved out of the menu: on a phone the menu is shut by default, and a guest
   who needs Spanish should not have to open a menu written in English to find
   it. A globe rather than a flag. Flags name countries, not languages, and on
   a London wedding a Union Jack reads as "where the wedding is" rather than
   "which language you are reading". The options carry each language's own
   name, which is the only label a speaker of it is certain to recognise. */
:root {
  --globe: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M11.99%202C6.47%202%202%206.48%202%2012s4.47%2010%209.99%2010C17.52%2022%2022%2017.52%2022%2012S17.52%202%2011.99%202zm6.93%206h-2.95c-.32-1.25-.78-2.45-1.38-3.56%201.84.63%203.37%201.91%204.33%203.56zM12%204.04c.83%201.2%201.48%202.53%201.91%203.96h-3.82c.43-1.43%201.08-2.76%201.91-3.96zM4.26%2014C4.1%2013.36%204%2012.69%204%2012s.1-1.36.26-2h3.38c-.08.66-.14%201.32-.14%202%200%20.68.06%201.34.14%202H4.26zm.82%202h2.95c.32%201.25.78%202.45%201.38%203.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66%202.49-2.93%204.33-3.56C8.81%205.55%208.35%206.75%208.03%208zM12%2019.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43%201.43-1.08%202.76-1.91%203.96zM14.34%2014H9.66c-.09-.66-.16-1.32-.16-2%200-.68.07-1.35.16-2h4.68c.09.65.16%201.32.16%202%200%20.68-.07%201.34-.16%202zm.25%205.56c.6-1.11%201.06-2.31%201.38-3.56h2.95c-.96%201.65-2.49%202.93-4.33%203.56zM16.36%2014c.08-.66.14-1.32.14-2%200-.68-.06-1.34-.14-2h3.38c.16.64.26%201.31.26%202s-.1%201.36-.26%202h-3.38z'/%3E%3C/svg%3E");
}

.pv-head-inner .pv-lang {
  display: flex;
  align-items: center;
  gap: .35rem;
  margin: 0;
  flex: 0 0 auto;
}

.pv-globe {
  display: inline-block;
  width: 1.02rem;
  height: 1.02rem;
  flex: 0 0 auto;
  background-color: var(--muted);
  -webkit-mask: var(--globe) no-repeat center / contain;
  mask: var(--globe) no-repeat center / contain;
}

.pv-head-inner .pv-lang select {
  width: auto;
  min-width: 6.6rem;          /* fits "Français" without clipping the arrow */
  padding: .3rem 1.5rem .3rem .45rem;
  font-size: .85rem;
  border-color: var(--line);
  background-color: transparent;
}
.pv-head-inner .pv-lang select:hover { border-color: var(--blue-strong); }


@media (max-width: 52rem) {
  /* the menu no longer carries a language row */
  .pv-nav .pv-lang { display: none; }
}

@media (max-width: 30rem) {
  /* three controls will not fit across a phone with words on all of them, so
     the menu button keeps its icon and gives up its label. The label stays in
     the accessibility tree, so the button is still announced as "Menu". */
  .pv-burger-label {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .pv-burger { padding: .45rem .55rem; }
  .pv-head-inner { gap: .5rem; padding-left: 1rem; padding-right: 1rem; }
  .pv-head-inner .pv-lang select { min-width: 6.1rem; font-size: .8rem; }
  .pv-brand-names { font-size: 1.12rem; }
  /* the date wraps to a second line at this width and doubles the height of
     the bar. It is the first thing on the welcome page anyway. */
  .pv-brand-date { display: none; }
}

/* --- header: never let the three controls collide -------------------------
   Reported overlapping on a real phone though it measured clean in a headless
   browser, which is what a system font larger than the default does to a
   native select. Rather than chase a breakpoint: the row may wrap, the brand
   and the select may both shrink, and the menu button never does. Worst case
   the language control drops to its own line, which is untidy but legible. */
.pv-head-inner {
  flex-wrap: wrap;
  row-gap: .4rem;
}

.pv-brand { min-width: 0; }
.pv-brand-names, .pv-brand-date {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pv-head-inner .pv-lang { min-width: 0; }
.pv-head-inner .pv-lang select {
  max-width: 100%;
  text-overflow: ellipsis;
}

/* the button keeps its size whatever else has to give */
.pv-burger { flex: 0 0 auto; }

@media (max-width: 52rem) {
  /* size to the longest option ("Français") rather than a guessed width: a
     fixed one either clips the label or wastes room the menu button needs */
  .pv-head-inner .pv-lang select {
    min-width: 0;
    width: auto;
    max-width: 9rem;
    font-size: .8rem;
    padding-right: 1.25rem;
  }
}

@media (max-width: 30rem) {
  .pv-head-inner .pv-lang select { font-size: .78rem; max-width: 8.2rem; }
}



/* --- gifts: two square cards that turn over -------------------------------
   Sizes here are all relative or intrinsic. The one thing that cannot be done
   in CSS is fitting an IBAN to whatever width the card ends up: that is
   measured in preview.js and applied per element, so it is right on any
   screen rather than on the screen sizes somebody happened to test. */



/* the warm half: given room to breathe, and a comfortable measure rather
   than the full width of the section */
.pv-gift-intro { max-width: 34rem; margin: 0 auto; }
/* the intro and the honeymoon note are one voice, so they are set the same:
   same size, same leading, same colour. Only the heading between them
   separates the two halves of the page. */
.pv-gift-intro p,
.pv-gift-lede {
  font-size: 1.02rem;
  line-height: 1.8;
  color: var(--ink);
  margin: 0 0 .9rem;
}
.pv-gift-intro p:last-child { margin-bottom: 0; }

.pv-gifts .rule { margin: 2.4rem auto; }

.pv-gift-fund { max-width: 32rem; margin: 0 auto; }
.pv-gift-fund .pv-h3 { margin-bottom: .5rem; }
.pv-gift-lede { margin-bottom: 0; }

/* --- the way down to the cards -------------------------------------------
   A drawn line fading into a chevron. Decorative only, so it is hidden from
   screen readers and it holds still for anyone who asks for less motion. */
.pv-arrow {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 1.8rem 0 .4rem;
}

.pv-arrow-line {
  width: 1px;
  height: 3.2rem;
  background: linear-gradient(to bottom, transparent, var(--blue-strong));
}

.pv-arrow-chev {
  display: block;
  width: 1.15rem;
  height: 1.15rem;
  margin-top: .2rem;
  background-color: var(--accent);
  -webkit-mask: var(--chev) no-repeat center / contain;
  mask: var(--chev) no-repeat center / contain;
  animation: pv-nudge 2.4s ease-in-out infinite;
}

@keyframes pv-nudge {
  0%, 100% { transform: translateY(0); opacity: .75; }
  50%      { transform: translateY(4px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .pv-arrow-chev { animation: none; }
}

.pv-gift-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  /* when the pair stacks into one column they become separate rows and would
     size independently; this keeps every row, and so every card, the same */
  grid-auto-rows: 1fr;
  gap: 1.3rem;
  margin: 1.2rem auto 0;
  /* two cards at a civil size, centred, rather than stretched to the section */
  max-width: 39rem;
}

.pv-gift-card {
  aspect-ratio: 1 / 1;      /* square, always */
  perspective: 1400px;
}

.pv-gift-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform .65s cubic-bezier(.2, .7, .2, 1);
}

.pv-gift-card.is-flipped .pv-gift-inner { transform: rotateY(180deg); }
/* the second card turns the other way, so the pair never faces the reader
   at the same moment */
.pv-gift-card.flip-reverse.is-flipped .pv-gift-inner { transform: rotateY(-180deg); }

@media (prefers-reduced-motion: reduce) {
  .pv-gift-inner { transition: none; }
}

.pv-face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  background: rgba(255, 255, 255, .9);
  border: 1px solid var(--blue-strong);
  border-radius: var(--radius);
  padding: 1.1rem;
  text-align: left;
  overflow: hidden;        /* nothing escapes the card */
}

.pv-face-front {
  align-items: center;
  justify-content: center;
  color: var(--ink);
  font-family: var(--sans);
  cursor: pointer;
  padding: 0;
  border-color: transparent;   /* the artwork draws its own frame */
}
/* The artwork is left exactly as it is on hover: no tint, no coloured border.
   Written with :not(:disabled) to match the specificity of the global
   button:hover rule, which otherwise paints every button gold. */
.pv-face-front:hover:not(:disabled),
.pv-face-front:focus:not(:disabled) {
  background-color: transparent;
  border-color: transparent;
}



.pv-face-back {
  transform: rotateY(180deg);
  /* top, not centre: the panel is scaled from its top edge, and a centred
     panel that overflows is already sticking out above before it shrinks */
  justify-content: flex-start;
}

/* scaled by preview.js only when the content would not otherwise fit the
   square, so the card keeps its shape and nothing is cropped */
.pv-account-inner {
  width: 100%;
  /* flex, so the children's margins count towards its height instead of
     collapsing out of the measurement */
  display: flex;
  flex-direction: column;
  transform-origin: top center;
}
.pv-gift-card.flip-reverse .pv-face-back { transform: rotateY(-180deg); }

.pv-account-only {
  margin: 0 0 .7rem;
  padding: .4rem .55rem;
  background: var(--blue);
  border: 1px solid var(--blue-mid);
  border-radius: var(--radius);
  font-size: .8rem;
  font-weight: 600;
  line-height: 1.35;
}

.pv-account-list { margin: 0; display: block; }
.pv-account-list dt {
  font-size: .72rem;
  color: var(--muted);
  letter-spacing: .03em;
}
.pv-account-list dd {
  margin: 0 0 .5rem;
  min-width: 0;            /* lets the value shrink instead of pushing out */
}
.pv-account-list dd:last-of-type { margin-bottom: 0; }

.pv-account-value {
  display: block;
  /* the site's own face, not a monospace one. tabular-nums keeps the digits
     on an even width so the numbers still line up and read cleanly */
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
  line-height: 1.4;
  overflow: hidden;        /* the fitter measures against this */
}

.pv-account-todo { margin: auto 0; color: var(--muted); font-size: .9rem; }

.pv-face-close {
  margin-top: .8rem;
  align-self: flex-start;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--blue-strong);
  font-size: .78rem;
  padding: .3rem .7rem;
}
.pv-face-close:hover:not(:disabled) { background: var(--blue); color: var(--ink); }

}


/* --- a slight lift on the cards ------------------------------------------
   Only things that read as a card. Inputs, chips, pills and table cells keep
   their flat borders, or the whole page starts to float. */
.pv-day-card,
.pv-gift-card .pv-face,
.pv-card,
.card,
.pv-account-only {
  box-shadow: var(--shadow);
}

/* the flip needs the shadow on the faces, not the shell, or it shows through
   the back of the card mid-turn */
.pv-gift-card { box-shadow: none; }


/* --- currency cards -------------------------------------------------------
   The front of each card is the artwork itself, which carries its own border
   and wording. Card and image are both square, so cover fits exactly and
   nothing is stretched. No wash over it: that existed to keep overlaid text
   readable, and the text is part of the picture now. */
/* the plate colours John specified, as components so the fade can be built
   from them without interpolating through grey */
.pv-gift-card[data-account="gbp"] { --plate-rgb: 208, 226, 242; }
.pv-gift-card[data-account="eur"] { --plate-rgb: 248, 245, 234; }

.pv-gift-card[data-account="gbp"] .pv-face-front {
  background-image: url('/gifts/card-pounds.webp');
  background-size: cover;
  background-position: center;
}

.pv-gift-card[data-account="eur"] .pv-face-front {
  background-image: url('/gifts/card-euros.webp');
  background-size: cover;
  background-position: center;
}

/* the hover tint would wash the pattern out */


/* --- desktop type ---------------------------------------------------------
   The whole site was set at phone sizes and left there, so on a large screen
   it read small and thin. Scaled up above 62rem only: phones keep what they
   had, since nothing there was too small. */
@media (min-width: 62rem) {
  .pv-lede            { font-size: 1.15rem; line-height: 1.8; }
  .pv-gift-intro p,
  .pv-gift-lede       { font-size: 1.15rem; line-height: 1.85; }
  .pv-card-title      { font-size: 1.3rem; }

  .pv-day-time        { font-size: .98rem; }
  /* sized so the longest title in any of the three languages stays on one
     line: a wrap there makes the whole timeline taller in some languages
     and not others, and the map can no longer match it */
  .pv-day-title       { font-size: 1.2rem; }
  .pv-day-detail      { font-size: 1rem; }
  .pv-day-detail .pv-maplink { font-size: 1rem; }

  .pv-faq-q           { font-size: 1.1rem; }
  .pv-faq-a           { font-size: 1.04rem; }

  .pv-account-list dt { font-size: .82rem; }
  .pv-account-only    { font-size: .92rem; }
  .pv-gift-plate      { font-size: 1.8rem; }

  .pv-navlink         { font-size: 1rem; }
  .pv-foot p          { font-size: .88rem; }
}

/* --- language dropdown, ours rather than the browser's --------------------
   A button and a list. Styled to match the site, and it keeps real focus
   behaviour: arrow into it, Escape out of it, click away to dismiss. */
.pv-lang { position: relative; flex: 0 0 auto; }

.pv-lang-button {
  display: flex;
  align-items: center;
  gap: .4rem;
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .32rem .55rem;
  font-family: var(--sans);
  font-size: .85rem;
  min-height: 0;
  cursor: pointer;
}
.pv-lang-button:hover:not(:disabled) {
  background: var(--blue);
  border-color: var(--blue-strong);
  color: var(--ink);
}
.pv-lang.is-open .pv-lang-button { border-color: var(--accent); background: var(--blue); }

.pv-lang-caret {
  width: .62rem;
  height: .62rem;
  background-color: var(--muted);
  -webkit-mask: var(--chev) no-repeat center / contain;
  mask: var(--chev) no-repeat center / contain;
  transition: transform .16s ease;
}
.pv-lang.is-open .pv-lang-caret { transform: rotate(180deg); }

.pv-lang-list {
  position: absolute;
  top: calc(100% + .3rem);
  right: 0;
  z-index: 60;
  margin: 0;
  padding: .25rem;
  list-style: none;
  min-width: 100%;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.pv-lang-option {
  display: block;
  width: 100%;
  text-align: left;
  white-space: nowrap;
  background: transparent;
  color: var(--ink);
  border: 0;
  border-radius: var(--radius);
  padding: .4rem .7rem;
  font-family: var(--sans);
  font-size: .88rem;
  min-height: 0;
  cursor: pointer;
}
.pv-lang-option:hover:not(:disabled) { background: var(--blue); color: var(--ink); }
.pv-lang-option[aria-selected="true"] { color: var(--accent); }

/* one focus ring, not two: the browser's default outline was drawing a blue
   box inside our gold one */
.pv-lang-button:focus,
.pv-lang-option:focus { outline: none; }
.pv-lang-button:focus-visible,
.pv-lang-option:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

@media (prefers-reduced-motion: reduce) {
  .pv-lang-caret { transition: none; }
}

/* --- header without the brand block ---------------------------------------
   Nav hard left, language hard right, both centred on the bar. The margin
   auto on the nav is what pushes the language over, so the two ends stay
   pinned however wide the screen is. */
.pv-head-inner { align-items: center; }
.pv-head-inner .pv-nav { margin-right: auto; }

@media (max-width: 52rem) {
  /* on a phone the menu button takes the left, language the right */
  .pv-burger { margin-right: auto; order: 1; }
  .pv-head-inner .pv-lang { order: 2; }
  .pv-head-inner .pv-nav { order: 3; margin-right: 0; }
}

/* the cards are held back until their patterns have decoded, so the section
   does not assemble itself in front of the reader */
.pv-gift-cards.is-loading { opacity: 0; }
.pv-gift-cards {
  opacity: 1;
  transition: opacity .25s ease;
}
@media (prefers-reduced-motion: reduce) {
  .pv-gift-cards { transition: none; }
}

/* --- moving between tabs --------------------------------------------------
   Out to the paper and back in, rather than a hard cut. The section is only
   ever faded, never moved, so nothing shifts on the way through. */
.pv-main {
  transition: opacity 220ms ease;
}
.pv-main.is-leaving { opacity: 0; }

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

/* --- the day: icon medallions --------------------------------------------
   One glyph per kind of moment, drawn the same way as the pin and the globe:
   a mask filled with currentColor, so they take the gold from the medallion
   and need no separate image request. */
:root {
  --ic-church: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M12%201.5%2013%203v2h2v2h-2v2.6l6%203.4v9.5h-6v-4a1%201%200%2000-2%200v4H5v-9.5l6-3.4V7H9V5h2V3zM7%2014.7V20h2.2v-2.6a2.8%202.8%200%20015.6%200V20H17v-5.3l-5-2.8z'/%3E%3C/svg%3E");
  --ic-bus: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M4%2016V6a3%203%200%20013-3h10a3%203%200%20013%203v10a2%202%200%2001-1%201.7V19a1%201%200%2001-1%201h-1a1%201%200%2001-1-1v-1H8v1a1%201%200%2001-1%201H6a1%201%200%2001-1-1v-1.3A2%202%200%20014%2016zm2-9v4h5V7zm7%200v4h5V7zM7.5%2013a1.2%201.2%200%20100%202.5%201.2%201.2%200%20000-2.5zm9%200a1.2%201.2%200%20100%202.5%201.2%201.2%200%20000-2.5z'/%3E%3C/svg%3E");
  --ic-glass: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M5%202h5l-.6%208.2a3.5%203.5%200%2001-2.4%203.2V20h2.5v2H4.5v-2H7v-6.6a3.5%203.5%200%2001-2.4-3.2zm9%200h5l.6%208.2a3.5%203.5%200%2001-2.4%203.2V20h2.3v2h-6.4v-2H15v-6.6a3.5%203.5%200%2001-2.4-3.2z'/%3E%3C/svg%3E");
  --ic-dinner: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M8.1%2013.34l2.83-2.83L3.91%203.5a4%204%200%2000%200%205.66l4.19%204.18zm6.78-1.81c1.53.71%203.68.21%205.27-1.38%201.91-1.91%202.28-4.65.81-6.12-1.46-1.46-4.2-1.1-6.12.81-1.59%201.59-2.09%203.74-1.38%205.27L3.7%2019.87l1.41%201.41L12%2014.41l6.88%206.88%201.41-1.41L13.41%2013l1.47-1.47z'/%3E%3C/svg%3E");
  --ic-night: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M12.7%202a8%208%200%20009.1%2011.2A9%209%200%201112.7%202zm-1.8%202.3a7%207%200%20108.3%209.4A10%2010%200%200110.9%204.3z'/%3E%3C/svg%3E");
}

/* ==========================================================================
   The day: timeline beside an illustrated map
   Desktop is two columns, the list on the left and the map on the right.
   A phone gets one column and the tall map, chosen by <picture> so only the
   one that will be shown is ever downloaded.
   ========================================================================== */

.pv-day { max-width: 84rem; }

/* centred, the same as the heading on every other tab */
.pv-day-head { margin-bottom: 1.4rem; }
.pv-day-head .pv-h2 { margin-bottom: .3rem; }
.pv-day-head .pv-lede { margin: 0 auto; }

/* --- the two panes -------------------------------------------------------- */
.pv-day-body {
  display: grid;
  grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
  gap: 1.4rem;
  /* stretch, not start: both columns take the height of the taller one, which
     is what lets the map match the timeline beside it */
  align-items: stretch;
  text-align: left;
}

/* --- the timeline --------------------------------------------------------- */
.pv-day-grid {
  --gap: .7rem;
  --card-border: 1px;
  --medal: 2.6rem;
  /* How far the medallion's centre sits from the card's left edge. Zero on a
     wide screen, where it straddles the edge as in the reference. Inside the
     card on a phone, so the cards run the full width and line up with the map
     above them. Everything else is derived from it. */
  --medal-offset: 0rem;

  display: grid;
  grid-template-columns: 1fr;
  grid-auto-rows: 1fr;        /* every card the same height */
  gap: var(--gap);
  /* fill the row: when the map is the taller of the two, the six cards share
     its height between them and the columns come out level */
  height: 100%;
  /* only as much room as the medallion actually needs outside the card */
  padding-left: max(0px, calc(var(--medal) / 2 - var(--medal-offset)));
}

.pv-day-card {
  position: relative;
  display: flex;
  align-items: center;        /* content centred in the equalised height */
  height: 100%;
  padding: .8rem 1rem .8rem calc(var(--medal-offset) + var(--medal) / 2 + 1rem);
  background: rgba(255, 255, 255, .86);
  border: var(--card-border) solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}



.pv-day-icon {
  position: absolute;
  /* offset by the border too: an absolute left resolves against the padding
     box, which sits inside the card's 1px border, so without this the
     medallion sits a pixel off the thread */
  left: calc(var(--medal-offset) - var(--medal) / 2 - var(--card-border));
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
  width: var(--medal);
  height: var(--medal);
  border-radius: 50%;
  background: #faf6ea;
  border: 1px solid rgba(143, 114, 32, .38);
  box-shadow: var(--shadow);
  display: grid;
  place-items: center;
}
.pv-day-icon::before {
  content: "";
  width: 55%;
  height: 55%;
  background-color: var(--accent);
  -webkit-mask: var(--ic) no-repeat center / contain;
  mask: var(--ic) no-repeat center / contain;
}
.pv-day-icon[data-icon="church"] { --ic: var(--ic-church); }
.pv-day-icon[data-icon="bus"]    { --ic: var(--ic-bus); }
.pv-day-icon[data-icon="glass"]  { --ic: var(--ic-glass); }
.pv-day-icon[data-icon="dinner"] { --ic: var(--ic-dinner); }
.pv-day-icon[data-icon="night"]  { --ic: var(--ic-night); }

.pv-day-text { min-width: 0; flex: 1 1 auto; }
.pv-day-time {
  margin: 0;
  font-size: .82rem;
  letter-spacing: .03em;
  color: var(--accent);
}
.pv-day-title {
  margin: .12rem 0 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.12rem;
  line-height: 1.25;
  color: var(--ink);
}
.pv-day-detail {
  margin: .1rem 0 0;
  font-size: .85rem;
  line-height: 1.5;
  color: var(--muted);
}
.pv-day-detail:empty { display: none; }
.pv-day-detail .pv-maplink { font-size: .85rem; margin-top: 0; }

/* --- the map -------------------------------------------------------------- */
.pv-map-pane {
  position: relative;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;   /* centred in its column */
}

.pv-map-img {
  display: block;
  /* Scaled to the height of the timeline beside it, keeping its own
     proportions, so the whole illustration is visible. The width follows from
     the height; it is not cropped and it is not stretched. */
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  border-radius: var(--radius);
  /* Soft edges as in the reference: the illustration dissolves into the paper
     on all four sides. Two crossed linear gradients rather than one radial,
     which would round the corners off and turn the map into an oval. */
  -webkit-mask:
    linear-gradient(to right, transparent 0, #000 7%, #000 93%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask:
    linear-gradient(to right, transparent 0, #000 7%, #000 93%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-composite: intersect;
}


.pv-signoff {
  margin: 1.5rem 0 0;
  text-align: center;
  font-size: .95rem;
  color: var(--muted);
}

/* --- one column ----------------------------------------------------------- */
@media (max-width: 86rem) {
  .pv-day-body { grid-template-columns: minmax(0, 1fr); }
  /* stacked, so the map goes back to its own proportions */
  .pv-map-pane { height: auto; }
  .pv-map-img { height: auto; object-fit: fill; }
  /* map first on a phone: it answers "where" before the list answers "when" */
  .pv-map-pane { order: -1; }
}

@media (max-width: 34rem) {
  /* medallion inside the card, so the cards are full width and their left
     edge matches the map above */
  .pv-day-grid {
    --medal: 2.15rem;
    --gap: .6rem;
    --medal-offset: calc(var(--medal) / 2 + .5rem);
  }
}

/* --- welcome: centred in the viewport -------------------------------------
   Only the welcome page, which is short enough to sit in the middle of the
   screen rather than hanging from the top. The other tabs stay top aligned.

   Auto margins rather than justify-content: centre. With justify-content, a
   page taller than the viewport is centred by overflowing equally in both
   directions and the top becomes unreachable; auto margins collapse to zero
   in that case and the content simply starts at the top. */
.pv-main:has(.pv-home) {
  display: flex;
  flex-direction: column;
  /* equal top and bottom, or the deeper bottom padding pushes the centred
     block above the middle of the screen */
  padding-bottom: var(--main-pad);
}
.pv-main:has(.pv-home) .pv-home {
  margin-block: auto;
}

/* the closing line on the gifts page, set like the rest of that page's prose */
.pv-gift-close {
  max-width: 34rem;
  margin: 1.9rem auto 0;
  font-size: 1.02rem;
  line-height: 1.8;
  color: var(--ink);
}
@media (min-width: 62rem) {
  .pv-gift-close { font-size: 1.15rem; line-height: 1.85; }
}
