/* =============================================================================
   ONYX & OYSTER — COMPONENTS
   Reusable pieces: navigation, buttons, cards, filters, finder, modal, forms.
   Each reads the --ctx-* ground tokens, so all of them work on any of the four
   section grounds without a variant.
   ========================================================================== */


/* =============================================================================
   NAVIGATION
   A dark shell throughout, as the brand requires. Transparent over the hero,
   solidifying to Onyx once the reader leaves it.
   ========================================================================== */
.oo-nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--oo-z-nav);
  height: var(--oo-nav-height);
  display: flex;
  align-items: center;
  color: var(--oo-ivory);
  --ctx-focus: var(--oo-focus-dark);
  /* .oo-btn--ghost:hover fills with --ctx-text and colors its label with
     --ctx-bg; neither was defined here, so both were guaranteed-invalid —
     the fill never appeared (background-color has no inherited fallback)
     and the label silently kept its own Ivory (color is inherited, so it
     fell back to the value already in effect). Dark ground regardless of
     scroll state (transparent-over-photo or the scrolled near-black fill
     below), so Onyx/Ivory is the correct approximation either way. */
  --ctx-text: var(--oo-ivory);
  --ctx-bg: var(--oo-onyx);
  background-color: transparent;
  border-bottom: var(--oo-hairline) solid transparent;
  transition:
    height           var(--oo-dur-base) var(--oo-ease),
    background-color var(--oo-dur-base) var(--oo-ease),
    border-color     var(--oo-dur-base) var(--oo-ease),
    backdrop-filter  var(--oo-dur-base) var(--oo-ease);
}

/* A soft gradient under the transparent state so the wordmark holds against a
   bright patch of photography without a hard bar appearing. */
.oo-nav::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0.55), rgba(0,0,0,0));
  opacity: 1;
  transition: opacity var(--oo-dur-base) var(--oo-ease);
  pointer-events: none;
}

.oo-nav.is-scrolled {
  height: var(--oo-nav-height-scrolled);
  background-color: rgba(0, 0, 0, 0.92);
  border-bottom-color: var(--oo-rule-on-dark-soft);
  backdrop-filter: blur(14px) saturate(120%);
}
.oo-nav.is-scrolled::before { opacity: 0; }

.oo-nav__inner {
  width: 100%;
  max-width: var(--oo-container-wide);
  margin-inline: auto;
  padding-inline: var(--oo-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--oo-space-lg);
  position: relative;
}

/* --- Brand lockup --------------------------------------------------------- */
.oo-nav__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--oo-space-sm);
  color: var(--oo-ivory);
  flex: none;
}
.oo-nav__brand .oo-emblem {
  height: 1.4rem;
  transition: opacity var(--oo-dur-base) var(--oo-ease);
}
.oo-nav__brand .oo-wordmark {
  font-size: 0.9375rem;
  letter-spacing: 0.26em;
}
.oo-nav__brand:hover .oo-emblem { opacity: 0.7; }

/* --- Links ---------------------------------------------------------------- */
.oo-nav__links {
  display: flex;
  align-items: center;
  gap: clamp(1.25rem, 2.4vw, 2.75rem);
}

.oo-nav__link {
  position: relative;
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-label);
  text-transform: uppercase;
  color: var(--oo-ivory);
  padding-block: var(--oo-space-2xs);
  transition: color var(--oo-dur-fast) var(--oo-ease);
}

/* Hairline that draws in from the left on hover and stays put when the
   section is the one currently in view. */
.oo-nav__link::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  height: var(--oo-hairline);
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--oo-dur-base) var(--oo-ease);
}
.oo-nav__link:hover::after,
.oo-nav__link[aria-current='true']::after { transform: scaleX(1); }
.oo-nav__link[aria-current='true'] { color: var(--oo-oyster); }

.oo-nav__actions {
  display: flex;
  align-items: center;
  gap: var(--oo-space-sm);
  flex: none;
}

