/*
 * Structure and components. Entirely venue-neutral: every colour comes from a
 * custom property, and the venue's values are applied at runtime from
 * /api/config (see applyTheme in js/app.js) rather than from a per-venue
 * stylesheet. The values below are the neutral fallback used before config
 * arrives, and for any token a venue leaves blank.
 */

/* Self-hosted brand faces. Declared here rather than per venue so a venue can
   simply name the family; unused faces are never fetched. */
@font-face {
  font-family: 'Gordita';
  src: url('../assets/fonts/Gordita-Light.woff2') format('woff2');
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: 'Gordita';
  src: url('../assets/fonts/Gordita-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Gordita';
  src: url('../assets/fonts/Gordita-Medium.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}

:root {
  --brand:            #2f3b34;
  --brand-strong:     #212a25;
  --brand-soft:       #5c6a61;
  --accent:           #e8dcc6;
  --accent-strong:    #dccdb0;

  --bg:               #faf7f2;
  --surface:          #ffffff;
  --surface-sunk:     #f7f4ee;
  --line:             #e6e1d8;
  --line-strong:      #d3ccc0;

  --text:             #2f3b34;
  --text-muted:       #667169;
  --text-on-brand:    #faf7f2;
  --text-on-accent:   #2f3b34;

  --ok:               #3f6b47;
  --warn:             #875c26;
  --danger:           #9E4B3C;

  /* 'Noto Color Emoji' has to sit BEFORE the generic sans-serif: a generic
     family always resolves, so any family listed after it is unreachable.
     It is last among the real families so it only ever supplies the country
     flags, which no text font on Windows has a glyph for. */
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Color Emoji', sans-serif;

  --radius-pill: 32px;
  --radius-card: 14px;
  --radius-field: 10px;

  --shadow-card: 0 1px 2px rgba(0, 0, 0, .05), 0 8px 24px rgba(0, 0, 0, .07);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, .07), 0 14px 34px rgba(0, 0, 0, .12);
}

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

/* The hidden attribute must actually hide.
 *
 * Browsers apply display:none to [hidden] from their own stylesheet, which any
 * author `display` rule outranks - so every component that sets display: flex
 * or grid silently stops honouring `hidden`. That was patched element by
 * element seven times before .note was missed and shipped as an empty yellow
 * bar above the calendar. One rule, with !important to outrank the component
 * styles, ends the whole class of it. */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* Theme files load asynchronously; hide the flash of unstyled brand. */
body[data-theme-pending] { visibility: hidden; }

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

/* ------------------------------------------------------------------ layout */

.shell { max-width: 620px; margin: 0 auto; padding: 0 20px 80px; }

/* Trim the gutters on small phones. Chrome eaten by padding comes straight
   out of the calendar cells, which are the smallest tap targets on the page. */
@media (max-width: 420px) {
  .shell { padding-left: 14px; padding-right: 14px; }
  .card { padding: 18px 16px; }
}

.masthead {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  padding: 40px 0 28px;
  text-align: center;
}
.masthead__logo { width: 190px; }

.eyebrow {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--brand-soft);
  margin: 0;
}

h1 {
  font-size: clamp(28px, 7vw, 40px);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .01em;
  line-height: 1.1;
  margin: 0;
  color: var(--brand);
}

h2 {
  font-size: 20px;
  font-weight: 600;
  text-transform: uppercase;
  margin: 0 0 6px;
  color: var(--brand);
}

/* pre-line, so line breaks typed into the admin's intro survive: the text is
   set with textContent (never innerHTML), which keeps the newlines in the node
   but leaves it to CSS whether they are drawn. Spaces still collapse. */
.lede {
  font-size: 16px;
  color: var(--brand-soft);
  margin: 0;
  max-width: 44ch;
  white-space: pre-line;
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: 24px;
  margin-bottom: 16px;
}

.step { display: none; }
.step[data-active] { display: block; animation: rise .32s ease both; }

@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .step[data-active] { animation: none; }
  * { transition-duration: .01ms !important; }
}

/* --------------------------------------------------------------- progress */

.progress {
  display: flex;
  gap: 6px;
  list-style: none;
  margin: 0 0 22px;
  padding: 0;
}
.progress li {
  flex: 1;
  height: 4px;
  border-radius: 2px;
  background: var(--line);
  transition: background .3s;
}
.progress li[data-done] { background: var(--accent); }
.progress li[data-current] { background: var(--brand); }

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 52px;
  padding: 0 30px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-decoration: none;
  /* A button label is two or three words and belongs on one line. Without
     this, any button in a flex row shrinks and breaks its own label -- "Add
     tag" became a two-line button half as wide as its own padding. */
  white-space: nowrap;
  cursor: pointer;
  transition: transform .15s, box-shadow .15s, background .15s, opacity .15s;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

