/* Breakpoints (the only ones used, all min-width, mobile-first):
     xs 340px · sm 640px · md 900px · lg 1200px · xl 1440px
   Kept in site.json under `breakpoints`. CSS cannot read a custom property
   inside a media query condition, so they live in main.css as literals and
   this comment is the single documented list. */

/* Generated. Do not edit this file.
   Tokens come from src/_data/site.json; the rest is assets/css/fonts.css and
   assets/css/main.css concatenated, so the page makes one stylesheet request.
   Edit those sources and run `npm run build`. */
:root {
  --color-body: #3A403D;
  --color-gold: #B5935F;
  --color-gold-ink: #7E6538;
  --color-ink: #1E2321;
  --color-ivory: #F6F1E8;
  --color-ivory-raised: #FBF8F2;
  --color-lagoon: #0F3D3B;
  --color-lagoon-mid: #1B4F4D;
  --color-muted: #5B615E;
  --color-muted-soft: #63696E;
  --color-on-dark: #F6F1E8;
  --color-on-dark-muted: rgba(246, 241, 232, 0.7);
  --color-on-dark-soft: rgba(246, 241, 232, 0.86);
  --color-placeholder-1: #CFC3AD;
  --color-placeholder-2: #B8AF9C;
  --color-placeholder-3: #9EA79C;
  --color-rule: rgba(15, 61, 59, 0.15);
  --color-rule-on-dark: rgba(246, 241, 232, 0.2);
  --color-rule-strong: rgba(15, 61, 59, 0.28);
  --color-sand: #EDE5D6;
  --color-whatsapp: #25D366;
  --color-whatsapp-dark: #1DA851;

  --font-display: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --font-body: 'Inter', 'Helvetica Neue', Arial, sans-serif;

  --type-eyebrow-size: 11px;
  --type-eyebrow-weight: 500;
  --type-eyebrow-tracking: 0.36em;

  --type-h1: clamp(44px, calc(44px + ((92 - 44) * ((100vw - 375px) / 1065))), 92px);
  --type-h1-weight: 300;
  --type-h1-leading: 0.98;
  --type-h1-tracking: -0.01em;

  --type-h2: clamp(34px, calc(34px + ((56 - 34) * ((100vw - 375px) / 1065))), 56px);
  --type-h2-weight: 300;
  --type-h2-leading: 1.05;

  --type-card-heading: clamp(19px, calc(19px + ((22 - 19) * ((100vw - 375px) / 1065))), 22px);
  --type-card-heading-weight: 400;
  --type-card-heading-leading: 1.25;
  --type-h3: clamp(26px, calc(26px + ((34 - 26) * ((100vw - 375px) / 1065))), 34px);
  --type-h3-weight: 400;
  --type-h3-leading: 1.1;

  --type-statement: clamp(24px, calc(24px + ((36 - 24) * ((100vw - 375px) / 1065))), 36px);
  --type-statement-weight: 300;
  --type-statement-leading: 1.35;

  --type-body: 16px;
  --type-body-weight: 300;
  --type-body-leading: 1.75;
  --type-body-lg: 17px;
  --type-body-sm: 15px;
  --type-body-sm-leading: 1.7;

  --type-list: clamp(16px, calc(16px + ((18 - 16) * ((100vw - 375px) / 1065))), 18px);
  --type-brand: clamp(18px, calc(18px + ((22 - 18) * ((100vw - 375px) / 1065))), 22px);

  --type-label: 12px;
  --type-label-tracking: 0.13em;
  --type-label-weight: 500;

  --type-meta: 11px;
  --type-meta-tracking: 0.18em;

  --type-fine: 12px;
  --type-fine-leading: 1.7;--type-timeline-year: clamp(28px, calc(28px + ((40 - 28) * ((100vw - 375px) / 1065))), 40px);
  --type-timeline-year-weight: 300;

  --type-stat: clamp(34px, calc(34px + ((48 - 34) * ((100vw - 375px) / 1065))), 48px);
  --type-stat-weight: 300;
  --type-stat-lg: clamp(44px, calc(44px + ((64 - 44) * ((100vw - 375px) / 1065))), 64px);
  --media-hover-scale: 1.03;
  --media-plan-frame-max: 390px;
  --media-plan-hover-scale: 1.02;
  --media-placeholder-min-height: 220px;
  --media-ratio-portrait: 3 / 4;
  --media-ratio-square: 1 / 1;
  --media-ratio-tall: 4 / 5;
  --media-ratio-wide: 16 / 9;
  --media-sizes-full: 100vw;
  --media-sizes-modal-panel: (max-width: 899px) 1px, 1200px;
  --media-sizes-plan-card: (max-width: 899px) 85vw, (max-width: 1439px) 41vw, 43vw;
  --media-sizes-square: (max-width: 899px) 90vw, (max-width: 1439px) 26vw, 27vw;
  --media-sizes-tall: (max-width: 899px) 90vw, (max-width: 1439px) 45vw, 46vw;
  --media-sizes-third: (max-width: 639px) 90vw, (max-width: 1199px) 44vw, 31vw;
  --media-sizes-wide: (max-width: 899px) 90vw, (max-width: 1439px) 52vw, 54vw;
  --header-anchor-gap: 24px;
  --header-bar-gap: 14px;
  --header-blur: 15px;
  --header-blur-scrolled: 20px;
  --header-border: rgba(255, 255, 255, 0.15);
  --header-border-width: 1px;
  --header-cta-hover: rgba(246, 241, 232, 0.16);
  --header-drawer-weight: 500;
  --header-outline-current: rgba(246, 241, 232, 0.2);
  --header-outline-hover: rgba(246, 241, 232, 0.1);
  --header-ease: cubic-bezier(0.215, 0.61, 0.355, 1);
  --header-height-desktop: 66px;
  --header-height-mobile: 52px;
  --header-link-size: 13px;
  --header-link-tracking: normal;
  --header-link-transform: none;
  --header-link-weight: 600;
  --header-lockup-height: 26px;
  --header-mark: /assets/brand/wyndham-icon-gold.svg;
  --header-mark-size: 24px;
  --header-nav-gap: 18px;
  --header-pad-desktop: 22px;
  --header-pad-mobile: 16px;
  --header-radius: 50px;
  --header-radius-open: 24px;
  --header-saturate: 1.8;
  --header-surface: rgba(10, 10, 10, 0.5);
  --header-surface-scrolled: rgba(10, 10, 10, 0.72);
  --header-surface-solid: #0F3D3B;
  --header-top: 26px;
  --header-underline-duration: 0.3s;
  --header-underline-offset: 7px;
  --header-width-desktop: 620px;
  --map-badge-size: 22px;
  --map-badge-text: 11px;
  --map-coast-width: 2;
  --map-draw-duration: 1.8s;
  --map-draw-ease: cubic-bezier(0.215, 0.61, 0.355, 1);
  --map-fill: rgba(15, 61, 59, 0.05);
  --map-halo: rgba(181, 147, 95, 0.18);
  --map-inset-opacity: 0.55;
  --map-label-halo: #F6F1E8;
  --map-label-halo-width: 5;
  --map-label-size: 40px;
  --map-label-size-lg: 17px;
  --map-label-size-sm: 21px;
  --map-label-tracking: 0.12em;
  --map-leader-duration: 0.5s;
  --map-leader-step: 0.06s;
  --map-leader-width: 1;
  --map-legend-bottom: 70;
  --map-legend-clear: 9;
  --map-legend-elbow-x: 540;
  --map-legend-gap: 14;
  --map-legend-line: 1.35em;
  --map-legend-size: 15px;
  --map-legend-sub-size: 12px;
  --map-legend-top: 70;
  --map-legend-x: 620;
  --map-list-gap: 14px;
  --map-list-meta-size: 11px;
  --map-list-name-size: 12px;
  --map-marker-duration: 0.6s;
  --map-marker-step: 0.09s;
  --map-max-width: 920px;
  --map-outline: rgba(15, 61, 59, 0.28);
  --map-outline-width: 1.5;
  --map-pulse-duration: 2.6s;
  --map-reference-opacity: 0.45;
  --map-sub-size: 32px;
  --map-sub-size-lg: 14px;
  --map-sub-size-sm: 17px;
  --map-tip-blur: 10px;
  --map-tip-border: #B5935F;
  --map-tip-border-width: 1px;
  --map-tip-ease: cubic-bezier(0.215, 0.61, 0.355, 1);
  --map-tip-ground: rgba(246, 241, 232, 0.85);
  --map-tip-ground-solid: #F6F1E8;
  --map-tip-in: 220ms;
  --map-tip-offset: 14px;
  --map-tip-out: 160ms;
  --map-tip-pad: 9px 12px;
  --map-tip-rise: 6px;
  --map-tip-size: 14px;
  --map-tip-text: #1E2321;
  --map-tip-time: #0F3D3B;
  --map-tip-time-size: 13px;
  --footer-band-gap: 48px;
  --footer-legal-column-gap: 48px;
  --footer-legal-columns: 1fr 1fr;
  --footer-legal-size: 12px;
  --footer-legal-weight: 400;
  --footer-nav-row-gap: 18px;
  --footer-nav-tracking: 0.14em;
  --footer-rule-gap: 32px;
  --footer-top-pad: 72px;
  --footer-underline-offset: 6px;
  --footer-widget-clearance: 56px;
  --hero-min-desktop: min(930px, 95vh);
  --hero-min-mobile: 76svh;
  --hero-min-mobile-fallback: 76vh;
  --hero-pad-end-desktop: 88px;
  --hero-pad-end-mobile: 56px;
  --hero-page-min-desktop: min(605px, 67vh);
  --hero-page-min-mobile: 61svh;
  --hero-page-min-mobile-fallback: 61vh;
  --hero-page-pad-end-desktop: 64px;
  --hero-page-pad-end-mobile: 48px;
  --hero-scrim-base: rgba(0, 0, 0, 0.62);
  --hero-scrim-end: 700px;
  --hero-scrim-hold: 420px;
  --hero-scrim-mid: rgba(0, 0, 0, 0.34);
  --hero-scrim-mid-stop: 560px;
  --hero-scrim-top: rgba(0, 0, 0, 0);
  --band-min-desktop: 60vh;
  --band-min-mobile: 70svh;
  --band-min-mobile-fallback: 70vh;
  --band-scale-from: 1.06;
  --band-scrim-edge: rgba(0, 0, 0, 0.30);
  --band-scrim-mid: rgba(0, 0, 0, 0.62);
  --widget-caret-width: 52px;
  --widget-divider: rgba(246, 241, 232, 0.22);
  --widget-gap: 10px;
  --widget-height: 48px;
  --widget-icon-size: 20px;
  --widget-icon-stroke: 1.25;
  --widget-journey-dot-size: 6px;
  --widget-journey-icon-size: 20px;
  --widget-journey-option-gap: 10px;
  --widget-journey-option-pad: 14px 16px;
  --widget-journey-option-radius: 10px;
  --widget-journey-step-in: 200ms;
  --widget-journey-step-shift: 12px;
  --widget-label-size: 13px;
  --widget-label-tracking: normal;
  --widget-modal-close-disc: rgba(15, 20, 19, 0.45);
  --widget-modal-in: 220ms;
  --widget-modal-lockup-width: 168px;
  --widget-modal-max-width: 560px;
  --widget-modal-max-width-journey: 880px;
  --widget-modal-pad: 28px;
  --widget-modal-panel-width: 320px;
  --widget-modal-radius: 20px;
  --widget-modal-scale-from: 0.98;
  --widget-modal-wash-height: 150px;
  --widget-offset: 24px;
  --widget-offset-mobile: 16px;
  --widget-pad: 20px;
  --widget-panel-gap: 10px;
  --widget-panel-in: 200ms;
  --widget-panel-radius: 18px;
  --widget-panel-rise: 6px;
  --widget-panel-row-height: 52px;
  --widget-panel-width: 248px;
  --widget-radius: 50px;
  --widget-wa-disc: 56px;
  --widget-wa-gap: 12px;
  --widget-wa-glyph: 24px;
  --widget-wa-glyph-disc: 26px;
  --widget-wa-height: 56px;
  --widget-wa-label-size: 15px;
  --widget-wa-label-weight: 600;
  --widget-wa-pad: 24px;
  --widget-z: 60;
  --field-consent-gap: 12px;
  --field-consent-gap-modal: 10px;
  --field-consent-leading: 1.6;
  --field-consent-leading-modal: 1.4;
  --field-consent-size: 14px;
  --field-consent-size-inline: 13px;
  --field-consent-size-modal: 13px;
  --field-height: 52px;
  --field-height-inline: 48px;
  --field-height-modal: 48px;
  --field-label-gap: 8px;
  --field-label-gap-modal: 5px;
  --field-modal-heading: clamp(21px, 2.2vw, 25px);
  --field-pad-block: 14px;
  --field-pad-block-modal: 12px;
  --field-pad-inline: 16px;
  --field-row-gap: 20px;
  --field-row-gap-inline: 16px;
  --field-row-gap-modal: 13px;
  --field-size: 15px;
  --lightbox-backdrop-centre: rgba(0, 0, 0, 0.55);
  --lightbox-backdrop-edge: rgba(0, 0, 0, 0.75);
  --lightbox-backdrop-solid: rgba(0, 0, 0, 0.8);
  --lightbox-blur: 16px;
  --lightbox-caption-gap: 18px;
  --lightbox-control-inset: 18px;
  --lightbox-control-size: 44px;
  --lightbox-duration: 250ms;
  --lightbox-ease: cubic-bezier(0.215, 0.61, 0.355, 1);
  --lightbox-focus-ring: 2px;
  --lightbox-icon-size: 22px;
  --lightbox-icon-stroke: 1.25;
  --lightbox-img-max-height: 85svh;
  --lightbox-img-max-width: 90vw;
  --lightbox-scale-from: 0.98;
  --lightbox-stage-pad: 24px;

  --space-gutter: clamp(24px, calc(24px + ((72 - 24) * ((100vw - 375px) / 1065))), 72px);
  --space-section: clamp(72px, calc(72px + ((140 - 72) * ((100vw - 375px) / 1065))), 140px);
  --space-section-tight: clamp(64px, calc(64px + ((120 - 64) * ((100vw - 375px) / 1065))), 120px);
  --grid-columns: 12;
  --grid-gap: clamp(16px, calc(16px + ((24 - 16) * ((100vw - 375px) / 1065))), 24px);
  --space-text-inset: calc(var(--space-gutter) - var(--grid-gap));
  --space-measure-center: 880px;
  --space-measure: 760px;
  --space-statement-gap: 40px;
  --space-timeline-rise: 26px;

  --brand-lockup-hero: clamp(44px, calc(44px + ((64 - 44) * ((100vw - 375px) / 1065))), 64px);
  --brand-idiliq-mark: 26px;
  --brand-idiliq-dev-mark: calc(clamp(38px, 4.4vw, 58px) * 81 / 50);
  --brand-idiliq-mark-inline: 18px;
  --brand-wyndham-wordmark-inline: 24px;
  --brand-idiliq-credit-width: 170px;
  --brand-statement-icon: 30px;

  --space-list-gap: clamp(16px, calc(16px + ((24 - 16) * ((100vw - 375px) / 1065))), 24px);
  --radius: 0;
  --hairline: 1px;
  --tap-target: 44px;
}

