/* ============================================================
   Frost Science · components and states

   §3.6 of the plan lists the states that get skipped in design and
   improvised in development. They are all here, so there is nothing left
   to improvise.

   Two rules hold across every component below:

   · No state is signalled by colour alone. Every one carries a glyph and a
     word, so it survives greyscale and colour-blindness (WCAG 1.4.1).
   · No literal hex, px radius or font name. Tokens only.

   Ornament comes from the museum's own graphic language — the dot, the drip,
   the dotted line — never a generic UI device. There is no spinner on this
   site because the dot cascade does that job, and no chevron progress bar
   because the dots do.
   ============================================================ */


/* ============================================================
   1 · Buttons
   ============================================================ */

/*
   Orange fill, navy ink — 6.17:1. White on orange is 2.86:1: the most
   natural button in the world, and it fails identically to orange text.
   It must never ship, and tools/e2e.mjs asserts it does not.
*/
.fs-btn--primary   { background: var(--fs-orange); color: var(--fs-text); }
.fs-btn--primary:hover { background: var(--fs-orange-soft); color: var(--fs-text); }

.fs-btn--secondary { background: transparent; color: var(--fs-text); border-color: var(--fs-text); }
.fs-btn--secondary:hover { background: var(--fs-text); color: var(--fs-text-inverse); }

.fs-btn--quiet { background: transparent; color: var(--fs-on-surface-link); padding-inline: var(--fs-space-2); }
.fs-btn--quiet:hover { background: var(--fs-surface-alt); }

.fs-btn--member { background: var(--fs-member-tint); color: var(--fs-member-ink); border-color: var(--fs-member-ink); }

/*
   Sold out is neutral, never red. Red means "you did something wrong";
   sold out means "you were too late", and those are different feelings.
   --fs-text-disabled is 1.79:1 and intentionally exempt under WCAG 1.4.3 —
   the low contrast IS the affordance. Do not raise it.
*/
.fs-btn--soldout {
  background: var(--fs-soldout-bg);
  color: var(--fs-text-muted);
  border-color: var(--fs-soldout-border);
  cursor: not-allowed;
}

.fs-surface-deep .fs-btn--secondary { color: var(--fs-text-inverse); border-color: var(--fs-text-inverse); }
.fs-surface-deep .fs-btn--secondary:hover { background: rgb(255 255 255 / .14); color: var(--fs-text-inverse); }


/* ============================================================
   2 · Date cell — six states
   The greyscale test lives here: screenshot this row with saturation at
   zero and every state must still be distinguishable.
   ============================================================ */

.fs-datecell {
  display: flex;
  flex-direction: column;
  gap: var(--fs-space-1);
  /* 48px on the purchase path, not 44. */
  min-height: var(--fs-control-height);
  padding: var(--fs-space-3) var(--fs-space-2);
  border: var(--fs-control-border) solid transparent;
  border-radius: var(--fs-radius-sm);
  background: var(--fs-surface);
  text-align: center;
  text-decoration: none;
  color: var(--fs-text);
  font-variant-numeric: var(--fs-numeric);
  cursor: pointer;
}

.fs-datecell__day   { font-family: var(--fs-font-display); font-weight: 700; font-size: var(--fs-h3); line-height: 1; }
.fs-datecell__price { font-size: var(--fs-caption); font-weight: 500; }
.fs-datecell__state { font-size: var(--fs-caption); display: flex; align-items: center; justify-content: center; gap: var(--fs-space-1); }

.fs-datecell[data-state='available'] { border-color: var(--fs-available-ink); background: var(--fs-available-tint); }
.fs-datecell[data-state='few']       { border-color: var(--fs-text); background: var(--fs-limited-tint); }
.fs-datecell[data-state='member']    { border-color: var(--fs-member-ink); background: var(--fs-member-tint); color: var(--fs-member-ink); }

.fs-datecell[data-state='soldout'] {
  border-color: var(--fs-soldout-border);
  background: var(--fs-soldout-bg);
  color: var(--fs-text-muted);
  cursor: not-allowed;
}
/* The day itself is struck through, not just dimmed — a second signal. */
.fs-datecell[data-state='soldout'] .fs-datecell__day { text-decoration: line-through; }

