/* ===================================================================
   Ruyi NQC — wedding invitation quote configurator
   Loads after studio.css and reuses its :root tokens, .wrap, .btn, .field,
   .form, .section__title, .eyebrow, .theme-toggle and .hp — nothing here
   redefines them. This file only adds the configurator's own components.
   =================================================================== */

/* The four colours this page needs that studio.css has no use for. Declared
   here rather than in the shared palette so the base stylesheet does not
   carry one page's fixed bar around; the light-theme half mirrors the block
   in studio.css and is keyed the same way. */
:root {
  --bar-bg:    rgba(28, 23, 21, .92);      /* the fixed mobile total bar */
  --track:     var(--ink-2);               /* the mobile progress bar's groove */
  --rose-wash: rgba(232, 180, 184, .11);   /* the tint inside a selected card */
  --bad-wash:  rgba(201, 139, 135, .08);   /* the tint behind a blocking notice */
}
html[data-theme="light"] {
  --bar-bg:    rgba(255, 255, 255, .94);
  /* --ink-2 is white here, which on the near-white page would leave the
     groove invisible; the hairline colour is the one that still reads. */
  --track:     var(--ink-3);
  --rose-wash: rgba(160, 80, 76, .09);
  --bad-wash:  rgba(130, 57, 58, .07);
}

/* ==================================================================
   TOP BAR + INTRO
   ================================================================== */
.qtop {
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--ink-3);
}
.qtop__row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.qtop__brand {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  text-decoration: none;
  color: var(--cream);
  font-size: .85rem;
  letter-spacing: .12em;
  text-transform: uppercase;
}
.qtop__mark { width: 1.6rem; aspect-ratio: 1; color: var(--rose); }
.qtop__tools { display: flex; align-items: center; gap: clamp(.75rem, 2.5vw, 1.1rem); }
.qtop__back {
  color: var(--cream-dim);
  text-decoration: none;
  font-size: .82rem;
  letter-spacing: .04em;
}
.qtop__back:hover { color: var(--rose); }

.qintro {
  padding: clamp(2.5rem, 7vw, 4rem) 0 clamp(1.5rem, 4vw, 2rem);
}
.qintro .section__title { font-size: clamp(1.9rem, 5.5vw, 3.1rem); }
.qintro .section__lede { margin-top: 1rem; }
.qintro em { font-style: italic; font-weight: 400; color: var(--rose); }

/* ==================================================================
   LAYOUT — form column + step rail/price sidebar

   This 70rem breakpoint and the mobile total bar's 70rem breakpoint below
   have to change together — a mismatch would leave a band of viewport
   widths showing neither the sidebar nor the fixed bar.
   ================================================================== */
#stepAnchor { display: block; scroll-margin-top: 1rem; }

.qlayout {
  display: grid;
  gap: clamp(1.5rem, 4vw, 2rem);
  padding-bottom: clamp(4rem, 10vw, 6rem);
}
.rail { display: none; }
.qform-col { min-width: 0; display: grid; gap: clamp(1.5rem, 4vw, 2rem); }
.qsummary-col { min-width: 0; }
.qsummary-col[hidden] { display: none; }

@media (min-width: 70rem) {
  /* Form on the left takes the flexible width; the rail and price panel
     share a single sticky column on the right, stacked as one unit so
     they scroll together instead of needing two separate sticky offsets. */
  .qlayout { grid-template-columns: minmax(0, 1fr) 20rem; align-items: start; }
  .rail { display: grid; gap: clamp(1.5rem, 4vw, 2rem); }
  .qsummary-col { display: grid; gap: clamp(1.5rem, 4vw, 2rem); position: sticky; top: 1.5rem; }

  /* Before a direction is chosen there is no flow to step through and no
     price to total, so the sidebar is withheld rather than shown empty —
     the form column takes the full width until then. */
  .qlayout--intro { grid-template-columns: minmax(0, 1fr); }
}

/* ==================================================================
   STEP RAIL (desktop) — jumps to any step; current/done states carry the
   only meaning, so there is no separate "step N of 7" caption to keep
   in sync with it.
   ================================================================== */
.rail__list {
  margin: 0;
  padding: clamp(1.25rem, 3vw, 1.75rem);
  border: 1px solid var(--ink-3);
  border-radius: 1.25rem;
  background: var(--ink-2);
  list-style: none;
  display: grid;
  gap: .3rem;
}
.rail__btn {
  width: 100%;
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .55rem .6rem;
  border: none;
  border-radius: .6rem;
  background: transparent;
  color: var(--muted);
  font: 500 .82rem/1.3 var(--sans);
  text-align: left;
  cursor: pointer;
  transition: background-color .2s var(--ease), color .2s var(--ease);
}
.rail__btn:hover { background: var(--ink); color: var(--cream-dim); }
.rail__num {
  flex: none;
  width: 1.6rem; height: 1.6rem;
  display: grid; place-items: center;
  border: 1px solid var(--ink-3);
  border-radius: 50%;
  font-family: var(--serif);
  font-size: .85rem;
  color: var(--muted);
}
.rail__step.is-done .rail__num { border-color: var(--rose-deep); color: var(--rose-deep); }
.rail__step.is-current .rail__btn { background: var(--ink); color: var(--cream); }
.rail__step.is-current .rail__num { border-color: var(--rose); background: var(--rose); color: var(--ink); }