/* The navigation's call to action stays discreet — smaller and lighter than a
   button in the body of the page, so it reads as part of the bar. */
.oo-nav__actions .oo-btn {
  min-height: 40px;
  padding-inline: var(--oo-space-lg);
  border-color: var(--oo-rule-on-dark);
}

/* Partner Login. Quieter than the traveller CTA beside it by design: the
   travellers are the primary audience and the partners already know they are
   looking for this. A hairline underline on hover, and nothing else. */
.oo-nav__partner {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding-inline: var(--oo-space-2xs);
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-label);
  text-transform: uppercase;
  color: var(--oo-stone);
  white-space: nowrap;
  transition: color var(--oo-dur-fast) var(--oo-ease);
}
.oo-nav__partner::after {
  content: '';
  position: absolute;
  inset-inline: var(--oo-space-2xs);
  inset-block-end: 10px;
  height: var(--oo-hairline);
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--oo-dur-base) var(--oo-ease);
}
.oo-nav__partner:hover { color: var(--oo-ivory); }
.oo-nav__partner:hover::after { transform: scaleX(1); }

/* --- Mobile menu toggle ---------------------------------------------------
   Two rules that cross into a close mark. 44px target throughout.
   ------------------------------------------------------------------------- */
.oo-nav__toggle {
  display: none;
  width: 44px;
  height: 44px;
  padding: 0;
  cursor: pointer;
  position: relative;
  color: var(--oo-ivory);
}
.oo-nav__toggle span {
  position: absolute;
  left: 11px;
  width: 22px;
  height: 1px;
  background-color: currentColor;
  transition: transform var(--oo-dur-base) var(--oo-ease),
              opacity   var(--oo-dur-fast) var(--oo-ease);
}
.oo-nav__toggle span:nth-child(1) { top: 18px; }
.oo-nav__toggle span:nth-child(2) { top: 25px; }

.oo-nav__toggle[aria-expanded='true'] span:nth-child(1) { transform: translateY(3.5px) rotate(45deg); }
.oo-nav__toggle[aria-expanded='true'] span:nth-child(2) { transform: translateY(-3.5px) rotate(-45deg); }

/* --- Mobile menu panel ----------------------------------------------------- */
.oo-menu {
  position: fixed;
  inset: 0;
  z-index: var(--oo-z-menu);
  background-color: var(--oo-onyx);
  color: var(--oo-ivory);
  --ctx-bg: var(--oo-onyx);
  --ctx-text: var(--oo-ivory);
  --ctx-focus: var(--oo-focus-dark);
  --ctx-muted: var(--oo-stone);
  --ctx-rule: var(--oo-rule-on-dark);
  --ctx-invert-bg: var(--oo-ivory);
  --ctx-invert-text: var(--oo-onyx);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: calc(var(--oo-nav-height) + var(--oo-space-2xl)) var(--oo-gutter) var(--oo-space-2xl);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--oo-dur-base) var(--oo-ease),
              visibility 0s linear var(--oo-dur-base);
}
.oo-menu.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--oo-dur-base) var(--oo-ease), visibility 0s;
}

.oo-menu__list { display: flex; flex-direction: column; }

.oo-menu__item { border-block-end: var(--oo-hairline) solid var(--oo-rule-on-dark-soft); }
.oo-menu__item:first-child { border-block-start: var(--oo-hairline) solid var(--oo-rule-on-dark-soft); }

.oo-menu__link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--oo-space-md);
  padding-block: var(--oo-space-md);
  font-family: var(--oo-font-display);
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  line-height: 1.2;
}
.oo-menu__index {
  font-family: var(--oo-font-body);
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-label);
  color: var(--oo-stone);
}

.oo-menu__footer {
  margin-block-start: var(--oo-space-2xl);
  display: flex;
  flex-direction: column;
  gap: var(--oo-space-md);
}
.oo-menu__meta {
  font-size: var(--oo-text-caption);
  letter-spacing: 0.08em;
  color: var(--oo-stone);
}