/* Dashed border is the third signal for closed, after the dash glyph and
   the word. Nothing here depends on the fill. */
.fs-datecell[data-state='closed'] {
  border-style: dashed;
  border-color: var(--fs-soldout-border);
  background: transparent;
  color: var(--fs-text-muted);
  cursor: not-allowed;
}

.fs-datecell[data-state='selected'] {
  border-width: 3px;
  border-color: var(--fs-text);
  background: var(--fs-orange);
  color: var(--fs-text);
  font-weight: 700;
}

.fs-datecell[data-state='past'] { opacity: .45; cursor: not-allowed; }


/* ============================================================
   3 · Form field — five states
   ============================================================ */

.fs-field { display: block; margin-bottom: var(--fs-space-4); }

.fs-field__label { display: block; font-size: var(--fs-body-sm); font-weight: 500; margin-bottom: var(--fs-space-2); }

/* Mark optional, not required. Required is the norm on these forms, so
   flagging it decorates every field and informs nobody. */
.fs-field__optional { font-weight: 400; color: var(--fs-on-surface-quiet); }

.fs-field__help { font-size: var(--fs-body-sm); color: var(--fs-on-surface-quiet); margin-top: var(--fs-space-2); }

/*
   Reserved slot, always present, so the layout never jumps under a thumb
   when validation fires. One line of --fs-body-sm at 1.4 plus its margin.
*/
.fs-field__error-slot { min-height: calc(var(--fs-body-sm) * 1.4 + var(--fs-space-2)); }

.fs-field__error,
.fs-field__valid {
  display: flex;
  align-items: flex-start;
  gap: var(--fs-space-2);
  margin-top: var(--fs-space-2);
  font-size: var(--fs-body-sm);
  font-weight: 500;
}

/* The glyph is decorative — the sentence carries the meaning, and it is a
   sentence, never "Invalid input". */
.fs-field__error { color: var(--fs-error); }
.fs-field__error::before { content: '\2715'; flex: none; font-weight: 700; }

.fs-field__valid { color: var(--fs-available-ink); }
.fs-field__valid::before { content: '\2713'; flex: none; font-weight: 700; }

.fs-field input[aria-invalid='true'],
.fs-field textarea[aria-invalid='true'],
.fs-field select[aria-invalid='true'] { border-color: var(--fs-error); }

.fs-field input:disabled,
.fs-field select:disabled,
.fs-field textarea:disabled {
  background: var(--fs-soldout-bg);
  color: var(--fs-text-disabled);
  border-color: var(--fs-soldout-border);
  cursor: not-allowed;
}

/*
   Error summary above the form on submit — focused, in a live region,
   counting the problems and linking each one to its field. A field-level
   error alone leaves someone at the bottom of a long form with no idea how
   many more there are.
*/
.fs-error-summary {
  padding: var(--fs-space-4);
  margin-bottom: var(--fs-space-5);
  border: var(--fs-control-border) solid var(--fs-error);
  border-radius: var(--fs-radius-md);
  background: var(--fs-surface);
}
.fs-error-summary h2 { font-size: var(--fs-h3); color: var(--fs-error); margin-bottom: var(--fs-space-3); }
.fs-error-summary ol { margin: 0; padding-left: var(--fs-space-5); }
.fs-error-summary a { color: var(--fs-error); font-weight: 500; }


/* ============================================================
   4 · Stepper
   ============================================================ */

.fs-stepper { display: inline-flex; align-items: stretch; gap: var(--fs-space-2); }

.fs-stepper__btn {
  width: var(--fs-control-height);
  min-height: var(--fs-control-height);
  border: var(--fs-control-border) solid var(--fs-text);
  border-radius: var(--fs-radius-sm);
  background: var(--fs-surface);
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

/*
   The number is typeable, not just steppable. Typing 40 beats tapping
   thirty-two times, and 16px is the floor here as everywhere — below it
   iOS Safari zooms on focus and throws the layout.
*/
.fs-stepper__input {
  width: 4.5rem;
  min-height: var(--fs-control-height);
  text-align: center;
  font-size: max(var(--fs-ui), 1rem);
  font-weight: 700;
  font-variant-numeric: var(--fs-numeric);
  border: var(--fs-control-border) solid var(--fs-text-disabled);
  border-radius: var(--fs-radius-sm);
}


/* ============================================================
   5 · Alert bar — three severities
   ============================================================ */

.fs-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--fs-space-3);
  padding: var(--fs-space-4);
  border-radius: var(--fs-radius-md);
  border-left: 4px solid;
}