/* ==================================================================
   STEP PROGRESS (mobile) — the rail's small-screen twin: informational,
   not a set of tap targets, since seven of those would not fit a thin bar.
   ================================================================== */
.step-progress { margin-bottom: 1.25rem; }
.step-progress[hidden] { display: none; }
@media (min-width: 70rem) { .step-progress { display: none; } }
.step-progress__label {
  margin: 0 0 .55rem;
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.step-progress__bar { height: .3rem; border-radius: 999px; background: var(--track); overflow: hidden; }
.step-progress__fill { height: 100%; background: var(--rose); border-radius: inherit; transition: width .3s var(--ease); }

/* ==================================================================
   STEP WRAPPERS — one visible at a time; a thin header carries the way
   back, since the rail (hidden below 70rem) is the only other route.
   ================================================================== */
.step {
  display: grid;
  gap: clamp(1.5rem, 4vw, 2rem);
  /* A grid item refuses to shrink below its content unless told to, and
     the day map's row of stops is wider than a phone the moment there
     are three of them. The step and the section inside it both carry
     this: without it the whole form column stretches to fit the map,
     and the map is the one thing on this page allowed to scroll
     sideways — inside itself. */
  min-width: 0;
}
.step[hidden] { display: none; }
.step__head { margin-bottom: -.5rem; }
.step__back {
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  font: 500 .8rem/1 var(--sans);
  letter-spacing: .04em;
  cursor: pointer;
}
.step__back:hover { color: var(--rose); }

/* ==================================================================
   FORM SECTIONS
   ================================================================== */
.qsection {
  min-width: 0;   /* see .step — the day map scrolls, the column does not */
  padding: clamp(1.5rem, 4vw, 2.25rem);
  border: 1px solid var(--ink-3);
  border-radius: 1.25rem;
  background: var(--ink-2);
}
.qsection[hidden] { display: none; }
.qsection__title {
  margin: 0 0 .35rem;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.35rem, 3.2vw, 1.7rem);
}
.qsection__note {
  margin: 0 0 1.25rem;
  font-size: .88rem;
  color: var(--cream-dim);
}
.qsection .form { margin-top: 0; }
.qsection__actions {
  display: flex;
  flex-wrap: wrap-reverse;
  gap: .85rem;
  margin-top: 1.75rem;
}
.qsection__actions .btn--wide { flex: 1 1 14rem; }

/* A conditionally-revealed sub-block: nested one notch below the question
   that triggered it, so the extra fields read as "belonging to" the
   checkbox/radio above rather than as siblings of equal weight. */
.qsub {
  margin: .9rem 0 0 0;
  padding: 1rem 1.1rem;
  border-left: 2px solid var(--rose-deep);
  border-radius: 0 .6rem .6rem 0;
  background: var(--ink);
}
.qsub .form { gap: .9rem; }
.qsub:last-child { margin-bottom: 0; }

/* ==================================================================
   OPTION CARDS — packages, language
   ================================================================== */
.opt-grid { display: grid; gap: .85rem; margin-top: 1.25rem; }
@media (min-width: 34rem) { .opt-grid--packages { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 30rem) { .opt-grid--lang { grid-template-columns: repeat(3, 1fr); } }

.opt-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: .6rem;
  padding: 1.15rem 1.25rem;
  border: 1px solid var(--ink-3);
  border-radius: 1rem;
  background: var(--ink);
  cursor: pointer;
  transition: border-color .2s var(--ease), transform .2s var(--ease), background-color .2s var(--ease);
}
.opt-card:hover { border-color: var(--rose-deep); transform: translateY(-2px); }
.opt-card input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
}
.opt-card input:focus-visible ~ * { outline: 2px solid var(--rose); outline-offset: 3px; border-radius: 2px; }
.opt-card.is-selected {
  border-color: var(--rose);
  background: linear-gradient(180deg, var(--rose-wash), transparent 60%);
}
.opt-card.is-selected::after {
  content: '';
  position: absolute;
  top: 1rem; right: 1rem;
  width: .55rem; height: .55rem;
  border-radius: 50%;
  background: var(--rose);
}

.opt-card--sm { padding: .85rem 1rem; gap: .3rem; }
.opt-card--sm .opt-card__name { font-family: var(--sans); font-size: .92rem; font-weight: 500; }
.opt-card--sm .opt-card__price { font-size: .7rem; }

/* ------------------------------------------------------------------
   Package cards — Ready Design (a stack of templates + a CTA pill) and
   Made for You (a dashed "build your own" well). Both share the same
   title/price header; the selected-dot from .opt-card above would sit
   on top of the price box here, so it is switched off in favour of the
   border + wash already carried by .opt-card.is-selected. */