/* Each row lifts in behind the one before it. */
.oo-menu .oo-menu__item,
.oo-menu .oo-menu__footer {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--oo-dur-slow) var(--oo-ease),
              transform var(--oo-dur-slow) var(--oo-ease);
}
.oo-menu.is-open .oo-menu__item,
.oo-menu.is-open .oo-menu__footer {
  opacity: 1;
  transform: none;
  transition-delay: calc(60ms * var(--oo-stagger, 0) + 80ms);
}


/* =============================================================================
   BUTTONS & LINK AFFORDANCES
   ========================================================================== */
.oo-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--oo-space-xs);
  min-height: 48px;
  padding: var(--oo-space-sm) var(--oo-space-xl);
  border: var(--oo-hairline) solid transparent;
  border-radius: var(--oo-radius);
  font-family: var(--oo-font-body);
  font-size: var(--oo-text-label);
  font-weight: 400;
  letter-spacing: var(--oo-tracking-button);
  text-transform: uppercase;
  text-align: center;
  cursor: pointer;
  transition: background-color var(--oo-dur-base) var(--oo-ease),
              border-color     var(--oo-dur-base) var(--oo-ease),
              color            var(--oo-dur-base) var(--oo-ease),
              opacity          var(--oo-dur-base) var(--oo-ease);
}

/* Solid — the ground's inverse. Reads as ivory on dark, onyx on light. */
.oo-btn--primary {
  background-color: var(--ctx-invert-bg);
  color: var(--ctx-invert-text);
  border-color: var(--ctx-invert-bg);
}
/* Context-aware hover fill, not a fixed color: Evergreen-on-Evergreen (an
   Evergreen section hovering to an Evergreen button) is invisible, so any
   ground that is itself Evergreen overrides --ctx-hover-bg/--ctx-hover-text
   to Onyx/Oyster instead (see .oo-theme-evergreen). Every other context
   falls back to the original Evergreen/Ivory hover via the var() defaults
   below, so this is a behavior-preserving generalization of the fix that
   was previously duplicated as #hotel-partners/#closing overrides. */
.oo-btn--primary:hover {
  background-color: var(--ctx-hover-bg, var(--oo-evergreen));
  border-color: var(--ctx-hover-bg, var(--oo-evergreen));
  color: var(--ctx-hover-text, var(--oo-ivory));
}

/* Outline — a hairline that fills on hover. */
.oo-btn--ghost {
  background-color: transparent;
  color: var(--ctx-text);
  border-color: var(--ctx-rule);
}
.oo-btn--ghost:hover {
  border-color: var(--ctx-text);
  background-color: var(--ctx-text);
  color: var(--ctx-bg);
}

/* Evergreen — reserved for the closing calls to action. */
.oo-btn--accent {
  background-color: var(--oo-evergreen);
  border-color: var(--oo-evergreen);
  color: var(--oo-ivory);
}
.oo-btn--accent:hover {
  background-color: var(--oo-ivory);
  border-color: var(--oo-ivory);
  color: var(--oo-evergreen);
}

.oo-btn[disabled],
.oo-btn[aria-disabled='true'] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

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

/* --- Quiet text link with a travelling rule ------------------------------- */
.oo-textlink {
  display: inline-flex;
  align-items: center;
  gap: var(--oo-space-2xs);
  min-height: 44px;
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-button);
  text-transform: uppercase;
  color: var(--ctx-text);
  position: relative;
  cursor: pointer;
}
.oo-textlink__label { position: relative; padding-block-end: 3px; }
.oo-textlink__label::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  height: var(--oo-hairline);
  background-color: currentColor;
  transform-origin: left;
  transition: transform var(--oo-dur-base) var(--oo-ease);
}
.oo-textlink:hover .oo-textlink__label::after { transform: scaleX(0); transform-origin: right; }

.oo-textlink svg {
  width: 1em; height: 1em;
  flex: none;
  transition: transform var(--oo-dur-base) var(--oo-ease);
}
.oo-textlink:hover svg { transform: translateX(4px); }