/* Self-hosted webfonts. Both families are SIL Open Font Licence 1.1, which
   permits self-hosting; the licence texts sit beside the files in
   /assets/brand/fonts/. Nothing is requested from Google Fonts, so the site
   makes no third-party font request and needs no consent gate for type. */

@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("/assets/brand/fonts/cormorant-garamond-latin-300-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/brand/fonts/cormorant-garamond-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/brand/fonts/cormorant-garamond-latin-500-normal.woff2") format("woff2");
}

/* Carried over with the header from IDI-1159. Registered so the file is usable;
   nothing on this branch sets it yet, so it triggers no request. */
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/brand/fonts/cormorant-garamond-latin-400-italic.woff2") format("woff2");
}

/* Inter, one variable file covering the whole 100-900 axis. It replaces the four static
   weights this file used to carry: fewer bytes than they came to together,
   and one request instead of four. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/brand/fonts/inter-latin-wght-normal.woff2") format("woff2");
}


/* Wyndham Grand Cap Cana — review build.
   Every colour, size and spacing value comes from tokens.css,
   which is generated from src/_data/site.json. Do not hard-code values here. */

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body { margin: 0; }
img, picture, svg { display: block; max-width: 100%; }
button, input, textarea, select { font: inherit; color: inherit; }
h1, h2, h3, h4, p, figure, blockquote, ul, ol { margin: 0; padding: 0; }
ul, ol { list-style: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------- base ---------- */
body {
  background: var(--color-ivory);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--type-body);
  font-weight: var(--type-body-weight);
  line-height: var(--type-body-leading);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--color-lagoon); }
a:hover { color: var(--color-gold); }

:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 3px;
}

.skip-link {
  position: absolute;
  min-height: var(--tap-target);
  align-items: center;
  left: var(--space-gutter);
  top: 0;
  transform: translateY(-120%);
  z-index: 100;
  background: var(--color-lagoon);
  color: var(--color-on-dark);
  padding: 12px 20px;
  font-size: var(--type-label);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  text-decoration: none;
}
.skip-link:focus { transform: translateY(0); color: var(--color-on-dark); }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- layout ---------- */
.wrap { padding-inline: var(--space-gutter); }

.grid {
  display: grid;
  /* minmax(0, 1fr), not 1fr. A 1fr track is at least min-content wide, so a
     column holding a form can refuse to shrink and push the page wider than
     the viewport -- measured at 320, where the phone row's button-plus-input
     took the signup column to 311px inside a 272px container and gave the
     homepage 15px of horizontal scroll. The md rule has always used minmax;
     this is the base catching up. */
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--grid-gap);
  row-gap: var(--space-section-tight);
}

.section { padding-block: var(--space-section); }
.section--tight { padding-block: var(--space-section-tight); }
.section--flush-top { padding-block-start: 0; }

.section--dark {
  background: var(--color-lagoon);
  color: var(--color-on-dark);
}
.section--dark-mid {
  background: var(--color-lagoon-mid);
  color: var(--color-on-dark);
}
.section--sand { background: var(--color-sand); }

.section--dark a, .section--dark-mid a { color: var(--color-on-dark); }
.section--dark a:hover, .section--dark-mid a:hover { color: var(--color-gold); }

.stack { display: flex; flex-direction: column; }
.stack--sm { gap: 16px; }
.stack--md { gap: 24px; }
.stack--lg { gap: 40px; }
.stack--xl { gap: 64px; }

/* ---------- grid placement ---------- */
/* Stacked, the map leads and the distances follow it; from md the row is two
   columns again and DOM order decides. */
.grid--map > .map-column { order: -1; }

/* ---------- bleeding a media column off the page edge ----------
   The column keeps its place in the grid and simply starts further left: one
   gutter takes it to the edge of the wrap, and the grid's own left offset is
   already zero for the first column. The row stops centring so the media can
   meet the section above with no gap. */
.grid--bleed-left { align-items: start; }
.grid--bleed-left > .bleed-left { margin-inline-start: calc(-1 * var(--space-gutter)); }
/* A bleeding map fills whatever width it is given rather than sitting on its
   own centred measure. */
.grid--bleed-left > .bleed-left .map { max-width: none; margin-inline: 0; }

.grid--center { align-items: center; }
.grid--start { align-items: start; }
.grid--end { align-items: end; row-gap: var(--grid-gap); }

/* Column placement is a wide-screen concern: below md every grid child is a
   single full-width row, so the utilities are defined at md and above only. */
.subgrid-2 {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}

.measure-center {
  max-width: var(--space-measure-center);
  margin-inline: auto;
  align-items: center;
  text-align: center;
}
.measure { max-width: var(--space-measure); }

/* The standalone opening paragraph, one shape everywhere it appears: a short
   gold hairline, then the statement, centred on the same measure. It used to be
   two shapes — the homepage carried the hairline and a 40px gap, the other
   pages carried neither — which is what this block exists to prevent. */
.statement-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-statement-gap);
  max-width: var(--space-measure-center);
  margin-inline: auto;
  text-align: center;
}
.mt-section { margin-top: var(--space-section-tight); }

/* ---------- brand marks ----------
   Supplied artwork, used as artwork: sized from a token, never recoloured, and
   always the variant whose ink suits the ground it sits on. Each one replaces
   type that said the same thing, so the alt text carries the name and nothing
   is announced twice. */
/* align-self, not just width:auto. These sit in .stack, a column flex box, and
   a stretched flex item ignores the intrinsic ratio and takes the column's full
   width — which stretches the artwork. */
/* The hero lockup. Below md the pill carries the lockup already, and two of
   them one above the other is the page saying the property's name twice before
   the tagline has said anything -- so here it is the pill's job on a phone and
   the hero's on a desktop, where the pill has only the crown. */
.hero .brand-lockup { display: none; }
.brand-lockup {
  display: block;
  align-self: start;
  width: auto;
  height: var(--brand-lockup-hero);
}

.brand-mark {
  display: block;
  align-self: start;
  width: auto;
  height: var(--brand-idiliq-mark);
}

/* Inline in an eyebrow, beside a word the mark does not itself say. Sized to
   the cap height of the type it sits with rather than to the standalone mark.
   The flex rule lives with .eyebrow further down, after the base sets display. */
.brand-mark--inline { height: var(--brand-idiliq-mark-inline); }
/* The Wyndham Grand wordmark on the Info page's "A Global Standard" row, the
   only element that takes this token -- the header and footer marks size from
   their own. It was first sized to the cap height of the eyebrow it replaced
   (12px, caps at 7.97px) and read too small to register as the brand; IDI-1182
   doubled it to 24px, caps ~16px, about 244px wide. Still an eyebrow: it keeps
   the heading's left edge and the stack's 24px gap to the heading, the same
   gap every type eyebrow on the site has. */
.brand-mark--wordmark { height: var(--brand-wyndham-wordmark-inline); }

.statement-block__mark {
  display: block;
  width: var(--brand-statement-icon);
  height: auto;
}

/* ---------- type ---------- */
.eyebrow {
  display: block;
  font-size: var(--type-eyebrow-size);
  font-weight: var(--type-eyebrow-weight);
  letter-spacing: var(--type-eyebrow-tracking);
  text-transform: uppercase;
  color: var(--color-gold-ink);
}
/* After .eyebrow, not before: the base sets display:block at the same
   specificity, so an earlier flex here would lose and drop the word below the
   mark. */
.eyebrow--mark {
  display: flex;
  align-items: center;
  gap: 12px;
}

.section--dark .eyebrow,
.section--dark-mid .eyebrow,
/* Ivory, not gold. Gold needs the ground under it at about 0.82 alpha to reach
   4.5:1, which is the heavy green scrim this ticket replaced; over a neutral
   scrim at 0.62 it measures 2.3:1. Gold stays everywhere it sits on a solid
   ground and passes. */
.hero .eyebrow { color: var(--color-on-dark); }

/* ---------- /brokers/ (IDI-1238) ----------

   The ledger: four markets as rows of place, units and year. It borrows the
   hairline vocabulary of the IDILIQ figures but not their shape -- that is a
   grid of eight stats about the company, this is four rows of the two things
   a broker weighs, where and since when. One row per market, rules between,
   nothing else.

   Written on a light ground, which is where it sits: the same ink tokens as
   the IDILIQ page's own markets block -- muted label, lagoon figure, gold-ink
   year -- so the two readings of the same four facts look related rather than
   like two different components. The on-dark variant is kept below it so the
   ledger is not locked to one ground. */
.ledger { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.ledger__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px;
  padding-block: 20px;
  border-block-start: var(--hairline) solid var(--color-rule);
}
.ledger__row:last-child { border-block-end: var(--hairline) solid var(--color-rule); }
.ledger__place {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
.ledger__count {
  font-family: var(--font-display);
  font-size: var(--type-stat);
  font-weight: var(--type-stat-weight);
  line-height: 1;
  color: var(--color-lagoon);
  font-variant-numeric: tabular-nums;
}
.ledger__suffix {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
/* The same move as .markets__since, so the two readings of these four facts
   stay related: ink for the words, the gold in a hairline beside them. */
.ledger__since {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  color: var(--color-ink);
  display: flex;
  align-items: center;
  gap: 10px;
}
.ledger__since::before {
  content: "";
  flex: none;
  width: 24px;
  height: var(--hairline);
  background: var(--color-gold);
}
.ledger__note {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  line-height: var(--type-body-sm-leading);
  color: var(--color-muted);
  max-width: var(--space-measure);
}
.section--dark .ledger__row { border-block-start-color: var(--color-rule-on-dark); }
.section--dark .ledger__row:last-child { border-block-end-color: var(--color-rule-on-dark); }
.section--dark .ledger__place { color: var(--color-on-dark); }
.section--dark .ledger__count { color: var(--color-gold); }
.section--dark .ledger__suffix,
.section--dark .ledger__since { color: var(--color-on-dark-muted); }
.section--dark .ledger__note { color: var(--color-on-dark-soft); }

/* What a broker receives. The status line is the point of the block: an
   unconfirmed item has to read as unconfirmed, not as missing. */
.kit { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--grid-gap); }
.kit__item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-block-start: 20px;
  border-block-start: var(--hairline) solid var(--color-rule);
}
.kit__label {
  font-family: var(--font-display);
  font-size: var(--type-card-heading);
  font-weight: var(--type-card-heading-weight);
  line-height: var(--type-card-heading-leading);
  color: var(--color-ink);
  margin: 0;
}
.kit__detail {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  line-height: var(--type-body-sm-leading);
  color: var(--color-muted);
  margin: 0;
}
.kit__status {
  font-family: var(--font-body);
  font-size: var(--type-fine);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  color: var(--color-lagoon);
  margin: 0;
}
/* Not a warning colour and not hidden: it is a fact about the item, set in
   the muted ink so it reads as a note rather than an alarm. */
.kit__status--pending { text-transform: none; letter-spacing: normal; color: var(--color-muted); font-style: italic; }

/* The three steps. Numbered because a broker cannot do the third before the
   first; nothing else on the page is. */
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--grid-gap); counter-reset: step; }
.steps__item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 16px; align-items: start; }
.steps__index {
  font-family: var(--font-display);
  font-size: var(--type-stat);
  font-weight: 300;
  line-height: 0.9;
  color: var(--color-gold);
  font-variant-numeric: tabular-nums;
}
.steps__text { display: flex; flex-direction: column; gap: 6px; }
.steps__heading {
  font-family: var(--font-display);
  font-size: var(--type-card-heading);
  font-weight: var(--type-card-heading-weight);
  line-height: var(--type-card-heading-leading);
  color: var(--color-ink);
  margin: 0;
}
.steps__body {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  line-height: var(--type-body-sm-leading);
  color: var(--color-muted);
  margin: 0;
}
/* On the dark band, which is where they sit. The gold numeral is already the
   on-dark gold and needs nothing; the two inks do. */
.section--dark .steps__heading { color: var(--color-on-dark); }
.section--dark .steps__body { color: var(--color-on-dark-soft); }