.opt-grid--packages .opt-card.is-selected::after { display: none; }

.opt-card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: .75rem;
  padding-bottom: .9rem;
  border-bottom: 1px solid var(--ink-3);
}
.opt-card__heading { display: grid; gap: .3rem; }
.opt-card__name { font-family: var(--serif); font-size: 1.2rem; }
.opt-card__sub { font-size: .82rem; color: var(--cream-dim); }
.opt-card__price-box { display: grid; justify-items: end; gap: .15rem; text-align: right; flex-shrink: 0; }
.opt-card__price-note { font-size: .66rem; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
.opt-card__price { font-family: var(--serif); font-size: 1.3rem; color: var(--gold); white-space: nowrap; }

/* Ready Design: two fanned template previews. The card is full-width until
   a package is chosen (the sidebar is withheld until then), so the previews
   and the well opposite them are sized against the viewport rather than
   pinned — at the narrow end they hold the 6rem they were drawn at. */
.opt-card__stack {
  position: relative;
  flex: 1 1 auto;
  min-height: clamp(9.5rem, 15vw, 12.5rem);
  display: flex;
  align-items: center;
  justify-content: center;
}
.opt-card__stack-img {
  position: absolute;
  display: block;
  width: clamp(6rem, 9vw, 8rem);
  aspect-ratio: 2 / 3;
  border-radius: .6rem;
  overflow: hidden;
  border: 1px solid var(--ink-3);
  box-shadow: 0 .6rem 1.6rem rgba(0, 0, 0, .35);
  transition: transform .25s var(--ease);
}
.opt-card__stack-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.opt-card__stack-img--back { z-index: 1; transform: translateX(-2rem) rotate(-6deg); filter: brightness(.82); }
.opt-card__stack-img--front { z-index: 2; transform: translateX(2rem) rotate(5deg); }
.opt-card--ready:hover .opt-card__stack-img--back,
.opt-card--ready.is-selected .opt-card__stack-img--back { transform: translateX(-2.4rem) rotate(-9deg); }
.opt-card--ready:hover .opt-card__stack-img--front,
.opt-card--ready.is-selected .opt-card__stack-img--front { transform: translateX(2.4rem) rotate(8deg); }

.opt-card__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  padding: .75rem 1.25rem;
  border: 1px solid var(--ink-3);
  border-radius: 999px;
  font-size: .85rem;
  letter-spacing: .02em;
  color: var(--cream);
  text-align: center;
  transition: border-color .2s var(--ease), background-color .2s var(--ease), color .2s var(--ease);
}
.opt-card--ready:hover .opt-card__cta { border-color: var(--rose-deep); }
.opt-card--ready.is-selected .opt-card__cta { border-color: var(--rose); background: var(--rose); color: var(--ink); }

/* Made for You: a dashed well that invites a custom brief, rather than
   picking from a gallery. */
.opt-card__custom-box {
  flex: 1 1 auto;
  min-height: clamp(9.5rem, 15vw, 12.5rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  padding: 1.5rem;
  border: 1.5px dashed var(--ink-3);
  border-radius: .9rem;
  color: var(--cream-dim);
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.opt-card__plus { width: 1.6rem; height: 1.6rem; color: var(--rose-deep); transition: color .2s var(--ease); }
.opt-card__custom-label { font-size: .92rem; letter-spacing: .01em; }
.opt-card--custom:hover .opt-card__custom-box,
.opt-card--custom.is-selected .opt-card__custom-box { border-color: var(--rose); color: var(--cream); }
.opt-card--custom:hover .opt-card__plus,
.opt-card--custom.is-selected .opt-card__plus { color: var(--rose); }

/* ==================================================================
   TEMPLATE GALLERY — the two finished designs. Two cards, not nine, so
   each one is big enough to carry everything about that design: the
   design itself, its name, the code the studio and the couple quote at
   each other afterwards, what it feels like, and a link to a real
   invitation built on it. Nothing opens on click except the choice —
   a card that only explains itself once picked asks you to pick blind.

   The card is a <div>, not a <label>: the preview link has to sit
   outside the label, or clicking it would choose the template as well.
   ================================================================== */
.tpl-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}
.tpl-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: .9rem .9rem 1rem;
  border: 1px solid var(--ink-3);
  border-radius: .9rem;
  background: var(--ink);
  transition: border-color .2s var(--ease), transform .2s var(--ease), background-color .2s var(--ease);
}
.tpl-card:hover { border-color: var(--rose-deep); transform: translateY(-2px); }
/* The label is the whole card bar the preview link, so the click target is
   everything you would expect it to be. */