.fs-alert__glyph { flex: none; font-weight: 700; line-height: 1.4; }
.fs-alert__word  { font-weight: 700; }

.fs-alert--info    { background: var(--fs-aquatic-tint);  border-color: var(--fs-aquatic-ink); }
.fs-alert--caution { background: var(--fs-limited-tint);  border-color: var(--fs-text); }
.fs-alert--closure { background: var(--fs-action-tint);   border-color: var(--fs-error); }

/* Ink is navy on every severity. The tints differ; the text does not, because
   an accent ink on a tint is a pairing nobody measured. */
.fs-alert { color: var(--fs-text); }


/* ============================================================
   6 · Hold timer and cart states
   ============================================================ */

.fs-hold {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--fs-space-3);
  padding: var(--fs-space-4);
  border-radius: var(--fs-radius-md);
  background: var(--fs-action-tint);
  color: var(--fs-text);
}

.fs-hold__clock {
  font-family: var(--fs-font-display);
  font-weight: 700;
  font-size: var(--fs-h3);
  font-variant-numeric: var(--fs-numeric);
}

/*
   Expiry says "Nothing was charged" before it says anything else. That is
   the sentence the guest needs, and it belongs above the explanation.
*/
.fs-hold--expired { background: var(--fs-soldout-bg); }

.fs-cart-empty { padding: var(--fs-space-6); text-align: center; color: var(--fs-on-surface-muted); }


/* ============================================================
   7 · Ornament — the dot, the drip, the dotted line
   The brand book's own devices. No generic UI decoration appears anywhere
   one of these does the same job.
   ============================================================ */

/* --- checkout progress: dots, not chevrons --- */

.fs-progress { display: flex; align-items: center; gap: var(--fs-space-3); list-style: none; margin: 0; padding: 0; }

.fs-progress__step { display: flex; align-items: center; gap: var(--fs-space-2); font-size: var(--fs-body-sm); }

.fs-progress__dot {
  width: 12px;
  height: 12px;
  border-radius: var(--fs-radius-pill);
  background: var(--fs-text-disabled);
  flex: none;
}

.fs-progress__step[aria-current='step'] .fs-progress__dot { background: var(--fs-orange); box-shadow: 0 0 0 3px var(--fs-action-tint); }
.fs-progress__step[data-done='true'] .fs-progress__dot { background: var(--fs-text); }
.fs-progress__step[aria-current='step'] { font-weight: 700; }

/* --- drip: map marker and selected state --- */

.fs-drip {
  position: relative;
  width: 28px;
  height: 36px;
  flex: none;
  /* The vessel shape from the brand book — a circle drawn to a point. */
  background: var(--fs-blue);
  border-radius: 50% 50% 50% 50% / 42% 42% 58% 58%;
  clip-path: path('M14 36C14 36 28 20.5 28 13A14 14 0 1 0 0 13C0 20.5 14 36 14 36Z');
}

.fs-drip--member { background: var(--fs-purple); }
.fs-drip--action { background: var(--fs-orange); }

/* --- dot cascade: the loading state. There is no spinner on this site. --- */

.fs-cascade { display: inline-flex; align-items: center; gap: var(--fs-space-2); }
.fs-cascade__dots { display: inline-flex; gap: 6px; }

.fs-cascade__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--fs-radius-pill);
  background: var(--fs-orange);
  animation: fs-cascade 1.1s var(--fs-ease) infinite;
}
.fs-cascade__dot:nth-child(2) { animation-delay: .15s; }
.fs-cascade__dot:nth-child(3) { animation-delay: .3s; }

@keyframes fs-cascade {
  0%, 100% { opacity: .25; transform: translateY(0); }
  40%      { opacity: 1;   transform: translateY(-4px); }
}

/*
   Under reduced motion the cascade is replaced by the word "Thinking".
   The dots are hidden rather than frozen, because three static dots read as
   a broken component.
*/
.fs-cascade__word { display: none; font-size: var(--fs-body-sm); font-weight: 500; }