@media (min-width: 900px) {
  .ledger__row { grid-template-columns: minmax(0, 1fr) auto auto; gap: 24px; align-items: baseline; }
  .ledger__since { text-align: end; min-width: 8ch; }
  .kit { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- the display face sets no common ligatures ----------

   Cormorant Garamond ships a Th ligature, and it is the wrong call for this
   site: at display sizes the joined Th reads as one wide glyph with the h's
   ascender growing out of the T's crossbar, and on "Things To Do" and "There
   is a place" -- both of which open a section -- it is the first thing the eye
   lands on. Set as separate letters it is quieter and matches the rest of the
   line.

   font-variant-ligatures and font-feature-settings both, because they are two
   different switches and engines disagree about which wins: the first is the
   high-level property, the second turns the raw OpenType features off for
   anything that reads them directly.

   ONE RULE, LISTING EVERY DISPLAY-FACE SELECTOR. CSS has no way to say "every
   element using --font-display", so this is the list, and it is the list to
   add to. If you set font-family: var(--font-display) anywhere new, add the
   selector here too.

   Inter is untouched. It has no Th ligature to suppress and its common
   ligatures -- fi, fl -- are wanted in body text. */
.display-1, .display-2, .display-3,
.statement,
.nav-drawer__link,
.plan-card__type,
.stat__value,
.list-numbered__index,
.list-note,
.entry__name,
.consent__title,
.markets__count,
.timeline__when,
.image-band__line {
  font-variant-ligatures: no-common-ligatures;
  font-feature-settings: "liga" 0, "clig" 0;
}

.display-1, .display-2, .display-3 {
  font-family: var(--font-display);
  color: var(--color-lagoon);
}
.section--dark .display-1, .section--dark .display-2, .section--dark .display-3,
.section--dark-mid .display-1, .section--dark-mid .display-2, .section--dark-mid .display-3,
.hero .display-1 { color: var(--color-on-dark); }

.display-1 {
  font-size: var(--type-h1);
  font-weight: var(--type-h1-weight);
  line-height: var(--type-h1-leading);
  letter-spacing: var(--type-h1-tracking);
}
.display-2 {
  font-size: var(--type-h2);
  font-weight: var(--type-h2-weight);
  line-height: var(--type-h2-leading);
}
.display-3 {
  font-size: var(--type-h3);
  font-weight: var(--type-h3-weight);
  line-height: var(--type-h3-leading);
}

.statement {
  font-family: var(--font-display);
  font-size: var(--type-statement);
  font-weight: var(--type-statement-weight);
  line-height: var(--type-statement-leading);
  color: var(--color-ink);
}

.prose { color: var(--color-body); }
.prose--lg { font-size: var(--type-body-lg); }
.prose--sm { font-size: var(--type-body-sm); line-height: var(--type-body-sm-leading); }
.section--dark .prose, .section--dark-mid .prose { color: var(--color-on-dark-soft); }

.meta {
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
.section--dark .meta, .section--dark-mid .meta { color: var(--color-on-dark-muted); }

.fine {
  font-size: var(--type-fine);
  line-height: var(--type-fine-leading);
  color: var(--color-muted-soft);
}

/* ---------- rules ---------- */
.rule {
  display: block;
  width: 100%;
  height: var(--hairline);
  background: var(--color-rule);
  border: 0;
  transform-origin: left center;
}
.rule--gold { background: var(--color-gold); }
.rule--short { width: 40px; }
.section--dark .rule, .section--dark-mid .rule { background: var(--color-rule-on-dark); }

/* ---------- buttons and links ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 30px;
  border: var(--hairline) solid transparent;
  border-radius: var(--radius);
  font-size: var(--type-label);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
.btn--solid { background: var(--color-ivory); color: var(--color-lagoon); }
.btn--solid:hover { background: var(--color-gold); color: var(--color-lagoon); }
.btn--lagoon { background: var(--color-lagoon); color: var(--color-ivory); }
.btn--lagoon:hover { background: var(--color-gold); color: var(--color-lagoon); }
.btn--gold { background: var(--color-gold); color: var(--color-ink); border-color: var(--color-gold); }
.btn--gold:hover { background: transparent; color: var(--color-ink); }
.btn--outline { background: transparent; color: var(--color-ivory); border-color: var(--color-on-dark-muted); }
.btn--inline { align-self: flex-start; }
.btn--ghost { background: transparent; color: var(--color-lagoon); border-color: var(--color-rule-strong); }
.btn--ghost:hover { background: var(--color-lagoon); color: var(--color-ivory); }
.btn--outline:hover { background: var(--color-ivory); color: var(--color-lagoon); }
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-height: var(--tap-target);
  font-size: var(--type-label);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  text-decoration: none;
  align-self: flex-start;
}
.link-arrow::after {
  content: "";
  display: inline-block;
  width: 32px;
  height: var(--hairline);
  background: var(--color-gold);
  transition: width 0.4s ease;
}
.link-arrow:hover::after { width: 48px; }

/* ---------- header: a floating pill, not a bar ----------
   Carried over from IDI-1159; everything else on that branch was rejected, so
   these tokens are defined locally in site.json against the current palette.
   The pill never docks, never changes height and never gains a shadow — past
   the hero its surface deepens instead. */
.header {
  position: fixed;
  top: var(--header-top);
  left: var(--space-gutter);
  right: var(--space-gutter);
  z-index: 60;
  min-height: var(--header-height-mobile);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--header-bar-gap);
  padding: 0 var(--header-pad-mobile);
  /* The original's one visible edge: a translucent white hairline along the
     bottom only, which on a 50px radius reads as a lit rim under the pill.
     Top and sides carry nothing. */
  border: 0;
  border-bottom: var(--header-border-width) solid var(--header-border);
  border-radius: var(--header-radius);
  background: var(--header-surface);
  -webkit-backdrop-filter: blur(var(--header-blur)) saturate(var(--header-saturate));
  backdrop-filter: blur(var(--header-blur)) saturate(var(--header-saturate));
  color: var(--color-on-dark);
  transition: background-color var(--header-underline-duration) var(--header-ease),
              backdrop-filter var(--header-underline-duration) var(--header-ease),
              border-radius var(--header-underline-duration) var(--header-ease);
}
.header.is-scrolled {
  background: var(--header-surface-scrolled);
  -webkit-backdrop-filter: blur(var(--header-blur-scrolled)) saturate(var(--header-saturate));
  backdrop-filter: blur(var(--header-blur-scrolled)) saturate(var(--header-saturate));
}
/* The transparent state is the unreadable one, so both fallbacks are solid. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .header, .header.is-scrolled { background: var(--header-surface-solid); }
}
@media (prefers-reduced-transparency: reduce) {
  .header, .header.is-scrolled {
    background: var(--header-surface-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

.header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--header-bar-gap);
  width: 100%;
  min-height: var(--header-height-mobile);
}

/* The gold crown is a decorative brand mark; the home link is named for
   assistive technology, so the mark is not asked to carry meaning. */
.brand {
  display: flex;
  align-items: center;
  text-decoration: none;
  min-height: var(--tap-target);
  flex: 1 1 auto;
  min-width: 0;
}
/* The crown belongs to the desktop pill, where the nav sits beside it and a
   full lockup would crowd the row. Below md the lockup takes its place. */
.brand__mark { display: none; width: var(--header-mark-size); height: auto; flex: none; }

/* The lockup, left-aligned, at a height that leaves even padding inside the
   52px pill -- 26px here, so 13px above and below. It is sized for the
   narrowest case: at 320 the pill leaves 182px between its padding and the
   burger, and this lockup is 162px wide, so it fits with room rather than
   just fitting. A fixed height rather than a fluid one, because it is a brand
   mark in a fixed-height bar: growing it with the viewport would make the
   same pill look different on two phones. */
.brand__lockup {
  display: block;
  height: var(--header-lockup-height);
  width: auto;
  flex: none;
}

.nav { display: none; align-items: center; gap: var(--header-nav-gap); }

/* One underline serves hover and the current page: it grows from the left on
   hover and sits at full width for aria-current. */
/* Typography matched to the original nav: Inter 13px at 300, no tracking and
   no uppercasing. Our own label type is tracked and uppercase, which is right
   everywhere else on the site but is not what this nav is. */
.nav__link {
  position: relative;
  font-size: var(--header-link-size);
  font-weight: var(--header-link-weight);
  letter-spacing: var(--header-link-tracking);
  text-transform: var(--header-link-transform);
  text-decoration: none;
  color: var(--color-on-dark);
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  white-space: nowrap;
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(50% - 0.5em - var(--header-underline-offset));
  height: var(--hairline);
  background: var(--color-gold);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--header-underline-duration) var(--header-ease);
}
.nav__link:hover::after,
.nav__link:focus-visible::after,
.nav__link[aria-current="page"]::after { transform: scaleX(1); }

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-target);
  min-width: var(--tap-target);
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--color-on-dark);
}
.nav-toggle__bars { display: inline-flex; flex-direction: column; gap: 5px; width: 20px; }
.nav-toggle__bars span { display: block; height: var(--hairline); background: currentColor; }

.lang-switcher { display: flex; align-items: center; gap: 12px; }
.lang-switcher ul { display: flex; gap: 12px; }

/* ---------- the pill, opened ----------
   It grows downward rather than becoming a full-screen takeover. */
.header.is-open {
  border-radius: var(--header-radius-open);
  flex-direction: column;
  align-items: stretch;
  padding-bottom: 16px;
}
.nav-drawer { display: none; }
.header.is-open .nav-drawer {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: 4px;
}
.nav-drawer__link {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 46px;
  font-family: var(--font-display);
  font-size: var(--type-h3);
  /* Not --header-link-weight. The drawer is set in Cormorant, which ships here
     at 300, 400 and 500 only; 600 would be a synthesised bold on a
     high-contrast serif. This is the heaviest real face there is. */
  font-weight: var(--header-drawer-weight);
  text-decoration: none;
  color: var(--color-on-dark);
}
.nav-drawer__link::after {
  content: "";
  position: absolute;
  left: 0;
  width: 2.2em;
  bottom: 8px;
  height: var(--hairline);
  background: var(--color-gold);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--header-underline-duration) var(--header-ease);
}
.nav-drawer__link:hover::after,
.nav-drawer__link:focus-visible::after,
.nav-drawer__link[aria-current="page"]::after { transform: scaleX(1); }

/* Brokers in the pill. A ring, not a fill: it is a different audience from
   the nine pages around it, and that is worth marking quietly. */
/* The outline item carries its state in the pill, not under it.

   Every other nav item grows a gold underline on hover and holds it for the
   current page. On a bordered pill that underline is a second outline inside
   the first, drawn at a different radius, and the two fight. So this one is
   filled instead: a faint ivory tint on hover, a stronger one with a gold edge
   for the page you are on. The ::after is switched off rather than restyled,
   which keeps every other item's underline exactly as it was.

   THIS BLOCK MUST STAY BELOW .nav__link AND .nav-drawer__link. Both of those
   set ::after { content: "" }, and `.nav__link--outline::after` carries the
   same specificity as `.nav__link::after` -- one class, one pseudo-element --
   so the later rule wins and nothing else can decide it. Written above them it
   looks right, computes to content: "" and draws the underline anyway, which
   is exactly what shipped and what IDI-1238 came back for. The footer pair is
   held beside the footer nav rules for the same reason.

   The fills are ivory at low alpha over the pill's own dark glass, so they
   read as the surface lifting rather than as a new colour. Contrast is
   measured over the composited result, not assumed from the token. */
.nav__link--outline {
  border: var(--hairline) solid var(--color-rule-on-dark);
  border-radius: 999px;
  padding-inline: 12px;
  transition: background-color var(--header-underline-duration) var(--header-ease),
              border-color var(--header-underline-duration) var(--header-ease);
}
.nav__link--outline::after { content: none; }
/* Ivory held through hover, against the global a:hover gold. The header action
   two rules down makes the same call for the same reason: gold text here
   measured 2.34:1 over the lit glass. */
.nav__link--outline:hover,
.nav__link--outline:focus-visible {
  color: var(--color-on-dark);
  border-color: var(--color-gold);
  background: var(--header-outline-hover);
}
/* The current page is filled with the brand ground rather than a brighter
   tint. The pill is translucent over whatever the hero happens to be, and an
   ivory fill at 0.2 measured 4.20:1 over a bright one -- under the bar, and
   dependent on the photograph, which is no way to set a state. Lagoon is
   opaque, so this reads the same on every page. */
.nav__link--outline[aria-current="page"],
.nav__link--outline[aria-current="page"]:hover {
  border-color: var(--color-gold);
  background: var(--color-lagoon);
  color: var(--color-ivory);
}

/* The drawer. Same idea at drawer scale: the item becomes a pill of its own
   rather than a line of Cormorant with a rule under it. */
.nav-drawer__link--outline {
  align-self: start;
  border: var(--hairline) solid var(--color-rule-on-dark);
  border-radius: 999px;
  padding-inline: 16px;
  min-height: 44px;
  transition: background-color var(--header-underline-duration) var(--header-ease),
              border-color var(--header-underline-duration) var(--header-ease);
}
.nav-drawer__link--outline::after { content: none; }
.nav-drawer__link--outline:hover,
.nav-drawer__link--outline:focus-visible {
  color: var(--color-on-dark);
  border-color: var(--color-gold);
  background: var(--header-outline-hover);
}
.nav-drawer__link--outline[aria-current="page"],
.nav-drawer__link--outline[aria-current="page"]:hover {
  border-color: var(--color-gold);
  background: var(--color-lagoon);
  color: var(--color-ivory);
}

@media (prefers-reduced-motion: reduce) {
  .nav__link--outline,
  .nav-drawer__link--outline { transition: none; }
}

/* The header action keeps ivory text on hover; only the surface lifts.
   Gold behind ivory is 2.56:1, so a gold fill is not available here. */
.header__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-target);
  margin-top: 12px;
  padding: 0 20px;
  border: var(--hairline) solid var(--color-gold);
  border-radius: var(--header-radius);
  background: transparent;
  color: var(--color-on-dark);
  font-size: var(--type-label);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color var(--header-underline-duration) var(--header-ease);
}
.header__cta:hover,
.header__cta:focus-visible {
  background: var(--header-cta-hover);
  color: var(--color-on-dark);
}

@media (prefers-reduced-motion: reduce) {
  .nav__link::after,
  .nav-drawer__link::after { transition: none; }
}

/* ---------- hero ---------- */
.hero {
  position: relative;
  /* svh keeps the hero clear of mobile browser chrome; vh is the fallback. */
  min-height: var(--hero-min-mobile-fallback);
  min-height: var(--hero-min-mobile);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  /* The safe zone. The hero is bottom-anchored, so on a tall viewport the type
     sits far below the pill and this does nothing. On a short one -- a laptop
     at 1440x720, a phone at 375x667 -- the content is taller than the hero and
     grows upward from the foot, and without a floor it grows straight under
     the fixed pill. /brokers/ did: -71px at 1440x720, -92px at 1366x650, -36px
     at 375x667, measured. min-height rather than height means the hero simply
     gets taller instead, which is the right answer at any width.

     It is padding and not a margin on .hero__inner so the scrim, which is
     inset:0 on the section, still covers the whole band. */
  padding-block-start: var(--clear-of-pill);
  padding-block-end: var(--hero-pad-end-mobile);
  background: var(--color-lagoon-mid);
  overflow: hidden;
}
.hero__media { position: absolute; inset: 0; z-index: 0; }
/* The figure was the missing link in the height chain. .media picture and
   .media img are already height:100%, but they were resolving against a figure
   with no height of its own — a hero slot passes no media--* ratio class — so
   the photo fell back to its intrinsic ratio and stopped partway down, leaving
   the hero's own background showing under it as a flat band: 473px of it at
   375, 252px at 768, 44px at 1440. Giving the figure the height completes the
   chain and object-fit does the rest. Covers the placeholder too, which carries
   the same class. */
.hero__media > .media { height: 100%; }
.hero__media img { width: 100%; height: 100%; object-fit: cover; }

/* Neutral black, not lagoon green: the scrim is there to carry the type, and a
   coloured one tints the photograph underneath it. Transparent at the top so
   the image is untouched where nothing sits on it, deepest at the foot where
   the heading and the buttons are. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top,
              var(--hero-scrim-base) 0,
              var(--hero-scrim-base) var(--hero-scrim-hold),
              var(--hero-scrim-mid) var(--hero-scrim-mid-stop),
              var(--hero-scrim-top) var(--hero-scrim-end));
}
/* A hero is always a photograph under a scrim, so everything inside it is
   on-dark ink. The eyebrow and the heading had their own rules and were fine;
   a paragraph did not exist in a hero until IDI-1214 and inherited the body
   colour -- dark grey on a dark photograph, measured at 1.25:1. */
.hero__inner { position: relative; z-index: 2; max-width: 760px; color: var(--color-on-dark); }
.hero__inner .prose { color: var(--color-on-dark-soft); }
.hero__actions { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 8px; }

.hero--page {
  min-height: var(--hero-page-min-mobile-fallback);
  min-height: var(--hero-page-min-mobile);
  padding-block-end: var(--hero-page-pad-end-mobile);
}

/* ---------- notices (review build only) ---------- */
.notice {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 760px;
  padding: clamp(20px, 4vw, 28px) clamp(20px, 4vw, 32px);
  background: var(--color-sand);
  border-left: 3px solid var(--color-gold);
}
.notice__title {
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--color-lagoon);
}

/* ---------- media ----------
   The sand ground belongs to the placeholder, not to every slot: a slot with a
   real photo must never show it. <picture> needs explicit block layout and a
   height, otherwise the img's height:100% resolves against an auto-height
   parent and the photo falls back to its natural aspect inside the box. */
.media { position: relative; overflow: hidden; }
.media picture { display: block; width: 100%; height: 100%; }
.media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.media--wide { aspect-ratio: var(--media-ratio-wide); }
.media--square { aspect-ratio: var(--media-ratio-square); }
.media--portrait { aspect-ratio: var(--media-ratio-portrait); }
.media--tall { aspect-ratio: var(--media-ratio-tall); }
.media--placeholder { background: var(--color-placeholder-1); }