.tpl-card__pick {
  display: grid;
  gap: .7rem;
  align-content: start;
  cursor: pointer;
}
.tpl-card input {
  position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip-path: inset(50%);
}
.tpl-card input:focus-visible ~ * { outline: 2px solid var(--rose); outline-offset: 2px; border-radius: 2px; }
.tpl-card.is-selected { border-color: var(--rose); background: linear-gradient(180deg, var(--rose-wash), transparent 65%); }
.tpl-card.is-selected .tpl-card__name { color: var(--rose); }
.tpl-card.is-selected::after {
  content: '';
  position: absolute;
  top: 1.4rem; right: 1.4rem;
  width: .55rem; height: .55rem;
  border-radius: 50%;
  background: var(--rose);
}
.tpl-card__shot {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: .6rem;
  overflow: hidden;
  border: 1px solid var(--ink-3);
  background: var(--ink-2);
}
/* Both previews are portrait, but not to the same ratio — one is a card,
   one is a phone screen. Anchoring the crop to the top keeps each design's
   own opening (the monogram, the swans) in frame at any card width. */
.tpl-card__shot img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: top;
  display: block;
}
.tpl-card__meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
}
.tpl-card__name { font-family: var(--serif); font-size: 1.05rem; }
.tpl-card__code {
  flex: none;
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.tpl-card__blurb { font-size: .82rem; line-height: 1.55; color: var(--cream-dim); }

/* The pick button reads as an action rather than a caption, and says out
   loud which card is the chosen one. */
.tpl-card__choose {
  display: block;
  margin-top: .2rem;
  padding: .6rem .9rem;
  border: 1px solid var(--ink-3);
  border-radius: .55rem;
  text-align: center;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--cream-dim);
  transition: border-color .2s var(--ease), background-color .2s var(--ease), color .2s var(--ease);
}
.tpl-card:hover .tpl-card__choose { border-color: var(--rose-deep); color: var(--cream); }
.tpl-card.is-selected .tpl-card__choose {
  border-color: var(--rose);
  background: var(--rose);
  color: var(--ink);
}
.tpl-card.is-selected .tpl-card__choose::after { content: ' \2713'; }

/* Sits on the floor of the card so both cards' links line up however long
   the blurbs above them run. */
.tpl-card__preview {
  margin-top: auto;
  padding-top: .75rem;
  align-self: center;
  font-size: .76rem;
  letter-spacing: .04em;
  color: var(--rose);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.tpl-card__preview:hover { border-bottom-color: currentColor; }

/* ==================================================================
   THE PACKAGE, ONCE IT IS CHOSEN — what replaces the two big cards.
   The choice is made on the way in and does not need re-making, so it
   collapses to one line; Change is what puts the cards back.
   ================================================================== */
.pkg-chosen {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: .9rem 1.1rem;
  border: 1px solid var(--ink-3);
  border-radius: .9rem;
  background: var(--ink);
}
.pkg-chosen[hidden] { display: none; }
.pkg-chosen__body { display: grid; gap: .2rem; }
.pkg-chosen__eyebrow {
  font-size: .66rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.pkg-chosen__name { font-family: var(--serif); font-size: 1.15rem; color: var(--cream); }
.pkg-chosen__sub { font-size: .82rem; color: var(--cream-dim); }
.pkg-chosen__change {
  flex: none;
  padding: .55rem 1rem;
  border: 1px solid var(--ink-3);
  border-radius: 999px;
  background: none;
  font: inherit;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--cream-dim);
  cursor: pointer;
  transition: border-color .2s var(--ease), color .2s var(--ease);
}
.pkg-chosen__change:hover { border-color: var(--rose); color: var(--rose); }

/* ==================================================================
   DELIVERY PROMISE — what stands in for the ladder when the package
   ships on a fixed turnaround and there is nothing to choose.
   ================================================================== */
.promise {
  display: flex;
  align-items: flex-start;
  gap: .9rem;
  margin: 1.1rem 0 0;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--sky);
  border-radius: .8rem;
  background: var(--ink);
}
/* The delivery notice follows whichever of the two shapes is on screen —
   the promise or the ladder — so the gap belongs to the notice. */
#deliveryFeedback { margin-top: 1rem; }
.promise__icon { flex: none; width: 1.5rem; height: 1.5rem; color: var(--sky); }
.promise__title { margin: 0 0 .35rem; font-family: var(--serif); font-size: 1.05rem; color: var(--cream); }
.promise__note { margin: 0; font-size: .86rem; line-height: 1.6; color: var(--cream-dim); }

/* ==================================================================
   CHECK ROW — "add another language" etc.
   ================================================================== */
.check-row {
  display: flex;
  align-items: flex-start;
  gap: .7rem;
  margin-top: 1.1rem;
  font-size: .92rem;
  cursor: pointer;
}
.check-row input { accent-color: var(--rose); width: 1.05rem; height: 1.05rem; flex: none; margin-top: .15rem; }
.check-row__note { color: var(--cream-dim); font-size: .82rem; }

/* ==================================================================
   THE DAY — a repeatable list of moments (ceremony, reception, …); one
   row always exists, and a handle or the arrows reorder them, since the
   order the rows sit in is the order the invitation reads them. The map
   above the rows is that same order, read back.
   ================================================================== */
