/* ==========================================================================
   Papa Remo Beach
   One stylesheet, no preprocessor, no build step. Sections are in cascade
   order: fonts, tokens, reset, type, buttons, layout, components, responsive.
   ========================================================================== */

/* ---- fonts --------------------------------------------------------------- */
@font-face {
  font-family: 'Archivo';
  src: url('../fonts/archivo-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Archivo Narrow';
  src: url('../fonts/archivo-narrow-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Archivo Narrow';
  src: url('../fonts/archivo-narrow-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---- tokens -------------------------------------------------------------- */
:root {
  /* Brand palette, from the supplied brand sheet. */
  --navy:      #0C1B31;
  --navy-800:  #16294A;   /* raised surfaces on navy */
  --cyan:      #019CC9;
  --cyan-700:  #0182A8;   /* cyan hover */
  --cyan-800:  #017091;   /* small text only — the only cyan that clears AA on
                             sand, white and grey. --cyan and --cyan-700 do not. */
  --orange:    #FF9323;
  --orange-600:#E87F13;   /* orange hover */
  --grey:      #F6F6F6;   /* UI surfaces, admin */

  /* Addition approved in design review: the brand grey is cool and clinical
     and fights the postcard motif. Sand is the paper tone. */
  --sand:      #FBF3E4;
  --sand-dark: #F2E6CE;   /* sand hover / edges */

  --paper:     #FFFFFF;
  --ink:       var(--navy);
  --ink-soft:  #5A6478;
  --line:      rgba(12, 27, 49, .14);
  --line-soft: rgba(12, 27, 49, .07);

  /* CONTRAST RULES — enforced at review, not negotiable:
     --orange on white is ~2.3:1 and FAILS AA. Orange is only ever a FILL,
       with --navy text on top of it. Never orange text on a light background.
     --cyan is a FILL and large-graphic colour only.
     --cyan-700 is decorative/hover only — it FAILS AA for small text
       (4.00:1 on sand). Use --cyan-800 for any text under 24px.
     Never put small text or an eyebrow on a --cyan background; use --navy.
     Body copy is always --navy on --sand, --paper or --grey. */

  /* type */
  --display: 'Archivo Narrow', 'Arial Narrow', Impact, sans-serif;
  --sans:    'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* spacing */
  --sp-1: .5rem;  --sp-2: 1rem;   --sp-3: 1.5rem;  --sp-4: 2rem;
  --sp-5: 3rem;   --sp-6: 4rem;   --sp-7: 6rem;    --sp-8: 8rem;

  --container: 1180px;
  --gutter: 1.25rem; /* side margin outside .container; narrower on phones below */
  --header-h:  72px;
  --radius:    4px;
  --radius-lg: 10px;
  --ease:      cubic-bezier(.2, .6, .2, 1);
  --shadow-card: 0 18px 40px -18px rgba(12, 27, 49, .35);
}

/* ---- reset --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}
img, picture, svg, video { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
a { color: inherit; }

/* A visible, non-colour-only focus ring everywhere. Navy is 15.65:1 on sand;
   a cyan ring is only 2.89:1 there and misses the 3:1 indicator threshold. */
:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
  border-radius: 2px;
}
/* Invert on dark surfaces, where a navy ring would vanish. */
.section--navy :focus-visible,
.site-footer :focus-visible,
.hero :focus-visible {
  outline-color: var(--sand);
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--navy); color: var(--sand);
  padding: var(--sp-2) var(--sp-3);
}
.skip-link:focus { left: 0; }

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

/* Honeypot: off-screen rather than visually-hidden's clip trick, so it stays
   in normal tab flow for nothing but a scraper that ignores CSS entirely —
   see honeypot_field() in includes/functions.php. */
.hp { position: absolute; left: -9999px; }

/* ---- typography ---------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -.01em;
  text-transform: uppercase;
  text-wrap: balance;
}
h1 { font-size: clamp(2.6rem, 7vw, 5.4rem); }
h2 { font-size: clamp(2rem, 4.4vw, 3.4rem); }
h3 { font-size: clamp(1.35rem, 2.4vw, 1.9rem); }
h4 { font-size: 1.1rem; letter-spacing: .02em; }

p { text-wrap: pretty; }

.eyebrow {
  font-family: var(--sans);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--cyan-800);
}
.lede { font-size: clamp(1.05rem, 1.6vw, 1.25rem); color: var(--ink-soft); }
.measure { max-width: 60ch; }
/* A narrow .container.measure block must still start on the page grid. Left
   alone, the container's auto margins centre the 60ch box, which floats
   left-aligned text ~300px in from every other section on a wide screen.
   Deliberately centred blocks opt out with .center-block. The start margin
   equals the .container's own edge: max(gutter, (viewport - max) / 2). */
.container.measure:not(.center-block) {
  margin-inline: max(var(--gutter), (100% - var(--container)) / 2) auto;
}
.stack > * + * { margin-top: var(--sp-2); }

/* ---- vertical rhythm ------------------------------------------------------ */
/* The reset above zeroes every margin, so a heading, its lede, the paragraph
   after it and the button row only breathe where a component says so — and
   most did not, so text blocks sat on top of each other, worst on phones.
   Sibling rules give every text block the same rhythm without touching
   markup: heading -> lede/paragraph 1 unit, paragraph -> paragraph 1 unit,
   anything -> button row 1.5 units. Components that already space themselves
   (postcard, factcard, strip head) are excused below so nothing doubles. */
.eyebrow { display: block; margin-bottom: var(--sp-1); }
:is(h1, h2, h3) + :is(p, .lede, ul, ol) { margin-top: var(--sp-2); }
:is(p, .lede) + :is(p, .lede) { margin-top: var(--sp-2); }
:is(p, .lede, ul, ol, .stats, .check-list, .grid) + .stack { margin-top: var(--sp-3); }
.factcard h3 + p { margin-top: 0; }      /* the h3 carries its own margin-bottom */
/* Two classes deep on purpose: the :is() rules above carry (0,1,1), which a
   single class would lose to. */
.postcard .postcard__sub { margin-top: 0; }  /* the postcard sets its own */

/* ---- buttons ------------------------------------------------------------- */
.btn {
  --btn-bg: var(--navy);
  --btn-fg: var(--sand);
  display: inline-flex; align-items: center; justify-content: center; gap: .6em;
  min-height: 50px;
  padding: .8rem 1.7rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 2px solid var(--btn-bg);
  border-radius: var(--radius);
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: transform .18s var(--ease), background .18s var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

/* Orange fill, navy text — the only accessible way to use orange. */
.btn--primary { --btn-bg: var(--orange); --btn-fg: var(--navy); }
.btn--primary:hover { --btn-bg: var(--orange-600); }

.btn--outline { --btn-bg: transparent; --btn-fg: var(--navy); border-color: var(--navy); }
.btn--outline:hover { --btn-bg: var(--navy); --btn-fg: var(--sand); }

/* For use on dark photography. */
.btn--light { --btn-bg: var(--sand); --btn-fg: var(--navy); }
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--sand); border-color: var(--sand); }
.btn--ghost:hover { --btn-bg: var(--sand); --btn-fg: var(--navy); }

.btn--sm { min-height: 40px; padding: .45rem 1.1rem; font-size: .9rem; }
.btn--block { display: flex; width: 100%; }

/* ---- layout -------------------------------------------------------------- */
.container { width: min(100% - 2 * var(--gutter), var(--container)); margin-inline: auto; }

.section { padding-block: clamp(3.5rem, 8vw, 6.5rem); }
.section--tight { padding-block: clamp(2rem, 4vw, 3.5rem); }
.section--sand  { background: var(--sand); }
.section--grey  { background: var(--grey); }
/* Navy on cyan is 5.42:1. White on cyan is 3.18:1 and fails for body copy,
   and orange on cyan is 1.43:1 and is effectively invisible. */
.section--cyan  { background: var(--cyan); color: var(--navy); }
.section--navy  { background: var(--navy); color: var(--sand); }
.section--cyan .eyebrow { color: var(--navy); }
/* --navy keeps its own rule for the dark band — orange on navy is 7.77:1. */
.section--navy .eyebrow { color: var(--orange); }
/* .lede is --ink-soft, which is 2.9:1 on navy. Inherit the band's own colour
   instead: sand on navy, navy on cyan — both above 5:1. */
.section--navy .lede,
.section--cyan .lede { color: inherit; }

.head-block { max-width: 46rem; margin-bottom: var(--sp-5); }
.head-block .eyebrow { display: block; margin-bottom: var(--sp-1); }

.grid { display: grid; gap: var(--sp-4); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }

/* Asymmetric by default — a 50/50 split reads as a template. */
.split { display: grid; grid-template-columns: 1.15fr .85fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.split--reverse .split__media { order: 2; }
/* Form beside a photo: the form column leads, the photo sticks under the
   header while the form scrolls. Below 900px the photo is dropped entirely —
   a full-width picture after a long form is dead weight on a phone. */
.split--form { grid-template-columns: 1.05fr .95fr; align-items: start; }
.split--form .split__media--sticky { position: sticky; top: calc(var(--header-h) + var(--sp-3)); }
.split--form .split__media img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }

/* ---- cards --------------------------------------------------------------- */
/* A card paints its own surface, so it must not inherit a section's text
   colour — on .section--navy that would be sand on white, 1.10:1. */
.card {
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform .22s var(--ease), box-shadow .22s var(--ease);
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-card); }
.card__media { aspect-ratio: 4 / 3; overflow: hidden; }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
.card__body { padding: var(--sp-3); }