/* Floor plans (IDI-1153).

   The drawings were lifted off a white page to sit on the sand. The card gives
   that page back: a fixed 4:3 frame with a gold hairline, the plan floating
   inside it at its own proportion. object-fit is contain and there is no
   aspect-ratio on the image, because a cropped floor plan is a wrong floor
   plan -- which is why a very tall drawing reads narrow in its card. The card
   is the index, the lightbox is the document.

   No fill on the frame. The section is already sand and the plans carry alpha,
   so the hairline is the whole of the frame; a fill would be the white box
   they were lifted off, put back. */
.plan-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: minmax(0, 1fr);
}
.plan-card { display: flex; flex-direction: column; gap: 12px; min-width: 0; }

/* Grid, not flex, and the plan's row is minmax(0, 1fr).

   As a flex column the frame's aspect-ratio lost to the image's intrinsic
   height -- a 4:3 box 416px wide came out 938px tall, because a flex item's
   automatic minimum size is its content and max-height: 100% resolves against
   an auto height, so it never bound. A grid row of minmax(0, 1fr) can shrink
   to nothing, which is what lets the ratio govern and the image be contained
   inside it rather than the other way round. */
.plan-card__frame {
  position: relative;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  justify-items: center;
  gap: 14px;
  width: 100%;
  aspect-ratio: 4 / 3;
  /* The ratio governs until the card gets wide, then the height budget does.
     At two columns a 656px card would be 492px tall on ratio alone, and the
     card with its heading and label would come to 567 against a 480 budget.
     Capping the frame keeps the budget and, because the plan is contained
     inside whatever height is left, actually gives the drawings more room
     than the three-column layout did: 315px tall rather than 219. */
  max-height: var(--media-plan-frame-max);
  padding: 28px;
  margin: 0;
  background: none;
  border: var(--hairline) solid var(--color-rule);
  border-radius: 0;
  cursor: pointer;
  transition: border-color 0.3s ease;
}
.plan-card__frame:hover { border-color: var(--color-gold); }
.plan-card__frame:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 3px;
}

/* The 380px ceiling sits on the grid ITEM, not on the image. On the image it
   became the frame's height -- a percentage height on the <img> resolves
   against an indefinite parent, falls back to intrinsic, and the cap then read
   as the row's content height, so a 4:3 box 416px wide came out 473px tall.
   Capping the item cannot inflate the container, and at every width the ratio
   binds first anyway; the ceiling is here for a very wide single column. */
.plan-card__plan {
  position: relative;
  display: block;
  min-height: 0;
  max-height: 380px;
  width: 100%;
  height: 100%;
}
/* Absolutely positioned, which is the point rather than a detail. An in-flow
   image contributes its intrinsic height to the frame's content, and a box
   only keeps its aspect-ratio while the content fits inside it -- so the
   drawing, 845px tall at this width, pushed a 4:3 box to 473px however it was
   capped. Out of flow it contributes nothing, the ratio governs outright, and
   object-fit contains the plan inside whatever that comes to. */
.plan-card__plan picture { position: absolute; inset: 0; display: block; }
/* The hover lift sits on the image, clear of the GSAP reveal, which writes an
   inline transform on the item and would beat this rule outright. */
.plan-card__plan img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform 0.6s ease;
}
.plan-card__frame:hover .plan-card__plan img { transform: scale(var(--media-plan-hover-scale)); }

.plan-card__view {
  align-self: end;
  font-family: var(--font-body);
  font-size: var(--type-fine);
  letter-spacing: var(--type-eyebrow-tracking);
  text-transform: uppercase;
  /* Ink, not gold. Measured on sand at 12px: brand gold is 2.30:1 and
     gold-ink 4.41:1, and both fail -- gold-ink carries the site's eyebrows
     but does it on ivory, which is lighter than this ground. So the gold on
     a card is the hairline, which is a border and has no text to fail, and
     the words are ink at 12.74:1. One gold accent per card rather than two
     was the better answer anyway. */
  color: var(--color-ink);
}

.plan-card__type {
  font-family: var(--font-display);
  font-size: var(--type-card-heading);
  font-weight: var(--type-card-heading-weight);
  line-height: var(--type-card-heading-leading);
  color: var(--color-ink);
  margin: 0;
}
.plan-card__label {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  line-height: var(--type-body-sm-leading);
  color: var(--color-muted);
  margin: 0;
}

/* Two columns from md, one below. Three never filled a row -- no type has
   three drawings -- so the third column was always empty and the grid read as
   short rather than deliberate. */
@media (min-width: 900px) {
  .plan-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (prefers-reduced-motion: reduce) {
  .plan-card__frame,
  .plan-card__plan img { transition: none; }
  .plan-card__frame:hover .plan-card__plan img { transform: none; }
}

/* Zoom affordance. A real <button>, so it is tabbable and works from the
   keyboard; the hover scale sits on the <img> to stay clear of the GSAP
   scale-in, which animates the <picture>. The two must not share an element:
   GSAP writes an inline transform, which would beat this rule outright. */
.media__zoom {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  overflow: hidden;
}
.media__zoom img { transition: transform 0.6s ease; }
.media__zoom:hover img { transform: scale(var(--media-hover-scale)); }
.media__zoom:focus-visible { outline: 2px solid var(--color-gold); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .media__zoom img { transition: none; }
  .media__zoom:hover img { transform: none; }
}

.placeholder {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 6px;
  padding: 24px;
  background: var(--color-placeholder-2);
  color: var(--color-lagoon);
  min-height: var(--media-placeholder-min-height);
  height: 100%;
}
.placeholder__label {
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  font-weight: 500;
}
.placeholder__note { font-size: var(--type-fine); color: var(--color-lagoon); }

.figure__caption {
  margin-top: 12px;
  font-size: var(--type-fine);
  color: var(--color-muted);
}

/* ---------- stats ---------- */
.stats { display: flex; flex-wrap: wrap; gap: clamp(24px, 4vw, 40px); }
.stat { display: flex; flex-direction: column; gap: 6px; }
.stat__value {
  font-family: var(--font-display);
  font-size: var(--type-stat);
  font-weight: var(--type-stat-weight);
  line-height: 1;
  color: var(--color-lagoon);
}
.stat__value--lg { font-size: var(--type-stat-lg); }
.stat__unit { font-size: 0.5em; }
.section--dark .stat__value, .section--dark-mid .stat__value { color: var(--color-on-dark); }
.stat__label {
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
.section--dark .stat__label, .section--dark-mid .stat__label { color: var(--color-on-dark-muted); }

.stats--columns {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
.stats--columns .stat {
  padding-top: 32px;
  border-top: var(--hairline) solid var(--color-rule);
}
.section--dark .stats--columns .stat,
.section--dark-mid .stats--columns .stat { border-top-color: var(--color-rule-on-dark); }

/* ---------- lists ---------- */
.list-rules > li {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding-block: 14px;
  border-top: var(--hairline) solid var(--color-rule);
  font-size: 14px;
}
.list-rules > li:last-child { border-bottom: var(--hairline) solid var(--color-rule); }
.list-rules__value { color: var(--color-lagoon); font-weight: 500; }

.list-numbered > li {
  display: flex;
  align-items: baseline;
  gap: var(--space-list-gap);
  padding-block: 22px;
  border-top: var(--hairline) solid var(--color-rule-strong);
  font-size: var(--type-list);
}
.list-numbered > li:last-of-type { border-bottom: var(--hairline) solid var(--color-rule-strong); }
.list-numbered__index {
  font-family: var(--font-display);
  font-size: 18px;
  color: var(--color-gold-ink);
  min-width: 28px;
  flex: none;
}
.list-note {
  padding: 24px 0 0 0;
  font-family: var(--font-display);
  font-size: 17px;
  font-style: italic;
  color: var(--color-muted);
}

.columns {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
.column-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-top: 36px;
  border-top: var(--hairline) solid var(--color-gold);
}
/* Pushed to the foot of the card so the three links line up with each other
   however unevenly the descriptions run, and given its own space above. */
.column-card__link { margin-block-start: auto; padding-block-start: 4px; }

/* An anchored arrival must clear the pill, which is fixed and would otherwise
   sit on the heading. --clear-of-pill is that distance: the pill's own offset
   from the top, its height, and a gap, so it tracks the header instead of
   repeating a number that would quietly go stale if the pill ever changed
   size. It is declared beside its first user and redeclared at md, where the
   pill is taller; the hero takes it too, as the safe zone nothing may enter. */
:root { --clear-of-pill: calc(var(--header-top) + var(--header-height-mobile) + var(--header-anchor-gap)); }
[id] { scroll-margin-top: var(--clear-of-pill); }

/* ---------- named entries (places, clubs, beaches) ---------- */
.entries { display: flex; flex-direction: column; gap: 28px; }
.entry {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 24px;
  border-top: var(--hairline) solid var(--color-rule);
}
.entry__name {
  font-family: var(--font-display);
  font-size: var(--type-h3);
  font-weight: var(--type-h3-weight);
  line-height: var(--type-h3-leading);
  color: var(--color-lagoon);
}
.section--dark .entry__name,
.section--dark-mid .entry__name { color: var(--color-on-dark); }

/* ---------- forms ---------- */
/* The metrics come from custom properties so the modal can reassign them on
   itself rather than re-stating every rule at a tighter size. */
.form { display: flex; flex-direction: column; gap: var(--field-row-gap); }
.field { display: flex; flex-direction: column; gap: var(--field-label-gap); }
.field__label {
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
.section--dark .field__label, .section--dark-mid .field__label { color: var(--color-on-dark-muted); }
.field__input, .field__textarea {
  min-height: var(--field-height);
  padding: var(--field-pad-block) var(--field-pad-inline);
  background: transparent;
  border: var(--hairline) solid var(--color-rule-strong);
  border-radius: var(--radius);
  color: inherit;
  font-size: var(--field-size);
}
.section--dark .field__input, .section--dark .field__textarea,
.section--dark-mid .field__input, .section--dark-mid .field__textarea {
  border-color: var(--color-on-dark-muted);
  color: var(--color-on-dark);
}
.field__textarea { min-height: 120px; resize: vertical; }

.fieldset {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
  padding: 0;
  border: 0;
}
.fieldset__legend {
  padding: 0;
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
.section--dark .fieldset__legend,
.section--dark-mid .fieldset__legend { color: var(--color-on-dark-muted); }
.fieldset__option {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--tap-target);
  font-size: var(--type-body-sm);
}
.fieldset__option input[type="checkbox"] {
  width: 20px;
  height: 20px;
  flex: none;
  accent-color: var(--color-gold);
}

.consent-field {
  display: flex;
  align-items: flex-start;
  gap: var(--field-consent-gap);
  font-size: var(--field-consent-size);
  line-height: var(--field-consent-leading);
}
.consent-field input[type="checkbox"] {
  width: 20px;
  height: 20px;
  margin: 2px 0 0;
  flex: none;
  accent-color: var(--color-gold);
}
.form__note { font-size: var(--type-fine); color: var(--color-muted); }
.section--dark .form__note, .section--dark-mid .form__note { color: var(--color-on-dark-muted); }

/* The two wrappers exist in both layouts. Stacked, they are the column the
   form has always been, with the gap it has always had -- so the default
   shape does not depend on which branch the template took. */
.form__fields, .form__consents, .form__actions {
  display: flex;
  flex-direction: column;
  gap: var(--field-row-gap);
}
.form__consents { gap: var(--field-consent-gap); }

/* ---------- the inline form (IDI-1213) ----------
   The homepage block only. One row of fields, one row of consents with the
   submit at its end, and the block half the height it was. Below md it is the
   stacked form again: three fields across a phone is three slivers. */
.form--inline {
  --field-height: var(--field-height-inline);
  --field-row-gap: var(--field-row-gap-inline);
  --field-consent-size: var(--field-consent-size-inline);
}
.form--inline .btn { align-self: flex-start; }
/* Scoped to this layout: the country button is the one part of the row that
   does not shrink, and every pixel it gives back is a pixel the number gets.
   /contact/ and the modal have a column to themselves and keep the roomier
   button. */
.form--inline .phone__country { padding-inline: 9px; gap: 4px; }

@media (min-width: 900px) {
  /* Two across at md, three at lg. Equal tracks either way -- minmax(0, 1fr),
     because the phone field's country button plus its input has a min-content
     width that would otherwise refuse to shrink and push the row wider than
     its column. */
  .form--inline .form__fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form--inline .form__actions {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: 32px;
  }
  .form--inline .form__consents { flex: 1 1 auto; }
  .form--inline .field--message { grid-column: 1 / -1; }
  .form--inline .field__textarea { min-height: 72px; }
  .form--inline .btn { flex: 0 0 auto; align-self: flex-end; }
}

@media (min-width: 1200px) {
  .form--inline .form__fields { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- footer ---------- */
.footer {
  position: relative;
  background: var(--color-lagoon);
  color: var(--color-on-dark);
  /* The extra bottom padding is room for the floating contact widget, which is
     fixed 16px off the bottom-right and 206px wide: on a narrow viewport it
     lands on the centred credit. Measured overlapping at 375 and clear from
     about 614px up, so it is dropped at sm. */
  padding-block: var(--footer-top-pad) calc(var(--footer-band-gap) + var(--footer-widget-clearance));
  overflow: hidden;
}
.footer__texture {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: var(--footer-texture);
  background-size: cover;
  background-position: center;
  opacity: 0.45;
}
/* One gap between the bands: brand row, then the legal row that carries the
   rule, the disclaimer and the credit. The space under that row is the footer's
   bottom padding, which is the same token. */
.footer__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--footer-band-gap);
}
/* Below 900 the whole footer is a centred stack; from 900 the top row splits,
   lockup against the left edge of the container and the nav against the right. */
.footer__top {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 40px;
}
/* The lockup is 42px tall, just under the target size. Extend the hit area
   with an overlay rather than a min-height, so the desktop layout is untouched. */
.footer__top > a { position: relative; display: inline-block; }
.footer__top > a::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 50%;
  transform: translateY(-50%);
  height: var(--tap-target);
}
.footer__lockup { width: 260px; height: auto; }

.footer__developer { width: var(--brand-idiliq-credit-width); height: auto; }

/* The two nav rows. Centred while the footer is a stack, ranged right from md
   with the lockup -- one block, so .footer__top still splits into two children
   and the second line of either row lands on the same edge. */
.footer__navs {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--footer-nav-row-gap);
}

.footer__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 28px;
}


/* The rule is this row's own top edge, so it spans the container. Stacked, the
   row is one column: paragraph full width, credit centred a band-gap beneath. */
.footer__legal {
  width: 100%;
  border-block-start: var(--hairline) solid var(--color-rule-on-dark);
  padding-block-start: var(--footer-rule-gap);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--footer-band-gap);
  justify-items: center;
}

/* One row, one treatment. The links used a border-bottom on a 44px-tall box
   while Cookie Preferences used a text underline, so the two rules sat at
   different heights. Both now draw the same text-decoration underline at the
   same offset, which tracks the text rather than the box — and the button
   stays a button. */