.btn--primary { background: var(--brand); color: var(--text-on-brand); }
.btn--primary:hover:not(:disabled) { background: var(--brand-strong); box-shadow: var(--shadow-lift); }

.btn--accent { background: var(--accent); color: var(--text-on-accent); }
.btn--accent:hover:not(:disabled) { background: var(--accent-strong); box-shadow: var(--shadow-lift); }

.btn--ghost {
  background: transparent;
  color: var(--brand-soft);
  border-color: var(--line-strong);
  min-height: 44px;
  padding: 0 20px;
}
.btn--ghost:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }

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

.btn__spinner {
  width: 15px; height: 15px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin .7s linear infinite;
  display: none;
}
.btn[data-busy] .btn__spinner { display: block; }
@keyframes spin { to { transform: rotate(360deg); } }

.actions { display: flex; gap: 10px; align-items: center; margin-top: 22px; }
.actions--split { justify-content: space-between; }

/* ------------------------------------------------------------------ picker */

/* -------------------------------------------------------------- calendar */
/* A month at a time, because the booking window runs 90 days and a flat list
   of every available Fri/Sat/Sun would be ~39 rows of scrolling. */

.calendar { margin-top: 18px; }

.calendar__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 14px;
}
.calendar__month {
  font-size: 16px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--brand);
}
.calendar__nav {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: none;
  border: 1.5px solid var(--line-strong);
  border-radius: 50%;
  background: var(--surface);
  color: var(--brand);
  cursor: pointer;
  transition: all .15s;
}
.calendar__nav:hover:not(:disabled) { border-color: var(--brand); background: var(--surface-sunk); }
.calendar__nav:disabled { opacity: .3; cursor: not-allowed; }

.calendar__weekdays,
.calendar__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.calendar__weekdays {
  margin-bottom: 6px;
}
.calendar__weekdays span {
  text-align: center;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.calendar__body { position: relative; min-height: 240px; }

.calendar__veil {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--surface) 75%, transparent);
  border-radius: var(--radius-field);
}

.cal-day {
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border: 1.5px solid transparent;
  border-radius: var(--radius-field);
  background: transparent;
  font: inherit;
  font-size: 15px;
  color: var(--text-muted);
  cursor: default;
}
.cal-day--blank { visibility: hidden; }

/* Available days are the only ones that read as interactive. */
.cal-day--open {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
  font-weight: 600;
  cursor: pointer;
  transition: all .15s;
}
.cal-day--open:hover { border-color: var(--brand); transform: translateY(-1px); }
.cal-day--open:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.cal-day--selected {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--text-on-brand);
}

.cal-day--closed { opacity: .38; }

.calendar__hint {
  font-size: 13px;
  color: var(--text-muted);
  text-align: center;
  margin: 14px 0 0;
  min-height: 20px;
}

/* --------------------------------------------------------- selected date */

.picked {
  margin-top: 22px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  animation: rise .3s ease both;
}
.picked__title {
  font-size: 16px;
  font-weight: 600;
  text-transform: uppercase;
  color: var(--brand);
  margin: 0 0 4px;
}

.offer {
  border-top: 1px solid var(--line);
  padding: 16px 0 0;
  margin-top: 16px;
}
.offer:first-child { border-top: 0; padding-top: 8px; margin-top: 0; }
.offer__head { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.offer__heading { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.offer__title { font-weight: 600; font-size: 16px; text-transform: uppercase; }

/* The date sits under the offer title rather than heading the whole panel:
   the offer is what the guest is choosing, the date is context for it. */
.offer__date {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.offer__deposit { font-size: 13px; color: var(--text-muted); white-space: nowrap; }

/* On a phone the nowrap deposit holds the right-hand column, which squeezes
   the title and date into two lines each. Stacked, both get the full width. */
@media (max-width: 460px) {
  .offer__head { flex-direction: column; align-items: flex-start; gap: 3px; }
}

/* The venue authors this in SevenRooms, so it arrives as real structure -
   paragraphs, a bold heading, bullet lists - rather than one run of text. */
.offer__desc { font-size: 14px; color: var(--brand-soft); margin: 10px 0 0; }
.offer__desc > :first-child { margin-top: 0; }
.offer__desc > :last-child { margin-bottom: 0; }
.offer__desc p { margin: 0 0 8px; }
.offer__desc h4 {
  margin: 14px 0 6px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--brand);
}
.offer__desc ul, .offer__desc ol { margin: 0 0 8px; padding-left: 20px; }
.offer__desc li { margin-bottom: 4px; }
.offer__desc strong { font-weight: 600; color: var(--brand); }
.offer__desc a { color: var(--brand); }

.times { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.time-chip {
  padding: 10px 18px;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  transition: all .15s;
}
.time-chip:hover { border-color: var(--brand); }
.time-chip[aria-pressed='true'] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--text-on-brand);
}

/* ------------------------------------------------------------------- forms */

.field { margin-bottom: 16px; }
.field--pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 460px) { .field--pair { grid-template-columns: 1fr; } }