/* ---- ticket stub (What's On) --------------------------------------------- */
/* The notches are real: two radial gradients punching the left and right
   edges, so the card reads as a torn stub rather than a rounded rectangle. */
.ticket {
  /* Punched holes must match the surface BEHIND the ticket. Default to the
     body surface, since an unmodified .section paints nothing of its own. */
  --ticket-notch: var(--paper);
  display: grid;
  grid-template-columns: 88px 1fr;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  position: relative;
}
.ticket::before,
.ticket::after {
  content: ''; position: absolute; left: 88px; width: 18px; height: 18px;
  background: var(--ticket-notch); border-radius: 50%; transform: translateX(-50%);
}
.ticket::before { top: -9px; }
.ticket::after  { bottom: -9px; }

/* This list must be maintained BY HAND: CSS cannot read an ancestor's computed
   background, so every new .section--* modifier needs a line here or the notch
   silently reverts to a wrong-coloured dot on that surface. */
.section--sand  .ticket { --ticket-notch: var(--sand); }
.section--grey  .ticket { --ticket-notch: var(--grey); }
.section--navy  .ticket { --ticket-notch: var(--navy); }
.section--cyan  .ticket { --ticket-notch: var(--cyan); }

.ticket__date {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: var(--navy); color: var(--sand);
  font-family: var(--display); line-height: 1;
  border-right: 2px dashed rgba(251, 243, 228, .35);
}
.ticket__day   { font-size: 2.4rem; font-weight: 700; }
.ticket__month { font-size: .8rem; letter-spacing: .18em; text-transform: uppercase; }
/* The body paints the ticket's own white surface, so it must not inherit a
   section's text colour — on .section--navy that would be sand on white. */
.ticket__body  { padding: var(--sp-3); color: var(--ink); }

.stack-lg > * + * { margin-top: var(--sp-3); }
.ticket__meta   { color: var(--ink-soft); font-size: .95rem; margin-bottom: var(--sp-2); }
.ticket__lineup { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: var(--sp-2); }
.ticket__price  { font-size: .95rem; }
.ticket__body h3 a { text-decoration: none; }
.ticket__body h3 a:hover { text-decoration: underline; }
.muted { color: var(--ink-soft); }

.chip {
  /* Self-surface component: declares its own colour so it never inherits a
     section's. See the note on .card. */
  color: var(--ink);
  display: inline-block;
  padding: .2rem .65rem;
  background: var(--sand);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: .82rem;
  font-weight: 600;
}
.chip--live { background: var(--orange); border-color: var(--orange); color: var(--navy); }
.chip--sold { background: var(--navy); border-color: var(--navy); color: var(--sand); }

/* ---- menu list (Menu page) ------------------------------------------------ */
/* One row per dish: name/description on the left, price pinned to the right
   and never wrapping, a baseline rule between rows rather than a dotted
   leader (simpler, and it survives long bilingual names at 320px). Contrast:
   --ink-soft is 5.95:1 on --paper and 5.39:1 on --sand, both above the 4.5:1
   AA floor this component must clear on either surface (see project rule 4).
*/
.menu-list__section-it {
  color: var(--ink-soft);
  font-style: italic;
  margin-top: -.35rem;
}
.menu-list {
  list-style: none;
  padding: 0;
  border-top: 1px solid var(--line);
}
.menu-list__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-3);
  padding-block: var(--sp-2);
  border-bottom: 1px solid var(--line);
}
.menu-list__main { min-width: 0; flex: 1 1 auto; }
.menu-list__name {
  font-weight: 700;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
}
.menu-list__name-it {
  color: var(--ink-soft);
  font-style: italic;
  font-size: .92rem;
}
.menu-list__desc {
  color: var(--ink-soft);
  font-size: .92rem;
  margin-top: .2rem;
}
.menu-list__price {
  font-family: var(--display);
  font-weight: 700;
  white-space: nowrap;
  flex-shrink: 0;
}

/* ---- privé area cards and wedding package lists (Private page) ------------ */
.prive-card__meta  { color: var(--ink-soft); font-size: .95rem; margin-top: .3rem; }
.prive-card__price { font-weight: 700; margin-top: var(--sp-1); }

/* A feature list with a check mark instead of a bullet — used for the wedding
   package tiers. --cyan-800 is the only cyan that clears 4.5:1 at this size
   on both --paper and --sand (see the --cyan-800 token note above). */
.check-list { list-style: none; padding: 0; margin: var(--sp-2) 0 0; display: flex; flex-direction: column; gap: .4rem; }
.check-list li { padding-left: 1.5rem; position: relative; }
.check-list li::before {
  content: "\2713";
  position: absolute; left: 0; top: 0;
  color: var(--cyan-800); font-weight: 700;
}

/* ---- form (Book a Table, and every future enquiry form) ------------------- */
.form { display: flex; flex-direction: column; gap: var(--sp-3); }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.form label {
  display: flex; flex-direction: column; gap: .4rem;
  font-weight: 700; font-size: .92rem;
}
.form input, .form select, .form textarea {
  padding: .75rem .9rem; border: 1px solid var(--line); border-radius: var(--radius);
  font: inherit; font-size: 1rem; font-weight: 400; background: var(--paper); color: var(--ink);
}
.form textarea { resize: vertical; min-height: 6rem; }
/* The native date picker glyphs default to a dark-on-transparent rendering
   that nearly disappears against a dark keyboard theme; this form only ever
   sits on --paper/--sand, both light, so the declaration is unconditional
   rather than a prefers-color-scheme switch. */