@media (prefers-reduced-motion: reduce) {
  .fs-cascade__dots { display: none; }
  .fs-cascade__word { display: inline; }
}

/* --- dotted line: divider and timeline --- */

.fs-dotted {
  border: 0;
  height: 2px;
  background-image: radial-gradient(currentColor 1.5px, transparent 1.5px);
  background-size: 10px 2px;
  background-repeat: repeat-x;
  opacity: .45;
  margin-block: var(--fs-space-6);
}

.fs-timeline { list-style: none; margin: 0; padding: 0 0 0 var(--fs-space-6); position: relative; }

.fs-timeline::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background-image: radial-gradient(currentColor 1.5px, transparent 1.5px);
  background-size: 2px 10px;
  opacity: .45;
}

.fs-timeline__item { position: relative; padding-bottom: var(--fs-space-5); }

.fs-timeline__item::before {
  content: '';
  position: absolute;
  left: calc(-1 * var(--fs-space-6) + 1px);
  top: 7px;
  width: 12px;
  height: 12px;
  border-radius: var(--fs-radius-pill);
  background: var(--fs-orange);
}

.fs-timeline__year { font-family: var(--fs-font-display); font-weight: 700; font-size: var(--fs-h3); }

/* --- dot field: CTA band and empty-state texture --- */

.fs-dotfield {
  background-image: radial-gradient(currentColor 1.5px, transparent 1.5px);
  background-size: 22px 22px;
}


/* ============================================================
   8 · Two registers, one website
   Carried by surface and rhythm, not by a second typeface.
   ============================================================ */

/* Invitation: white or accent, generous leading. */
.fs-register-invitation { font-size: var(--fs-h3); line-height: 1.55; max-width: 40ch; }

/*
   Policy: paper panel, navy rule, tighter measure, no exclamation points.
   The surface is doing the tonal work that a second typeface would
   otherwise have to do.
*/
.fs-register-policy {
  background: var(--fs-surface-paper);
  border-left: 3px solid var(--fs-text);
  padding: var(--fs-space-5);
  font-size: var(--fs-body-sm);
  line-height: 1.65;
  border-radius: var(--fs-radius-sm);
}


/* ============================================================
   9 · Availability advisory
   TNEW is the authority on inventory and price. We are a cache, and the
   site says so wherever a cached number appears.
   ============================================================ */

.fs-advisory {
  display: flex;
  align-items: center;
  gap: var(--fs-space-2);
  font-size: var(--fs-caption);
  color: var(--fs-on-surface-quiet);
  margin-top: var(--fs-space-3);
}


/* ============================================================
   9b · Framed media
   ------------------------------------------------------------
   Any box that declares an aspect ratio and crops an image to it.

   Absolutely positioned rather than `width:100%; height:100%`, because a
   percentage height against a parent whose own height comes from
   aspect-ratio is a circular reference that browsers resolve to `auto` —
   and `auto` means the image sets its own height and the frame stops
   framing. This version cannot be argued with by a later specificity fight.
   ============================================================ */

.fs-m-c__media,
.fs-m-b__media,
.fs-m-f__media,
.fs-m-q__frame,
.fs-exhibition__experience .fs-media,
.fs-m-r__item {
  position: relative;
  overflow: hidden;
  display: block;
}

.fs-m-c__media > img,
.fs-m-b__media > img,
.fs-m-f__media > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.fs-m-c__media { aspect-ratio: 4 / 3; }
.fs-m-b__media { aspect-ratio: 4 / 3; }
.fs-m-f__media { aspect-ratio: 4 / 3; }

/* Portraits and galleries keep their own ratios but the same framing. */
.fs-m-s__photo,
.fs-m-r__img,
.fs-exhibition__experience img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.fs-m-s__photo { aspect-ratio: 4 / 5; }

/* ============================================================
   10 · Shared archive and page furniture
   ============================================================ */

.fs-archive-grid {
  list-style: none;
  margin: var(--fs-space-6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--fs-space-5);
  grid-template-columns: 1fr;
}
@media (min-width: 768px)  { .fs-archive-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .fs-archive-grid { grid-template-columns: repeat(3, 1fr); } }