/* Country code and number read as one control, so they sit in one border-less
   row rather than looking like two unrelated fields. */
/* A fixed first column rather than auto: the codes differ in width, so an
   auto column would shift the number field sideways on every change. */
.phone-input { display: grid; grid-template-columns: 7.5rem minmax(0, 1fr); gap: 8px; }
.phone-input select { padding-inline: 10px; }

label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--brand-soft);
  margin-bottom: 6px;
}

/* Opt-out rather than opt-in.
 *
 * This used to list the types it styled, which meant every new field silently
 * fell back to the browser default and rendered a different size from the one
 * above it - first on the login form, then on the terms link. Excluding the
 * handful of genuinely different controls fixes the whole class: any input
 * type added later is styled without anyone having to remember this rule.
 * Colour swatches, checkboxes and the file picker keep their own styling. */
input:not([type='checkbox']):not([type='radio']):not([type='color']):not([type='file']):not([type='range']):not([type='submit']):not([type='button']):not([type='image']):not([type='reset']),
select,
textarea {
  width: 100%;
  /* 53px, not 50: inputs reach 53 on their own from line-height, while a
     select stops at the floor. Matching the floor to what inputs actually
     render keeps a select from sitting 2px shorter than the field above it. */
  min-height: 53px;
  padding: 13px 15px;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-field);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 16px; /* 16px keeps iOS from zooming on focus */
  transition: border-color .15s, box-shadow .15s;
}
textarea { min-height: 90px; resize: vertical; }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 15%, transparent);
}

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

.field__error {
  display: none;
  font-size: 13px;
  color: var(--danger);
  margin-top: 5px;
}
.field[data-invalid] .field__error { display: block; }

.checkbox {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 14px;
  color: var(--brand-soft);
  cursor: pointer;
}
.checkbox input { width: 18px; height: 18px; margin-top: 2px; flex: none; accent-color: var(--brand); }
.checkbox a { color: var(--brand); }

/* The acceptance box is a .field so it can show an error like any other, but
   it must not inherit the input spacing meant for text fields. */
.field--check { margin-bottom: 14px; }
.field--check .field__error { margin-top: 6px; }
.field--check[data-invalid] .checkbox input { outline: 2px solid var(--danger); outline-offset: 2px; }
.checkbox label { text-transform: none; letter-spacing: 0; font-size: 14px; font-weight: 400; margin: 0; }

/* --------------------------------------------------------------- summaries */

.summary {
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 18px;
  margin-bottom: 20px;
}
.summary dl { display: grid; grid-template-columns: auto 1fr; gap: 8px 16px; margin: 0; }
.summary dt { font-size: 13px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; }
.summary dd { margin: 0; font-size: 15px; font-weight: 600; text-align: right; }
.summary__total { border-top: 1px solid var(--line); margin-top: 14px; padding-top: 14px; }
.summary__grand { font-weight: 600; color: var(--brand); padding-top: 8px; border-top: 1px solid var(--line); }
.summary__total dt.summary__grand { text-transform: uppercase; }

.note {
  display: flex;
  gap: 10px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--brand-soft);
  background: var(--accent);
  border-radius: var(--radius-field);
  padding: 14px 16px;
  margin: 16px 0;
}
.note strong { color: var(--brand); }

.alert {
  font-size: 14px;
  border-radius: var(--radius-field);
  padding: 13px 16px;
  margin-bottom: 16px;
  border: 1px solid;
}
.alert--error { color: var(--danger); border-color: var(--danger); background: color-mix(in srgb, var(--danger) 7%, var(--surface)); }
.alert--ok { color: var(--ok); border-color: var(--ok); background: color-mix(in srgb, var(--ok) 8%, var(--surface)); }

.countdown { font-size: 13px; color: var(--text-muted); text-align: center; margin-top: 14px; }
.countdown strong { color: var(--warn); }

/* ------------------------------------------------------------------ states */