.form input[type="date"] { color-scheme: light; }
.field-error { color: #B42318; font-weight: 600; font-size: .85rem; }
.field-help { color: var(--ink-soft); font-weight: 400; font-size: .85rem; }
.form-alert {
  padding: var(--sp-2) var(--sp-3); border-radius: var(--radius); font-weight: 600;
  background: #FDECEC; color: #7F1D1D; border: 1px solid #F3C9C6;
}
.form__actions { display: flex; gap: var(--sp-2); align-items: center; }
@media (max-width: 640px) { .form__row { grid-template-columns: 1fr; } }

/* Curated controls: taller targets, a visible focus ring, a drawn chevron on
   selects instead of the browser's, and fieldset groups on sand so the form
   reads as a couple of clear steps rather than a wall of boxes. */
.form input, .form select, .form textarea {
  min-height: 3rem; padding: .8rem 1rem;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.form input:focus-visible, .form select:focus-visible, .form textarea:focus-visible {
  outline: none; border-color: var(--navy); box-shadow: 0 0 0 3px rgba(1, 156, 201, .28);
}
.form select {
  appearance: none; -webkit-appearance: none; padding-right: 2.6rem; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath d='M3 6l5 5 5-5' fill='none' stroke='%230C1B31' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 1rem center;
}
.form ::placeholder { color: rgba(12, 27, 49, .42); }
.form select:disabled { opacity: .55; cursor: not-allowed; }
/* One card, two steps (Book a Table). Panels are plain fieldsets; the card
   carries the surface. Step tabs are status, not navigation — the buttons
   inside the panels move you — except a completed step, which is clickable. */
.form-card {
  padding: var(--sp-3); border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--sand); display: flex; flex-direction: column; gap: var(--sp-3);
}
.form-steps {
  list-style: none; margin: 0; padding: 0 0 var(--sp-2);
  display: flex; gap: var(--sp-3); border-bottom: 1px solid var(--line);
}
.form-steps__item {
  display: flex; align-items: center; gap: .55rem;
  font-family: var(--display); font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-soft);
}
.form-steps__item.is-active { color: var(--navy); }
.form-steps__item.is-done { color: var(--cyan-800); cursor: pointer; }
.form-steps__item.is-active .form__step { background: var(--navy); }
.form-steps__item.is-done .form__step { background: var(--cyan-800); }
.form__panel { border: 0; margin: 0; padding: 0; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.form__panel[hidden] { display: none; }
.form__legend { font-weight: 700; font-size: .92rem; padding: 0; margin-bottom: .4rem; }

/* Pill switch between the two reservation kinds (Book a table / Reserve
   sunbeds). Hidden without JS, when both forms show under their own
   headings; the sliding pill is pure CSS driven by data-kind. */
.switch { display: none; position: relative; grid-template-columns: 1fr 1fr; gap: 4px; padding: 4px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 999px; }
.js .switch { display: grid; }
.switch__pill { position: absolute; top: 4px; left: 4px; width: calc(50% - 6px); height: calc(100% - 8px);
  background: var(--navy); border-radius: 999px; transition: transform .4s var(--ease); }
[data-kind="sunbed"] .switch__pill { transform: translateX(calc(100% + 4px)); }
.switch button { position: relative; z-index: 1; min-height: 46px; padding: .6rem .5rem; border: 0; background: none; border-radius: 999px;
  display: flex; align-items: center; justify-content: center; gap: .5rem; cursor: pointer;
  font-weight: 700; font-size: .8rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft);
  transition: color .3s var(--ease); }
.switch button[aria-selected="true"] { color: var(--sand); }
.switch button .icon { width: 18px; height: 18px; }
.form-kind__title { font-size: 1.1rem; }
.no-js-only { display: block; }
.js .no-js-only { display: none; }
.js .form-kind[hidden] { display: none; }
.form-kind__note { margin: 0; }

/* Option cards (sunbeds): a radio drawn as a card with the price on the right. */
.opt-cards { display: grid; gap: .6rem; }
/* `.form label` (a column flex) would win on specificity, so the card is
   addressed as .form .opt-card and lays out as a row: dot, name, price. */
.form .opt-card, .opt-card { position: relative; display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: .8rem;
  padding: .85rem 1rem; border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper); cursor: pointer;
  font-weight: 400; font-size: 1rem; text-align: left;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease); }
@media (max-width: 480px) {
  .switch button { font-size: .72rem; letter-spacing: .05em; gap: .35rem; padding-inline: .25rem; white-space: nowrap; }
  .switch button .icon { display: none; }
}
.opt-card:hover { border-color: var(--cyan-800); }
.opt-card input { position: absolute; opacity: 0; width: 0; height: 0; }
.opt-card__dot { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--cyan-800); position: relative; }
.opt-card__dot::after { content: ''; position: absolute; inset: 3px; border-radius: 50%; background: var(--navy); transform: scale(0); transition: transform .2s var(--ease); }
.opt-card__name { font-weight: 600; }
.opt-card__price { font-family: var(--display); font-size: 1.05rem; letter-spacing: .02em; white-space: nowrap; }
.opt-card:has(input:checked) { border-color: var(--navy); box-shadow: 0 0 0 2px rgba(1, 156, 201, .25); }
.opt-card:has(input:checked) .opt-card__dot::after { transform: scale(1); }
.opt-card:has(input:focus-visible) { outline: 2px solid var(--navy); outline-offset: 2px; }
.js-only { display: none; }
.js .js-only { display: inline-flex; }
.js .form__actions.js-only { display: flex; }
.form__step {
  display: inline-grid; place-items: center; width: 1.6rem; height: 1.6rem;
  border-radius: 50%; background: var(--ink-soft); color: var(--sand);
  font-family: var(--sans); font-size: .8rem; letter-spacing: 0;
}
/* Date field: our own calendar glyph, and in Chromium the native indicator is
   stretched invisibly over the whole field so a tap anywhere opens the
   picker. Firefox/Safari get the same via showPicker() in main.js. */
.date-field { display: block; position: relative; min-width: 0; }
/* min-width:0 alongside width:100%: a date input carries a UA-imposed
   intrinsic minimum (the widest the spinner text can get), which width:100%
   does not defeat. On a 320px phone it held the booking band open past the
   viewport and gave the whole page a horizontal scrollbar. */
.date-field input[type="date"] {
  width: 100%; min-width: 0; padding-right: 2.8rem; position: relative;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18' fill='none' stroke='%230C1B31' stroke-width='1.8' stroke-linecap='round'%3E%3Crect x='2' y='3.5' width='14' height='12' rx='2'/%3E%3Cpath d='M2 7.5h14M6 2v3M12 2v3'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 1rem center;
}
.date-field input[type="date"]::-webkit-calendar-picker-indicator {
  position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer;
}
.date-field input[type="date"]::-webkit-date-and-time-value { text-align: left; }
[data-date-pretty-out]:empty { display: none; }
.form label, .form__row > label { min-width: 0; }
.form__actions { flex-wrap: wrap; }
.form__actions .field-help { margin: 0; flex: 1 1 16rem; }
@media (max-width: 640px) { .form__actions .btn { width: 100%; } }

/* ---- "view more dates" ----------------------------------------------------
   A <details> under the board. Styled as a quiet full-width control rather
   than a button, so it reads as "there is more below" and not as the section's
   call to action — that is the Book a Table button underneath it. */
.datelist__more { margin-top: var(--sp-2); }
.datelist__more > summary {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .7rem 1rem; cursor: pointer; list-style: none;
  border: 1px solid rgba(251, 243, 228, .28); border-radius: var(--radius);
  font-family: var(--display); font-weight: 700; font-size: .92rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--sand);
}
.datelist__more > summary::-webkit-details-marker { display: none; }
.datelist__more > summary:hover { border-color: var(--sand); background: rgba(251, 243, 228, .08); }
.datelist__more-count {
  background: rgba(251, 243, 228, .16); border-radius: 999px;
  padding: 0 .5rem; font-size: .82rem; letter-spacing: 0;
}
/* The chevron, matching the FAQ one. */
.datelist__more > summary::after {
  content: ""; width: .5rem; height: .5rem;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .2s var(--ease);
}
.datelist__more[open] > summary::after { transform: rotate(-135deg); }
.datelist__more[open] > summary { margin-bottom: var(--sp-2); }

/* ---- FAQ accordion --------------------------------------------------------
   Native <details>. The marker is drawn as a rotating chevron rather than the
   browser's own triangle so it matches the rest of the site, and the whole
   summary row is the target — a heading alone would be a small tap area. */
.faq-list { display: flex; flex-direction: column; }
.faq { border-bottom: 1px solid var(--line); }
.faq:first-child { border-top: 1px solid var(--line); }
.faq > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) 0; cursor: pointer; list-style: none;
}
.faq > summary::-webkit-details-marker { display: none; }
.faq > summary h3 {
  flex: 1 1 auto; min-width: 0;
  font-size: clamp(1.05rem, 2vw, 1.3rem); text-transform: none; letter-spacing: 0;
}
/* The chevron. A border-only square rotated 45deg, so there is no icon to load
   and it inherits the text colour. */
.faq > summary::after {
  content: ""; flex: 0 0 auto;
  width: .55rem; height: .55rem; margin-right: .2rem;
  border-right: 2px solid var(--cyan-800); border-bottom: 2px solid var(--cyan-800);
  transform: rotate(45deg); transform-origin: center;
  transition: transform .2s var(--ease);
}
.faq[open] > summary::after { transform: rotate(-135deg); }
.faq > summary:hover h3 { color: var(--cyan-800); }
.faq__answer { padding: 0 2rem var(--sp-3) 0; }
.faq__answer p { color: var(--ink-soft); }