.fs-filters { display: flex; flex-wrap: wrap; gap: var(--fs-space-5); margin-block: var(--fs-space-5); }
.fs-filters__group { min-width: 0; }
.fs-filters__group legend { margin-bottom: var(--fs-space-2); }

.fs-filters__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--fs-space-1);
  min-height: var(--fs-target-min);
  padding: var(--fs-space-2) var(--fs-space-4);
  border: var(--fs-control-border) solid var(--fs-soldout-border);
  border-radius: var(--fs-radius-pill);
  font-size: var(--fs-body-sm);
  font-weight: 500;
  color: var(--fs-on-surface);
  text-decoration: none;
}
.fs-filters__chip:hover { border-color: var(--fs-text); color: var(--fs-on-surface); }
/* Selected carries a check mark as well as a fill — never colour alone. */
.fs-filters__chip.is-active { background: var(--fs-text); color: var(--fs-text-inverse); border-color: var(--fs-text); }

.fs-notice { padding: var(--fs-space-4); border-radius: var(--fs-radius-md); margin-block: var(--fs-space-4); }

.fs-routes { list-style: none; margin: var(--fs-space-5) 0; padding: 0; }
.fs-routes li + li { margin-top: var(--fs-space-2); }
.fs-routes a { display: flex; align-items: center; min-height: var(--fs-target-min); font-weight: 700; font-size: var(--fs-h3); }

.fs-results { list-style: none; margin: var(--fs-space-6) 0 0; padding: 0; }
.fs-results__item { padding-block: var(--fs-space-5); border-bottom: 1px solid var(--fs-soldout-border); }

.fs-log { list-style: none; margin: var(--fs-space-5) 0 0; padding: 0; }
.fs-log li { padding-block: var(--fs-space-4); border-top: 1px solid var(--fs-soldout-border); }
.fs-log__time { font-size: var(--fs-caption); color: var(--fs-on-surface-quiet); font-variant-numeric: var(--fs-numeric); }

/* Exhibition */
.fs-exhibition__head { display: grid; gap: var(--fs-space-6); grid-template-columns: 1fr; }
@media (min-width: 1024px) { .fs-exhibition__head { grid-template-columns: 1.6fr 1fr; align-items: start; } }

.fs-exhibition__subtitle { font-size: var(--fs-h3); color: var(--fs-action-ink); font-weight: 700; margin-block: var(--fs-space-3); }
.fs-exhibition__panel { padding: var(--fs-space-5); border-radius: var(--fs-radius-lg); }
@media (min-width: 1024px) { .fs-exhibition__panel { position: sticky; top: calc(64px + var(--fs-space-4)); } }

.fs-exhibition__facts { margin: 0 0 var(--fs-space-5); }
.fs-exhibition__facts > div + div { margin-top: var(--fs-space-4); }
.fs-exhibition__facts dt { font-size: var(--fs-caption); font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--fs-text-quiet); }
.fs-exhibition__facts dd { margin: var(--fs-space-1) 0 0; font-weight: 500; }
.fs-exhibition__buy { width: 100%; }

.fs-exhibition__experiences {
  list-style: none; margin: var(--fs-space-6) 0 0; padding: 0;
  display: grid; gap: var(--fs-space-5); grid-template-columns: 1fr;
}
@media (min-width: 768px) { .fs-exhibition__experiences { grid-template-columns: repeat(2, 1fr); } }
.fs-exhibition__experience img { border-radius: var(--fs-radius-md); aspect-ratio: 4 / 3; object-fit: cover; width: 100%; }
.fs-exhibition__experience h3 { margin-top: var(--fs-space-3); font-size: var(--fs-body); }
.fs-exhibition__experience p { margin-top: var(--fs-space-2); font-size: var(--fs-body-sm); color: var(--fs-on-surface-muted); }