/* =============================================================================
   REVEAL ON SCROLL
   A single 900ms lift. Applied sparingly — one or two elements per view.
   ========================================================================== */
.oo-reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity var(--oo-dur-reveal) var(--oo-ease),
              transform var(--oo-dur-reveal) var(--oo-ease);
  transition-delay: var(--oo-reveal-delay, 0ms);
}
.oo-reveal.is-visible { opacity: 1; transform: none; }


/* =============================================================================
   CATEGORY CARD
   Photograph, label and name always legible. Description, action and arrow
   arrive on hover — and equally on keyboard focus, so the reveal is never
   pointer-only.
   ========================================================================== */
.oo-category-card {
  position: relative;
  display: block;
  overflow: hidden;
  background-color: var(--oo-onyx-raised);
  cursor: pointer;
  isolation: isolate;
  width: 100%;
  text-align: left;
  color: var(--oo-ivory);
  --ctx-focus: var(--oo-focus-dark);
}

.oo-category-card__media { aspect-ratio: 3 / 4; }
.oo-category-card__media img {
  transition: transform var(--oo-dur-image) var(--oo-ease),
              filter    var(--oo-dur-base) var(--oo-ease);
}
.oo-category-card:hover .oo-category-card__media img,
.oo-category-card:focus-visible .oo-category-card__media img { transform: scale(1.05); }

.oo-category-card__scrim {
  position: absolute;
  inset: 0;
  background: var(--oo-scrim-card);
  transition: opacity var(--oo-dur-base) var(--oo-ease);
}
.oo-category-card:hover .oo-category-card__scrim,
.oo-category-card:focus-visible .oo-category-card__scrim { opacity: 0.94; }

.oo-category-card__body {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  padding: var(--oo-space-lg);
  display: flex;
  flex-direction: column;
  gap: var(--oo-space-2xs);
}

.oo-category-card__name {
  font-family: var(--oo-font-display);
  font-size: var(--oo-text-h3);
  line-height: 1.1;
  color: var(--oo-ivory);
}

.oo-category-card__eyebrow { color: var(--oo-oyster); }

/* The reveal. Collapsed by grid-template-rows so nothing jumps when it opens. */
.oo-category-card__reveal {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows var(--oo-dur-slow) var(--oo-ease),
              opacity var(--oo-dur-base) var(--oo-ease);
}
.oo-category-card__reveal > * { overflow: hidden; }

.oo-category-card:hover .oo-category-card__reveal,
.oo-category-card:focus-visible .oo-category-card__reveal {
  grid-template-rows: 1fr;
  opacity: 1;
}

.oo-category-card__description {
  font-size: var(--oo-text-small);
  line-height: var(--oo-leading-normal);
  color: var(--oo-oyster);
  padding-block-start: var(--oo-space-xs);
  max-width: 32ch;
}

.oo-category-card__action {
  display: inline-flex;
  align-items: center;
  gap: var(--oo-space-2xs);
  margin-block-start: var(--oo-space-md);
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-button);
  text-transform: uppercase;
  color: var(--oo-ivory);
}
.oo-category-card__action svg { width: 1em; height: 1em; }

/* Touch devices have no hover; the panel simply stays open. */
@media (hover: none) {
  .oo-category-card__reveal { grid-template-rows: 1fr; opacity: 1; }
}


/* =============================================================================
   EXPERIENCE CARD
   An editorial journal entry rather than a product tile: no border, no shadow,
   no price. The photograph and the name carry it.
   ========================================================================== */
.oo-experience-card {
  display: flex;
  flex-direction: column;
  background: none;
  text-align: left;
  cursor: pointer;
  color: inherit;
  padding: 0;
  width: 100%;
}