/* ---- filters (gallery) ----------------------------------------------------- */
/* Hidden without JS: every photo shows and the pills would do nothing. */
.filters { display: none; flex-wrap: wrap; justify-content: center; gap: .5rem; margin-bottom: var(--sp-4); }
.js .filters { display: flex; }
.filter {
  padding: .6rem 1.1rem; border-radius: 999px; cursor: pointer;
  border: 1.5px solid var(--navy); background: transparent; color: var(--navy);
  font-weight: 700; font-size: .8rem; letter-spacing: .08em; text-transform: uppercase;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.filter:hover { background: rgba(12, 27, 49, .08); }
.filter[aria-pressed="true"] { background: var(--navy); color: var(--sand); }
.gallery-item[hidden] { display: none; }

/* ---- gallery ------------------------------------------------------------- */
/* Three columns, dense-packed so portrait photos (is-tall) can span two rows
   without leaving holes. Captions overlay the bottom edge, same treatment as
   the strip. Each tile links to the -lg file: full size with zero JS. */
.gallery-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-flow: dense;
  gap: var(--sp-2);
}
.gallery-item {
  position: relative; margin: 0; border-radius: var(--radius-lg); overflow: hidden;
  background: var(--sand);
}
.gallery-item a { display: block; aspect-ratio: 4 / 3; }
.gallery-item.is-tall { grid-row: span 2; }
.gallery-item.is-tall a { aspect-ratio: auto; height: 100%; }
.gallery-item.is-wide { grid-column: span 2; }
.gallery-item.is-wide a { aspect-ratio: auto; height: 100%; }   /* takes the row height its neighbours set */
.gallery-item img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease); }
.gallery-item:hover img, .gallery-item:focus-within img { transform: scale(1.04); }
.gallery-item a:focus-visible { outline-offset: -4px; }
.gallery-item__caption {
  position: absolute; inset: auto 0 0 0; pointer-events: none;
  padding: var(--sp-4) var(--sp-2) var(--sp-2);
  background: linear-gradient(180deg, rgba(12,27,49,0) 0%, rgba(12,27,49,.78) 100%);
  color: var(--sand); font-size: .9rem; line-height: 1.3;
}
@media (max-width: 900px) { .gallery-grid { grid-template-columns: repeat(2, 1fr); } }

/* ---- booking band + review badges (home, above the footer) ---------------- */
.cta-band .lede { color: rgba(251, 243, 228, .85); }
.cta-band__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-3); }
/* Two columns: the pitch and the review badges left, the booking card right.
   The card is sand on navy, so it carries its own ink colour. */
.cta-split { display: grid; grid-template-columns: .9fr 1.1fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: start; }
/* min-width:0 on both columns. A grid item defaults to min-width:auto and so
   refuses to shrink below its content's minimum — the review badges inside the
   pitch have a wide minimum, which pushed this column to 336px on a 320px
   phone and took the whole page's horizontal scroll with it. */
.cta-split > * { min-width: 0; }
.cta-split__pitch { padding-top: var(--sp-2); }
.cta-split__call { margin-top: var(--sp-3); color: rgba(251, 243, 228, .85); }
.cta-split__call a { color: var(--sand); font-weight: 700; }
.cta-split .trust { justify-content: flex-start; margin-top: var(--sp-4); }
.cta-split .trust__badge { min-width: 0; width: 100%; }
.cta-split .trust li { flex: 1 1 100%; }
.form-card { color: var(--ink); }
.form-card a { color: var(--cyan-800); }
@media (max-width: 900px) { .cta-split { grid-template-columns: 1fr; gap: var(--sp-4); } }
.trust {
  list-style: none; padding: 0; margin: var(--sp-4) 0 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-2);
}
.trust__badge {
  display: grid; grid-template-columns: auto 1fr; column-gap: .8rem; align-items: center;
  text-align: left; text-decoration: none; color: var(--sand);
  padding: .75rem 1.1rem; min-width: 250px;
  border: 1px solid rgba(251, 243, 228, .25); border-radius: var(--radius-lg);
  background: rgba(251, 243, 228, .06);
  transition: background .18s var(--ease), transform .18s var(--ease);
}
.trust__badge:hover { background: rgba(251, 243, 228, .12); transform: translateY(-2px); }
.trust__rating { grid-row: 1 / 3; font-family: var(--display); font-size: 2rem; line-height: 1; }
.trust__star { color: var(--orange); font-size: .7em; margin-left: .1em; vertical-align: .15em; }
.trust__src { font-weight: 700; font-size: .92rem; }
.trust__meta { font-size: .8rem; opacity: .8; }

/* ---- collage ------------------------------------------------------------- */
/* Layered cut-out PNGs with real shadows and slight rotation. This is the
   main thing separating this design from a stock card grid — keep it. */
.collage { position: relative; }
.collage__cut {
  position: absolute;
  filter: drop-shadow(0 14px 22px rgba(12, 27, 49, .28));
  pointer-events: none;
}
.collage__cut--tilt-l { transform: rotate(-4deg); }
.collage__cut--tilt-r { transform: rotate(5deg); }

/* ---- reveal -------------------------------------------------------------- */
/* Scoped to .js deliberately. The .in class is only ever added by JavaScript,
   so if main.js fails to run, an unscoped rule would leave every section
   permanently at opacity 0. Hiding is opt-in, applied only once JS is proven
   to be running. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.js [data-reveal].in { opacity: 1; transform: none; }

/* ---- motion kill switch -------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .js [data-reveal] { opacity: 1; transform: none; }
  .postcard { transform: none !important; }
}

/* ---- responsive ---------------------------------------------------------- */
@media (max-width: 1100px) {
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
/* The horizontal bar holds the logo, eight labels and the Book button. Below
   about 1000px they no longer fit side by side, so the bar hands over to the
   hamburger — which carries the same nav_items() list, so nothing is lost.
   Measured, not guessed: the unwrapped list is ~700px, and with the logo, the
   button and the gutters that needs a viewport around 980px. */
@media (max-width: 1000px) {
  .main-nav { display: none; }
  /* Without JS the panel ships open and this button would do nothing. */
  .nav-toggle { display: none; }
  .js .nav-toggle { display: flex; }
}
@media (max-width: 900px) {
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .split { grid-template-columns: 1fr; gap: var(--sp-4); }
  .split--reverse .split__media { order: 0; }
  .split--form .split__media--sticky { display: none; }
  /* Decorative cut-outs are absolutely positioned with negative offsets and
     nothing clips them, so they push the page wider than the viewport. The
     layered effect does not read at this size regardless. */
  .collage__cut { display: none; }
}
@media (max-width: 640px) {
  :root { --gutter: 1rem; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .ticket { grid-template-columns: 68px 1fr; }
  .ticket::before, .ticket::after { left: 68px; }
  .ticket__day { font-size: 1.9rem; }
}

/* ---- quick nav ------------------------------------------------------------ */
.quicknav { background: var(--sand); }
.quicknav__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-4) var(--sp-2);
}
.quicknav__item {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2);
  text-decoration: none;
  text-align: center;
}
.quicknav__item .icon {
  width: 64px; height: 64px;
  color: var(--navy);
  transition: transform .25s var(--ease), color .25s var(--ease);
}
.quicknav__item:hover .icon { transform: rotate(-5deg) scale(1.08); color: var(--cyan); }
.quicknav__label {
  font-family: var(--display);
  font-weight: 700;
  font-size: .95rem;
  letter-spacing: .1em;
  text-transform: uppercase;
}
/* Below the desktop grid this used to be a fixed two columns all the way down,
   which left eight 137px tiles running to 694px — most of a screen of nothing
   but links, between the hero and the first real content. auto-fit sizes the
   columns to the space instead, so a tablet gets five or six and a phone three,
   and the block stays a quick-nav rather than a page of its own. */
@media (max-width: 900px) {
  .quicknav__grid {
    grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
    gap: var(--sp-3) .75rem;
  }
  .quicknav__item { gap: .55rem; padding: .7rem .4rem; }
  .quicknav__item .icon { width: 44px; height: 44px; }
  .quicknav__label { font-size: .8rem; letter-spacing: .08em; line-height: 1.3; }
}