/* THE DAY MAP — the rows read back as a running order, sitting above the
   rows themselves. Horizontal and scrollable, so a five-stop day is a
   glance rather than a scroll; each stop is a button back to its row. */
.daymap {
  margin-top: 1.25rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--ink-3);
  border-radius: 1rem;
  background: var(--ink);
}
.daymap__title {
  margin: 0 0 .8rem;
  font-size: .7rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--cream-dim);
}
.daymap__track {
  display: flex;
  align-items: stretch;
  gap: .5rem;
  margin: 0;
  padding: 0 0 .25rem;
  list-style: none;
  overflow-x: auto;
  scrollbar-width: thin;
}
.daymap__track:empty { display: none; }
.daymap__stop { display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; }
/* The arrow between stops — drawn on the gap rather than as its own
   element, so the list stays exactly one item per moment. */
.daymap__stop + .daymap__stop::before {
  content: "\2192";
  color: var(--muted);
  font-size: .9rem;
  line-height: 1;
}
.daymap__btn {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .55rem .8rem;
  border: 1px solid var(--ink-3);
  border-radius: .7rem;
  background: var(--ink-2);
  color: var(--cream);
  font: inherit;
  text-align: start;
  cursor: pointer;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.daymap__btn:hover { border-color: var(--rose-deep); }
.daymap__num {
  flex: 0 0 auto;
  width: 1.5rem; height: 1.5rem;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--rose-wash);
  border: 1px solid var(--rose-deep);
  color: var(--rose);
  font-size: .72rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.daymap__body { display: grid; gap: .1rem; }
.daymap__what { font-size: .85rem; line-height: 1.3; white-space: nowrap; }
.daymap__where {
  font-size: .74rem;
  line-height: 1.3;
  color: var(--cream-dim);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* A moment still missing a field — the map doubles as the step's checklist. */
.daymap__stop.is-partial .daymap__btn { border-style: dashed; }
.daymap__stop.is-partial .daymap__num {
  background: var(--bad-wash);
  border-style: dashed;
}
.daymap__stop.is-partial .daymap__where { color: var(--rose); }
.daymap__empty { margin: 0; font-size: .82rem; color: var(--cream-dim); }
.daymap__empty[hidden] { display: none; }

.venues { display: grid; gap: 1rem; margin-top: 1.25rem; }
.venue {
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--ink-3);
  border-radius: 1rem;
  background: var(--ink);
}
.venue__head { display: flex; align-items: center; gap: .6rem; margin-bottom: .9rem; }
.venue__index {
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.venue__tools { display: flex; align-items: center; gap: .4rem; margin-inline-start: auto; }

/* The drag handle. Wide enough to be a comfortable finger target, and
   `touch-action: none` is what stops the browser scrolling the page out
   from under a drag that has already started. */
.venue__drag {
  flex: 0 0 auto;
  width: 1.9rem; height: 1.9rem;
  display: grid; place-items: center;
  border: 1px solid var(--ink-3);
  border-radius: .5rem;
  background: transparent;
  color: var(--muted);
  cursor: grab;
  touch-action: none;
}
.venue__drag svg { width: .6rem; height: 1rem; fill: currentColor; }
.venue__drag:hover:not(:disabled) { border-color: var(--rose-deep); color: var(--cream); }
.venue__drag:disabled { opacity: .3; cursor: default; }
.venue.is-dragging .venue__drag { cursor: grabbing; }

/* The row under the finger rides above its neighbours and drops its
   transition, so it tracks the pointer exactly; the rest slide into the
   space it leaves. */
.venue.is-dragging {
  position: relative;
  z-index: 2;
  border-color: var(--rose-deep);
  box-shadow: 0 1rem 2rem var(--art-shadow);
  transition: none;
}
.venues.is-reordering { user-select: none; }
.venues.is-reordering .venue:not(.is-dragging) { transition: transform .18s var(--ease); }

.venue__remove,
.venue__move {
  width: 1.6rem; height: 1.6rem;
  display: grid; place-items: center;
  border: none;
  border-radius: 50%;
  background: var(--ink-3);
  color: var(--cream-dim);
  cursor: pointer;
  font-size: .9rem;
  line-height: 1;
}
.venue__remove[hidden] { display: none; }
.venue__remove:hover,
.venue__move:hover:not(:disabled) { background: var(--rose-deep); color: var(--ink); }
/* The arrows stay in place at the ends of the list rather than vanishing,
   so the row's controls never shift under a finger mid-reorder. */
.venue__move:disabled { opacity: .3; cursor: default; }
.venues__add { margin-top: 1rem; padding: .7rem 1.2rem; font-size: .72rem; }
/* .qsection .form zeroes its own top margin, which would leave the timing
   note pinned to the "add another" button — this is the one form on the
   page that follows a control rather than a heading. */
.qsection .day__notes { margin-top: 1.75rem; }

/* ==================================================================
   CONSENT — the two checkboxes gating submission on the review step.
   ================================================================== */
.consent-list { display: grid; gap: .85rem; margin: 1.5rem 0; }
.consent-list .check-row { margin-top: 0; font-size: .86rem; color: var(--cream-dim); }

/* ==================================================================
   DELIVERY LADDER
   ================================================================== */
.ladder { display: grid; gap: .5rem; margin: 1.1rem 0 1.35rem; }
.ladder__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .65rem .9rem;
  border: 1px solid var(--ink-3);
  border-radius: .6rem;
  font-size: .85rem;
  color: var(--cream-dim);
  transition: border-color .2s var(--ease), color .2s var(--ease), background-color .2s var(--ease);
}
.ladder__row[data-tier="standard"] .ladder__fee { color: var(--sky); }
.ladder__fee { font-variant-numeric: tabular-nums; }
.ladder__row.is-active {
  border-color: var(--rose);
  background: var(--ink);
  color: var(--cream);
}
.ladder__row.is-active .ladder__fee { color: var(--rose); font-weight: 500; }
.ladder__row.is-blocked { border-color: var(--rose-deep); opacity: .55; }

/* ==================================================================
   NOTICES — inline validation / status copy
   ================================================================== */
/* studio.css writes hints in --muted, which is right for an aside. On this
   form a hint carries the reason a field is being asked for at all — the
   welcome message's "it is the first thing your guests read" is the whole
   argument for filling it in — so here it reads at secondary strength
   instead of tertiary. */
.qform .field__hint { color: var(--cream-dim); }

.notice {
  margin: 0;
  padding: .85rem 1rem;
  border-radius: .7rem;
  border: 1px solid var(--ink-3);
  background: var(--ink);
  font-size: .86rem;
  color: var(--cream-dim);
}
.notice[hidden] { display: none; }
.notice--info { border-color: var(--sky); color: var(--cream); }
.notice--bad { border-color: var(--rose-deep); color: var(--rose); background: var(--bad-wash); }
.notice--good { border-color: var(--sky); color: var(--cream); }

/* The banner a step raises when Continue is pressed with something still
   missing. It sits directly above that button, because the sidebar status
   line it used to be said in alone is a column away on desktop and off
   the bottom of the page on a phone — the message was there and nobody
   was ever looking at it. Full-strength text, a solid left rule and a
   flash on arrival, so it cannot be mistaken for more helper copy. */
.step-alert {
  margin: 1.75rem 0 0;
  border-inline-start: 3px solid var(--rose-deep);
  color: var(--cream);
  font-size: .9rem;
  animation: step-alert-in .28s var(--ease) both;
}
.step-alert::before {
  content: "!";
  display: inline-grid;
  place-items: center;
  width: 1.15rem; height: 1.15rem;
  margin-inline-end: .55rem;
  vertical-align: -.2rem;
  border-radius: 50%;
  background: var(--rose-deep);
  color: var(--ink);
  font-size: .74rem;
  font-weight: 600;
}
@keyframes step-alert-in {
  from { opacity: 0; transform: translateY(-.35rem); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .step-alert { animation: none; }
}

/* The same message again, on the control it is actually about. This is
   the one a couple reads without being told to look anywhere — it lands
   in the field's own grid, right under the input, in the page's primary
   text colour rather than the dimmed one helper copy uses. */
.field__in.is-invalid,
.field__in.is-invalid:focus {
  border-color: var(--rose-deep);
  background: var(--bad-wash);
}

/* ==================================================================
   ADD-ON ROWS
   ================================================================== */
.addon-list { display: grid; gap: .6rem; margin-top: 1rem; }
.addon-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: .9rem;
  padding: .9rem 1.1rem;
  border: 1px solid var(--ink-3);
  border-radius: .8rem;
  background: var(--ink);
  cursor: pointer;
  transition: border-color .2s var(--ease), background-color .2s var(--ease);
}
.addon-row:hover { border-color: var(--rose-deep); }
.addon-row:has(input:checked), .addon-row.is-checked { border-color: var(--rose); background: var(--ink-2); }
.addon-row input { accent-color: var(--rose); width: 1.15rem; height: 1.15rem; }
.addon-row__main { display: grid; gap: .2rem; }
.addon-row__name { font-size: .95rem; }
.addon-row__note { font-size: .8rem; color: var(--muted); }
.addon-row__price { font-size: .85rem; color: var(--gold); white-space: nowrap; }

/* ==================================================================
   UPLOADS
   ================================================================== */
.upload { margin-top: 1rem; }
.upload__btn { padding: .75rem 1.3rem; }
.upload__hint { margin: .5rem 0 0; font-size: .78rem; color: var(--cream-dim); }
.upload__list {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin: .9rem 0 0;
  padding: 0;
  list-style: none;
}
.upload__item {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .4rem .5rem .4rem .4rem;
  border: 1px solid var(--ink-3);
  border-radius: .6rem;
  background: var(--ink);
  font-size: .78rem;
  color: var(--cream-dim);
  max-width: 12rem;
}
.upload__item__thumb {
  width: 1.9rem; height: 1.9rem;
  border-radius: .35rem;
  object-fit: cover;
  flex: none;
  background: var(--ink-3);
}
.upload__item__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upload__item__remove {
  flex: none;
  width: 1.2rem; height: 1.2rem;
  display: grid; place-items: center;
  border: none;
  border-radius: 50%;
  background: var(--ink-3);
  color: var(--cream-dim);
  cursor: pointer;
  font-size: .8rem;
  line-height: 1;
}
.upload__item__remove:hover { background: var(--rose-deep); color: var(--ink); }

/* ==================================================================
   PRICE PANEL — the live summary
   ================================================================== */
.price-panel {
  padding: clamp(1.25rem, 3vw, 1.75rem);
  border: 1px solid var(--ink-3);
  border-radius: 1.25rem;
  background: var(--ink-2);
}
/* The sidebar panel gives way to the mobile-total-bar's own expandable
   breakdown below — that one reuses these child classes directly, without
   this wrapper, so hiding it here can't touch that copy. */
@media (max-width: 69.99rem) { .price-panel { display: none; } }
.price-panel__title {
  margin: 0 0 1rem;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.1rem;
  color: var(--cream);
}
.price-panel__lines { display: grid; gap: .65rem; margin: 0 0 1rem; padding: 0; list-style: none; }
.price-panel__line { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; font-size: .88rem; }
.price-panel__line-label { color: var(--cream-dim); }
.price-panel__line-amount { color: var(--cream); font-variant-numeric: tabular-nums; white-space: nowrap; }
.price-panel__lines:empty::before {
  content: 'Choose a package to begin.';
  display: block;
  font-size: .85rem;
  color: var(--muted);
  font-style: italic;
}
.price-panel__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--ink-3);
  font-family: var(--serif);
  font-size: 1.5rem;
}
.price-panel__total span:last-child { color: var(--gold); }
.price-panel__egp { margin: .4rem 0 1.25rem; font-size: .82rem; color: var(--muted); }
.price-panel__note { margin: .85rem 0 0; font-size: .76rem; line-height: 1.5; color: var(--muted); }
.price-panel #formStatus { margin-top: .85rem; }
.price-panel #formStatus:empty { display: none; }