/* Membership tiers */
.fs-tiers {
  list-style: none; margin: var(--fs-space-6) 0 0; padding: 0;
  display: grid; gap: var(--fs-space-5); grid-template-columns: 1fr;
}
@media (min-width: 768px)  { .fs-tiers { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .fs-tiers { grid-template-columns: repeat(3, 1fr); } }

.fs-tier {
  display: flex; flex-direction: column; gap: var(--fs-space-3);
  padding: var(--fs-space-5);
  border: var(--fs-control-border) solid var(--fs-soldout-border);
  border-radius: var(--fs-radius-lg);
}
.fs-tier.is-featured { border-color: var(--fs-text); border-width: 3px; }
.fs-tier--donor { background: var(--fs-surface-deep); color: var(--fs-text-inverse); --fs-on-surface: var(--fs-text-inverse); --fs-on-surface-muted: var(--fs-text-inverse); }

.fs-tier__flag { font-size: var(--fs-eyebrow); font-weight: 700; letter-spacing: .16em; text-transform: uppercase; }
.fs-tier__name { margin: 0; font-size: var(--fs-h3); }
.fs-tier__price { font-family: var(--fs-font-display); font-weight: 700; font-size: 2.5rem; line-height: 1; font-variant-numeric: var(--fs-numeric); }
.fs-tier__dots { letter-spacing: .2em; color: var(--fs-orange); }
.fs-tier__admits { display: flex; flex-direction: column; gap: var(--fs-space-1); font-size: var(--fs-body-sm); }
.fs-tier__tagline { font-size: var(--fs-body-sm); color: var(--fs-on-surface-muted); }
.fs-tier__benefits { list-style: none; margin: 0; padding: 0; font-size: var(--fs-body-sm); flex: 1; }
.fs-tier__benefits li { padding-left: var(--fs-space-5); position: relative; margin-bottom: var(--fs-space-2); }
.fs-tier__benefits li::before { content: '\2713'; position: absolute; left: 0; font-weight: 700; }

/* Prose */
.fs-prose h2 { font-size: var(--fs-h2); margin-top: var(--fs-space-6); }
.fs-prose h3 { font-size: var(--fs-h3); margin-top: var(--fs-space-5); }
.fs-prose p + p { margin-top: var(--fs-space-4); }
.fs-prose ul, .fs-prose ol { margin: var(--fs-space-4) 0 0; padding-left: var(--fs-space-5); }
.fs-single__content { margin-top: var(--fs-space-5); }

/* Pagination */
.pagination, .nav-links { display: flex; flex-wrap: wrap; gap: var(--fs-space-2); margin-top: var(--fs-space-6); }
.page-numbers {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--fs-target-min); min-height: var(--fs-target-min);
  padding-inline: var(--fs-space-3);
  border: var(--fs-control-border) solid var(--fs-soldout-border);
  border-radius: var(--fs-radius-sm);
  text-decoration: none; font-weight: 500; color: var(--fs-on-surface);
}
.page-numbers.current { background: var(--fs-text); color: var(--fs-text-inverse); border-color: var(--fs-text); }

/* ============================================================
   §12 · The purchase path (W-01, W-02, cart)

   Everything here inherits --fs-target-min: 48px from
   .fs-purchase-path. Nothing on this path is 44.
   ============================================================ */

.fs-checkout__heading { margin-bottom: var(--fs-space-3); }
.fs-checkout__when,
.fs-checkout__note { color: var(--fs-text-muted); }
.fs-checkout__when { display: flex; gap: var(--fs-space-4); align-items: baseline; flex-wrap: wrap; }
.fs-checkout__sub { margin-top: var(--fs-space-7); }

/* The cheapest-day line. The single most useful sentence on the screen for
   anyone with a flexible date, so it gets the aquatic tint rather than being
   another grey paragraph. */
.fs-checkout__cheapest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--fs-space-2);
  margin-block: var(--fs-space-5);
  padding: var(--fs-space-3) var(--fs-space-4);
  border-radius: var(--fs-radius-sm);
  background: var(--fs-aquatic-tint);
  font-weight: 500;
}

/* ---------- calendar ---------- */

.fs-cal { margin-top: var(--fs-space-6); max-width: 46rem; }

.fs-cal__nav {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--fs-space-4);
  margin-bottom: var(--fs-space-4);
}
.fs-cal__month { margin: 0; font-size: var(--fs-h3); text-align: center; }
.fs-cal__step {
  display: inline-flex;
  align-items: center;
  gap: var(--fs-space-2);
  min-height: var(--fs-target-min);
  font-weight: 700;
}
.fs-cal__nav > .fs-cal__step:last-child { justify-self: end; }

.fs-cal__weekdays,
.fs-cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: var(--fs-space-2);
}