@media (max-width: 620px) {
  .quicknav__grid {
    grid-template-columns: repeat(auto-fit, minmax(6.2rem, 1fr));
    gap: var(--sp-2) .6rem;
  }
  .quicknav__item { gap: .45rem; padding: .6rem .3rem; }
  .quicknav__item .icon { width: 36px; height: 36px; }
  .quicknav__label { font-size: .68rem; letter-spacing: .06em; line-height: 1.25; }
}

/* ---- hero slideshow ------------------------------------------------------ */
/* Slides stack; only the active one is visible. A plain opacity transition,
   and main.js re-derives every slide's state on visibilitychange — a
   background tab freezes CSS transitions and would otherwise strand the hero
   half-faded. */
.hero__bg .hero-slide {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0;
  transition: opacity 1.4s var(--ease);
}
.hero__bg .hero-slide.is-active { opacity: 1; }

/* ---- photo strip --------------------------------------------------------- */
/* The track scrolls natively (overflow + scroll-snap), so it works without JS.
   The prev/next buttons are an enhancement and only show once JS has run. */
.hstrip-section { background: var(--paper); overflow: hidden; }
.hstrip__head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
.hstrip__nav { display: none; gap: .5rem; flex-shrink: 0; }
/* Headless strip (photo_strip_track): buttons on their own row, right-aligned,
   sitting between whatever came before in the section and the track. */
.hstrip__nav-row { display: flex; justify-content: flex-end; margin: var(--sp-3) 0 var(--sp-2); }
.js .hstrip__nav { display: flex; }
.hstrip__btn {
  width: 44px; height: 44px; border-radius: 50%;
  border: 2px solid var(--navy); background: var(--paper); color: var(--navy);
  font-size: 1.2rem; line-height: 1; cursor: pointer;
  transition: background .18s var(--ease), color .18s var(--ease), transform .18s var(--ease);
}
.hstrip__btn:hover { background: var(--navy); color: var(--sand); transform: translateY(-2px); }
.hstrip__track {
  display: flex; gap: var(--sp-2);
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* Bleed to the viewport edge, but line the first card up with the container. */
  padding: 0 max(var(--gutter), calc((100vw - var(--container)) / 2)) var(--sp-2);
  scrollbar-width: none;
  cursor: grab;
}
.hstrip__track::-webkit-scrollbar { display: none; }
.hstrip__track.is-dragging { cursor: grabbing; scroll-snap-type: none; }
.hstrip__item {
  position: relative;
  flex: 0 0 min(80vw, 420px);
  aspect-ratio: 4 / 3;
  margin: 0;
  scroll-snap-align: start;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--sand);
}
.hstrip__item img { width: 100%; height: 100%; object-fit: cover; }
/* Optional caption, overlaid along the bottom edge. Sand on a navy gradient —
   the same treatment as .datelist__caption, so galleries read as one system. */
.hstrip__caption {
  position: absolute; inset: auto 0 0 0;
  padding: var(--sp-4) var(--sp-2) var(--sp-2);
  background: linear-gradient(180deg, rgba(12,27,49,0) 0%, rgba(12,27,49,.78) 100%);
  color: var(--sand); font-size: .92rem; line-height: 1.3;
}
.hstrip__head .lede { margin: var(--sp-1) 0 0; max-width: 56ch; }

/* ---- upcoming dates (home, beach-party section) --------------------------- */
/* A compact calendar-style list. Hovering or focusing a date swaps the stage
   image beside it to that event's poster; without JS the rows are plain links
   and the stage shows its default photo. Lives on the navy band, so the text
   is sand and the accents orange (7.77:1 on navy). */
.datelist { list-style: none; padding: 0; margin: var(--sp-3) 0; display: flex; flex-direction: column; gap: .4rem; }
.datelist__intro { font-size: .9rem; color: rgba(251, 243, 228, .75); margin-top: var(--sp-2); }
.datelist__row {
  display: grid; grid-template-columns: 56px 1fr auto; align-items: center; gap: var(--sp-2);
  padding: .55rem .75rem;
  border: 1px solid rgba(251, 243, 228, .16);
  border-radius: var(--radius);
  color: inherit; text-decoration: none;
  transition: background .18s var(--ease), border-color .18s var(--ease), transform .18s var(--ease);
}
.datelist__row:hover,
.datelist__row:focus-visible,
.datelist__row.is-active {
  background: rgba(251, 243, 228, .08);
  border-color: var(--orange);
  transform: translateX(4px);
}
.datelist__date  { text-align: center; font-family: var(--display); line-height: 1; }
.datelist__day   { display: block; font-size: 1.5rem; font-weight: 700; }
.datelist__month { display: block; font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; color: var(--orange); margin-top: .15rem; }
.datelist__title { display: block; font-weight: 600; }
.datelist__meta  { display: block; font-size: .85rem; color: rgba(251, 243, 228, .72); }
.datelist__tag   { font-family: var(--display); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase; color: var(--orange); white-space: nowrap; }

.datelist__stage {
  position: relative; margin: 0;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg); overflow: hidden;
  background: var(--navy-800);
}
.datelist__stage img { width: 100%; height: 100%; object-fit: cover; display: block; transition: opacity .35s var(--ease); }
.datelist__stage.is-swapping img { opacity: 0; }
.datelist__caption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: .7rem .9rem;
  background: linear-gradient(0deg, rgba(12, 27, 49, .8), rgba(12, 27, 49, 0));
  font-family: var(--display); font-size: .95rem; letter-spacing: .05em; text-transform: uppercase;
  color: var(--sand);
  opacity: 0; transition: opacity .25s var(--ease);
}
.datelist__stage.has-caption .datelist__caption { opacity: 1; }

/* ---- stats (Why Paparemo) ------------------------------------------------- */
/* Every figure rendered here is sourced — see docs/CONTENT-TO-CONFIRM.md
   "Published stats". Do not add a number without a source; the content guard
   is not clever enough to catch an invented statistic, so the discipline is
   human. */
.stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); margin: var(--sp-3) 0; }
.stats__n {
  display: block; font-family: var(--display); font-weight: 700; line-height: 1;
  font-size: clamp(1.9rem, 3.4vw, 2.6rem);
  color: var(--cyan-800);   /* 5.63:1 on paper, 5.11:1 on sand */
}
.stats__l { display: block; font-size: .85rem; color: var(--ink-soft); margin-top: .3rem; line-height: 1.35; }
@media (max-width: 640px) {
  .datelist__row { grid-template-columns: 48px 1fr; }
  .datelist__tag { display: none; }
}

/* ---- hero ---------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: min(88svh, 780px);
  display: grid;
  place-items: center;
  padding: calc(var(--header-h) + var(--sp-5)) 0 var(--sp-6);
  overflow: hidden;
}
.hero__bg { position: absolute; inset: 0; z-index: 0; }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; }
/* Legibility scrim. Navy, not black — black greys out the sea. */
.hero__bg::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(12,27,49,.35) 0%, rgba(12,27,49,.15) 45%, rgba(12,27,49,.5) 100%);
}
.hero__inner { position: relative; z-index: 1; width: 100%; }

/* The postcard. Rotation is small and deliberate — 1.5deg reads as a placed
   object; anything more reads as a gimmick. */
.postcard {
  position: relative;   /* stamp and postmark are absolutely positioned against this */
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  max-width: 940px;
  margin-inline: auto;
  padding: clamp(1.1rem, 2.4vw, 1.9rem);
  background: var(--sand);
  border-radius: 6px;
  box-shadow: 0 30px 60px -20px rgba(12, 27, 49, .55);
  --pc-rot: -1.5deg;   /* phones use a smaller tilt, set in the media query below */
  transform: rotate(var(--pc-rot));
  animation: postcard-in .7s var(--ease) both;
}
@keyframes postcard-in {
  from { opacity: 0; transform: rotate(calc(var(--pc-rot) - 2.5deg)) translateY(26px) scale(.97); }
  to   { opacity: 1; transform: rotate(var(--pc-rot)) translateY(0) scale(1); }
}