.oo-experience-card__media { aspect-ratio: 4 / 3; margin-block-end: var(--oo-space-md); }
.oo-experience-card__media img {
  transition: transform var(--oo-dur-image) var(--oo-ease);
}
.oo-experience-card:hover .oo-experience-card__media img,
.oo-experience-card:focus-visible .oo-experience-card__media img { transform: scale(1.04); }

.oo-experience-card__meta {
  display: flex;
  align-items: baseline;
  gap: var(--oo-space-2xs);
  margin-block-end: var(--oo-space-xs);
}
.oo-experience-card__category {
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-label);
  text-transform: uppercase;
  color: var(--ctx-accent);
}
.oo-experience-card__divider { color: var(--ctx-rule); }
.oo-experience-card__location {
  font-size: var(--oo-text-label);
  letter-spacing: 0.1em;
  color: var(--ctx-muted);
}

.oo-experience-card__name {
  font-family: var(--oo-font-display);
  font-size: var(--oo-text-h3);
  line-height: 1.16;
  margin-block-end: var(--oo-space-2xs);
  transition: opacity var(--oo-dur-fast) var(--oo-ease);
}
.oo-experience-card:hover .oo-experience-card__name { opacity: 0.72; }

.oo-experience-card__summary {
  font-size: var(--oo-text-small);
  line-height: var(--oo-leading-relaxed);
  color: var(--ctx-muted);
  margin-block-end: var(--oo-space-md);
  max-width: 40ch;
}

.oo-experience-card__action { margin-block-start: auto; }

/* Filtering. Hidden items leave the flow entirely so the grid reflows cleanly. */
.oo-experience-card[hidden] { display: none; }


/* =============================================================================
   FILTER ROW
   ========================================================================== */
.oo-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--oo-space-2xs) var(--oo-space-lg);
  padding-block-end: var(--oo-space-md);
  border-block-end: var(--oo-hairline) solid var(--ctx-rule-soft);
}

.oo-filter {
  position: relative;
  min-height: 44px;
  padding: var(--oo-space-2xs) 0;
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-label);
  text-transform: uppercase;
  color: var(--ctx-muted);
  cursor: pointer;
  transition: color var(--oo-dur-fast) var(--oo-ease);
}
.oo-filter::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  inset-block-end: 8px;
  height: var(--oo-hairline);
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--oo-dur-base) var(--oo-ease);
}
.oo-filter:hover { color: var(--ctx-text); }
.oo-filter:hover::after { transform: scaleX(1); }

.oo-filter[aria-pressed='true'] { color: var(--ctx-accent); }
.oo-filter[aria-pressed='true']::after { transform: scaleX(1); }

.oo-filters__count {
  margin-inline-start: auto;
  font-size: var(--oo-text-label);
  letter-spacing: 0.1em;
  color: var(--ctx-muted);
}


/* =============================================================================
   EMPTY STATE
   ========================================================================== */
.oo-empty {
  grid-column: 1 / -1;
  padding-block: var(--oo-space-3xl);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--oo-space-md);
}
.oo-empty .oo-emblem { height: 2.25rem; color: var(--ctx-rule); }
.oo-empty__heading { font-family: var(--oo-font-display); font-size: var(--oo-text-h3); }
.oo-empty__body { color: var(--ctx-muted); max-width: 44ch; font-size: var(--oo-text-small); }
.oo-empty[hidden] { display: none; }


/* =============================================================================
   HERO FINDER
   Frosted dark glass over the photograph. Narrows the collection and moves the
   reader to it — it does not check availability and does not reserve anything.
   ========================================================================== */
.oo-finder {
  background-color: var(--oo-glass-bg);
  border: var(--oo-hairline) solid var(--oo-glass-border);
  backdrop-filter: var(--oo-glass-blur);
  -webkit-backdrop-filter: var(--oo-glass-blur);
  color: var(--oo-ivory);
  --ctx-focus: var(--oo-focus-dark);
  display: grid;
  grid-template-columns: repeat(3, 1fr) auto;
  align-items: stretch;
}