/* ==================================================================
   MOBILE FIXED TOTAL BAR
   ================================================================== */
.mobile-total-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: .9rem;
  padding: .8rem var(--gut);
  border-top: 1px solid var(--ink-3);
  background: var(--bar-bg);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.mobile-total-bar__toggle {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.mobile-total-bar__label { font-size: .74rem; color: var(--cream-dim); }
.mobile-total-bar__chevron { color: var(--cream-dim); flex: none; transition: transform .2s var(--ease); }
.mobile-total-bar__toggle[aria-expanded="true"] .mobile-total-bar__chevron { transform: rotate(180deg); }
.mobile-total-bar__amount { margin-right: auto; font-family: var(--serif); font-size: 1.15rem; color: var(--gold); }
.mobile-total-bar__link {
  padding: .5rem 1rem;
  border: none;
  border-radius: 999px;
  background: var(--cream);
  color: var(--ink);
  font: 500 .74rem/1 var(--sans);
  letter-spacing: .08em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}
.mobile-total-bar[hidden] { display: none; }
@media (min-width: 70rem) { .mobile-total-bar { display: none; } }
@media (max-width: 69.99rem) { main { padding-bottom: 4.75rem; } }

/* Expands upward from the fixed bar itself — a lightweight bottom sheet
   rather than a second fixed element, so there is no bar height to keep
   in sync separately. */
.mobile-breakdown {
  position: absolute;
  left: 0; right: 0;
  bottom: 100%;
  max-height: min(65vh, 28rem);
  overflow-y: auto;
  padding: 1.25rem var(--gut);
  border-top: 1px solid var(--ink-3);
  background: var(--ink-2);
}
.mobile-breakdown[hidden] { display: none; }

/* ==================================================================
   REVIEW STEP
   ================================================================== */
.qsection--review { scroll-margin-top: 1.5rem; }
.review-grid {
  display: grid;
  gap: 1rem;
  margin-top: 1.25rem;
}
@media (min-width: 40rem) { .review-grid { grid-template-columns: 1fr 1fr; } }
.review-card {
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--ink-3);
  border-radius: .9rem;
  background: var(--ink);
}
.review-card__title {
  margin: 0 0 .7rem;
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.review-card__list { margin: 0; display: grid; gap: .4rem; }
.review-card__list div { display: flex; justify-content: space-between; gap: 1rem; font-size: .88rem; }
.review-card__list dt { color: var(--muted); }
.review-card__list dd { margin: 0; text-align: right; color: var(--cream-dim); }
.review-card__list:empty::before { content: 'Not filled in yet.'; font-size: .82rem; font-style: italic; color: var(--muted); }

.review-price {
  margin-top: 1.25rem;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--ink-3);
  border-radius: .9rem;
  background: var(--ink);
}