.postcard__media {
  border-radius: 3px; overflow: hidden; align-self: stretch;
  min-width: 0;   /* a grid item defaults to min-width:auto and would otherwise
                     refuse to shrink below the photo's intrinsic width */
}
.postcard__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

.postcard__body {
  display: flex; flex-direction: column;
  padding-left: clamp(1rem, 2.4vw, 1.8rem);
  border-left: 1px solid var(--line);
}
/* Sized for the postcard, not for the page. The sitewide h1 scale runs up to
   5.4rem, which is right for a full-bleed page hero and far too big inside a
   ~480px card column — a two-word line then breaks in the wrong place and
   crowds out the paragraph and buttons below it. */
.postcard__title {
  font-size: clamp(1.9rem, 3.4vw, 3.1rem);
  line-height: 1.04;
  margin-bottom: var(--sp-2);
}
.postcard__sub   { color: var(--ink-soft); margin-bottom: var(--sp-3); }
.postcard__cta   { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: auto; }

/* Next-party line. Rendered only when a future event exists. */
.postcard__next {
  margin-top: var(--sp-3);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--line);
  font-size: .95rem;
}
.postcard__next strong { font-family: var(--display); letter-spacing: .04em; }

/* Stamp and postmark, top-right of the card. */
/* The franking row: place on the left, postmark and stamp on the right, all
   in normal flow so the heading below always gets the card's full width. */
.postcard__franking {
  display: flex; align-items: center; gap: .6rem;
  margin-bottom: var(--sp-2);
}
.postcard__franking .eyebrow { margin: 0 auto 0 0; }

.postcard__stamp {
  flex: 0 0 auto;
  width: 54px; height: 64px;
  display: grid; place-items: center;
  background: var(--sand);
  color: var(--cyan);
  border: 2px solid var(--line);
  /* Perforated edge, drawn not imported. The -webkit- pair covers Safari
     before 15.4, which only supports the prefixed property. */
  -webkit-mask-image: radial-gradient(circle at 6px 6px, transparent 5px, #000 5px);
  -webkit-mask-size: 12px 12px;
  mask-image: radial-gradient(circle at 6px 6px, transparent 5px, #000 5px);
  mask-size: 12px 12px;
  animation: stamp-in .5s var(--ease) .12s both;
  transform: rotate(4deg);
}
@keyframes stamp-in {
  from { opacity: 0; transform: rotate(12deg) scale(.8); }
  to   { opacity: 1; transform: rotate(4deg) scale(1); }
}
.postcard__stamp .icon { width: 32px; height: 32px; }
.postcard__postmark {
  flex: 0 0 auto;
  font-family: var(--display);
  font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--cyan-800);   /* was --cyan-700 */
  border: 1.5px solid currentColor;
  border-radius: 999px;
  padding: .35rem .7rem;
  transform: rotate(-9deg);
  /* No opacity: this is 9.92px text, and cyan-800 at .8 is 3.59:1 — still
     failing. Solid is 5.11:1. The faded-ink look comes from the letterspacing
     and the thin ring, not from fading the ink itself. It escaped every earlier
     check because it is display:none below 900px, so mobile Lighthouse runs
     never rendered it. */
}

/* 900px, not 760px: the card is rotated -1.5deg, so its corners sit wider than
   its box. At 768px — a very common tablet width — an un-flattened card bled
   past the viewport edge and made the whole page scroll horizontally. This
   breakpoint now matches the nav's. */
/* On phones and tablets the card is still a postcard — just a smaller one.
   Everything scales down together (photo, title, stamp, postmark, padding)
   rather than flattening into a full-width panel: a landscape photo of fixed
   height on top, the message below, a 1deg tilt. The tilt is safe because
   the card is narrower than the viewport by more than its rotated corners
   need (sin 1deg x half the card height is under 5px each side), and .hero
   clips overflow regardless. */
@media (max-width: 900px) {
  /* The whole card must fit in one phone screen under the header: photo,
     title, one short paragraph, both buttons on one row, next-party line. */
  .hero { padding: calc(var(--header-h) + var(--sp-3)) 0 var(--sp-4); min-height: min(84svh, 720px); }
  .postcard {
    --pc-rot: -1deg;
    grid-template-columns: 1fr;
    gap: .85rem;
    width: min(100% - 1.5rem, 480px);
    padding: .8rem .8rem 1rem;
    box-shadow: 0 24px 50px -16px rgba(12, 27, 49, .7), 0 2px 6px rgba(12, 27, 49, .18);
  }
  /* A height cap, NOT an aspect-ratio plus a max-height. With both set, the
     ratio derives the width from the capped height — 170px tall became 302px
     wide — so the photo sat in a 454px card with 150px of bare sand beside it.
     A stated height with width:100% caps the card the same way and lets the
     photo fill the frame edge to edge; object-fit:cover does the cropping. */
  .postcard__media {
    width: 100%;
    aspect-ratio: auto;
    height: clamp(150px, 30vw, 230px);
    max-height: none;
    align-self: stretch;
  }
  .postcard__media img { object-position: center 58%; }   /* the bar and the branded glass stay in frame */
  .postcard__body { padding-left: 0; border-left: 0; padding-top: .85rem; border-top: 1px solid var(--line); }
  .postcard__body .eyebrow { font-size: .68rem; margin-bottom: .45rem; }
  .postcard__title { font-size: clamp(1.7rem, 7.6vw, 2.3rem); line-height: 1; margin-bottom: .55rem; }
  .postcard__sub   { font-size: .9rem; line-height: 1.45; margin-bottom: .9rem; }
  .postcard__cta   { gap: .5rem; }
  .postcard__cta .btn { flex: 1 1 0; padding: .7rem .6rem; font-size: .78rem; letter-spacing: .04em; white-space: nowrap; }
  .postcard__next  { font-size: .82rem; margin-top: .85rem; padding-top: .6rem; }
  /* The franking row is in flow now, so these only need sizing — the old
     top/right offsets positioned them over the card and are gone with it.
     On the narrowest screens the place name and the postmark together would
     wrap the row, so the postmark steps aside and the stamp does the work. */
  .postcard__franking { gap: .45rem; margin-bottom: .5rem; }
  .postcard__stamp { width: 42px; height: 50px; }
  .postcard__stamp .icon { width: 26px; height: 26px; }
  .postcard__postmark { font-size: .55rem; padding: .25rem .5rem; letter-spacing: .1em; }
}

@media (max-width: 420px) {
  .postcard__postmark { display: none; }
}

/* ---- season bar ---------------------------------------------------------- */
.season-bar {
  background: var(--navy); color: var(--sand);
  text-align: center; padding: .7rem var(--sp-2); font-size: .92rem;
}
.season-bar--soon { background: var(--orange); color: var(--navy); }
.season-bar a { color: inherit; }

/* ---- site header --------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(251, 243, 228, .92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid transparent;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.site-header.is-stuck {
  border-bottom-color: var(--line-soft);
  box-shadow: 0 6px 20px -14px rgba(12, 27, 49, .5);
}
.header-inner {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--header-h);
}
.brand { margin-right: auto; display: flex; }
.brand-logo { width: 62px; height: auto; }

.nav-list { display: flex; gap: var(--sp-3); list-style: none; padding: 0; }
.nav-list a {
  text-decoration: none;
  font-family: var(--display); font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase; font-size: .95rem;
  padding-bottom: 3px;
  border-bottom: 2px solid transparent;
  /* A label must never break in half. "What's On" and "Private & Events"
     both contain a space, and without this they fold onto two lines as the
     bar tightens — which reads as a broken header rather than a full one,
     and is what the mobile breakpoint below is set to avoid. */
  white-space: nowrap;
}
.nav-list a:hover { border-bottom-color: var(--cyan); }
.nav-list a[aria-current="page"] { border-bottom-color: var(--orange); }

.nav-toggle {
  display: none; flex-direction: column; gap: 5px;
  width: 46px; height: 46px; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer;
}
.nav-toggle span { display: block; width: 24px; height: 2px; background: var(--navy); }

/* The drawer is navy, not sand: it covers the page rather than continuing it,
   and a solid dark panel says so. Sand-on-navy at this size is 15.65:1. */