.loading, .empty {
  text-align: center;
  padding: 44px 20px;
  color: var(--text-muted);
  font-size: 15px;
}
.loading__dot {
  width: 30px; height: 30px;
  margin: 0 auto 14px;
  border: 3px solid var(--line);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}

.skeleton {
  height: 70px;
  border-radius: var(--radius-card);
  background: linear-gradient(90deg, var(--surface-sunk) 25%, var(--line) 50%, var(--surface-sunk) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.3s infinite;
  margin-bottom: 10px;
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* ------------------------------------------------------------ confirmation */

.confirmed { text-align: center; }
.confirmed__mark {
  width: 68px; height: 68px;
  margin: 0 auto 20px;
  border-radius: 50%;
  background: var(--accent);
  display: grid;
  place-items: center;
}
.confirmed__ref {
  display: inline-block;
  font-size: 26px;
  font-weight: 600;
  letter-spacing: .1em;
  background: var(--surface-sunk);
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius-field);
  padding: 12px 24px;
  margin: 6px 0 18px;
}

.footer {
  text-align: center;
  font-size: 13px;
  color: var(--text-muted);
  padding: 30px 0 0;
}
.footer a { color: var(--brand-soft); }

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

/* ------------------------------------------------------------------ modal */
/* Used for the terms, so a guest can read them without leaving checkout and
   losing their hold. <dialog> handles the backdrop, focus and Escape. */

.modal {
  width: min(760px, 100vw - 32px);
  height: min(80vh, 900px);
  max-width: none;
  max-height: none;
  padding: 0;
  border: none;
  border-radius: var(--radius-card);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}
.modal::backdrop { background: rgba(0, 0, 0, .45); }

/* Rows fixed, frame elastic: the terms scroll inside rather than the modal
   growing past the viewport. */
.modal[open] { display: grid; grid-template-rows: auto 1fr auto; }

.modal__head,
.modal__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
}
.modal__head { border-bottom: 1px solid var(--line); }
.modal__foot { border-top: 1px solid var(--line); }

.modal__title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--brand);
}

.modal__close {
  flex: none;
  width: 40px;
  height: 40px;
  border: 1.5px solid var(--line-strong);
  border-radius: 50%;
  background: var(--surface);
  color: var(--brand);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}
.modal__close:hover { border-color: var(--brand); background: var(--surface-sunk); }

.modal__frame { width: 100%; height: 100%; border: 0; background: #fff; }
.modal__foot a { color: var(--brand); font-size: 14px; }

/* Full height on a phone: 80vh of a small screen is not enough to read terms. */
@media (max-width: 560px) {
  .modal { width: 100vw; height: 100dvh; border-radius: 0; }
}


/* ------------------------------------------------------------ nothing here */

/* Shown when a link names no booking type. Deliberately plain: it is the whole
   page in that case, and dressing it up would suggest something went wrong at
   the venue rather than with the link. */
.no-type h2 { margin-bottom: 8px; }
.no-type p { color: var(--text-muted); }
.no-type__support { margin-top: 12px; }

/* ------------------------------------------------------------------ offers */

/* What is on offer, above the date picker. Read before anything is chosen, so
   it is laid out to be scanned rather than studied. */
.offers h2 { margin-bottom: 4px; }
.offers .lede { margin-bottom: 18px; }

/* Only shown when a venue runs more than one kind of celebration: a "Birthday"
   heading on a page already about birthdays is noise. */
.offers__group {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 22px 0 10px;
}
.offers__group:first-child { margin-top: 0; }

/* .offer-card, not .offer: the times panel under the calendar has owned
   .offer since long before this card existed, and giving both the same name
   put display:flex on the times panel from further down the stylesheet --
   which turned its title, its description and its time chips into three tall
   columns squeezed side by side. */
.offer-card {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  padding: 14px 0;
}
.offer-card + .offer-card { border-top: 1px solid var(--line); }

.offer-card__image {
  width: 104px;
  height: 104px;
  flex: none;
  object-fit: cover;
  border-radius: var(--radius-field);
  background: var(--surface-sunk);
}

.offer-card__body { min-width: 0; }
.offer-card__title {
  margin: 0 0 4px;
  font-size: 16.5px;
  font-weight: 600;
  color: var(--brand);
}
.offer-card__text {
  margin: 0 0 8px;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--text);
}
.offer-card__text:last-child { margin-bottom: 0; }

/* On a phone the image above the words reads better than a narrow column of
   text squeezed beside it. */
@media (max-width: 520px) {
  .offer-card { flex-direction: column; gap: 10px; }
  .offer-card__image { width: 100%; height: 160px; }
}