/* Made for You only — a booked consultation is part of the request, so
   this block carries the rose border the rest of the review does not, and
   sits between the price and the consents where it can't be scrolled past. */
.consult {
  margin-top: 1.25rem;
  padding: 1.1rem 1.25rem;
  border: 1px solid var(--rose-deep);
  border-radius: .9rem;
  background: var(--ink);
}
.consult[hidden] { display: none; }
.consult__note { margin: 0 0 1rem; font-size: .88rem; line-height: 1.6; color: var(--cream-dim); }
.consult__link { display: inline-flex; padding: .75rem 1.3rem; }
.consult__field { margin-top: 1.1rem; }

/* ==================================================================
   REQUEST SENT — the screen that replaces the whole form once the
   request is away. Centred rather than the form's left-aligned column:
   nothing here is being filled in, so there is no column to keep. The
   rings are the only drawn thing on the page, and they carry the rose
   the invitations side of the studio is written in.
   ================================================================== */
.qdone {
  padding: clamp(3.5rem, 12vw, 7rem) 0 clamp(4rem, 12vw, 7rem);
  text-align: center;
  outline: none;
}
.qdone[hidden] { display: none; }
/* .wrap centres at the page's full measure; the ending wants a
   narrower one, so it re-centres inside it. */
.qdone__inner { width: min(100% - 2 * var(--gut), 38rem); margin-inline: auto; }
.qdone__rings {
  width: clamp(4.5rem, 16vw, 6rem);
  height: auto;
  /* studio.css sets every svg to display:block, so text-align can't centre
     this one — it centres itself. */
  margin: 0 auto 1.6rem;
  color: var(--rose);
  animation: qdone-rise .7s var(--ease) both;
}
.qdone .eyebrow { margin-bottom: .8rem; }
.qdone__title {
  margin: 0;
  font-size: clamp(1.9rem, 6vw, 3rem);
  line-height: 1.15;
  letter-spacing: -.01em;
  animation: qdone-rise .7s var(--ease) .08s both;
}
.qdone__title em { font-style: italic; font-weight: 400; color: var(--rose); }
.qdone__lede {
  margin: 1.4rem auto 0;
  max-width: 32rem;
  line-height: 1.75;
  color: var(--cream-dim);
  animation: qdone-rise .7s var(--ease) .16s both;
}
.qdone__email { color: var(--cream); white-space: nowrap; }
.qdone__wish {
  margin: 1.1rem auto 0;
  max-width: 32rem;
  font-size: 1.02rem;
  line-height: 1.7;
  color: var(--rose);
  animation: qdone-rise .7s var(--ease) .24s both;
}
.qdone__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .8rem;
  justify-content: center;
  margin-top: 2rem;
  animation: qdone-rise .7s var(--ease) .32s both;
}
/* .btn is inline-flex, which outranks the [hidden] attribute on its own —
   the walk-back button hides itself once the walk back is called off. */