.footer__nav a,
.footer__prefs {
  font-family: inherit;
  font-size: var(--type-label);
  /* The same weight token the header nav uses, so the two navs cannot drift. */
  font-weight: var(--header-link-weight);
  letter-spacing: var(--footer-nav-tracking);
  text-transform: uppercase;
  color: var(--color-on-dark-soft);
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  text-decoration: none;
  text-decoration-thickness: var(--hairline);
  text-underline-offset: var(--footer-underline-offset);
}
.footer__prefs { cursor: pointer; }
.footer__nav a:hover,
.footer__nav a:focus-visible,
.footer__nav a[aria-current="page"],
.footer__prefs:hover,
.footer__prefs:focus-visible {
  color: var(--color-gold);
  text-decoration-line: underline;
  text-decoration-color: var(--color-gold);
}

/* The legal row, after the treatment it narrows: same selector weight as
   ".footer__nav a", so source order is what decides and this has to come later.
   It borrows the disclaimer's colour rather than declaring one, because it is
   the same register -- small print under the footer's own words. Everything
   else is the row above's, so the two cannot drift: same uppercase, same
   tracking, same underline tracking the text rather than the box. Hover and
   current still go gold, which is the whole footer's behaviour, not this row's.
   Measured on the lagoon ground at the figure in the done comment. */
.footer__nav--legal { gap: 20px; }
.footer__nav--legal a,
.footer__nav--legal .footer__prefs {
  font-size: var(--footer-legal-size);
  font-weight: var(--footer-legal-weight);
  color: var(--color-on-dark-muted);
}

.footer .fine { color: var(--color-on-dark-muted); }
/* Ranged left on the container's own edge, the same edge as the rule above it
   and the lockup above that. Full width while the row is stacked. */
.footer__disclaimer { text-align: left; justify-self: stretch; }


/* ---------- cookie consent ---------- */
/* Both overlays are position:fixed, so a hidden one would still cover the
   page and swallow clicks without this. */
.consent[hidden],
.consent-panel[hidden],
.nav-drawer[hidden] { display: none; }

.consent {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 80;
  background: var(--color-ivory);
  border-top: var(--hairline) solid var(--color-rule-strong);
  padding: 28px var(--space-gutter);
}
.consent__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  max-width: 1400px;
  margin-inline: auto;
}
.consent__copy { max-width: 620px; display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.consent__copy a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
}
.consent__title {
  font-family: var(--font-display);
  font-size: var(--type-h3);
  font-weight: 400;
  color: var(--color-lagoon);
}
.consent__actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.consent__actions .btn { flex: 1 1 100%; }

.consent-panel {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: end center;
  background: rgba(15, 61, 59, 0.45);
  padding: 0;
  border: 0;
}
.consent-panel__card {
  width: min(680px, 100%);
  max-height: 88vh;
  overflow-y: auto;
  background: var(--color-ivory);
  padding: 40px var(--space-gutter);
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.consent-category {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 20px;
  border-top: var(--hairline) solid var(--color-rule);
}
.consent-category__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.consent-category__title {
  font-size: var(--type-label);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--color-lagoon);
}
.consent-category__toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--tap-target);
  font-size: 13px;
}
.consent-category__toggle input { width: 20px; height: 20px; accent-color: var(--color-gold); }

/* ---------- legal pages ---------- */
.legal { max-width: 760px; display: flex; flex-direction: column; gap: 40px; }
.legal section { display: flex; flex-direction: column; gap: 14px; }
.legal ul { display: flex; flex-direction: column; gap: 10px; }
.legal ul li { padding-left: 20px; position: relative; }
.legal ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.75em;
  width: 8px;
  height: var(--hairline);
  background: var(--color-gold);
}
.table-scroll { overflow-x: auto; }
.table-scroll:focus-visible { outline: 2px solid var(--color-gold); outline-offset: 2px; }

.legal__table {
  width: 100%;
  min-width: 480px;
  border-collapse: collapse;
  font-size: var(--type-body-sm);
  text-align: left;
}
.legal__table th,
.legal__table td {
  padding: 14px 16px 14px 0;
  border-top: var(--hairline) solid var(--color-rule);
  vertical-align: top;
}
.legal__table th {
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--color-muted);
}

.todo {
  display: inline-block;
  padding: 2px 8px;
  background: var(--color-sand);
  border: var(--hairline) solid var(--color-gold);
  font-size: 13px;
  color: var(--color-lagoon);
}

/* ---------- gallery ---------- */
.gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gap);
}
.gallery__item { display: flex; flex-direction: column; }

/* ---------- location map ----------
   Two levels: the Cap Cana area, and a locator inset showing where that sits on
   the country. Inline SVG, no tiles and no embed, so nothing here reaches the
   network and no consent gate is needed. The land is the palette at low alpha
   and stays a quiet ground; the one saturated thing on it is the Wyndham
   marker. */
.map {
  position: relative;
  margin: 0 auto;
  max-width: var(--map-max-width);
  width: 100%;
}
/* Clipped to the frame. The coastline is drawn past the edges so its end caps
   fall outside the map; without this they would show as two stubs floating over
   the section above and the column beside it. The locator inset keeps its own
   overflow: it paints a couple of units past its nested viewBox and that is
   still well inside this frame. */
.map__svg { display: block; width: 100%; height: auto; overflow: hidden; }

.map__land { fill: var(--map-fill); stroke: none; }
.map__coast {
  fill: none;
  stroke: var(--map-outline);
  stroke-width: var(--map-coast-width);
  stroke-linejoin: round;
  stroke-linecap: round;
}

.map__point { fill: var(--color-lagoon); }
.map__marker--primary .map__point { fill: var(--color-gold); }
/* No transform on the pulse. Scaling it meant its centre depended on
   transform-origin resolving against transform-box: fill-box, which is exactly
   the kind of thing that drifts between engines and zoom levels. Animating the
   radius instead means the ring grows about its own cx/cy by definition, so it
   cannot come away from the dot. */
.map__pulse { fill: var(--map-halo); }

/* ---------- the legend column ----------
   One column out over the sea, every name left-aligned to the same x, the rows
   in their dots’ north-to-south order. Placement is computed in the mapLegend
   filter, so nothing here positions anything: this block is type and colour
   only.

   No halo. A label in the column crosses neither the coast nor another label,
   which is the only thing the halo was ever for. */
.map__label {
  fill: var(--color-body);
  font-family: var(--font-body);
  font-size: var(--map-legend-size);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--map-label-tracking);
  text-transform: uppercase;
}
.map__legend-item--primary .map__label { fill: var(--color-gold-ink); }
.map__sub,
.map__distance {
  font-size: var(--map-legend-sub-size);
  letter-spacing: var(--map-label-tracking);
}
.map__distance { fill: var(--color-muted); }
.map__legend-item--primary .map__sub { fill: var(--color-gold-ink); }

.map__legend-item { cursor: pointer; outline: none; }

/* ---------- the column, below 640 ----------
   Off the map and under it, set in px so the type does not scale with the
   viewBox. The dots stay; the leaders and the SVG column go, because a leader
   pointing at a label that is no longer beside the map points at nothing. */
.map__list {
  display: grid;
  gap: var(--map-list-gap);
  margin-block-start: var(--map-list-gap);
  /* The map bleeds to the edge of the viewport and should: it is a picture of
     a place. Words are not, and these were sitting hard against the glass, so
     the list takes the page's own gutter while the map above it keeps none. */
  padding-inline: var(--space-gutter);
}
.map__list-item {
  display: grid;
  grid-template-columns: var(--map-badge-size) minmax(0, 1fr);
  align-items: baseline;
  column-gap: 10px;
  row-gap: 2px;
  padding-block-end: var(--map-list-gap);
  border-block-end: var(--hairline) solid var(--color-rule);
}
/* The meta line sits under the name, in the name's column, so the badge holds
   the left edge for the whole row rather than only its first line. */
.map__list-name, .map__list-meta { grid-column: 2; }
.map__list-item:last-child { border-block-end: 0; padding-block-end: 0; }
.map__list-name {
  font-size: var(--map-list-name-size);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--map-label-tracking);
  text-transform: uppercase;
  color: var(--color-body);
}
.map__list-meta {
  font-size: var(--map-list-meta-size);
  letter-spacing: var(--map-label-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
.map__list-item--primary .map__list-name,
.map__list-item--primary .map__list-meta { color: var(--color-gold-ink); }

/* ---------- the badges ----------

   One disc, two homes: over its dot on the map and at the head of its row in
   the list. Same size, same colours, same digit, so the pairing needs no
   motion and no hover -- a reader matches them by looking.

   The number is the row's place in the map's own north-to-south order and
   comes from the mapLegend filter, the same value the list reads, so the two
   cannot drift. The property has no number: it has the star, and its dot is
   already the only gold one on the map. */
.map__badge,
.map__list-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--map-badge-size);
  height: var(--map-badge-size);
  border-radius: 50%;
  background: var(--color-lagoon);
  color: var(--color-ivory);
  font-family: var(--font-body);
  font-size: var(--map-badge-text);
  font-weight: var(--type-label-weight);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  transition: background-color 0.3s ease, color 0.3s ease;
}
.map__badge--primary,
.map__list-badge--primary { color: var(--color-gold); }
.map__badge-star { display: block; }

/* Over the map, positioned as a percentage of the viewBox. pointer-events is
   none on purpose: the marker underneath keeps the tap, the hit area and the
   focus ring, and the badge only names it. */
.map__canvas { position: relative; }
.map__badges { position: absolute; inset: 0; pointer-events: none; }
/* --badge-nudge is written by map.js where two discs would cover each other;
   it is 0 for every badge that has room, which is most of them. */
.map__badge {
  position: absolute;
  transform: translate(-50%, calc(-50% + var(--badge-nudge, 0px)));
}

.map__list-badge { grid-row: span 2; align-self: start; }

/* Tap either half and both light up.

   Inverted rather than recoloured. Lagoon on gold measures 4.17:1 at 11px,
   which is under the bar for text; ivory on lagoon is 10.66:1 resting, so the
   selected state keeps the ink and swaps the ground, with the gold carried by
   a ring instead of a fill. */
.map__badge.is-active,
.map__list-item.is-active .map__list-badge {
  background: var(--color-ivory);
  color: var(--color-lagoon);
  box-shadow: 0 0 0 2px var(--color-gold);
}
.map__badge--primary.is-active,
.map__list-item--primary.is-active .map__list-badge { color: var(--color-gold-ink); }
.map__list-item.is-active { background: var(--color-sand); }

@media (prefers-reduced-motion: reduce) {
  .map__badge, .map__list-badge { transition: none; }
}

/* Mobile-first, and min-width only like every other query in this file: the
   map carries dots alone by default, and the column returns to it at sm. */
.map__legend,
.map__leaders { display: none; }

/* The leader is the quietest line on the map: the coast tone, one unit wide.
   The primary’s is gold, the one saturated thing here as everywhere else. */
.map__leader {
  fill: none;
  stroke: var(--map-outline);
  stroke-width: var(--map-leader-width);
  stroke-linecap: round;
  stroke-linejoin: round;
}
.map__leader--primary { stroke: var(--color-gold); }

/* ---------- marker targets and tooltips ---------- */
.map__hit { fill: transparent; }
.map__marker { cursor: default; outline: none; }
.map__marker[data-marker] { cursor: pointer; }
.map__marker:focus-visible .map__hit {
  stroke: var(--color-gold);
  stroke-width: 2;
}
/* The legend row is a focus stop too, and has no hit circle to ring. */
.map__legend-item:focus-visible .map__label {
  stroke: var(--color-gold);
  stroke-width: 2;
  paint-order: stroke fill;
}

/* A light glass plate that follows the cursor.

   HTML, not SVG: backdrop-filter is not dependable on SVG shapes, an HTML box
   sizes itself, and its type does not scale with the viewBox.

   Position and motion are split across two elements on purpose. The outer box
   carries the position as a transform that changes on every mousemove and is
   never transitioned, so tracking cannot lag. The inner box carries the
   entrance, so the rise animates once and is not restarted by movement. Both
   are transform and opacity only — nothing here touches layout. */
.map__tip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  pointer-events: none;
  transform: translate(var(--tip-x, 0px), var(--tip-y, 0px));
  will-change: transform;
}
.map__tip-inner {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--map-tip-pad);
  border: var(--map-tip-border-width) solid var(--map-tip-border);
  border-radius: var(--radius);
  background: var(--map-tip-ground);
  -webkit-backdrop-filter: blur(var(--map-tip-blur));
  backdrop-filter: blur(var(--map-tip-blur));
  white-space: nowrap;
  opacity: 0;
  transform: translate(calc(-1 * var(--map-tip-rise)), calc(-1 * var(--map-tip-rise)));
  transition: opacity var(--map-tip-out) var(--map-tip-ease),
              transform var(--map-tip-out) var(--map-tip-ease);
}
.map__tip.is-visible .map__tip-inner {
  opacity: 1;
  transform: translate(0, 0);
  transition-duration: var(--map-tip-in);
}

/* Translucent with nothing blurred behind it is the unreadable case. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .map__tip-inner { background: var(--map-tip-ground-solid); }
}
@media (prefers-reduced-transparency: reduce) {
  .map__tip-inner {
    background: var(--map-tip-ground-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

.map__tip-name,
.map__tip-time {
  font-family: var(--font-body);
  letter-spacing: 0;
  text-transform: none;
}
.map__tip-name {
  color: var(--map-tip-text);
  font-size: var(--map-tip-size);
  font-weight: var(--type-label-weight);
}
.map__tip-time {
  color: var(--map-tip-time);
  font-size: var(--map-tip-time-size);
}
.map__tip-time:empty { display: none; }

@media (prefers-reduced-motion: reduce) {
  .map__tip-inner { transition: none; transform: translate(0, 0); }
}

.map__inset { overflow: visible; opacity: var(--map-inset-opacity); }
.map__inset-outline {
  fill: var(--map-fill);
  stroke: var(--map-outline);
  stroke-width: var(--map-outline-width);
  stroke-linejoin: round;
}
.map__inset-box {
  fill: none;
  stroke: var(--color-gold);
  stroke-width: 2;
}
.map__inset-point { fill: var(--color-gold); }

/* ---------- animation initial states (JS-gated) ---------- */
.js .anim-fade { opacity: 0; transform: translateY(28px); }
.js .anim-scale picture { transform: scale(1.08); }
.js .anim-rule { transform: scaleX(0); }

@media (prefers-reduced-motion: reduce) {
  .js .anim-fade { opacity: 1; transform: none; }
  .js .anim-scale picture { transform: none; }
  .js .anim-rule { transform: none; }
}

/* The map reveal is a transition rather than a scripted tween. pathLength="1"
   normalises the coast so 1px is its whole length; the unit matters, because
   stroke-dashoffset takes a length and a bare number is dropped. Doing it in
   CSS also sidesteps a tween writing unitless frames the browser ignores.
   animations.js only adds is-revealed.

   Order: the coast draws, the Wyndham marker lands on it, the rest follow one
   after another, and the locator arrives last. */
.js .map__coast {
  stroke-dasharray: 1px;
  stroke-dashoffset: 1px;
  transition: stroke-dashoffset var(--map-draw-duration) var(--map-draw-ease);
}
.js .map__land { opacity: 0; transition: opacity var(--map-draw-duration) var(--map-draw-ease); }
.js .map__marker,
.js .map__inset {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity var(--map-marker-duration) var(--map-draw-ease),
              transform var(--map-marker-duration) var(--map-draw-ease);
}