.fs-cal__weekdays {
  margin-bottom: var(--fs-space-2);
  font-size: var(--fs-caption);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--fs-text-quiet);
  text-align: center;
}

.fs-cal__grid { list-style: none; margin: 0; padding: 0; }
.fs-cal__cell { min-height: 1px; }

@media (max-width: 560px) {
  /* Seven columns of price below 560 gives cells too narrow for a currency
     figure. Four columns keeps the price readable, which is the point of the
     screen — a calendar that hides its prices is the current site. */
  .fs-cal__weekdays { display: none; }
  .fs-cal__grid { grid-template-columns: repeat(4, 1fr); }
  .fs-cal__cell:empty { display: none; }
}

/* ---------- ticket rows ---------- */

.fs-tickets { list-style: none; margin: var(--fs-space-6) 0 0; padding: 0; }

.fs-tickets__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--fs-space-4);
  padding-block: var(--fs-space-4);
  border-bottom: 2px dotted var(--fs-soldout-border);
}

.fs-tickets__row.is-member { background: var(--fs-member-tint); }
.fs-tickets__label label { font-weight: 700; display: block; }
.fs-tickets__note { font-size: var(--fs-body-sm); color: var(--fs-text-quiet); }
.fs-tickets__price { font-weight: 700; font-variant-numeric: var(--fs-numeric); white-space: nowrap; }

@media (max-width: 560px) {
  .fs-tickets__row { grid-template-columns: minmax(0, 1fr) auto; }
  .fs-tickets__price { grid-column: 1; grid-row: 2; }
  .fs-tickets__row .fs-stepper { grid-column: 2; grid-row: 1 / span 2; }
}

/* ---------- showtimes ---------- */

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

.fs-shows__label {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--fs-space-3);
  min-height: var(--fs-target-min);
  padding: var(--fs-space-3) var(--fs-space-4);
  border: var(--fs-control-border) solid var(--fs-soldout-border);
  border-radius: var(--fs-radius-sm);
  cursor: pointer;
}

.fs-shows__label:has(input:checked) { border-color: var(--fs-text); border-width: 3px; background: var(--fs-available-tint); }
.fs-shows__label:has(input:focus-visible) { outline: var(--fs-focus-width) solid var(--fs-focus-ring); outline-offset: var(--fs-focus-offset); }

.fs-shows__time { font-weight: 700; font-variant-numeric: var(--fs-numeric); }

/* Sold out is dashed and struck, never hidden. Hiding it makes the day look
   empty and removes the only evidence that demand exists. */
.fs-shows__item.is-soldout .fs-shows__label { border-style: dashed; background: var(--fs-soldout-bg); cursor: not-allowed; }
.fs-shows__item.is-soldout .fs-shows__time { text-decoration: line-through; }
.fs-shows__state { font-size: var(--fs-body-sm); white-space: nowrap; }

/* ---------- totals and cart ---------- */

.fs-checkout__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--fs-space-4);
  margin-top: var(--fs-space-6);
  padding-top: var(--fs-space-5);
  border-top: 2px solid var(--fs-text);
}

.fs-checkout__total,
.fs-cart__total { display: flex; align-items: baseline; gap: var(--fs-space-3); font-size: var(--fs-h3); }
.fs-checkout__total strong,
.fs-cart__total strong { font-family: var(--fs-font-display); }

.fs-cart { max-width: 36rem; margin-top: var(--fs-space-6); }
.fs-cart__lines { list-style: none; margin: var(--fs-space-4) 0; padding: 0; }
.fs-cart__lines li { display: flex; justify-content: space-between; gap: var(--fs-space-4); padding-block: var(--fs-space-3); border-bottom: 2px dotted var(--fs-soldout-border); }
.fs-cart__total { padding-top: var(--fs-space-4); border-top: 2px solid var(--fs-text); }
.fs-cart__actions { display: flex; flex-wrap: wrap; gap: var(--fs-space-3); margin-top: var(--fs-space-5); }
.fs-cart__convert,
.fs-cart__seam { margin-top: var(--fs-space-5); font-size: var(--fs-body-sm); color: var(--fs-text-muted); }
.fs-checkout__closed { margin-top: var(--fs-space-6); }