.mobile-nav {
  padding: var(--sp-3) 1.25rem calc(var(--sp-4) + env(safe-area-inset-bottom));
  background: var(--navy); color: var(--sand);
}
.mobile-nav ul { list-style: none; padding: 0; margin-bottom: var(--sp-3); }
/* Scoped to the LIST links, never a bare `.mobile-nav a`: the Book button is
   also an <a> in here, and a rule that broad outranks .btn's own colour — it
   turned the button's label sand-on-orange at 2.02:1, against this
   stylesheet's own rule that orange is only ever a fill under navy. */
.mobile-nav ul a {
  display: block; padding: .95rem 0; text-decoration: none;
  color: var(--sand);
  font-family: var(--display); font-weight: 700;
  font-size: clamp(1.6rem, 6.5vw, 2.1rem); line-height: 1.1;
  letter-spacing: .01em; text-transform: uppercase;
  border-bottom: 1px solid rgba(251, 243, 228, .18);
}
.mobile-nav ul a:hover, .mobile-nav ul a:focus-visible { color: var(--orange); }
.mobile-nav ul a[aria-current="page"] { color: var(--orange); }
/* The Book button keeps its orange fill, so it still reads as the one action
   rather than becoming another line in the list. */
.mobile-nav .btn { margin-top: var(--sp-2); width: 100%; }
.mobile-nav__call {
  display: block; text-align: center; margin-top: var(--sp-3);
  color: var(--sand); font-family: var(--display); font-size: 1.25rem;
}

/* ---- footer -------------------------------------------------------------- */
.site-footer { background: var(--navy); color: var(--sand); padding-top: var(--sp-6); }
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--sp-4); }
/* h2, not h4: on a thin page like /404 or /thank-you the footer headings were
   the only ones after the h1, so the document skipped h1 -> h4. h4 is kept in
   the selector so the styling is identical wherever it is still used. */
.site-footer h2,
.site-footer h4 {
  color: var(--orange); margin-bottom: var(--sp-2);
  font-size: .8rem; letter-spacing: .18em;
}
.footer-logo { width: 96px; height: auto; }
.footer-tag { color: rgba(251, 243, 228, .72); margin-top: var(--sp-2); max-width: 26ch; }
.info-list { font-style: normal; line-height: 1.9; list-style: none; padding: 0; }
.info-list a { color: inherit; }
.footer-base {
  margin-top: var(--sp-5); padding-block: var(--sp-3);
  border-top: 1px solid rgba(251, 243, 228, .16);
  font-size: .85rem; color: rgba(251, 243, 228, .6);
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--sp-1) var(--sp-3);
}
/* A flex row: the p + p rhythm rule must not push the credit down. That rule
   is two :is() selectors deep, (0,2,0), so the override has to match it —
   hence two classes, not `.footer-base p`. */
.footer-base p, .footer-base .footer-credit { margin: 0; }
.footer-credit a { color: var(--sand); text-decoration-color: rgba(251, 243, 228, .4); }
.footer-credit a:hover { text-decoration-color: currentColor; }
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px) { .footer-grid { grid-template-columns: 1fr; } }

/* ---- home ---------------------------------------------------------------- */
.center-block { margin-inline: auto; text-align: center; }

.framed-img { border-radius: var(--radius-lg); box-shadow: var(--shadow-card); }

.factcard {
  padding: var(--sp-3);
  background: var(--paper);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line-soft);
}
/* --cyan-800, not --cyan-700: this heading is 1.05rem (~16.8px), well under the
   24px threshold, so it takes the only cyan that clears AA on grey. */
.factcard h3 { font-size: 1.05rem; margin-bottom: var(--sp-1); color: var(--cyan-800); }

.section--navy .split__media img { border-radius: var(--radius-lg); }

/* ==========================================================================
   Usability polish pass (interior pages) — appended, not interleaved, so the
   diff against the original component library stays easy to read.
   ========================================================================== */

/* ticket__details (a larger "Details" link on the ticket-stub card) was
   removed here: whats-on.php no longer uses the .ticket component — its
   upcoming-dates list now shares datelist_section() with the home page,
   whose whole row is already a link. event.php never used .ticket. */

/* ---- anchor targets clear the sticky header --------------------------------
   Any element an in-page link jumps to (e.g. whats-on.php's "See Dates",
   menu.php's jump nav) must not land underneath the sticky site header. */
[id] { scroll-margin-top: calc(var(--header-h) + var(--sp-2)); }

/* ---- page hero (every page except home) ----------------------------------- */
/* A full-bleed approved photo under a navy scrim, content anchored to the
   bottom where the gradient is darkest (.85) so sand text stays >10:1 and the
   orange eyebrow stays 7.77:1. Echoes the home hero without the postcard. */
.page-hero {
  position: relative; overflow: hidden; color: var(--sand);
  min-height: min(56svh, 520px);
  display: grid; align-items: end;
  padding: calc(var(--header-h) + var(--sp-5)) 0 var(--sp-5);
}
.page-hero--short { min-height: min(38svh, 340px); }
.page-hero__bg { position: absolute; inset: 0; z-index: 0; }
.page-hero__bg img { width: 100%; height: 100%; object-fit: cover; }
.page-hero__bg::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(12,27,49,.55) 0%, rgba(12,27,49,.35) 40%, rgba(12,27,49,.85) 100%);
}
/* The inner is a plain .container so the text's left edge lines up with every
   section below it. Line length is capped on the heading and lede themselves —
   never with .measure on the container, which would centre a narrower block
   and push the text off the page grid on wide screens. */