/* Leaders draw label-to-dot, so the dash starts at the column end of the path
   — which is where the filter starts the path. The type follows them in. */
.js .map__leader {
  stroke-dasharray: 1px;
  stroke-dashoffset: 1px;
  transition: stroke-dashoffset var(--map-leader-duration) var(--map-draw-ease);
}
.js .map__legend-item {
  opacity: 0;
  transition: opacity var(--map-marker-duration) var(--map-draw-ease);
}

.js .map.is-revealed .map__coast { stroke-dashoffset: 0px; }
.js .map.is-revealed .map__land { opacity: 1; }
.js .map.is-revealed .map__marker { opacity: 1; transform: none; }
.js .map.is-revealed .map__inset { opacity: var(--map-inset-opacity); transform: none; }

/* The primary marker lands just before the coast finishes drawing. */
.js .map.is-revealed .map__marker--primary {
  transition-delay: calc(var(--map-draw-duration) - 0.55s);
}
.js .map.is-revealed .map__marker--secondary:nth-of-type(2) { transition-delay: calc(var(--map-draw-duration) - 0.2s); }
.js .map.is-revealed .map__marker--secondary:nth-of-type(3) { transition-delay: calc(var(--map-draw-duration) - 0.2s + var(--map-marker-step)); }
.js .map.is-revealed .map__marker--secondary:nth-of-type(4) { transition-delay: calc(var(--map-draw-duration) - 0.2s + (2 * var(--map-marker-step))); }
.js .map.is-revealed .map__marker--secondary:nth-of-type(5) { transition-delay: calc(var(--map-draw-duration) - 0.2s + (3 * var(--map-marker-step))); }
.js .map.is-revealed .map__marker--secondary:nth-of-type(6) { transition-delay: calc(var(--map-draw-duration) - 0.2s + (4 * var(--map-marker-step))); }
.js .map.is-revealed .map__marker--secondary:nth-of-type(7) { transition-delay: calc(var(--map-draw-duration) - 0.2s + (5 * var(--map-marker-step))); }
.js .map.is-revealed .map__marker--secondary:nth-of-type(8) { transition-delay: calc(var(--map-draw-duration) - 0.2s + (6 * var(--map-marker-step))); }
.js .map.is-revealed .map__inset { transition-delay: calc(var(--map-draw-duration) + 1.1s); }

/* Coast, dots, leaders, then the names. Each leader waits for the dots to have
   landed, and each label waits for its own leader to arrive. */
.js .map.is-revealed .map__leader { stroke-dashoffset: 0px; }
.js .map.is-revealed .map__legend-item { opacity: 1; }
.js .map.is-revealed .map__leader:nth-of-type(1) { transition-delay: calc(var(--map-draw-duration) + 0.5s); }
.js .map.is-revealed .map__leader:nth-of-type(2) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-step)); }
.js .map.is-revealed .map__leader:nth-of-type(3) { transition-delay: calc(var(--map-draw-duration) + 0.5s + (2 * var(--map-leader-step))); }
.js .map.is-revealed .map__leader:nth-of-type(4) { transition-delay: calc(var(--map-draw-duration) + 0.5s + (3 * var(--map-leader-step))); }
.js .map.is-revealed .map__leader:nth-of-type(5) { transition-delay: calc(var(--map-draw-duration) + 0.5s + (4 * var(--map-leader-step))); }
.js .map.is-revealed .map__leader:nth-of-type(6) { transition-delay: calc(var(--map-draw-duration) + 0.5s + (5 * var(--map-leader-step))); }
.js .map.is-revealed .map__leader:nth-of-type(7) { transition-delay: calc(var(--map-draw-duration) + 0.5s + (6 * var(--map-leader-step))); }
.js .map.is-revealed .map__leader:nth-of-type(8) { transition-delay: calc(var(--map-draw-duration) + 0.5s + (7 * var(--map-leader-step))); }
.js .map.is-revealed .map__legend-item:nth-of-type(1) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration)); }
.js .map.is-revealed .map__legend-item:nth-of-type(2) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration) + var(--map-leader-step)); }
.js .map.is-revealed .map__legend-item:nth-of-type(3) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration) + (2 * var(--map-leader-step))); }
.js .map.is-revealed .map__legend-item:nth-of-type(4) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration) + (3 * var(--map-leader-step))); }
.js .map.is-revealed .map__legend-item:nth-of-type(5) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration) + (4 * var(--map-leader-step))); }
.js .map.is-revealed .map__legend-item:nth-of-type(6) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration) + (5 * var(--map-leader-step))); }
.js .map.is-revealed .map__legend-item:nth-of-type(7) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration) + (6 * var(--map-leader-step))); }
.js .map.is-revealed .map__legend-item:nth-of-type(8) { transition-delay: calc(var(--map-draw-duration) + 0.5s + var(--map-leader-duration) + (7 * var(--map-leader-step))); }

/* The primary marker keeps a slow pulse once it has arrived, so the eye knows
   which of the nine things on the map is the property. */
.js .map.is-revealed .map__pulse {
  animation: map-pulse var(--map-pulse-duration) var(--map-draw-ease) infinite;
  animation-delay: calc(var(--map-draw-duration) + 0.2s);
}
@keyframes map-pulse {
  0%   { r: 11px; opacity: 0.9; }
  70%  { r: 21px; opacity: 0; }
  100% { r: 21px; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .js .map__coast,
  .js .map__land,
  .js .map__marker,
  .js .map__leader,
  .js .map__legend-item,
  .js .map__inset { transition: none; }
  .js .map.is-revealed .map__marker,
  .js .map.is-revealed .map__leader,
  .js .map.is-revealed .map__legend-item,
  .js .map.is-revealed .map__inset { transition-delay: 0s; }
  .js .map.is-revealed .map__pulse { animation: none; }
}

/* ---------- floating contact widget ----------
   Bottom-right, above the page, below the lightbox and the consent panel.

   It borrows rather than declares: the pill and the panel take the header
   pill's surface, blur and saturation, and the modal backdrop takes the
   lightbox's, so the three glass surfaces on this site cannot drift apart.
   Only position, size, motion and the panel and modal shapes are its own.

   Gated on .js. Everything it does needs JavaScript, and a pill that does
   nothing is worse than no pill. */
.widget { display: none; }
.js .widget {
  position: fixed;
  right: calc(var(--widget-offset-mobile) + env(safe-area-inset-right, 0px));
  bottom: calc(var(--widget-offset-mobile) + env(safe-area-inset-bottom, 0px));
  z-index: var(--widget-z);
  display: flex;
  /* column-reverse, so the panel — second in the DOM, after the pill that
     controls it — is drawn above the pill rather than below it, where it would
     run off the bottom of the viewport. DOM order stays the reading order. */
  flex-direction: column-reverse;
  align-items: flex-end;
  gap: var(--widget-panel-gap);
}

/* Out of the way of the drawer, the lightbox and the consent panel. */
.js .widget.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--widget-panel-in) var(--header-ease), visibility 0s linear var(--widget-panel-in);
}

/* The header's SCROLLED surface, not its resting one. The header sits over the
   hero photograph, where 0.55 is plenty; this floats over whatever section the
   reader has reached, and over ivory that same 0.55 put the label at 4.15:1.
   0.72 is the value the header itself moves to once it is over content, so the
   widget is still taking the header's glass rather than inventing its own. */
.widget__pill {
  display: flex;
  align-items: stretch;
  border-radius: var(--widget-radius);
  /* The nav pill's one visible edge, on this pill too. overflow:hidden clips the
     segments to the rounded shape, so the border goes on this box, not on them. */
  border-bottom: var(--header-border-width) solid var(--header-border);
  overflow: hidden;
  background: var(--header-surface-scrolled);
  -webkit-backdrop-filter: blur(var(--header-blur-scrolled)) saturate(var(--header-saturate));
  backdrop-filter: blur(var(--header-blur-scrolled)) saturate(var(--header-saturate));
}

.widget__main,
.widget__caret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--widget-height);
  border: 0;
  background: none;
  color: var(--color-on-dark);
  cursor: pointer;
  font-family: inherit;
}
.widget__main {
  gap: var(--widget-gap);
  padding-inline: var(--widget-pad);
  font-size: var(--widget-label-size);
  font-weight: var(--header-link-weight);
  letter-spacing: var(--widget-label-tracking);
}
/* A hairline between the two, not a gap: they are one pill carrying two
   controls, and a gap would read as two. */
.widget__caret {
  width: var(--widget-caret-width);
  border-inline-start: var(--hairline) solid var(--widget-divider);
}
.widget__main:hover,
.widget__caret:hover { background: var(--header-cta-hover); }

/* The pill clips to its own rounded shape, which would clip an outline drawn
   outside a segment. Drawn inside, the whole ring is visible on both. */
.widget__main:focus-visible,
.widget__caret:focus-visible,
.widget__row:focus-visible { outline-offset: -4px; }

.widget__icon {
  width: var(--widget-icon-size);
  height: var(--widget-icon-size);
  flex: none;
}
/* Stroke width belongs here: var() is not dependable in an SVG presentation
   attribute. */
.widget__icon [stroke] { stroke-width: var(--widget-icon-stroke); }
.widget__icon--caret { transition: transform var(--widget-panel-in) var(--header-ease); }
[aria-expanded="true"] .widget__icon--caret { transform: rotate(180deg); }

/* ---------- the brokers page's floating WhatsApp control (IDI-1246) ----------
   Not a variant of the pill -- a replacement for it. On /brokers/ the pill, the
   caret, the panel and the modal are not rendered, so everything below styles
   the one link that takes their place. It sits in the same .widget box, so the
   fixed corner, the safe-area insets, the z-index and .is-hidden are inherited
   and not restated: the drawer, the lightbox and the consent panel move it
   aside exactly as they move the pill.

   This is the one surface on the site that does not borrow the header's glass.
   It is WhatsApp's own green-filled lockup and has to read as WhatsApp or it is
   not worth doing. White on #25D366 measures 1.98:1, under AA, shipped as a
   brand exception on instruction; the compensation is size, so the label is
   15px/600 against the pill's 13px/300 and the glyph is 24px against 20px. */
.js .widget--wa { gap: 0; }

.widget__wa {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  /* A disc below sm. The labelled pill is ~190px wide, and at 375 that is the
     width that lands on the centred footer credit -- the overlap the footer's
     widget clearance exists for. The label stays in the DOM, hidden. */
  width: var(--widget-wa-disc);
  height: var(--widget-wa-disc);
  padding: 0;
  gap: var(--widget-wa-gap);
  border-radius: 50%;
  background: var(--color-whatsapp);
  color: #fff;
  text-decoration: none;
  font-family: inherit;
  font-size: var(--widget-wa-label-size);
  font-weight: var(--widget-wa-label-weight);
  line-height: 1;
  transition: background-color 0.3s ease;
}
/* The global a:hover at the top of this file turns any anchor gold, and it
   outranks the flat colour above -- (0,1,1) against (0,1,0). Unfixed, hovering
   turned the glyph and the label #B5935F over the green, and on a touch screen
   the hover sticks after a tap, so the disc went gold and stayed gold. White is
   re-asserted on every interactive state rather than on :hover alone, so no
   other state can pick the gold up later. Same call, same reason, as
   .nav__link--outline above. The fill is the only thing hover changes.

   -webkit-tap-highlight-color is deliberately left at the browser default: no
   rule sets it anywhere on this site, and singling this control out would make
   it the one link that flashes differently. */
.widget__wa:hover,
.widget__wa:focus,
.widget__wa:focus-visible,
.widget__wa:active,
.widget__wa:visited { color: #fff; }
.widget__wa:hover { background: var(--color-whatsapp-dark); }

.widget__wa-glyph {
  width: var(--widget-wa-glyph-disc);
  height: var(--widget-wa-glyph-disc);
  flex: none;
}

/* Hidden, not absent: it is the link's visible name from sm up and its
   accessible name is the same string at every width. */
.widget__wa-label {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- the panel ---------- */
.widget__panel {
  width: var(--widget-panel-width);
  border-radius: var(--widget-panel-radius);
  border-bottom: var(--header-border-width) solid var(--header-border);
  overflow: hidden;
  background: var(--header-surface-scrolled);
  -webkit-backdrop-filter: blur(var(--header-blur-scrolled)) saturate(var(--header-saturate));
  backdrop-filter: blur(var(--header-blur-scrolled)) saturate(var(--header-saturate));
  opacity: 0;
  transform: translateY(var(--widget-panel-rise));
  transition: opacity var(--widget-panel-in) var(--header-ease),
              transform var(--widget-panel-in) var(--header-ease);
}
.widget__panel.is-open { opacity: 1; transform: translateY(0); }
.widget__panel[hidden] { display: none; }
.widget__panel-inner { display: flex; flex-direction: column; }

.widget__row {
  display: flex;
  align-items: center;
  gap: var(--widget-gap);
  min-height: var(--widget-panel-row-height);
  padding-inline: var(--widget-pad);
  color: var(--color-on-dark);
  text-decoration: none;
  font-size: var(--widget-label-size);
  font-weight: var(--header-link-weight);
  letter-spacing: var(--widget-label-tracking);
}
.widget__row + .widget__row { border-block-start: var(--hairline) solid var(--widget-divider); }
.widget__row:hover { background: var(--header-cta-hover); color: var(--color-on-dark); }

/* ---------- the modal ---------- */
.widget-dialog {
  position: fixed;
  inset: 0;
  z-index: calc(var(--widget-z) + 10);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--lightbox-stage-pad);
  background: radial-gradient(circle at center,
              var(--lightbox-backdrop-centre) 0%,
              var(--lightbox-backdrop-edge) 100%);
  -webkit-backdrop-filter: blur(var(--lightbox-blur));
  backdrop-filter: blur(var(--lightbox-blur));
  opacity: 0;
  transition: opacity var(--widget-modal-in) var(--lightbox-ease);
}
.widget-dialog.is-open { opacity: 1; }
/* display:flex above beats the browser default for [hidden], and a closed
   dialog that still lays out is a full-screen sheet swallowing every click on
   the page. Same reason .lightbox carries this rule. */
.widget-dialog[hidden] { display: none; }

/* One column on a phone, two from md. The panel itself never scrolls: the
   photograph is clipped to these corners, and a panel that scrolled would
   slide it out of them. The left column scrolls instead. */
.widget-dialog__panel {
  position: relative;
  width: 100%;
  max-width: var(--widget-modal-max-width);
  max-height: 85svh;
  overflow: hidden;
  border-radius: var(--widget-modal-radius);
  background: var(--color-ivory);
  display: grid;
  grid-template-columns: 1fr;
  /* minmax(0, 1fr), not auto. An auto row sizes to its content, so with a tall
     form the row grew past the panel's max-height: the panel clipped at 85svh
     but the image column kept the row's full height, and the lockup anchored
     to its foot ended up below the clip. A row that cannot exceed the panel
     puts the foot of the photograph where the foot of the modal is. */
  grid-template-rows: minmax(0, 1fr);
  transform: scale(var(--widget-modal-scale-from));
  transition: transform var(--widget-modal-in) var(--lightbox-ease);
}

/* The modal reassigns the form's metrics on itself. One set of rules, two
   sets of numbers: the page has all the height it wants, the modal is clamped
   to 85svh, and a form that scrolls inside a dialog is a form people abandon.
   Scrolling stays available for genuinely short viewports. */
.widget-dialog__main {
  --field-height: var(--field-height-modal);
  --field-pad-block: var(--field-pad-block-modal);
  --field-label-gap: var(--field-label-gap-modal);
  --field-row-gap: var(--field-row-gap-modal);
  --field-consent-size: var(--field-consent-size-modal);
  --field-consent-leading: var(--field-consent-leading-modal);
  --field-consent-gap: var(--field-consent-gap-modal);

  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  padding: var(--widget-modal-pad);
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* One step down from the h3 the heading takes elsewhere. In a dialog the
   heading has less to do -- the reader opened this deliberately and is looking
   straight at it -- so it can give its height to the fields. */
.widget-dialog__main .display-3 { font-size: var(--field-modal-heading); }

/* Below md the photograph is not rendered at all. A slim banner was the other
   option and it would have cost a request and a band of vertical space on the
   screen that has least of it, to show a crop too short to read. */
.widget-dialog__aside { display: none; }
.widget-dialog.is-open .widget-dialog__panel { transform: scale(1); }

/* Top-right of the modal, which from md means on top of the photograph. The
   disc is what makes it readable there: a render is not a surface you can
   assume anything about, and an ivory glyph on a bright sky is invisible. On a
   phone there is no photograph, so it sits on the ivory and takes the lagoon
   ink instead. z-index because it is painted before nothing -- it is the last
   child, but the image column creates no stacking context of its own. */
.widget-dialog__close {
  position: absolute;
  z-index: 2;
  top: var(--lightbox-control-inset);
  inset-inline-end: var(--lightbox-control-inset);
  width: var(--lightbox-control-size);
  height: var(--lightbox-control-size);
  min-width: var(--tap-target);
  min-height: var(--tap-target);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--color-lagoon);
  cursor: pointer;
}
.widget-dialog__close:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 2px;
}
.widget-dialog__close svg { width: var(--lightbox-icon-size); height: var(--lightbox-icon-size); }
.widget-dialog__close svg [stroke] { stroke-width: var(--widget-icon-stroke); }