.oo-finder__field {
  display: flex;
  flex-direction: column;
  gap: var(--oo-space-3xs);
  padding: var(--oo-space-md) var(--oo-space-lg);
  border-inline-end: var(--oo-hairline) solid var(--oo-rule-on-dark-soft);
  min-width: 0;
}

.oo-finder__label {
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-label);
  text-transform: uppercase;
  color: var(--oo-stone);
}

/* The control itself. Chevron drawn as a background image so the native select
   arrow can be removed without losing the affordance. */
.oo-finder__control {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  min-height: 32px;
  padding: 0;
  padding-inline-end: var(--oo-space-md);
  background-color: transparent;
  border: 0;
  color: var(--oo-ivory);
  font-size: var(--oo-text-small);
  letter-spacing: 0.02em;
  cursor: pointer;
  background-repeat: no-repeat;
  background-position: right center;
  background-size: 10px 6px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23A99D92' stroke-width='1'/%3E%3C/svg%3E");
}
.oo-finder__control:focus-visible { outline-offset: 6px; }

/* Options inherit the system palette, so they are explicitly set back to brand. */
.oo-finder__control option { background-color: var(--oo-onyx); color: var(--oo-ivory); }

/* Date inputs need the indicator inverted to stay visible on a dark ground. */
.oo-finder__control[type='date'] { background-image: none; }
.oo-finder__control[type='date']::-webkit-calendar-picker-indicator {
  filter: invert(1) opacity(0.5);
  cursor: pointer;
}
.oo-finder__control[type='date']:invalid { color: var(--oo-stone); }

.oo-finder__submit {
  border: 0;
  border-radius: 0;
  padding-inline: var(--oo-space-xl);
  background-color: var(--oo-ivory);
  color: var(--oo-onyx);
  min-height: 100%;
  white-space: nowrap;
}
.oo-finder__submit:hover { background-color: var(--oo-evergreen); color: var(--oo-ivory); }


/* =============================================================================
   MODAL
   Used for experience details and for all four forms. Focus is trapped while
   open, Escape closes, and focus returns to whatever opened it.
   ========================================================================== */
.oo-modal {
  position: fixed;
  inset: 0;
  z-index: var(--oo-z-modal);
  display: grid;
  place-items: center;
  padding: var(--oo-gutter);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--oo-dur-base) var(--oo-ease),
              visibility 0s linear var(--oo-dur-base);
}
.oo-modal.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--oo-dur-base) var(--oo-ease), visibility 0s;
}

/* The scrim reads as background dismissal, which is the only reason it blurs. */
.oo-modal__backdrop {
  position: absolute;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  cursor: pointer;
}

.oo-modal__panel {
  position: relative;
  width: min(46rem, 100%);
  max-height: min(86dvh, 54rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  background-color: var(--oo-onyx-soft);
  border: var(--oo-hairline) solid var(--oo-rule-on-dark);
  color: var(--oo-ivory);
  --ctx-bg: var(--oo-onyx-soft);
  --ctx-text: var(--oo-ivory);
  --ctx-muted: var(--oo-stone);
  --ctx-rule: var(--oo-rule-on-dark);
  --ctx-rule-soft: var(--oo-rule-on-dark-soft);
  --ctx-accent: var(--oo-accent-on-dark);
  --ctx-focus: var(--oo-focus-dark);
  --ctx-invert-bg: var(--oo-ivory);
  --ctx-invert-text: var(--oo-onyx);
  transform: translateY(18px) scale(0.985);
  transition: transform var(--oo-dur-slow) var(--oo-ease);
}
.oo-modal.is-open .oo-modal__panel { transform: none; }

.oo-modal__panel--wide { width: min(58rem, 100%); }

.oo-modal__close {
  position: absolute;
  inset-block-start: var(--oo-space-sm);
  inset-inline-end: var(--oo-space-sm);
  z-index: var(--oo-z-raised);
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  color: var(--oo-ivory);
  background-color: rgba(0, 0, 0, 0.5);
  border-radius: 50%;
  cursor: pointer;
  transition: background-color var(--oo-dur-fast) var(--oo-ease);
}
.oo-modal__close:hover { background-color: rgba(0, 0, 0, 0.85); }
.oo-modal__close svg { width: 14px; height: 14px; }

.oo-modal__media { aspect-ratio: 16 / 9; }

.oo-modal__body { padding: var(--oo-space-xl) clamp(1.5rem, 4vw, 3rem) var(--oo-space-2xl); }

.oo-modal__title {
  font-family: var(--oo-font-display);
  font-size: var(--oo-text-h2);
  line-height: 1.12;
  margin-block: var(--oo-space-xs) var(--oo-space-md);
}

.oo-modal__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--oo-space-2xs) var(--oo-space-md);
  padding-block: var(--oo-space-md);
  margin-block: var(--oo-space-lg);
  border-block: var(--oo-hairline) solid var(--oo-rule-on-dark-soft);
  font-size: var(--oo-text-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--oo-stone);
}
.oo-modal__meta dt { color: rgba(169, 157, 146, 0.7); }
.oo-modal__meta dd { margin: 0; color: var(--oo-ivory); }
.oo-modal__meta > div { display: flex; gap: var(--oo-space-2xs); }