.qdone__actions .btn[hidden] { display: none; }
.qdone__timer {
  margin: 1.1rem 0 0;
  min-height: 1.2em;
  font-size: .8rem;
  letter-spacing: .04em;
  color: var(--muted);
}

@keyframes qdone-rise {
  from { opacity: 0; transform: translateY(.9rem); }
  to   { opacity: 1; transform: none; }
}
/* The screen has to read the same standing still — the rise is a grace
   note, never the thing that makes the words appear. */
@media (prefers-reduced-motion: reduce) {
  .qdone__rings,
  .qdone__title,
  .qdone__lede,
  .qdone__wish,
  .qdone__actions { animation: none; }
}

/* ==================================================================
   FIELD VALIDATION — the dialling code beside the number, and the line
   that says what is wrong underneath either of them.

   Both belong here rather than in studio.css: .field is shared with the
   studio page's contact form, and nothing there has a compound control
   or per-field errors to show.
   ================================================================== */

/* Two controls, one field: the code is only as wide as its own content so
   the number keeps the rest of the row. */
.field__group { display: flex; gap: .5rem; align-items: stretch; }
.field__group > .field__in { min-width: 0; }
.field__in--code {
  flex: none;
  width: auto;
  padding-inline: .9rem 2.2rem;
  font-variant-numeric: tabular-nums;
}

/* The message sits under the control it belongs to, in the field's own
   grid column, so a two-up row never has an error pointing at the wrong
   side. Hidden until there is something to say — an empty slot would
   still take a line of height in the grid. */
.field__err {
  font-size: .78rem;
  line-height: 1.45;
  color: var(--rose);
}
.field__err[hidden] { display: none; }
.field__in.is-invalid,
.field__group:has(.is-invalid) .field__in.is-invalid {
  border-color: var(--rose-deep);
}
.field__in.is-invalid:focus { border-color: var(--rose); }