/* Translucent with nothing blurred behind it is the unreadable case. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .widget__pill, .widget__panel { background: var(--header-surface-solid); }
  .widget-dialog { background: var(--lightbox-backdrop-solid); }
}
@media (prefers-reduced-transparency: reduce) {
  .widget__pill, .widget__panel {
    background: var(--header-surface-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .widget__panel,
  .widget__icon--caret,
  .widget-dialog,
  .widget-dialog__panel,
  .js .widget.is-hidden { transition: none; }
  .widget__panel { transform: none; }
  .widget-dialog__panel { transform: none; }
}

/* ---------- the form's own states ---------- */
/* The honeypot: out of the layout, out of the tab order, out of the
   accessibility tree. */
.form__trap {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* The outcome of a submit, boxed and centred: as a line of body text under
   the button it read as part of the form and was missed. No `display` here,
   so the element's `hidden` attribute still hides it. Square and 1px, like
   the fields. The error is the field errors' red, so a failure never reads
   as a success -- on the dark sections the two used to be the same colour. */
.form__status {
  margin: 0;
  padding: 16px 20px;
  border: 1px solid var(--color-rule-strong);
  font-size: var(--type-body-sm);
  text-align: center;
}
.form__status--ok {
  border-color: var(--color-lagoon);
  background: rgba(15, 61, 59, 0.06);
  color: var(--color-lagoon);
}
.form__status--error {
  border-color: var(--color-error, #8a3324);
  background: rgba(138, 51, 36, 0.06);
  color: var(--color-error, #8a3324);
}
.section--dark .form__status, .section--dark-mid .form__status {
  border-color: var(--color-rule-on-dark);
  color: var(--color-on-dark);
}
.section--dark .form__status--ok, .section--dark-mid .form__status--ok {
  border-color: var(--color-gold);
  background: rgba(181, 147, 95, 0.12);
}
.section--dark .form__status--error, .section--dark-mid .form__status--error {
  border-color: #F4B9A8;
  background: rgba(244, 185, 168, 0.1);
}

/* ---------- scaling up ----------
   Mobile-first. Only these four breakpoints are used anywhere in this file:
     sm 640 · md 900 · lg 1200 · xl 1440
   They are recorded in src/_data/site.json under `breakpoints`; CSS cannot
   read a custom property inside a media query condition, so the values are
   literals here and that key is the documented list. */

/* sm — room for two columns and side-by-side buttons. */
@media (min-width: 640px) {
  .js .widget {
    right: calc(var(--widget-offset) + env(safe-area-inset-right, 0px));
    bottom: calc(var(--widget-offset) + env(safe-area-inset-bottom, 0px));
  }

  /* Room for the label, so the disc becomes the pill the brief asks for. */
  .widget__wa {
    width: auto;
    height: var(--widget-wa-height);
    padding-inline: var(--widget-wa-pad);
    border-radius: var(--widget-radius);
  }
  .widget__wa-glyph { width: var(--widget-wa-glyph); height: var(--widget-wa-glyph); }
  .widget__wa-label {
    position: static;
    width: auto; height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
  }
  .footer { padding-block-end: var(--footer-band-gap); }

  /* The legend column goes back on the map, and the list under it goes away.
     The badges go with the list: above 640 every dot has its name beside it on
     a leader, so a number would be a second answer to a question already
     answered. */
  .map__legend,
  .map__leaders { display: block; }
  .map__list,
  .map__badges { display: none; }

  .columns, .stats--columns, .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .consent__actions .btn { flex: 1 1 auto; min-width: 150px; }
}

/* md — the 12-column grid, the horizontal nav and the desktop hero. */
@media (min-width: 900px) {
  .header {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    width: auto;
    min-width: var(--header-width-desktop);
    max-width: calc(100vw - (2 * var(--space-gutter)));
    min-height: var(--header-height-desktop);
    padding: 0 var(--header-pad-desktop);
  }
  .header__bar { width: auto; min-height: 0; }
  /* The pill is taller from md, so everything that clears it clears more. */
  :root { --clear-of-pill: calc(var(--header-top) + var(--header-height-desktop) + var(--header-anchor-gap)); }
  .brand { flex: none; }
  /* md and up: the crown comes back and the lockup steps aside, because the
     hero carries the lockup there and the nav needs the room. */
  .brand__lockup { display: none; }
  .brand__mark { display: block; }
  .hero .brand-lockup { display: block; }
  .header.is-open {
    flex-direction: row;
    align-items: center;
    border-radius: var(--header-radius);
    padding-bottom: 0;
  }
  .nav { display: flex; }
  .nav-toggle { display: none; }


  /* The text column keeps the same distance from the image as it keeps from the
     page gutter: --space-text-inset is the gutter minus the grid gap, so adding
     it to the one-gap separation lands exactly on the gutter. One token serves
     both orientations. Below md the row is a single column with no image beside
     the text, so it stays off. */
  .grid--map > .map-column { order: 0; }

  .split-text { padding-inline-end: var(--space-text-inset); }
  .split-text--flip {
    padding-inline-end: 0;
    padding-inline-start: var(--space-text-inset);
  }

  .grid { grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr)); }

  .col-1-4 { grid-column: 1 / span 4; }
  .col-1-5 { grid-column: 1 / span 5; }
  .col-1-6 { grid-column: 1 / span 6; }
  .col-1-7 { grid-column: 1 / span 7; }
  .col-6-7 { grid-column: 6 / span 7; }
  .col-7-6 { grid-column: 7 / span 6; }
  .col-8-5 { grid-column: 8 / span 5; }
  .col-9-4 { grid-column: 9 / span 4; }

  .subgrid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .subgrid-2__full { grid-column: 1 / span 2; }

  .nav { display: flex; }
  .nav-toggle { display: none; }


  .hero { min-height: var(--hero-min-desktop); padding-block-end: var(--hero-pad-end-desktop); }
  .hero--page { min-height: var(--hero-page-min-desktop); padding-block-end: var(--hero-page-pad-end-desktop); }

  .list-note { padding-left: 52px; }
  .footer__top {
    flex-direction: row;
    align-items: flex-start;
  }
  /* Side by side from md: the paragraph takes the wider column and the credit
     the narrower one, ranged to the rule's right edge and starting at the same
     top as the text rather than centred under it. The ratio is what sets the
     paragraph's measure, so it lives in a token. */
  .footer__legal {
    grid-template-columns: var(--footer-legal-columns);
    column-gap: var(--footer-legal-column-gap);
    align-items: start;
    justify-items: stretch;
  }
  .footer__developer { justify-self: end; }
  /* Right-aligned against the container edge, not merely pushed there: the
     nav is the last item in a space-between row, and its own items range
     right so a wrapped second line stays on the same edge. */
  .footer__nav { justify-content: flex-end; }
  .footer__navs { align-items: flex-end; }

}

/* lg — the third column. */
@media (min-width: 1200px) {
  .columns, .stats--columns, .gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- lightbox: a viewer, not a page ----------
   The backdrop dims and blurs the page instead of replacing it, so the gallery
   stays visible behind the image and closing reads as stepping back rather than
   navigating away. Nothing is boxed: the controls are thin glyphs floating on
   44px targets, and the image gets the rest of the viewport. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: flex;
  padding: 0;
  border: 0;
  background: radial-gradient(circle at center,
              var(--lightbox-backdrop-centre) 0%,
              var(--lightbox-backdrop-edge) 100%);
  -webkit-backdrop-filter: blur(var(--lightbox-blur));
  backdrop-filter: blur(var(--lightbox-blur));
  opacity: 0;
  transition: opacity var(--lightbox-duration) var(--lightbox-ease);
}
.lightbox.is-open { opacity: 1; }
.lightbox[hidden] { display: none; }

/* A translucent overlay with nothing blurred behind it is the unreadable case,
   so both fallbacks are opaque enough to carry ivory on their own. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .lightbox { background: var(--lightbox-backdrop-solid); }
}
@media (prefers-reduced-transparency: reduce) {
  .lightbox {
    background: var(--lightbox-backdrop-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* The sand ground, for the floor plans (IDI-1153).

   A plan is dark ink lifted off its page, so it carries no ground of its own
   and the backdrop shows straight through the drawing. Over the dark backdrop
   that is unreadable -- the walls and dimensions vanish -- so these open on the
   site's own sand, which is what the page beneath them uses and what the ink
   was drawn to sit on. The blur goes with it: there is nothing to separate a
   flat opaque ground from, and it only softened the page showing round the
   edges.

   Every control and the caption flip to dark ink, or they would be ivory on
   sand. Contrast is measured in the done comment, not assumed. */
.lightbox--sand {
  background: var(--color-sand);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .lightbox--sand { background: var(--color-sand); }
}
@media (prefers-reduced-transparency: reduce) {
  .lightbox--sand { background: var(--color-sand); }
}
.lightbox--sand .lightbox__caption,
.lightbox--sand .lightbox__count,
.lightbox--sand .lightbox__control { color: var(--color-ink); }
.lightbox--sand .lightbox__control:hover { color: var(--color-gold); }

.lightbox__stage {
  position: relative;
  z-index: 1;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--lightbox-stage-pad);
  min-height: 0;
}
.lightbox__figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--lightbox-caption-gap);
  max-width: 100%;
  max-height: 100%;
  transform: scale(var(--lightbox-scale-from));
  transition: transform var(--lightbox-duration) var(--lightbox-ease);
}
.lightbox.is-open .lightbox__figure { transform: scale(1); }
.lightbox__figure picture { display: block; min-height: 0; }
.lightbox__figure img {
  display: block;
  max-width: var(--lightbox-img-max-width);
  max-height: var(--lightbox-img-max-height);
  width: auto;
  height: auto;
  object-fit: contain;
}

.lightbox__caption {
  font-family: var(--font-body);
  font-size: var(--type-body-sm);
  line-height: var(--type-body-sm-leading);
  color: var(--color-on-dark);
  text-align: center;
  max-width: 68ch;
}
.lightbox__caption[hidden] { display: none; }

/* Where you are in the group. It carries real information, so it outlived the
   bar it used to sit in; a group of one has nothing to say and is hidden. */
.lightbox__count {
  position: absolute;
  top: var(--lightbox-control-inset);
  left: var(--lightbox-control-inset);
  z-index: 3;
  display: flex;
  align-items: center;
  min-height: var(--lightbox-control-size);
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  color: var(--color-on-dark);
}
.lightbox__count[hidden] { display: none; }

/* One control: a 44px target with a thin glyph centred in it. No border and no
   fill — the icon is the whole affordance. */
.lightbox__control {
  position: absolute;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--lightbox-control-size);
  height: var(--lightbox-control-size);
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--color-on-dark);
  cursor: pointer;
  transition: color var(--lightbox-duration) var(--lightbox-ease);
}
.lightbox__control:hover { color: var(--color-gold); }
.lightbox__control:focus-visible {
  outline: var(--lightbox-focus-ring) solid var(--color-gold);
  outline-offset: 2px;
}
.lightbox__control[hidden] { display: none; }
.lightbox__icon {
  width: var(--lightbox-icon-size);
  height: var(--lightbox-icon-size);
}

.lightbox__close {
  top: var(--lightbox-control-inset);
  right: var(--lightbox-control-inset);
}
.lightbox__nav { top: 50%; transform: translateY(-50%); }
.lightbox__nav--prev { left: var(--lightbox-control-inset); }
.lightbox__nav--next { right: var(--lightbox-control-inset); }

@media (prefers-reduced-motion: reduce) {
  .lightbox,
  .lightbox__figure,
  .lightbox__control { transition: none; }
  .lightbox__figure { transform: none; }
}

/* ---------- the modal's form column ----------
   The heading and the form. Before IDI-1152 a three-question journey ran here
   first; the modal now opens straight on the form. */
.widget-dialog__head { display: flex; flex-direction: column; gap: 8px; }
.widget-dialog__head[hidden], .widget-dialog__form[hidden] { display: none; }

@media (min-width: 900px) {
  .widget-dialog__panel {
    max-width: var(--widget-modal-max-width-journey);
    grid-template-columns: 1fr var(--widget-modal-panel-width);
  }

  /* Flush to the modal's top, right and foot, and meeting the content column
     on its left with nothing between them. The corners come from the panel's
     own overflow, so the image needs no radius of its own and cannot
     disagree with the modal's. */
  .widget-dialog__aside {
    display: block;
    position: relative;
    min-width: 0;
    min-height: 0;
    background: var(--color-lagoon);
  }
  /* <picture> needs the height too. It is a real element between the column
     and the image, and an img's height:100% resolves against it -- left auto
     it takes its content height and the photograph stops short, with the
     column's ground showing beneath. The same reason .media picture carries
     this rule. */
  .widget-dialog__aside picture {
    display: block;
    width: 100%;
    height: 100%;
  }
  .widget-dialog__image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* Transparent to a dark foot, tall enough that the fade reads as a gradient
     rather than a band with an edge. 0.58 rather than the 0.5 first drawn:
     against this render the lockup measured 4.85:1 at 1024, which passes and
     has almost nothing in hand, and the photograph here comes from a
     media.json slot that somebody can swap for a brighter one without ever
     looking at this file. */
  .widget-dialog__wash {
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: var(--widget-modal-wash-height);
    background: linear-gradient(to bottom,
                rgba(0, 0, 0, 0) 0%,
                rgba(0, 0, 0, 0.28) 55%,
                rgba(0, 0, 0, 0.58) 100%);
    pointer-events: none;
  }
  .widget-dialog__lockup {
    position: absolute;
    left: 50%;
    bottom: 22px;
    transform: translateX(-50%);
    width: var(--widget-modal-lockup-width);
    height: auto;
  }

  /* Over the photograph now, so ivory on a disc rather than lagoon on ivory. */
  .widget-dialog__close {
    color: var(--color-ivory);
    background: var(--widget-modal-close-disc);
  }
  .widget-dialog__close:hover { background: rgba(15, 20, 19, 0.62); }
}