.oo-modal__includes { margin-block: var(--oo-space-lg); }
.oo-modal__includes li {
  position: relative;
  padding-inline-start: var(--oo-space-md);
  padding-block: var(--oo-space-3xs);
  font-size: var(--oo-text-small);
  color: var(--oo-stone);
}
.oo-modal__includes li::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.85em;
  width: 8px;
  height: var(--oo-hairline);
  background-color: var(--oo-stone);
}

.oo-modal__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--oo-space-md);
  align-items: center;
  margin-block-start: var(--oo-space-xl);
}


/* =============================================================================
   FORMS
   Restrained and spacious: hairline underlines rather than boxes, uppercase
   micro-labels, generous vertical rhythm.
   ========================================================================== */
.oo-form { display: flex; flex-direction: column; gap: var(--oo-space-lg); }

.oo-form__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--oo-space-lg);
}
.oo-form__grid > .oo-field--full { grid-column: 1 / -1; }

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

.oo-field__label {
  font-size: var(--oo-text-label);
  letter-spacing: var(--oo-tracking-label);
  text-transform: uppercase;
  color: var(--ctx-muted);
}
.oo-field__optional {
  text-transform: none;
  letter-spacing: 0.02em;
  opacity: 0.7;
}

.oo-field__input {
  width: 100%;
  min-height: 48px;               /* comfortable touch target */
  padding: var(--oo-space-2xs) 0;
  background-color: transparent;
  border: 0;
  border-block-end: var(--oo-hairline) solid var(--ctx-rule);
  border-radius: 0;
  color: var(--ctx-text);
  font-size: var(--oo-text-body);  /* 17px — keeps iOS from zooming on focus */
  transition: border-color var(--oo-dur-fast) var(--oo-ease);
}
.oo-field__input::placeholder { color: var(--ctx-muted); opacity: 0.55; }
.oo-field__input:hover { border-block-end-color: var(--ctx-muted); }
.oo-field__input:focus { outline: none; border-block-end-color: var(--ctx-text); }
.oo-field__input:focus-visible {
  outline: var(--oo-focus-width) solid var(--ctx-focus);
  outline-offset: 4px;
}

textarea.oo-field__input {
  min-height: 7rem;
  padding-block: var(--oo-space-2xs);
  resize: vertical;
  line-height: var(--oo-leading-normal);
}

select.oo-field__input {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  padding-inline-end: var(--oo-space-lg);
  background-repeat: no-repeat;
  background-position: right 0.35rem center;
  background-size: 10px 6px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23A99D92' stroke-width='1'/%3E%3C/svg%3E");
}
select.oo-field__input option { background-color: var(--oo-onyx); color: var(--oo-ivory); }