.page-hero__inner { position: relative; z-index: 1; }
.page-hero h1 { max-width: 14ch; }
.page-hero .eyebrow { color: var(--orange); }
.page-hero h1 { color: var(--sand); margin-bottom: var(--sp-2); }
.page-hero .lede { color: var(--sand); opacity: .92; max-width: 56ch; }
.page-hero__cta { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
/* Focus rings must invert on the dark hero, as they do on .hero and .section--navy. */
.page-hero :focus-visible { outline-color: var(--sand); }

/* ---- menu jump nav (Menu page) --------------------------------------------- */
/* Plain anchor links to each section id — works with zero JS. Sticks under
   the site header so it stays reachable while scrolling a 65-dish menu. */
.menu-jump {
  position: sticky; top: var(--header-h); z-index: 40;
  background: var(--sand);
  border-bottom: 1px solid var(--line);
}
.menu-jump__track {
  display: flex; gap: .5rem;
  overflow-x: auto;
  padding-block: var(--sp-2);
  scrollbar-width: none;
}
.menu-jump__track::-webkit-scrollbar { display: none; }
.menu-jump__link {
  flex: 0 0 auto;
  display: inline-flex; align-items: center;
  min-height: 44px;
  padding: .5rem 1rem;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--display); font-weight: 700; font-size: .85rem;
  letter-spacing: .04em; text-transform: uppercase;
  text-decoration: none; color: var(--navy);
  white-space: nowrap;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.menu-jump__link:hover,
.menu-jump__link:focus-visible {
  background: var(--navy); color: var(--sand); border-color: var(--navy);
}

/* ---- form error summary (book, contact, private, villas) ------------------ */
/* form_error_summary() in includes/functions.php renders this on top of the
   existing per-field inline errors — a guest hitting several invalid fields
   at once gets one place that lists all of them, not just the first one they
   scroll past. */
.form-alert p { margin: 0; }
.form-alert ul { margin: .4rem 0 0; padding-left: 1.2rem; }
.form-alert li + li { margin-top: .2rem; }

/* ---- venue map (includes/venue-map.php; private.php and beach.php) -------- */
/* A schematic, illustrative zone plan — not a real floor plan. See the
   module docblock in includes/venue-map.php for what is and is not sourced. */
.venue-map-layout { align-items: start; }
.venue-map-figure { margin: 0; }
.venue-map {
  display: block; width: 100%; height: auto;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
}
.venue-map__label {
  font-family: var(--display); font-weight: 700; font-size: 15px;
  letter-spacing: .04em;
  fill: var(--navy);
}
.venue-map__label--sm { font-size: 12px; }
.venue-map__marker {
  font-family: var(--display); font-weight: 700; font-size: 12px;
  fill: var(--navy);
}
.venue-map__note {
  margin-top: var(--sp-2);
  font-size: .85rem;
  color: var(--ink-soft);
}
.venue-map__legend-wrap { padding-top: var(--sp-1); }
.venue-map__legend {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: .55rem;
}
.venue-map__legend li { padding-left: 0; }
.venue-map__legend strong { font-family: var(--display); letter-spacing: .02em; }
.venue-map__legend-wrap h3 { margin-top: var(--sp-3); margin-bottom: .3rem; font-size: 1rem; }
.venue-map__areas { margin: 0; padding-left: 1.2rem; color: var(--ink-soft); }
@media (max-width: 900px) { .venue-map-layout { row-gap: var(--sp-3); } }

/* ---- villa listing cards (Villas page) ------------------------------------ */
/* Airbnb-style cards. There are no villa photographs (see villas_list()'s
   docblock in includes/villas.php) — every card shows one of the four
   approved SETTING photos, cycled, with a "Shared beach" chip over the image
   so it never reads as a photo of that specific villa. */
.villa-card { display: flex; flex-direction: column; }
.villa-card .card__media { position: relative; aspect-ratio: 4 / 3; }
.villa-card__chip {
  position: absolute; left: var(--sp-2); bottom: var(--sp-2);
  background: var(--paper); border-color: var(--line);
}
.villa-card__body {
  display: flex; flex-direction: column; flex: 1 1 auto;
  gap: .3rem;
}
.villa-card__meta { color: var(--ink-soft); font-size: .92rem; }
.villa-card__amenities {
  display: flex; flex-wrap: wrap; gap: .4rem;
  margin-top: .3rem;
}
.villa-card__rates { margin-top: .3rem; }
.villa-card__action { margin-top: auto; padding-top: var(--sp-2); }

/* ---- menu dish thumbnail (Menu page) --------------------------------------- */
/* Optional — most dishes have none. menu.php renders this only when
   image_path is set AND the file resolves on disk; the row is otherwise
   identical, so this must not depend on the image being present to lay out
   correctly (see .menu-list__main's own flex: 1 1 auto). */
.menu-list__img {
  flex-shrink: 0;
  align-self: center;
  width: 72px;
  height: 72px;
  border-radius: var(--radius);
  object-fit: cover;
}

/* ---- Press & awards (Story page) ------------------------------------------
   What other people have said. Quiet by design: the venue's own voice carries
   the rest of the page, so this section is set smaller and lighter and lets
   the outlet names do the work. Renders only when there is something in it,
   so there is no empty state to style. */
.press__head { margin-bottom: var(--sp-3); }

.press {
  display: grid;
  /* minmax(0, …) because a grid item's default min-width is auto, which lets
     a long unbroken quote or URL push the whole track wider than the page. */
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.press__item {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--paper);
}

/* The whole card is the link when there is one. Padding lives on the inner
   element so the hit area fills the card rather than sitting inside it. */
.press__item > a,
.press__item > :is(p, blockquote):first-child { display: block; }

.press__link,
.press__item {
  color: inherit;
  text-decoration: none;
}
.press__link { padding: var(--sp-2); display: block; height: 100%; }
.press__item:not(:has(.press__link)) { padding: var(--sp-2); }

.press__link:hover .press__title,
.press__link:focus-visible .press__title { text-decoration: underline; }
.press__link:focus-visible { outline: 3px solid var(--cyan-800); outline-offset: 2px; border-radius: 12px; }

.press__logo {
  display: block;
  width: auto;
  max-width: 120px;
  max-height: 44px;
  height: auto;
  margin-bottom: var(--sp-1);
  object-fit: contain;
}

.press__kind {
  margin: 0 0 .4rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cyan-800);
}

.press__quote {
  margin: 0 0 .55rem;
  padding: 0;
  border: 0;
  font-size: 1.02rem;
  line-height: 1.5;
  font-style: italic;
}

.press__title { margin: 0 0 .55rem; font-weight: 700; line-height: 1.35; }

.press__source {
  margin: 0;
  font-size: .82rem;
  color: var(--ink-soft);
}
.press__outlet { font-weight: 700; color: var(--ink); }
.press__dated::before { content: " · "; }

/* ---- Three generations (Story page) ---------------------------------------
   A portrait beside a numbered list of the three people. The list is an <ol>
   because the order is the point — it is a sequence in time, not a set. */
.gens__head { max-width: 46rem; margin-bottom: var(--sp-3); }

.gens {
  display: grid;
  /* minmax(0, …): a grid item's default min-width is auto, so a long name or
     an unbroken word would otherwise stretch the column past the container. */
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: var(--sp-4);
  align-items: start;
}
@media (max-width: 900px) { .gens { grid-template-columns: minmax(0, 1fr); gap: var(--sp-3); } }

.gens__figure { margin: 0; min-width: 0; }
.gens__figure img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
}
.gens__caption {
  margin-top: .6rem;
  font-size: .84rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.gens__list {
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 0;
  display: grid;
  gap: var(--sp-3);
  counter-reset: gen;
}

.gen {
  counter-increment: gen;
  position: relative;
  padding-left: 3.1rem;
  min-width: 0;
}
/* The generation number, set as a display numeral rather than a bullet. */
.gen::before {
  content: counter(gen);
  position: absolute;
  left: 0;
  top: -.15rem;
  width: 2.2rem;
  height: 2.2rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--sand);
  border: 1px solid var(--line);
  font-family: var(--display);
  font-size: 1rem;
  font-weight: 700;
  color: var(--navy);
}
/* On sand, the circle needs to read as a distinct object rather than vanish. */
.section--sand .gen::before { background: var(--paper); }

.gen__role {
  margin: 0 0 .1rem;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  /* --cyan is 3.18:1 on white and fails AA at this size; --cyan-800 clears it.
     Same rule as every other small cyan text on the site. */
  color: var(--cyan-800);
}
.gen__name { margin: 0 0 .4rem; font-size: clamp(1.15rem, 2vw, 1.45rem); line-height: 1.2; }
.gen__body { margin: 0; }

.gens__close {
  margin: var(--sp-3) 0 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  max-width: 44rem;
  font-style: italic;
  color: var(--ink-soft);
}

/* ---- In memoriam (Story page) ----------------------------------------------
   A dedication, so it is set apart from the copy around it and kept quiet:
   no colour of its own, no icon, nothing that reads as decoration. The whole
   block is left off the page when its words are cleared in admin. */
.memoriam {
  max-width: 44rem;
  margin: var(--sp-4) 0 0;
  padding: var(--sp-2) 0 var(--sp-2) var(--sp-3);
  border-left: 2px solid var(--line);
}
.memoriam__title {
  margin: 0 0 .35rem;
  font-family: var(--display);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.memoriam__body { margin: 0; line-height: 1.65; }

/* The guest-artist line, printed only once somebody has named them. */
.known__artists {
  margin-top: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 1px solid rgba(251, 243, 228, .22);
  font-size: .95rem;
}

/* ---- Partner logos ---------------------------------------------------------
   A grid, not a sliding carousel. With four logos a carousel hides three of
   them behind a control nobody presses, needs JavaScript to mean anything at
   all, and on a phone is worse than simply wrapping to two columns. This shows
   every partner at once at every width, and only starts scrolling if the list
   ever outgrows the space. */
.partners-band {
  padding: var(--sp-4) 0;
  background: var(--sand);
  border-top: 1px solid var(--line-soft);
}

.partners__head {
  margin: 0 0 var(--sp-2);
  text-align: center;
  font-family: var(--display);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.partners {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.partner { min-width: 0; }

.partner__link {
  display: grid;
  place-items: center;
  min-height: 104px;
  padding: var(--sp-2);
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--ink);
  transition: border-color .2s var(--ease), transform .2s var(--ease);
}
a.partner__link:hover { border-color: var(--line); transform: translateY(-2px); }
a.partner__link:focus-visible { outline: 3px solid var(--cyan-800); outline-offset: 2px; }

.partner__logo {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 64px;
  /* Logos are all different shapes; contain keeps each one whole rather than
     cropping a wordmark to fit a box it was never drawn for. */
  object-fit: contain;
}

/* Shown until a logo file is uploaded, so a card is never blank. */
.partner__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: .04em;
  text-align: center;
  color: var(--ink-soft);
}

@media (prefers-reduced-motion: reduce) {
  a.partner__link:hover { transform: none; }
}