/* The form is display:flex, which beats the browser default for [hidden], so
   hiding it in script left it laid out with its status line and its "not
   connected" note still on screen. Same trap the modal and
   the lightbox already carry this rule for. */
.form[hidden] { display: none; }


/* ---------- the phone field ----------
   A dial-code button, the national number, and a searchable list of countries
   (IDI-1166). The button and the input read as one control, so they share a
   border and the seam between them is a hairline rather than two edges. */
.phone { position: relative; display: flex; align-items: stretch; }
.phone__country {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  padding-inline: 12px;
  border: var(--hairline) solid var(--color-rule);
  border-inline-end: 0;
  border-start-start-radius: var(--field-radius, 0);
  border-end-start-radius: var(--field-radius, 0);
  background: none;
  color: inherit;
  font: inherit;
  font-size: var(--type-body-sm);
  cursor: pointer;
  min-height: var(--tap-target);
}
.phone__country:hover { border-color: var(--color-gold); }
.phone__country:focus-visible { outline: 2px solid var(--color-gold); outline-offset: -2px; }
.phone__flag { font-size: 1.1em; line-height: 1; }
.phone__code { font-variant-numeric: tabular-nums; }
.phone__caret { width: 14px; height: 14px; }
.phone__caret [stroke] { stroke-width: var(--widget-icon-stroke); }
.phone__number { flex: 1 1 auto; min-width: 0; }

/* The list is positioned over the page rather than pushing it: a field that
   grows by 300px when opened moves everything under it, and the reader loses
   their place in the form. */
.phone__list {
  position: absolute;
  z-index: 20;
  inset-inline-start: 0;
  top: calc(100% + 6px);
  width: min(360px, 100%);
  max-height: 320px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: var(--hairline) solid var(--color-rule);
  border-radius: var(--widget-journey-option-radius);
  background: var(--color-ivory);
  box-shadow: 0 18px 40px rgba(15, 61, 59, 0.16);
}
.phone__list[hidden] { display: none; }
.phone__search-label {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.phone__options {
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-y: auto;
  min-height: 0;
}
.phone__option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: var(--type-body-sm);
  color: var(--color-ink);
  cursor: pointer;
}
.phone__option[hidden] { display: none; }
.phone__option-name { flex: 1 1 auto; min-width: 0; }
.phone__option-code { color: var(--color-muted); font-variant-numeric: tabular-nums; }
.phone__option:hover,
.phone__option.is-active { background: var(--color-sand); }
/* Chosen, not merely under the cursor: the gold says which one the field is
   currently set to, and survives the pointer moving away. */
.phone__option.is-selected { color: var(--color-lagoon); }
.phone__option.is-selected .phone__option-code { color: var(--color-gold-ink); }
.phone__empty, .phone__hint { margin: 0; color: var(--color-muted); }
.phone__hint { margin-block-start: 6px; color: var(--color-error, #8a3324); }
.phone__hint[hidden] { display: none; }

/* A field error the server returned (IDI-1152). Same colour as the phone hint. */
.field__error { margin: 6px 0 0; color: var(--color-error, #8a3324); }
/* On the dark sections -- the homepage block -- the red is lifted so it reads
   against the lagoon ground as well as it does on ivory. */
.section--dark .field__error, .section--dark .phone__hint,
.section--dark-mid .field__error, .section--dark-mid .phone__hint { color: #F4B9A8; }

.section--dark .phone__country,
.section--dark-mid .phone__country { border-color: var(--color-rule-on-dark); color: var(--color-on-dark); }
.section--dark .phone__option,
.section--dark-mid .phone__option { color: var(--color-ink); }

/* A submit in flight: the button says so and stops taking presses. */
.form.is-busy { cursor: progress; }
.form.is-busy .btn[aria-busy="true"] { opacity: 0.7; }

/* ---------- the cookie table ---------- */
.cookie-table__scroll { overflow-x: auto; }
.cookie-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--type-body-sm);
  text-align: start;
}
.cookie-table th, .cookie-table td {
  padding: 12px 14px;
  border-block-end: var(--hairline) solid var(--color-rule);
  text-align: start;
  vertical-align: top;
}
.cookie-table thead th {
  font-size: var(--type-meta);
  letter-spacing: var(--type-meta-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
  font-weight: 500;
}
.cookie-table tbody th { font-weight: 400; white-space: nowrap; }
.cookie-table code { font-size: 0.95em; }

/* ---------- /idiliq/ ----------
   The developer's page. It reuses the site's grid, type and grounds; what is
   here is the three things that page needs and no other page does. */

/* The hero without a photograph. Every other page opens on the place; this one
   opens on the company, which has nothing to photograph that is not somebody
   else's building. Type on ivory, and the gold hairline underneath is the only
   mark on it. */
.hero--type { padding-block-start: calc(var(--space-section) + 72px); }
.hero--type__inner { max-width: var(--space-measure); }
.hero--type .rule { width: 96px; }

/* By the Numbers: the developer band's hairline column, eight of them. One up
   from the phone, two from sm, four from md -- eight across would set the
   figures smaller than the labels deserve. */
.stats--grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--grid-gap) var(--grid-gap);
}
.stats--grid .stat {
  padding-top: 28px;
  border-top: var(--hairline) solid var(--color-rule-on-dark);
}

/* The heading is the company's mark; the words behind it are for the outline
   and the accessible name, not for the eye. */
.heading--mark { display: block; }
.brand-mark--heading {
  height: clamp(38px, 4.4vw, 58px);
  margin-block: 4px 0;
}
/* The IDILIQ Developments lockup: the same wordmark with DEVELOPMENTS set
   under it, in a canvas 81 units tall against the bare mark's 50. Both are
   drawn at the same scale on the same 200-unit width -- rendered to the same
   width, their IDILIQ ink occupies exactly the same rows, 0 to 498 of a 2000px
   render in both files -- so matching the cap height is only a matter of
   giving this one 81/50 of the height, which is what the token says in so many
   words. Sized here rather than by editing .brand-mark--heading, because the
   IDILIQ page still wears the bare mark and must not move. */
.brand-mark--heading-dev { height: var(--brand-idiliq-dev-mark); }

/* Four markets: four columns, each a place, a figure and the year it opened.
   The figure is the display face at stat size -- it is the thing worth
   reading, and the word after it is not. */
.markets {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  border-top: var(--hairline) solid var(--color-rule);
}
.markets__col {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-block: 28px;
  border-bottom: var(--hairline) solid var(--color-rule);
}
.markets__place {
  font-size: var(--type-label);
  font-weight: var(--type-label-weight);
  letter-spacing: var(--type-label-tracking);
  text-transform: uppercase;
  color: var(--color-muted);
}
.markets__figure { display: flex; align-items: baseline; gap: 10px; }
.markets__count {
  font-family: var(--font-display);
  font-size: var(--type-stat);
  font-weight: var(--type-stat-weight);
  line-height: 1;
  color: var(--color-lagoon);
  font-variant-numeric: tabular-nums;
}
.markets__suffix {
  font-size: var(--type-body-sm);
  color: var(--color-muted);
}
/* Ink, and the gold moved into a hairline.

   The year was set in gold-ink, which measured 4.41:1 on sand -- under AA, at
   15px. gold-ink is an ivory colour and this is not an ivory ground; the plan
   cards found the same thing and gave up their gold "View plan" for the same
   reason. The accent is not lost, it is moved: a 24px gold hairline in front
   of the year, which is the site's own rule vocabulary and has no contrast
   bar to clear because it carries no words. Ink on sand measures 12.74:1. */
.markets__since {
  font-size: var(--type-body-sm);
  color: var(--color-ink);
  display: flex;
  align-items: center;
  gap: 10px;
}
.markets__since::before {
  content: "";
  flex: none;
  width: 24px;
  height: var(--hairline);
  background: var(--color-gold);
}

@media (min-width: 640px) {
  .stats--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .markets { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--grid-gap); }
}

/* ---------- the timeline ----------
   Below md: a ruled list down one gold line. A staircase is a horizontal idea
   and rotating it would say nothing, so on a phone it is simply a list that
   knows what order it is in. */
.timeline { margin-top: 48px; --timeline-horizontal: 0; }
.timeline__frame { position: relative; }
.timeline__track { display: flex; flex-direction: column; }
.timeline__rule { display: none; }
.timeline__step {
  position: relative;
  padding: 0 0 32px 28px;
  border-left: var(--hairline) solid var(--color-gold);
}
.timeline__step:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline__step::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-gold);
  transform: translateX(-3.5px);
}
/* The years are figures, and they are set like the page's other figures: the
   display face at its own fluid size, 28 on a phone and 40 across a desktop.
   The milestone under each one stays Inter at body-small -- the year is what
   the eye scans a timeline for, and the sentence is what it reads once it has
   stopped. */
.timeline__when {
  display: block;
  font-family: var(--font-display);
  font-size: var(--type-timeline-year);
  font-weight: var(--type-timeline-year-weight);
  line-height: 1;
  color: var(--color-gold);
  font-variant-numeric: tabular-nums;
}
.timeline__what {
  display: block;
  margin-top: 8px;
  max-width: 34ch;
  font-size: var(--type-body-sm);
  line-height: 1.5;
  color: var(--color-on-dark-soft);
}

@media (min-width: 900px) {
  .stats--grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 48px var(--grid-gap); }
  .markets { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* One rule, nine points, all of it inside the container: nothing scrolls and
     nothing hides off the edge. A label sits above or below by its index and
     hangs from the rule on a gold line whose length is its tier --
     animations.js raises a tier whenever a label would land on the one before
     it on the same side, so no two ever overlap at any width.

     --timeline-horizontal is how the script asks CSS which arrangement is
     live. The media query owns that decision and the measuring code reads it,
     rather than the breakpoint being written twice. */
  .timeline {
    --timeline-horizontal: 1;
    --timeline-stem: 30px;
    /* A fallback only. animations.js measures the tallest label and writes the
       real step back, because the step has to clear a whole label: two labels
       on different tiers must not be able to touch, and a label's height
       depends on the year's size, the milestone's length and the width it
       wraps at. 38px was fine when the years were 26px and is not now. */
    --timeline-tier: 104px;
    --tiers: 0;
    margin-top: 64px;
  }
  .timeline__track {
    display: grid;
    grid-template-columns: repeat(9, minmax(0, 1fr));
    min-height: calc(2 * (var(--timeline-stem) + var(--tiers) * var(--timeline-tier) + 92px));
  }
  .timeline__rule {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    height: var(--hairline);
    background: var(--color-gold);
    transform-origin: left center;
  }
  .timeline__step { padding: 0; border-left: 0; }
  /* The point, on the rule, at the middle of its own column. */
  .timeline__step::before {
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
  /* The line from the rule out to the label. */
  .timeline__step::after {
    content: "";
    position: absolute;
    left: 50%;
    width: var(--hairline);
    height: calc(var(--timeline-stem) + var(--tier, 0) * var(--timeline-tier));
    background: var(--color-gold);
    transform: translateX(-50%);
  }
  /* Odd milestones -- the 1st, 3rd, 5th -- hang above the rule. */
  .timeline__step:nth-child(odd)::after { bottom: 50%; }
  .timeline__step:nth-child(even)::after { top: 50%; }

  .timeline__label {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    /* Wide enough that the longest year -- "October 2026" at 40px -- stays on
       one line. At 210px it broke after "October", which made that label a
       line taller than every other and pushed the whole track down with it. */
    max-width: 244px;
    text-align: center;
  }
  /* A date is one thing and never two lines of one. */
  .timeline__when { white-space: nowrap; }
  .timeline__step:nth-child(odd) .timeline__label {
    bottom: calc(50% + var(--timeline-stem) + var(--tier, 0) * var(--timeline-tier) + 10px);
  }
  .timeline__step:nth-child(even) .timeline__label {
    top: calc(50% + var(--timeline-stem) + var(--tier, 0) * var(--timeline-tier) + 10px);
  }
  .timeline__what { max-width: none; margin-top: 6px; }

  /* The first and last labels range to the ends of the rule instead of
     centring on their points. Centred, a 210px label on a 144px column hangs
     past the container on both ends -- which is the one thing this
     arrangement exists to avoid. */
  .timeline__step:first-of-type .timeline__label {
    left: 0;
    transform: none;
    text-align: left;
  }
  .timeline__step:last-of-type .timeline__label {
    left: auto;
    right: 0;
    transform: none;
    text-align: right;
  }
}

/* ---------- the closing image band (IDI-1214) ----------
   The hero's parts, rearranged for centred type: the same neutral scrim
   colours held through the middle rather than at the foot, the same display
   face, the same button the hero uses on a dark ground. What it replaces is a
   button alone in an empty strip. */
.image-band {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--band-min-mobile-fallback);
  min-height: var(--band-min-mobile);
  padding: var(--space-section-tight) var(--space-gutter);
  background: var(--color-lagoon-mid);
  overflow: hidden;
  color: var(--color-on-dark);
}
.image-band__media { position: absolute; inset: 0; z-index: 0; }
/* Same chain as the hero: the figure needs its own height or the photograph
   resolves against its intrinsic ratio and stops partway down. */
.image-band__media > .media { height: 100%; }
.image-band__media img { width: 100%; height: 100%; object-fit: cover; }

/* Deepest through the middle, because that is where the type is. */
.image-band__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to bottom,
              var(--band-scrim-edge) 0%,
              var(--band-scrim-mid) 38%,
              var(--band-scrim-mid) 62%,
              var(--band-scrim-edge) 100%);
}
.image-band__inner {
  position: relative;
  z-index: 2;
  align-items: center;
  max-width: var(--space-measure);
  text-align: center;
}
.image-band__lockup {
  width: auto;
  height: var(--brand-lockup-hero);
}
.image-band__line {
  font-family: var(--font-display);
  font-size: var(--type-statement);
  font-weight: var(--type-statement-weight);
  line-height: var(--type-statement-leading);
  color: var(--color-on-dark);
}
.image-band .btn { align-self: center; }

/* The settle. The media partial marks every figure anim-scale; this only says
   how far it starts from here, which is gentler than a content image's. */
.js .image-band .anim-scale picture { transform: scale(var(--band-scale-from)); }
@media (prefers-reduced-motion: reduce) {
  .js .image-band .anim-scale picture { transform: none; }
}

@media (min-width: 900px) {
  .image-band {
    min-height: var(--band-min-desktop);
    padding-block: var(--space-section);
  }
}