.oo-field__hint {
  font-size: var(--oo-text-caption);
  color: var(--ctx-muted);
  line-height: var(--oo-leading-normal);
}

/* Error state. Colour is never the only signal — the message itself carries it,
   and aria-invalid carries it to assistive technology. */
.oo-field__error {
  display: none;
  align-items: flex-start;
  gap: var(--oo-space-3xs);
  font-size: var(--oo-text-caption);
  color: #E8A5A5;              /* 7.4 : 1 on the modal ground */
  line-height: var(--oo-leading-normal);
}
.oo-field.is-invalid .oo-field__error { display: flex; }
.oo-field.is-invalid .oo-field__input { border-block-end-color: #E8A5A5; }
.oo-field__error svg { width: 14px; height: 14px; margin-block-start: 2px; flex: none; }

/* Consent checkbox. The box is drawn rather than native so it matches the rest
   of the system, but the input itself remains a real checkbox underneath. */
.oo-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--oo-space-sm);
  cursor: pointer;
  padding-block: var(--oo-space-2xs);
}
.oo-consent input {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 20px;
  height: 20px;
  margin: 2px 0 0;
  border: var(--oo-hairline) solid var(--ctx-rule);
  background-color: transparent;
  cursor: pointer;
  transition: background-color var(--oo-dur-fast) var(--oo-ease),
              border-color var(--oo-dur-fast) var(--oo-ease);
}
.oo-consent input:checked {
  background-color: var(--ctx-text);
  border-color: var(--ctx-text);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M1 5l3.5 3.5L11 1.5' fill='none' stroke='%23000' stroke-width='1.6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 11px 9px;
}
.oo-consent__text {
  font-size: var(--oo-text-caption);
  line-height: var(--oo-leading-normal);
  color: var(--ctx-muted);
}

/* Honeypot. Removed from the accessibility tree and from the tab order so no
   real person can land in it — only a script filling every field will. */
.oo-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --- Form status ----------------------------------------------------------
   A live region. Announces validation summaries, send failures and the preview
   notice. Success replaces the form entirely and is handled below.
   ------------------------------------------------------------------------- */
.oo-form__status {
  display: none;
  gap: var(--oo-space-2xs);
  padding: var(--oo-space-sm) var(--oo-space-md);
  border-inline-start: 2px solid currentColor;
  font-size: var(--oo-text-small);
  line-height: var(--oo-leading-normal);
}
.oo-form__status.is-visible { display: flex; }
.oo-form__status--error  { color: #E8A5A5; background-color: rgba(232, 165, 165, 0.07); }
.oo-form__status--notice { color: var(--oo-oyster); background-color: rgba(220, 207, 195, 0.07); }
.oo-form__status a { text-decoration: underline; text-underline-offset: 0.24em; }

/* --- Success --------------------------------------------------------------
   Shown only after the form service confirms receipt. Nothing here is ever
   displayed on the strength of a click alone.
   ------------------------------------------------------------------------- */
.oo-form__success { display: none; text-align: center; padding-block: var(--oo-space-xl); }
.oo-form__success.is-visible { display: block; }
.oo-form__success .oo-emblem {
  height: 2.5rem;
  margin-inline: auto;
  margin-block-end: var(--oo-space-lg);
  color: var(--oo-oyster);
}
.oo-form__success-heading {
  font-family: var(--oo-font-display);
  font-size: var(--oo-text-h3);
  margin-block-end: var(--oo-space-sm);
}
.oo-form__success-body {
  color: var(--ctx-muted);
  font-size: var(--oo-text-small);
  max-width: 40ch;
  margin-inline: auto;
}

/* Pending state on the submit button. */
.oo-btn.is-pending { pointer-events: none; opacity: 0.6; }
.oo-btn__spinner {
  width: 13px; height: 13px;
  border: 1.5px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: oo-spin 720ms linear infinite;
  display: none;
}
.oo-btn.is-pending .oo-btn__spinner { display: block; }
@keyframes oo-spin { to { transform: rotate(360deg); } }
