@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/fraunces-600-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/fraunces-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   What To Do In Malta
   Activity-marketplace layout: search hero, destination rails, a filterable
   things-to-do grid and an A-Z. Photography is our own generated set (see
   docs/compliance.md). No partner markup, copy or branding is reproduced.
   ========================================================================== */

/* Figtree, self-hosted. It used to come from fonts.googleapis.com, which hands
   every EU visitor's IP address to Google before they have consented to
   anything: a Munich court ruled that a GDPR breach in 2022, and this site
   states plainly that GDPR applies to it. Self-hosting removes the transfer
   entirely and drops a render-blocking third-party request with it. */
/* latin-ext */
/* Figtree ships one file per subset covering the whole 400-800 range, so every
   weight below resolves to the same two URLs. They used to point at five
   separately named but byte-identical copies, which cost eight redundant
   requests on the critical path. Do not re-split them. */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/figtree-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/figtree-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/figtree-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/figtree-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/figtree-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(fonts/figtree-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/figtree-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(fonts/figtree-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(fonts/figtree-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Figtree';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(fonts/figtree-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;

  --font-display: 'Fraunces', Georgia, serif;

  /* Marketplace palette. Deep navy carries every piece of text, one saturated
     blue carries every action, and the page underneath stays pure white with no
     tinted bands. Contrast comes from hairline borders rather than shadow or
     colour, which is what keeps a dense listing grid calm at 59 cards. */
  --primary:      #c8402a;
  --primary-deep: #a63120;
  --primary-ink:  #2b211a;
  --secondary:    #e0765c;

  /* The accent tokens used to be orange. They now resolve to the action blue so
     every existing rule that referenced them re-tones instead of breaking. */
  --accent:       #c8402a;
  --accent-btn:   #c8402a;
  --accent-btn-2: #a63120;
  --accent-soft:  #fdeee9;

  --bg:           #ffffff;
  --bg-tint:      #fdf7f1;
  --muted:        #f3ece4;
  --border:       #e3d9cd;
  --border-soft:  #ece4da;

  --fg:           #2b211a;
  --fg-mid:       #6b5c4e;
  --fg-soft:      #6c5e51;   /* 4.94:1 on white, 4.68:1 on the tint. #858995 was 3.49:1, under AA. */

  --success:      #0f766e;
  --red:          #c72a3d;
  --wash:         #fdf1e6;

  --r-sm: 8px; --r: 12px; --r-lg: 12px; --r-xl: 16px; --r-pill: 80px;
  /* Cards, the modal and the trust boxes all ask for this. It was used in
     seven rules and never defined, so every one of them computed to 0 and
     rendered square while the reference is rounded. */
  --r-card: 12px;

  /* Cards carry no shadow at all: a 1px border does the separating. The only
     real elevation on the site is a floating panel (search suggestions, sticky
     booking box), which is what --sh-lg is reserved for. */
  --sh-sm: none;
  --sh:    0 1px 2px rgba(26,43,73,.06);
  --sh-lg: 0 0 32px rgba(0,0,0,.06), 0 4px 8px rgba(0,0,0,.16);

  /* One geometric sans for everything, headings included. Figtree is the
     closest freely licensed match to the tall-x-height geometric face the
     large activity marketplaces use, and keeping headings and body in the same
     family is what makes those listing pages read as a product rather than an
     article. */
  --font-h: 'Figtree', system-ui, -apple-system, sans-serif;
  --font-b: 'Figtree', system-ui, -apple-system, sans-serif;

  --container: 1200px;
  --pad: clamp(1.15rem, 4vw, 2.5rem);
  --ease: cubic-bezier(.22,.9,.32,1);
  --hdr-h: 80px;
}

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--hdr-h) + 12px); }

body {
  background: var(--bg); color: var(--fg); font-family: var(--font-b);
  font-size: 1rem; line-height: 1.65; -webkit-font-smoothing: antialiased; overflow-x: hidden;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; }
h1,h2,h3,h4 {
  font-family: var(--font-h); line-height: 1.2; letter-spacing: -.018em; text-wrap: balance;
}
p { text-wrap: pretty; }
::selection { background: var(--primary); color: #fff; }
:focus-visible { outline: 3px solid var(--primary); outline-offset: 3px; border-radius: 6px; }

.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--pad); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--fg); color: #fff; padding: .85rem 1.35rem; font-weight: 600; border-radius: 0 0 10px 0;
}
.skip:focus { left: 0; }

/* ==========================================================================
   Scroll progress
   ========================================================================== */
.progress {
  position: fixed; top: 0; left: 0; height: 3px; width: 100%; z-index: 100;
  transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--primary), var(--secondary), var(--accent));
}

/* ==========================================================================
   Header
   ========================================================================== */
/* The header is opaque on every page now. It used to float transparently over a
   photographic hero; the hero is a pale wash, so white-on-photo type has nothing
   left to sit on and the bar simply stays white. */
.site-header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 60;
  /* The drop-down panels hang off this. */
  isolation: isolate;
  background: #fff; border-bottom: 1px solid var(--border-soft);
  transition: box-shadow .3s var(--ease);
}
.site-header__inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; min-height: var(--hdr-h); }
.site-header.is-solid { box-shadow: 0 1px 8px rgba(26,43,73,.07); }

/* ---------------------------------------------------------------------------
   The search that docks into the header
   ---------------------------------------------------------------------------
   Once the hero search scrolls away there is nothing to search with until you
   scroll back up, which on a page that is mostly one long grid is most of the
   visit. The reference solves it by sliding a compact copy into the header, so
   this does too.

   It occupies its space from the start and only fades in, rather than being
   inserted on scroll. Inserting it would reflow the header every time you
   crossed the trigger, and that is a layout shift on the element pinned to the
   top of the screen.
   --------------------------------------------------------------------------- */
.hdrsearch {
  position: relative; flex: 1 1 auto; min-width: 0; max-width: 26rem;
  margin-inline: auto;
  display: flex; align-items: center; gap: .55rem;
  background: #fff; border: 1px solid var(--border); border-radius: var(--r-pill);
  padding: .5rem 1.1rem;
  box-shadow: 0 1px 2px rgba(26,43,73,.06);
  opacity: 0; transform: translateY(-8px); pointer-events: none;
  transition: opacity .26s var(--ease), transform .26s var(--ease);
}
.hdrsearch > svg { width: 17px; height: 17px; flex: none; color: var(--fg-soft); }
.hdrsearch input {
  flex: 1; min-width: 0; border: 0; background: none; font: inherit; font-size: .92rem;
  color: var(--fg); padding: .18rem 0;
}
.hdrsearch input::placeholder { color: var(--fg-soft); }
.hdrsearch input:focus { outline: none; }
.hdrsearch:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent); }

.site-header.is-docked .hdrsearch { opacity: 1; transform: none; pointer-events: auto; }

/* Nothing to dock into on a phone until the brand gives way. Below 900px the
   wordmark hides once the bar is showing, which is enough room without
   removing the logo itself or the way back to the homepage. */
@media (max-width: 899px) {
  .site-header.is-docked .brand__name { display: none; }
  .hdrsearch { max-width: none; }
}
/* On a small phone the wordmark alone is not enough room and the placeholder
   gets cut mid-word. The Contact pill stands down while the bar is showing;
   it is still one tap away in the menu beside it, and searching is the more
   likely thing to want halfway down a grid of two dozen places. */
@media (max-width: 560px) {
  .site-header.is-docked .nav__cta { display: none; }
  .hdrsearch { padding-inline: .9rem; }
  /* Holding the space open costs nothing at desktop widths and buys a stable
     header. At 360px it is the difference between the burger fitting and not,
     so here the bar leaves the flow until it is wanted. The shift that causes
     is both tiny and inside the 500ms after a scroll, which is why it does not
     count against CLS. */
  .hdrsearch { display: none; }
  .site-header.is-docked .hdrsearch { display: flex; }
}
@media (prefers-reduced-motion: reduce) {
  .hdrsearch { transition: opacity .01ms; transform: none; }
}

.brand { display: flex; align-items: center; gap: .65rem; text-decoration: none; flex: none; }
.brand__mark { width: 38px; height: 38px; flex: none; border-radius: var(--r-sm); }
.brand__name { font-weight: 800; font-size: 1.02rem; letter-spacing: -.02em; line-height: 1.1; color: var(--fg); }
.brand__name span { display: block; font-weight: 600; font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; margin-top: 2px; color: var(--fg-soft); }

.nav { display: flex; align-items: center; gap: .1rem; }
.nav a {
  padding: .6rem .9rem; border-radius: var(--r-sm);
  font-size: .94rem; font-weight: 600; color: var(--fg); text-decoration: none;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.nav a:not(.nav__cta):hover { background: var(--muted); }
.nav__cta {
  margin-left: .5rem; background: var(--primary); color: #fff !important;
  border-radius: var(--r-pill); padding: .62rem 1.3rem; font-weight: 700;
}
.nav__cta:hover { background: var(--primary-deep); }
/* Matches the burger's own threshold below, so there is no band where a
   narrower window shows neither the plain links nor the menu button, and no
   band where it shows both at once. */
@media (max-width: 1100px) { .nav a:not(.nav__cta) { display: none; } }
/* Below about 420px the brand, the Contact pill and the burger no longer fit
   the container between them, and the header pushed the whole document 20px
   wider than the screen. It was invisible on the homepage and the guides only
   because a scrolling rail there clips the overflow; on the five plain pages
   (about, contact and the three legal ones) the body itself scrolled sideways.
   Nothing is removed, everything just gives up a little room. */
@media (max-width: 420px) {
  .site-header__inner { gap: .6rem; }
  .brand { min-width: 0; gap: .5rem; }
  .brand__mark { width: 32px; height: 32px; }
  .brand__name { font-size: .92rem; }
  .brand__name span { font-size: .58rem; letter-spacing: .07em; }
  .nav__cta { margin-left: 0; padding-inline: .95rem; }
}

/* ==========================================================================
   Buttons
   ========================================================================== */
/* Flat pills. The gradient fill, the drop shadow, the lift on hover and the
   sweeping shine have all gone: the reference does none of them, and on a page
   this dense they read as decoration competing with the cards. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  min-height: 48px; padding: .8rem 1.7rem; border-radius: var(--r-pill);
  border: 2px solid transparent;
  font-family: var(--font-b); font-weight: 700; font-size: .97rem; text-decoration: none; cursor: pointer;
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
.btn--primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn--primary:hover { background: var(--primary-deep); border-color: var(--primary-deep); }
.btn--glass { background: #fff; color: var(--primary); border-color: var(--primary); }
.btn--glass:hover { background: var(--accent-soft); }
.btn--ghost { background: #fff; color: var(--primary); border-color: var(--primary); }
.btn--ghost:hover { background: var(--accent-soft); }

/* ==========================================================================
   Sections
   ========================================================================== */
/* Tighter vertical rhythm. The reference stacks many short sections rather than
   a few tall ones, so the padding comes down and the H2 comes down with it. */
.section { padding-block: clamp(2.25rem, 4.5vw, 3.25rem); }
.section--tint { background: var(--bg-tint); }

.section__head { display: flex; align-items: baseline; justify-content: space-between; gap: 2rem; flex-wrap: wrap; margin-bottom: 1.15rem; }
.section__head > div { max-width: 46rem; }
/* The uppercase pill kicker is gone: the reference labels a section with its
   heading alone, and the pill was competing with the chips below it. */
.kicker { display: none; }
.section__head h2 { font-size: clamp(1.5rem, 1.2rem + 1.05vw, 1.78rem); font-weight: 800; letter-spacing: -.022em; margin-bottom: .35rem; }
.section__head p { color: var(--fg-mid); font-size: .95rem; }
.section__more {
  font-size: .93rem; font-weight: 700; color: var(--primary); text-decoration: none; white-space: nowrap;
}
.section__more:hover { text-decoration: underline; }

/* ==========================================================================
   FILTERS
   ========================================================================== */
.filters { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.85rem; }
.filter {
  min-height: 44px; display: inline-flex; align-items: center; gap: .45rem;
  padding: .5rem 1.05rem; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border-soft); background: #fff;
  font: inherit; font-size: .89rem; font-weight: 600; color: var(--fg-mid);
  transition: all .22s var(--ease);
}
.filter:hover { border-color: var(--primary); color: var(--primary-deep); }
.filter[aria-pressed="true"] {
  background: var(--primary-ink); border-color: var(--primary-ink); color: #fff;
  box-shadow: 0 6px 16px rgba(12,74,110,.24);
}
.filter b { font-weight: 700; opacity: .6; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   DIRECTORY, sticky filter rail beside the results
   ========================================================================== */
/* The filters used to be a sticky 252px sidebar. They are now a full-width
   horizontal chip bar above the results, which is how the reference does it and
   which gives the grid the whole container width. The buttons keep their
   .opt / data-k / data-v contract, so the filtering script is untouched. */
.dir { display: block; }

.rail {
  display: flex; align-items: center; gap: .55rem; flex-wrap: nowrap;
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
  padding-bottom: .3rem; margin-bottom: 1.1rem;
  /* On a phone the row scrolls, and the soft right edge is what says so. */
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 34px), transparent);
}
.rail::-webkit-scrollbar { display: none; }
/* With ten chips there is no reason to hide any of them on a real screen.
   Wrapping beats scrolling here: a filter you cannot see is a filter nobody
   uses, and the row was being cut off mid-chip with nothing to say so. */
@media (min-width: 900px) {
  .rail {
    flex-wrap: wrap; overflow-x: visible; row-gap: .5rem;
    -webkit-mask-image: none; mask-image: none;
  }
  .rail__search { min-width: 17rem; }
}

.rail__search {
  display: flex; align-items: center; gap: .5rem; background: #fff; flex: none;
  border: 1px solid var(--border); border-radius: var(--r-pill); padding-left: .95rem;
  min-width: 15rem;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.rail__search:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent); }
.rail__search svg { width: 16px; height: 16px; color: var(--fg-soft); flex: none; }
.rail__search input { flex: 1; min-width: 0; border: 0; background: none; font: inherit; font-size: .9rem; padding: .62rem .25rem; }
.rail__search input:focus { outline: none; }
.rail__clear {
  border: 0; background: none; cursor: pointer; color: var(--fg-soft);
  font-size: 1.1rem; line-height: 1; padding: .5rem .75rem; border-radius: 50%;
}
.rail__clear:hover { color: var(--primary); background: var(--accent-soft); }

/* The group labels are read by screen readers but the chips speak for
   themselves visually, exactly as they do on the reference. */
.rail__group { display: flex; align-items: center; gap: .45rem; flex: none; }
/* Hidden in flow, NOT with position:absolute. An absolutely positioned label has
   no positioned ancestor here, so its containing block is the viewport: it
   escapes the rail's overflow clip, sits at its static position several hundred
   pixels along the scrolled row, and drags the layout viewport out with it. That
   stretched the fixed header on phones and pushed its Contact button off screen. */
.rail__group h3 {
  width: 1px; height: 1px; margin: 0; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.rail__opts { display: flex; gap: .45rem; flex: none; }
.rail__sep { width: 1px; height: 26px; background: var(--border); flex: none; margin-inline: .35rem; }

/* Wrapping .rail alone did not deliver what its comment promises. The chips do
   not sit in the rail, they sit inside .rail__group, and both that and
   .rail__opts are flex: none above, so they kept their intrinsic width and
   pushed past the screen instead of wrapping. Between 900px and about 1180px
   that left "Museums and indoors" outside the viewport with no scroll to reach
   it, because the rail had just stopped being a scroll container: the filter
   was not merely hard to see, it was unusable. These have to come after the
   rules they override, not inside the earlier media block, because both are
   single-class selectors and source order is what decides. */
@media (min-width: 900px) {
  .rail__group { flex: 0 1 auto; flex-wrap: wrap; min-width: 0; }
  .rail__opts  { flex: 0 1 auto; flex-wrap: wrap; min-width: 0; }
  /* Once the groups wrap, the divider between them is stranded at the end of a
     row with nothing on its right. The line break already separates them. */
  .rail__sep { display: none; }
}

.opt {
  display: inline-flex; align-items: center; gap: .4rem; white-space: nowrap;
  min-height: 40px; padding: .45rem 1rem; border-radius: var(--r-pill); cursor: pointer;
  border: 1px solid var(--border); background: #fff;
  font: inherit; font-size: .88rem; font-weight: 600; color: var(--fg);
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
.opt:hover { background: var(--muted); }
.opt b { font-weight: 600; font-size: .8rem; color: var(--fg-soft); font-variant-numeric: tabular-nums; }
.opt[aria-pressed="true"] { background: var(--fg); border-color: var(--fg); color: #fff; }
.opt[aria-pressed="true"] b { color: rgba(255,255,255,.72); }

.results__bar {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  flex-wrap: wrap; padding-bottom: 1.15rem; margin-bottom: 1.6rem;
  border-bottom: 1px solid var(--border-soft);
}
.results__bar p { font-size: .93rem; color: var(--fg-mid); }
.results__bar strong { color: var(--fg); font-variant-numeric: tabular-nums; }
.results__right { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; }
.clear-all {
  border: 0; background: none; cursor: pointer; font: inherit; font-size: .88rem; font-weight: 600;
  color: var(--accent-btn); padding: .35rem .1rem; border-bottom: 1px solid currentColor;
}
.clear-all:hover { color: var(--accent-btn-2); }
/* One attribution line for the whole grid, because the cards themselves now
   follow the reference's rating shape (score, star, count) with no room for a
   partner name in it. */
.results__src {
  font-size: .82rem; line-height: 1.5; color: var(--fg-soft);
  max-width: 62ch; margin: -.35rem 0 1.1rem;
}

/* ==========================================================================
   ATTRACTION CARDS
   ========================================================================== */
/* Four across at desktop, matching the reference grid. */
.cards { display: grid; gap: 1.5rem 1.25rem; grid-template-columns: repeat(auto-fill, minmax(240px,1fr)); }
@media (min-width: 700px)  { .cards { grid-template-columns: repeat(2,1fr); } }
@media (min-width: 1000px) { .cards { grid-template-columns: repeat(3,1fr); } }
@media (min-width: 1240px) { .cards { grid-template-columns: repeat(4,1fr); } }

/* The listing card. Bordered, flat, square-shouldered image on top, and a fixed
   bottom row separated by a hairline. Nothing lifts or glows on hover, because
   twenty of these animating at once is what made the old grid feel restless. */
.ac {
  display: flex; flex-direction: column; overflow: hidden; background: #fff;
  border: 1px solid var(--border); border-radius: var(--r-card);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
/* Every card is interactive now, so every card gets the same affordance:
   a guide card navigates, a brief card opens the detail panel.

   Four things move together on hover, at deliberately different speeds so it
   reads as one gesture rather than four twitches: the card lifts quickly, the
   cue pill slides up just behind it, the title takes the action colour, and the
   image keeps pushing in long after the rest has settled. Colour and shadow are
   unconditional; everything that actually moves is behind the motion query. */
.ac:hover, .ac:focus-within {
  border-color: var(--border);
  box-shadow: 0 10px 28px rgba(26,43,73,.14);
}
.ac:hover .ac__body h3, .ac:focus-within .ac__body h3 { color: var(--primary); }
.ac__body h3 { transition: color .2s var(--ease); }
.ac.is-out { display: none; }
article.ac { background: #fff; position: relative; }

/* A transparent target covering the whole card. The card is an <article>, not a
   button, because a button may only contain phrasing content and this one holds
   an image and headings. The label stays in the accessibility tree for screen
   readers while being invisible on screen. */
.ac__hit {
  position: absolute; inset: 0; z-index: 3; cursor: pointer;
  background: none; border: 0; padding: 0;
  font-size: 0; color: transparent;
}
.ac__hit:focus-visible { outline: 3px solid var(--primary); outline-offset: -3px; border-radius: var(--r-card); }

/* Entrance. Deliberately a CSS animation rather than a JS-applied one: an
   animation always runs to completion, so a card can never be left stranded at
   opacity 0 if a script fails. It also re-runs for free when a filter brings a
   card back from display:none, which is exactly when it is wanted.
   --i is the card's index, written by the build; the cap keeps the last card in
   a long grid from waiting on a nineteen-step stagger. */
@media (prefers-reduced-motion: no-preference) {
  .ac {
    animation: acIn .42s var(--ease) backwards;
    animation-delay: calc(min(var(--i, 0), 9) * 32ms);
  }
  @keyframes acIn {
    from { opacity: 0; transform: translateY(12px) scale(.99); }
    to   { opacity: 1; transform: none; }
  }
}

.ac__media { position: relative; aspect-ratio: 3/2; overflow: hidden; background: var(--muted); }
.ac__media img { width: 100%; height: 100%; object-fit: cover; }
/* The cue: a white pill that rises out of the bottom of the image and names
   what the click does, which differs per card. It is decorative reinforcement,
   never the only signal, so it is pointer-transparent and the whole card stays
   the target. */
.ac__cue {
  position: absolute; z-index: 2; left: .6rem; bottom: .6rem; pointer-events: none;
  display: inline-flex; align-items: center; gap: .3rem;
  background: #fff; color: var(--fg);
  font-size: .78rem; font-weight: 700; white-space: nowrap;
  padding: .34rem .5rem .34rem .72rem; border-radius: var(--r-pill);
  box-shadow: 0 2px 10px rgba(26,43,73,.24);
  opacity: 0; transition: opacity .22s var(--ease);
}
.ac__cue svg { width: 13px; height: 13px; }
.ac:hover .ac__cue, .ac:focus-within .ac__cue { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .ac { transition: transform .26s var(--ease), border-color .2s var(--ease), box-shadow .26s var(--ease); }
  .ac:hover, .ac:focus-within { transform: translateY(-4px); }

  /* Slower than the lift on purpose: the image is still settling once the card
     has arrived, which is what stops the whole thing feeling mechanical. */
  .ac__media img { transition: transform .7s var(--ease); }
  .ac:hover .ac__media img, .ac:focus-within .ac__media img { transform: scale(1.07); }

  .ac__cue { transform: translateY(8px); transition: opacity .22s var(--ease), transform .28s var(--ease); }
  .ac:hover .ac__cue, .ac:focus-within .ac__cue { transform: none; }
}

/* The one badge on the image, dark and small, in the reference's top-left slot. */
.ac__badge {
  position: absolute; z-index: 2; left: .6rem; top: .6rem;
  background: var(--fg); color: #fff;
  font-size: .72rem; font-weight: 700; letter-spacing: .01em;
  padding: .28rem .6rem; border-radius: 6px;
  display: inline-flex; align-items: center; gap: .35rem;
}
.ac__badge svg { width: 13px; height: 13px; }

.ac__body { padding: .8rem .85rem .55rem; display: flex; flex-direction: column; gap: .22rem; flex: 1; }
/* Grey location eyebrow above the title, the reference's card hierarchy: it
   sets this uppercase and carries the category beside the place. */
.ac__area {
  font-size: .8rem; font-weight: 500; letter-spacing: 0; color: var(--fg-mid);
}
.ac__body h3 {
  font-size: .99rem; font-weight: 700; letter-spacing: -.012em; line-height: 1.32; color: var(--fg);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ac__body p {
  font-size: .86rem; color: var(--fg-mid); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ac__meta {
  display: flex; flex-wrap: wrap; gap: .2rem .8rem;
  font-size: .84rem; color: var(--fg-mid);
}
.ac__meta > span { display: inline-flex; align-items: center; gap: .3rem; }
.ac__meta svg { width: 13px; height: 13px; flex: none; }
.ac__go { display: none; }

.no-results { padding: 3.5rem 1rem; text-align: center; }
.no-results p { color: var(--fg-mid); margin-bottom: 1.25rem; }

/* ==========================================================================
   PLAN BAND, wide editorial rows
   ========================================================================== */
.plan { display: grid; gap: 1.4rem; grid-template-columns: 1fr; }
@media (min-width: 860px) { .plan { grid-template-columns: repeat(3,1fr); } }

.plan-card {
  position: relative; overflow: hidden; border-radius: var(--r-lg); min-height: 340px;
  display: flex; align-items: flex-end; color: #fff; text-decoration: none;
  box-shadow: var(--sh); transition: transform .34s var(--ease), box-shadow .34s var(--ease);
}
.plan-card:hover { transform: translateY(-6px); box-shadow: var(--sh-lg); }
.plan-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 1s var(--ease); }
.plan-card:hover img { transform: scale(1.07); }
.plan-card::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(4,26,40,.05) 30%, rgba(4,26,40,.82) 100%);
}
.plan-card__body { position: relative; z-index: 2; padding: 1.7rem 1.5rem 1.8rem; }
.plan-card__n { font-family: var(--font-h); font-size: .78rem; font-weight: 700; letter-spacing: .16em; opacity: .8; }
.plan-card h3 { font-size: 1.28rem; font-weight: 700; margin: .4rem 0 .55rem; line-height: 1.2; }
.plan-card p { font-size: .93rem; color: rgba(255,255,255,.9); }

/* ==========================================================================
   STANDARDS, editorial, oversized ghost numerals
   ========================================================================== */
.standards { display: grid; gap: 0; border-top: 1px solid var(--border-soft); }
.standard {
  display: grid; grid-template-columns: 1fr; gap: .35rem;
  padding-block: 2rem; border-bottom: 1px solid var(--border-soft); position: relative;
}
@media (min-width: 820px) {
  .standard { grid-template-columns: 7rem minmax(0,22rem) minmax(0,1fr); gap: 2.5rem; align-items: start; }
}
.standard__n {
  font-family: var(--font-h); font-size: clamp(2.6rem, 2rem + 1.6vw, 3.6rem);
  font-weight: 700; line-height: .85; letter-spacing: -.05em;
  color: transparent; -webkit-text-stroke: 2px #93cbe8;
  transition: -webkit-text-stroke-color .3s var(--ease);
}
.standard:hover .standard__n { -webkit-text-stroke-color: var(--accent); }
@supports not (-webkit-text-stroke: 2px red) { .standard__n { color: var(--border); } }
.standard h3 { font-size: 1.22rem; font-weight: 600; }
.standard p { color: var(--fg-mid); font-size: .96rem; }

/* ==========================================================================
   CTA
   ========================================================================== */
.cta { position: relative; overflow: hidden; background: var(--primary-ink); color: #fff; padding-block: clamp(3rem,7vw,4.75rem); }
.cta::before {
  content: ''; position: absolute; inset: -40%;
  background: radial-gradient(40% 50% at 20% 30%, #0e7490 0%, transparent 60%),
              radial-gradient(38% 46% at 80% 70%, #b45309 0%, transparent 58%),
              radial-gradient(44% 48% at 60% 20%, #0369a1 0%, transparent 62%);
  background-size: 200% 200%; filter: blur(40px); opacity: .85;
  animation: mesh 16s ease-in-out infinite alternate;
}
@keyframes mesh {
  0%   { background-position: 0% 0%, 100% 0%, 60% 100%; transform: scale(1) }
  100% { background-position: 30% 40%, 70% 30%, 20% 60%; transform: scale(1.08) }
}
.cta__inner { position: relative; z-index: 1; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 2rem; }
.cta h2 { font-size: clamp(1.6rem,1.25rem + 1.6vw,2.3rem); font-weight: 700; margin-bottom: .7rem; }
.cta p { color: #d5eaf5; max-width: 52ch; }
.cta strong { color: #fff; }

/* ==========================================================================
   FOOTER
   ========================================================================== */
/* Deep navy footer, the reference's closing block. */
.site-footer { background: var(--fg); color: rgba(255,255,255,.78); padding-block: 3rem 1.75rem; }

.footer-cols { display: grid; grid-template-columns: 1fr; gap: 2.25rem; padding-bottom: 2.25rem; }
@media (min-width: 620px) { .footer-cols { grid-template-columns: repeat(3,1fr); } }
@media (min-width: 920px) { .footer-cols { grid-template-columns: 1.8fr 1fr 1fr 1fr; } }
.footer-brand .brand__name { color: #fff; }
.footer-brand .brand__name span { color: rgba(255,255,255,.6); }
.footer-brand p { color: rgba(255,255,255,.65); font-size: .89rem; max-width: 34ch; margin-top: 1rem; }
.footer-col h2 { font-size: .95rem; font-weight: 700; text-transform: none; letter-spacing: 0; color: #fff; margin-bottom: .9rem; }
.footer-col ul { list-style: none; padding: 0; display: grid; gap: .55rem; }
.footer-col a { font-size: .89rem; color: rgba(255,255,255,.72); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(255,255,255,.28); transition: color .2s var(--ease); }
.footer-col a:hover { color: #fff; text-decoration-color: #fff; }
/* ---------------------------------------------------------------------------
   Consent
   ---------------------------------------------------------------------------
   Bottom-anchored rather than a full-screen interstitial: the page has to stay
   readable while the question is open, because a banner that blocks the content
   pressures the answer, and a pressured yes is not consent. Accept and Decline
   are deliberately the same size, the same height and the same weight. Do not
   make Accept the bigger button.
   --------------------------------------------------------------------------- */
.consent {
  position: fixed; z-index: 90; left: 0; right: 0; bottom: 0;
  background: #fff; border-top: 1px solid var(--border);
  box-shadow: 0 -6px 28px rgba(26,43,73,.13);
  padding: 1.1rem var(--pad) 1.2rem;
}
.consent[hidden] { display: none; }
.consent__inner {
  width: 100%; max-width: var(--container); margin-inline: auto;
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem 2rem;
  flex-wrap: wrap;
}
/* Without a basis the copy takes the whole row and shoulders the buttons onto
   a line of their own even on a wide screen. */
.consent__inner > div:first-child { flex: 1 1 32rem; min-width: 0; }
.consent__title { font-size: 1rem; font-weight: 700; margin-bottom: .3rem; }
.consent p { font-size: .87rem; line-height: 1.55; color: var(--fg-mid); max-width: 68ch; }
.consent p a { color: var(--primary); text-underline-offset: 3px; }
.consent__acts { display: flex; gap: .6rem; flex: none; }
.consent__acts .btn { min-height: 44px; padding-inline: 1.6rem; flex: 1; }
@media (max-width: 620px) {
  .consent__inner { flex-direction: column; align-items: stretch; }
  /* The basis above is a width. Once the row becomes a column it is read as a
     height instead, which stretched the banner to 622px on a phone. */
  .consent__inner > div:first-child { flex: 0 1 auto; }
  .consent__acts .btn { flex: 1; padding-inline: 1rem; }
}

/* The withdraw route. A button, not a link, because it opens a dialog rather
   than navigating, but it has to read as part of the footer sentence. */
.footer-bottom__link {
  background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
  color: rgba(255,255,255,.72); text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: rgba(255,255,255,.28);
}
.footer-bottom__link:hover { color: #fff; text-decoration-color: #fff; }

.footer-bottom {
  border-top: 1px solid rgba(255,255,255,.16); padding-top: 1.5rem;
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  font-size: .84rem; color: rgba(255,255,255,.55);
}

/* ==========================================================================
   HORIZONTAL RAIL, the reference's core section pattern: a heading, a row of
   tiles that scrolls sideways, and a circular arrow on the right edge.
   ========================================================================== */
.hrail-wrap { position: relative; }
.hrail {
  display: grid; grid-auto-flow: column; gap: 1rem;
  grid-auto-columns: minmax(148px, 1fr);
  overflow-x: auto; scroll-snap-type: x proximity; scroll-behavior: smooth;
  scrollbar-width: none; -ms-overflow-style: none; padding-bottom: .25rem;
}
.hrail::-webkit-scrollbar { display: none; }
.hrail > * { scroll-snap-align: start; }
@media (min-width: 700px)  { .hrail { grid-auto-columns: minmax(190px, 1fr); } }
/* Fixed track widths, not minmax(0, …). A zero minimum lets the tracks shrink to
   fit the container, which defeats the whole point: the rail is meant to size
   six tiles and let the rest overflow into the scroll. */
@media (min-width: 1000px) { .hrail--six { grid-auto-columns: calc((100% - 5rem) / 6); } }
/* A card rail shows four, the same as the results grid below it. */
.hrail--four { grid-auto-columns: minmax(250px, 1fr); }
@media (min-width: 1240px) { .hrail--four { grid-auto-columns: calc((100% - 3.75rem) / 4); } }

.hrail__nav {
  position: absolute; top: 50%; z-index: 5;
  width: 40px; height: 40px; border-radius: 50%; cursor: pointer;
  background: #fff; border: 1px solid var(--border); color: var(--primary);
  box-shadow: var(--sh-lg);
  display: none; align-items: center; justify-content: center;
  transition: background .18s var(--ease);
}
.hrail__nav:hover { background: var(--accent-soft); }
.hrail__nav svg { width: 18px; height: 18px; }
.hrail__nav--prev { left: -20px; transform: translateY(-50%) rotate(180deg); }
.hrail__nav--next { right: -20px; transform: translateY(-50%); }
.hrail__nav[hidden] { display: none !important; }
@media (min-width: 1000px) { .hrail__nav { display: flex; } }

/* Square destination tile: image, caption, nothing else. */
.dt { display: block; text-decoration: none; }
/* height:auto is load-bearing. The img carries width/height attributes for CLS,
   and an explicit height beats aspect-ratio, so without this the tiles keep the
   source 4:3 instead of cropping to the square the rail is built on. */
.dt img {
  width: 100%; height: auto; aspect-ratio: 1/1; object-fit: cover;
  /* Was --r-sm, which left the destination tiles rounded 8px while every
     listing card beside them was 12px. Two corner radii on one screen reads as
     an accident, and the reference uses one. */
  border-radius: var(--r-card); background: var(--muted); display: block;
  transition: filter .2s var(--ease);
}
.dt:hover img { filter: brightness(.94); }
.dt b { display: block; margin-top: .6rem; font-size: .95rem; font-weight: 700; color: var(--fg); letter-spacing: -.01em; }
.dt i { display: block; font-style: normal; font-size: .82rem; color: var(--fg-mid); margin-top: .1rem; }

/* ==========================================================================
   TRUST ROW. Three claims, each of which has to be literally true: this site
   holds no reviews, no ratings of its own and no invented prices, so none of
   these boxes may imply otherwise.
   ========================================================================== */
.trust { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 800px) { .trust { grid-template-columns: repeat(3,1fr); } }
.trust__box {
  border: 1px solid var(--border); border-radius: var(--r-card);
  padding: 1.6rem 1.4rem; text-align: center;
}
.trust__ic {
  width: 46px; height: 46px; margin: 0 auto .85rem; border-radius: var(--r-sm);
  background: var(--accent-soft); color: var(--primary);
  display: flex; align-items: center; justify-content: center;
}
.trust__ic svg { width: 23px; height: 23px; }
.trust__box h3 { font-size: 1rem; font-weight: 700; margin-bottom: .4rem; color: var(--fg); }
.trust__box p { font-size: .88rem; color: var(--fg-mid); line-height: 1.5; }

/* ==========================================================================
   Reveal. The reference animates nothing on scroll, and with sections this
   short the fade was firing constantly, so it is now a no-op that still leaves
   the markup and the no-JS path intact.
   ========================================================================== */
.js .reveal { opacity: 1; transform: none; }

/* ==========================================================================
   Article / legal pages
   ========================================================================== */
.page-head { border-bottom: 1px solid var(--border-soft); background: var(--bg-tint); padding-block: calc(var(--hdr-h) + 2rem) clamp(2rem,4vw,2.75rem); }
.page-head h1 { font-size: clamp(2rem,1.4rem + 2.4vw,3rem); font-weight: 700; }
.page-head p { color: var(--fg-soft); font-size: .9rem; font-weight: 600; margin-top: .65rem; }
.article { max-width: 74ch; margin-inline: auto; padding-block: clamp(2.5rem,5vw,3.75rem) clamp(3rem,7vw,4.5rem); }

/* The reading measure is right for prose and wrong for anything built from
   cards: constrained to it, a card grid sits in a narrow column with dead
   space either side of a 1200px page. Non-prose blocks pull back out to the
   container width. Margins rather than grid, because grid would stop the
   prose margins collapsing and re-space every paragraph using this class. */
.article > .wide {
  --bleed: min(calc(100vw - 2 * var(--pad)), calc(var(--container) - 2 * var(--pad)));
  width: var(--bleed);
  margin-inline: calc((100% - var(--bleed)) / 2);
}
.article h2 { font-size: 1.34rem; font-weight: 600; margin-top: 2.5rem; margin-bottom: .75rem; }
.article p { color: var(--fg-mid); }
.article p + p { margin-top: .95rem; }
.article ul { margin: .95rem 0 0; padding-left: 1.2rem; color: var(--fg-mid); }
.article li { margin-bottom: .55rem; }
.article a { color: var(--primary-deep); font-weight: 600; }
.callout {
  background: linear-gradient(135deg, var(--muted), #fff); border: 1px solid var(--border);
  border-left: 4px solid var(--primary); border-radius: var(--r); padding: 1.2rem 1.35rem; margin-top: 1.35rem;
}
.callout p { color: var(--fg); }
.breadcrumb { font-size: .86rem; color: var(--fg-soft); margin-bottom: .85rem; }
.breadcrumb a { color: var(--primary-deep); text-decoration: none; font-weight: 600; }
.notfound { text-align: center; padding-block: calc(var(--hdr-h) + 4rem) clamp(4rem,10vw,7rem); }
.notfound h1 { font-size: clamp(2.1rem,1.5rem + 2.8vw,3.2rem); font-weight: 700; margin-bottom: 1rem; }
.notfound p { color: var(--fg-mid); max-width: 48ch; margin: 0 auto 2.25rem; }

/* pages without a hero need the solid header from the start */
.no-hero .site-header { position: sticky; background: rgba(255,255,255,.93); backdrop-filter: saturate(180%) blur(16px); border-bottom-color: var(--border-soft); }
.no-hero .brand__name { color: var(--fg); }
.no-hero .nav a { color: var(--fg-mid); }
.no-hero .nav a.nav__cta { color: #fff !important; }
.no-hero .page-head { padding-top: 2.5rem; }


/* ==========================================================================
   GUIDE PAGES
   ========================================================================== */
.guide-hero { position: relative; overflow: hidden; isolation: isolate; }
.guide-hero__media { position: absolute; inset: 0; z-index: -2; }
.guide-hero__media img { width: 100%; height: 100%; object-fit: cover; }
.guide-hero::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  /* The mid-stop used to thin to .26, which is exactly where the breadcrumb and
     chips sit. Over a dark photo that was fine; over Blue Lagoon's turquoise it
     measured 3.43:1 and 2.84:1 against a 4.5 requirement. Held darker through
     the middle so the small text passes on any image, bright ones included. */
  background: linear-gradient(180deg, rgba(4,26,40,.80) 0%, rgba(4,26,40,.55) 45%, rgba(4,26,40,.88) 100%);
}
.guide-hero__inner { color: #fff; padding-block: calc(var(--hdr-h) + 3.5rem) clamp(2.5rem,6vw,4rem); max-width: 48rem; }
.guide-hero h1 { font-size: clamp(2.1rem,1.4rem + 3.2vw,3.5rem); font-weight: 700; margin-bottom: .9rem; }
.guide-hero__sub { font-size: clamp(1rem,.97rem + .3vw,1.15rem); color: rgba(255,255,255,.93); }
.guide-hero__chips { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.4rem; }
.gchip {
  display: inline-flex; align-items: center; gap: .4rem;
  /* Dark, not light. A white tint under white text is contrast working against
     itself, which is how the chips measured 2.84:1. */
  background: rgba(4,26,40,.5); border: 1px solid rgba(255,255,255,.34);
  backdrop-filter: blur(8px); padding: .35rem .8rem; border-radius: 999px;
  font-size: .8rem; font-weight: 600;
}
.gchip svg { width: 14px; height: 14px; }

.guide-layout { display: grid; grid-template-columns: 1fr; gap: 2.5rem; align-items: start; padding-block: clamp(2.5rem,6vw,4rem); }
@media (min-width: 1000px) { .guide-layout { grid-template-columns: minmax(0,1fr) 300px; gap: 3.5rem; } }

.guide-body { max-width: 68ch; }
.guide-body > * + * { margin-top: 1.15rem; }
.guide-body h2 {
  font-size: clamp(1.35rem,1.15rem + .8vw,1.7rem); font-weight: 700;
  margin-top: 2.75rem; padding-top: 1.5rem; border-top: 1px solid var(--border-soft);
}
/* The rule and the extra top space both mean "a new section starts here",
   which is meaningless above the article's own first heading: there is
   nothing before it but the hero. Homepage-only in practice, the one guide
   article that opens on an h2 rather than a paragraph. */
.guide-body > h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.guide-body h3 { font-size: 1.12rem; font-weight: 600; margin-top: 1.9rem; }
.guide-body p, .guide-body li { color: var(--fg-mid); }
.guide-body ul, .guide-body ol { padding-left: 1.25rem; }
.guide-body li { margin-bottom: .5rem; }
.guide-body a { color: var(--primary-deep); font-weight: 600; }
.guide-body strong { color: var(--fg); }

.verdict {
  border-left: 4px solid var(--accent); background: linear-gradient(135deg, var(--accent-soft), #fff);
  border-radius: var(--r); padding: 1.25rem 1.4rem;
}
.verdict h2 { margin: 0 0 .5rem; padding: 0; border: 0; font-size: 1.1rem; }
.verdict p { color: var(--fg); }

.factbox {
  background: #fff; border: 1px solid var(--border-soft); border-radius: var(--r-lg);
  padding: 1.35rem 1.4rem; box-shadow: var(--sh-sm);
}
@media (min-width: 1000px) { .factbox { position: sticky; top: calc(var(--hdr-h) + 1.25rem); } }
.factbox h2 { font-size: .78rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--fg-soft); margin-bottom: 1rem; }
.factbox dl { display: grid; gap: .85rem; margin: 0; }
.factbox dt { font-size: .74rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-soft); }
.factbox dd { margin: .15rem 0 0; font-size: .93rem; color: var(--fg); font-weight: 500; }
.factbox__note { margin-top: 1.15rem; padding-top: 1.15rem; border-top: 1px solid var(--border-soft); font-size: .82rem; color: var(--fg-soft); }

.faq { border-top: 1px solid var(--border-soft); }
.faq details { border-bottom: 1px solid var(--border-soft); }
.faq summary {
  cursor: pointer; padding: 1.05rem 2rem 1.05rem 0; font-weight: 600; font-size: 1rem;
  list-style: none; position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+'; position: absolute; right: .35rem; top: 50%; transform: translateY(-50%);
  font-size: 1.4rem; font-weight: 400; color: var(--primary-deep); line-height: 1;
}
.faq details[open] summary::after { content: '\2212'; }
.faq summary:hover { color: var(--primary-deep); }
.faq details p { color: var(--fg-mid); padding-bottom: 1.1rem; font-size: .95rem; }

.nearby { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fill,minmax(230px,1fr)); margin-top: 1.25rem; }
.nearby a {
  display: flex; gap: .85rem; align-items: center; padding: .8rem; border-radius: var(--r);
  border: 1px solid var(--border-soft); text-decoration: none; background: #fff;
  transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.nearby a:hover { border-color: var(--primary); transform: translateY(-2px); }
.nearby img { width: 64px; height: 48px; object-fit: cover; border-radius: 8px; flex: none; }
.nearby b { display: block; font-size: .92rem; font-weight: 600; color: var(--fg); }
.nearby span { font-size: .8rem; color: var(--fg-soft); }

/* Inbound internal links, generated per guide by tools/build.py. Sits outside the
   two-column guide layout so it spans the full measure, which is what stops three
   tiles being squeezed into the 68ch article column. */
.related { padding-block: 0 clamp(2.5rem,6vw,4rem); }
.related h2 { font-size: 1.15rem; margin: 0 0 .25rem; letter-spacing: -.01em; }
.related .nearby a { align-items: flex-start; }
.related .nearby img { margin-top: .15rem; }
/* The contextual link back to the directory. Reads as a sentence rather than a
   fourth tile, because it goes somewhere different from the three above it. */
.related__all { margin: .9rem 0 0; color: var(--muted, #5b6470); font-size: .95rem; }
.related__all a { color: inherit; text-decoration: underline; text-underline-offset: .18em; }
.related__all a:hover, .related__all a:focus-visible { color: var(--ink, #16202b); }

.updated { font-size: .84rem; color: var(--fg-soft); margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--border-soft); }

.ac__go { color: var(--primary-deep); font-weight: 700; }
a.ac { text-decoration: none; color: inherit; }

/* generic page intro for about / contact / plan */
.page-intro { padding-block: calc(var(--hdr-h) + 2.5rem) clamp(2rem,4vw,3rem); background: var(--bg-tint); border-bottom: 1px solid var(--border-soft); }
.page-intro h1 { font-size: clamp(2rem,1.4rem + 2.6vw,3.1rem); font-weight: 700; margin-bottom: .8rem; }
.page-intro p { color: var(--fg-mid); font-size: 1.05rem; max-width: 56ch; }


/* ==========================================================================
   SEARCH SUGGESTIONS (typeahead)
   ========================================================================== */
.sug-wrap { position: relative; flex: 1; min-width: 0; display: flex; align-items: center; gap: .5rem; }

.sug {
  position: absolute; z-index: 70; top: calc(100% + .6rem); left: 0; right: 0;
  background: #fff; border: 1px solid var(--border-soft); border-radius: 14px;
  box-shadow: 0 16px 44px rgba(12,36,52,.18); overflow: hidden;
  max-height: min(60vh, 25rem); overflow-y: auto; padding: .3rem;
  text-align: left;
}
.sug[hidden] { display: none; }

.sug__head {
  font-size: .68rem; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
  color: var(--fg-soft); padding: .6rem .75rem .4rem;
}

.sug__item {
  display: flex; align-items: center; gap: .7rem; width: 100%;
  padding: .55rem .75rem; border-radius: 10px; cursor: pointer;
  border: 0; background: none; font: inherit; text-align: left; color: inherit;
  text-decoration: none;
}
.sug__item:hover, .sug__item.is-active { background: var(--bg-tint); }
.sug__item.is-active { outline: 2px solid var(--primary); outline-offset: -2px; }

.sug__ico {
  width: 34px; height: 34px; flex: none; border-radius: 9px; display: grid; place-items: center;
  background: var(--muted); color: var(--primary-deep);
}
.sug__ico svg { width: 17px; height: 17px; }
.sug__txt { min-width: 0; flex: 1; }
.sug__name { display: block; font-size: .94rem; font-weight: 600; color: var(--fg); line-height: 1.3; }
.sug__name mark { background: none; color: var(--accent-btn); font-weight: 700; }
.sug__meta { display: block; font-size: .79rem; color: var(--fg-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sug__tag {
  flex: none; font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent-btn); background: var(--accent-soft); padding: .2rem .5rem; border-radius: 999px;
}
.sug__none { padding: 1rem .85rem; font-size: .9rem; color: var(--fg-mid); }
.sug__all {
  display: block; width: 100%; padding: .7rem .75rem; margin-top: .2rem;
  border: 0; border-top: 1px solid var(--border-soft); background: none; cursor: pointer;
  font: inherit; font-size: .87rem; font-weight: 600; color: var(--primary-deep); text-align: left;
}
.sug__all:hover { background: var(--bg-tint); }

.rail__search input::-webkit-search-cancel-button { display: none; }

/* a card jumped to from a suggestion gets a brief ring so it is findable */
@keyframes flash { 0%,100% { box-shadow: var(--sh); } 35% { box-shadow: 0 0 0 4px var(--primary), var(--sh-lg); } }
.ac.is-target, .ix__row.is-target { animation: flash 1.6s var(--ease) 1; }
.ix__row.is-target .ix__name { color: var(--primary-deep); }

/* ==========================================================================
   Reduced motion
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .js .reveal { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------------------
   Booking block. Rendered by tools/build.py from data/partner-products.json,
   and only when tools/affiliate.py has tracking configured. The disclosure sits
   inside the block so it always precedes the first partner link.
   --------------------------------------------------------------------------- */
.book {
  margin: 2.25rem 0; padding: 1.35rem 1.4rem;
  background: var(--bg-tint); border: 1px solid var(--border-soft);
  border-radius: var(--r-lg);
}
.book h2 { margin: 0 0 .35rem; padding: 0; border: 0; font-size: 1.1rem; }
.book__disclosure {
  margin: 0 0 1rem; font-size: .84rem; line-height: 1.55; color: var(--fg-soft);
}
.book__disclosure a { color: var(--primary-deep); }
/* Each option is a product card, not a line of prose: the partner's own photo,
   the title, the rating with its count, then price and a call to action over a
   hairline. The whole card is one anchor, so a click anywhere on it is the same
   tracked link. */
.book__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }
.book__item { margin: 0; }
.book__card {
  display: block; text-decoration: none; color: inherit; background: #fff;
  border: 1px solid var(--border); border-radius: var(--r-card); overflow: hidden;
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.book__card:hover, .book__card:focus-visible {
  border-color: var(--fg-soft); box-shadow: 0 2px 10px rgba(26,43,73,.09);
}
.book__img {
  width: 100%; height: auto; aspect-ratio: 16/9; object-fit: cover;
  background: var(--muted); display: block;
}
.book__body { display: block; padding: .7rem .8rem .55rem; }
.book__name {
  font-weight: 700; font-size: .92rem; line-height: 1.35; color: var(--fg);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.book__meta { display: flex; align-items: baseline; gap: .45rem; margin-top: .3rem; font-size: .8rem; }
/* The count is never separated from the score, so one review always reads as
   one review rather than as a verdict. */
.book__rate { font-weight: 700; color: var(--fg); display: inline-flex; align-items: baseline; gap: .22rem; }
.book__rate::before { content: '\2605'; font-size: .78rem; }
.book__rate i { font-style: normal; font-weight: 500; color: var(--fg-mid); }
.book__by { color: var(--fg-mid); }
.book__foot {
  display: flex; align-items: baseline; justify-content: space-between; gap: .2rem .5rem;
  /* Wraps rather than clips: in the 300px guide sidebar the price and the call
     to action cannot share a line, and nowrap silently cut the CTA in half. */
  flex-wrap: wrap;
  padding: .55rem .8rem .7rem; border-top: 1px solid var(--border-soft);
}
.book__price { font-weight: 800; font-size: .95rem; color: var(--fg); white-space: nowrap; }
.book__price b { font-weight: 500; font-size: .78rem; color: var(--fg-mid); }
.book__price i { font-style: normal; font-weight: 500; font-size: .72rem; color: var(--fg-soft); }
.book__cta { font-size: .82rem; font-weight: 700; color: var(--primary); white-space: nowrap; }
.book__cta::after { content: ' \203A'; }
.book__card:hover .book__cta { text-decoration: underline; }
.book__more, .book__note {
  margin: .9rem 0 0; font-size: .8rem; line-height: 1.55; color: var(--fg-soft);
}

/* "Tours available" badge on an attraction card. */
.ac__book {
  font-weight: 650; color: var(--success);
}

/* ---------------------------------------------------------------------------
   Plan your trip: on-page nav, trip-length comparison, season table
   --------------------------------------------------------------------------- */
.toc {
  background: var(--bg-tint); border: 1px solid var(--border-soft); border-radius: var(--r);
  padding: 1.1rem 1.3rem; margin-bottom: 2rem;
}
.toc h2 {
  font-size: .74rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--fg-soft); margin: 0 0 .6rem;
}
.toc ol { margin: 0; padding-left: 1.1rem; display: grid; gap: .3rem; }
.toc li { color: var(--fg-soft); margin: 0; }
.toc a { color: var(--primary-deep); font-weight: 600; }

.triplen {
  display: grid; gap: .9rem; margin-top: 1.15rem;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.triplen__item {
  background: #fff; border: 1px solid var(--border-soft); border-radius: var(--r);
  padding: 1rem 1.1rem; box-shadow: var(--sh-sm);
}
.triplen__item--pick { border-color: var(--primary); box-shadow: var(--sh); }
.triplen__item b { display: block; font-size: 1.02rem; color: var(--fg); margin-bottom: .4rem; }
.triplen__item b span {
  display: block; font-size: .72rem; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--primary-deep); margin-top: .2rem;
}
.triplen__item p { margin: 0; font-size: .9rem; line-height: 1.55; }

/* Wide content must scroll in its own box, never the page body. */
.tablewrap { overflow-x: auto; margin-top: 1.15rem; -webkit-overflow-scrolling: touch; }
.ptable { width: 100%; border-collapse: collapse; min-width: 30rem; }
.ptable th {
  text-align: left; padding: .6rem .75rem .6rem 0; border-bottom: 2px solid var(--fg);
  font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
}
.ptable td { padding: .8rem .75rem .8rem 0; border-bottom: 1px solid var(--border-soft); color: var(--fg-mid); }
.ptable tbody tr:last-child td { border-bottom: 0; }
.ptable td:first-child { font-weight: 650; color: var(--fg); white-space: nowrap; }

/* Factual summary drawn from operator listings. Facts only, their wording is
   never reproduced, and partner ratings are attributed rather than marked up. */
/* Was a bordered, left-accented box, which read as an alert rather than as
   part of the product. The reference states the same facts as a plain list of
   ticks under the heading, so it does too. */
.bookfacts { margin: 0 0 1.4rem; padding: 0; border: 0; background: none; }
.bookfacts ul { list-style: none; padding-left: 0 !important; }
.bookfacts li { position: relative; padding-left: 1.6rem; }
.bookfacts li::before {
  content: ""; position: absolute; left: 0; top: .45em; width: .95rem; height: .6rem;
  border-left: 2px solid var(--success); border-bottom: 2px solid var(--success);
  transform: rotate(-45deg);
}

/* The booking card, above the at-a-glance list in the sticky column: price
   first, then the action, then the two things a buyer actually wants assured. */
.bookcard {
  border: 1px solid var(--border); border-radius: var(--r-card);
  padding: 1.1rem 1.15rem; margin-bottom: 1.35rem; background: var(--bg-tint);
}
.bookcard__from { display: block; font-size: 1.02rem; font-weight: 700; color: var(--fg); margin-bottom: .8rem; }
.bookcard__go { width: 100%; justify-content: center; }
.bookcard__trust { list-style: none; margin: .9rem 0 0; padding: 0; display: grid; gap: .4rem; }
.bookcard__trust li { font-size: .82rem; color: var(--fg-mid); padding-left: 1.4rem; position: relative; }
.bookcard__trust li::before {
  content: ""; position: absolute; left: 0; top: .4em; width: .8rem; height: .5rem;
  border-left: 2px solid var(--success); border-bottom: 2px solid var(--success);
  transform: rotate(-45deg);
}
.bookfacts h2 { margin: 0 0 .7rem; padding: 0; border: 0; font-size: 1.06rem; }
.bookfacts ul { margin: 0; padding-left: 1.15rem; display: grid; gap: .4rem; }
.bookfacts li { color: var(--fg-mid); font-size: .93rem; line-height: 1.5; margin: 0; }
.bookfacts b { color: var(--fg); }
.bookfacts__src { margin: .9rem 0 0; font-size: .79rem; color: var(--fg-soft); line-height: 1.5; }

/* ---------------------------------------------------------------------------
   Guide sidebar: on the homepage this is the whole commercial block (what is
   bookable, then the booking CTA, then the fact box); on a guide page it is
   the booking CTA and the fact box. Either way it tracks the reader down the
   page, so the way to book is always one glance away rather than buried below
   a long article.
   --------------------------------------------------------------------------- */
.guide-side { display: grid; gap: 1.25rem; align-items: start; }
@media (min-width: 1000px) {
  .guide-side {
    position: sticky; top: calc(var(--hdr-h) + 1.25rem);
    /* A sticky column taller than the screen pins its top and hides its own
       foot: there is no way to scroll to the last booking option, because the
       column never moves. Capping it to the viewport and letting it scroll
       inside itself is what makes the whole list reachable. */
    max-height: calc(100vh - var(--hdr-h) - 2.5rem);
    overflow-y: auto; overscroll-behavior: contain;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .guide-side::-webkit-scrollbar { display: none; }
  /* The column is sticky as a whole now, so the inner box must not be. */
  .factbox { position: static; }
}
/* The fact panel is written for a 68ch article column; in the 300px sidebar
   its own margin doubled up with the grid gap above, so the top of the block
   sat noticeably further from the price chips than the gap between every
   other pair of panels below it. */
.guide-side .bookfacts { margin: 0; }

/* Sidebar variant of the booking block: narrower, stacked, no thumbnails, so a
   300px column stays readable. */
.book--side { margin: 0; padding: 1.15rem 1.2rem; }
.book--side h2 { font-size: 1rem; }
.book--side .book__disclosure { font-size: .78rem; margin-bottom: .8rem; }
/* The sidebar used to hide the product photo, which left three lines of grey
   text that read as prose rather than as something you can book. The image is
   the thing that makes the card look bookable, so it stays. */
.book--side .book__meta { font-size: .78rem; }
.book--side .book__note, .book--side .book__more { font-size: .74rem; }

/* ---------------------------------------------------------------------------
   Commercial line on an attraction card. Values come from
   data/partner-products.json only. The rating carries the partner's name so it
   can never read as a review this site hosts.
   --------------------------------------------------------------------------- */
/* The bottom row is the same shape on all 59 cards: something on the left,
   something right-aligned on the right, over one hairline. Only 14 attractions
   have partner products, so the other 45 fill the identical slots with facts we
   actually hold (how long to allow, when to go) rather than an invented price or
   an empty gap. Nothing here is ever fabricated, and a rating always names the
   partner it came from. */
.ac__offer {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .4rem .6rem;
  padding: .35rem .85rem .9rem;
  margin-top: auto; min-height: 34px;
}
.ac__price { font-weight: 800; font-size: 1rem; color: var(--fg); margin-left: auto; white-space: nowrap; }
.ac__price b { font-weight: 400; font-size: .82rem; color: var(--fg-mid); margin-right: .15rem; }
.ac__rate { font-size: .86rem; color: var(--fg); display: inline-flex; align-items: center; gap: .22rem; }
.ac__rate b { font-weight: 700; }
.ac__star { width: 13px; height: 13px; flex: none; fill: var(--fg); }
.ac__rate i { font-style: normal; font-weight: 400; color: var(--fg-mid); font-size: .82rem;
  font-variant-numeric: tabular-nums; }
/* The editorial equivalent, same slots, same weights. */
.ac__fact { font-size: .86rem; font-weight: 700; color: var(--fg); }
.ac__fact--r { margin-left: auto; font-size: .82rem; font-weight: 500; color: var(--fg-mid); text-align: right; }
.ac__offer { row-gap: .45rem; }
.ac__body { flex: 1 1 auto; }

/* Commercial chips in a guide hero: price and rating belong beside the title,
   which is the standard anatomy for an activity page. */
.gchip--price { font-weight: 700; background: rgba(4,26,40,.62); }
.gchip--free { background: rgba(4,60,48,.62); border-color: rgba(167,243,208,.55); }

/* Price on an A-Z row, the list-view equivalent of the card's commercial line. */
.ix__price {
  font-size: .8rem; font-weight: 700; color: var(--fg); margin-top: .12rem;
}

/* Filter matched places we cover but have no photograph of yet. */
.cards-empty {
  grid-column: 1 / -1; margin: .5rem 0 0; padding: 1.5rem 1.25rem;
  border: 1px dashed var(--border); border-radius: var(--r-card);
  text-align: center; font-size: .9rem; color: var(--fg-mid);
}


/* ==========================================================================
   DETAIL PANEL, for attractions with no written guide yet
   ========================================================================== */
/* The source blocks are in the document for crawlers, not for readers. */
.det-src[hidden] { display: none; }

.modal {
  /* margin:auto is what centres a native dialog, and the global `* { margin: 0 }`
     reset above wipes it, which pinned the panel to the top-left corner. */
  margin: auto;
  width: min(620px, calc(100vw - 2rem)); max-height: min(88vh, 880px);
  padding: 0; border: 0; border-radius: var(--r-card);
  background: #fff; color: var(--fg);
  /* Clip to the rounded box. The close button used to hang outside the corner,
     which is most of what made the panel look like a cut-off fragment rather
     than a finished sheet. */
  overflow: hidden;
  box-shadow: var(--sh-lg);
}
.modal::backdrop { background: rgba(26,43,73,.55); }
@media (prefers-reduced-motion: no-preference) {
  .modal[open] { animation: modalIn .28s var(--ease); }
  .modal[open]::backdrop { animation: fadeIn .28s var(--ease); }
  @keyframes modalIn { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
  @keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
}
.modal__body {
  max-height: min(88vh, 880px); overflow-y: auto; overscroll-behavior: contain;
  /* The bar itself is chrome the panel does not need; the fade below is what
     tells you there is more, and it goes away once there is not. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.modal__body::-webkit-scrollbar { display: none; }

/* Fade at the foot of the sheet, so a mid-sentence edge reads as "keep going"
   rather than as a rendering fault. Removed the moment you reach the end, or
   when the content was never long enough to scroll. */
.modal::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 56px;
  background: linear-gradient(180deg, rgba(255,255,255,0), #fff 86%);
  pointer-events: none; transition: opacity .2s var(--ease);
}
.modal.is-end::after { opacity: 0; }

/* Inside the sheet, over the image, where it cannot be mistaken for page furniture. */
.modal__close {
  position: absolute; top: .7rem; right: .7rem; z-index: 4;
  width: 36px; height: 36px; border-radius: 50%; cursor: pointer;
  background: rgba(255,255,255,.94); border: 0; color: var(--fg);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 10px rgba(26,43,73,.28);
  transition: background .18s var(--ease);
}
.modal__close:hover { background: #fff; }
.modal__close svg { width: 17px; height: 17px; }

.det__img { width: 100%; height: auto; aspect-ratio: 2.4/1; object-fit: cover; display: block; }
.det { padding-bottom: 1.4rem; }
.det > *:not(.det__img) { margin-inline: 1.4rem; }
.det__eyebrow { margin-top: 1.1rem; font-size: .84rem; color: var(--fg-mid); }
.det h2 { font-size: 1.5rem; font-weight: 800; letter-spacing: -.022em; margin-top: .15rem; }
.det__meta { margin-top: .35rem; font-size: .9rem; font-weight: 600; color: var(--fg); }
.det__sum { margin-top: .8rem; color: var(--fg-mid); line-height: 1.6; }
.det .book { margin-top: 1.3rem; }
.det__note {
  margin-top: 1.1rem; padding-top: .9rem; border-top: 1px solid var(--border-soft);
  font-size: .84rem; color: var(--fg-soft);
}

/* In narrow contexts, the detail panel and the guide sidebar, booking options
   become rows rather than banners. A 16:9 photo per product made each option
   278px tall, which pushed the sidebar to 2016px in a 900px viewport: sticky
   pinned its top and the last option simply could not be reached. At row size
   the whole column fits the screen. */
.det .book, .book--side { background: none; border: 0; padding: 0; }
.det .book { margin-top: 1.2rem; }
.det .book h2, .book--side h2 { font-size: .95rem; }
.det .book__card, .book--side .book__card {
  display: grid; grid-template-columns: 104px minmax(0, 1fr); grid-template-rows: auto auto;
}
.det .book__img, .book--side .book__img {
  grid-row: 1 / 3; width: 104px; height: 100%; aspect-ratio: auto;
  min-height: 92px; border-radius: 0;
}
/* A product the partner published no photograph for. The compact card above is a
   two column grid built around that thumbnail, so with no image in the markup the
   body was laid into the 104px thumbnail column and the footer beside it: the
   title clipped after two words and the price line ran out past the card edge.
   Popeye Village is the case that exposed it, because its one Klook listing
   carries neither an image nor a price, and that card is the whole of the
   booking block on the homepage. With no image the card is a single column. */
.det .book__card:not(:has(.book__img)),
.book--side .book__card:not(:has(.book__img)) { grid-template-columns: minmax(0, 1fr); }

.det .book__body, .book--side .book__body { padding: .6rem .8rem .3rem; }
.det .book__name, .book--side .book__name { font-size: .88rem; }
.det .book__foot, .book--side .book__foot { border-top: 0; padding: 0 .8rem .6rem; min-height: 0; }
/* Not every partner listing publishes a picture. The two-column rule above
   reserves 104px for one, so when it is absent the title lands in the image
   slot and wraps inside 104px with the rest of the row empty beside it. A card
   with nothing to put there gets one column instead. */
.det .book__card--noimg, .book--side .book__card--noimg { grid-template-columns: minmax(0, 1fr); }

/* ==========================================================================
   MOBILE BOOKING BAR
   On desktop the booking column is sticky and sits 11% down the page. On a
   phone it stacks below the article, which put Blue Lagoon's first bookable
   option 6208px down an 8803px page. This carries the real cheapest price and
   a jump to the options, and gets out of the way once they are on screen.
   ========================================================================== */
.bookbar { display: none; }
@media (max-width: 999px) {
  .bookbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 70;
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: .7rem 1rem calc(.7rem + env(safe-area-inset-bottom));
    background: #fff; border-top: 1px solid var(--border);
    box-shadow: 0 -2px 16px rgba(26,43,73,.12);
    transition: transform .28s var(--ease);
  }
  /* Hidden once the booking options are visible, so it never covers them. */
  .bookbar.is-gone { transform: translateY(110%); }
  .bookbar__price { font-weight: 800; font-size: 1.05rem; color: var(--fg); }
  .bookbar__price b { font-weight: 500; font-size: .8rem; color: var(--fg-mid); }
  .bookbar__price i { display: block; font-style: normal; font-weight: 500; font-size: .76rem; color: var(--fg-soft); }
  .bookbar .btn { min-height: 44px; padding-inline: 1.4rem; }
  /* Clear the bar so it never sits on top of the last of the page. */
  .site-footer { padding-bottom: 5.5rem; }
}
@media (prefers-reduced-motion: reduce) {
  .bookbar { transition: none; }
  .bookbar.is-gone { display: none; }
}

/* ==========================================================================
   Tap targets. Footer and breadcrumb links rendered 18px tall, under the 24px
   minimum, which on a phone means a link between two other links with almost
   no margin for a thumb. The text size is unchanged; only the target grows.
   ========================================================================== */
@media (max-width: 900px) {
  .footer-col a, .footer-col li > a { display: inline-block; padding-block: .35rem; }
  .footer-col ul { gap: .3rem; }
  .breadcrumb a { display: inline-block; padding-block: .3rem; }
  .ix__name a { display: inline-block; padding-block: .2rem; }
}


/* ==========================================================================
   FAVOURITES
   A heart on each card, kept in the browser. No account, nothing sent
   anywhere, and nothing that pretends this site takes bookings.
   ========================================================================== */
.nav__fav {
  border: 1px solid var(--border); background: #fff; cursor: pointer; font: inherit;
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .5rem .85rem; margin-left: .4rem; border-radius: var(--r-pill);
  font-size: .9rem; font-weight: 700; color: var(--fg);
  transition: background .18s var(--ease), border-color .18s var(--ease), color .18s var(--ease);
}
.nav__fav svg { width: 16px; height: 16px; }
.nav__fav:hover { background: var(--muted); }
.nav__fav[aria-pressed="true"] { background: var(--fg); border-color: var(--fg); color: #fff; }
.nav__fav.is-empty { display: none; }

/* The heart sits opposite the category badge, as it does on the reference. */
.ac__fav {
  position: absolute; z-index: 4; right: .55rem; top: .55rem;
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  background: rgba(255,255,255,.92); border: 0; color: var(--fg);
  display: flex; align-items: center; justify-content: center; padding: 0;
  box-shadow: 0 1px 6px rgba(26,43,73,.25);
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.ac__fav svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; }
.ac__fav:hover { background: #fff; }
.ac__fav[aria-pressed="true"] { color: var(--red); }
.ac__fav[aria-pressed="true"] svg { fill: var(--red); stroke: var(--red); }
@media (prefers-reduced-motion: no-preference) {
  .ac__fav[aria-pressed="true"] { animation: favPop .3s var(--ease); }
  @keyframes favPop { 0% { transform: scale(1); } 45% { transform: scale(1.28); } 100% { transform: scale(1); } }
}
/* While the saved filter is on, everything unsaved is out of the way. */
.ac.is-unsaved { display: none; }

.saved-empty {
  grid-column: 1 / -1; margin: .5rem 0 0; padding: 1.75rem 1.25rem;
  border: 1px dashed var(--border); border-radius: var(--r-card);
  text-align: center; font-size: .92rem; color: var(--fg-mid);
}

/* ==========================================================================
   Grouped search suggestions
   ========================================================================== */
.sug__group {
  font-size: .7rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--fg-soft); padding: .6rem .85rem .3rem;
}
.sug__group:not(:first-child) { border-top: 1px solid var(--border-soft); margin-top: .25rem; }

/* ==========================================================================
   MOBILE DRAWER
   Below 1100px the plain nav links are hidden, which leaves a phone with no
   way to reach them. The drawer carries the same short list, rendered once by
   tools/build.py from NAV, so there is nothing to duplicate or keep in sync.
   ========================================================================== */
.nav__burger {
  display: none; border: 1px solid var(--border); background: #fff; cursor: pointer;
  width: 42px; height: 42px; border-radius: var(--r-sm); margin-left: .4rem;
  align-items: center; justify-content: center; color: var(--fg); padding: 0;
}
.nav__burger svg { width: 20px; height: 20px; }
.nav__burger:hover { background: var(--muted); }
@media (max-width: 1100px) { .nav__burger { display: inline-flex; } }

.drawer { position: fixed; inset: 0; z-index: 80; }
.drawer[hidden] { display: none; }
.drawer::before {
  content: ''; position: absolute; inset: 0; background: rgba(26,43,73,.5);
}
.drawer__panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(380px, 88vw); background: #fff;
  padding: 1.1rem 1.15rem calc(1.5rem + env(safe-area-inset-bottom));
  overflow-y: auto; overscroll-behavior: contain;
  box-shadow: -8px 0 28px rgba(26,43,73,.18);
}
@media (prefers-reduced-motion: no-preference) {
  .drawer::before { animation: fadeIn .2s var(--ease); }
  .drawer__panel { animation: drawerIn .26s var(--ease); }
  @keyframes drawerIn { from { transform: translateX(100%); } to { transform: none; } }
}
.drawer__close {
  border: 0; background: none; cursor: pointer; color: var(--fg);
  width: 40px; height: 40px; border-radius: 50%; margin-left: auto; display: block;
}
.drawer__close:hover { background: var(--muted); }
.drawer__close svg { width: 19px; height: 19px; }

.drawer__links { display: grid; gap: .1rem; margin-bottom: .9rem; }
.drawer__links a {
  display: block; padding: .8rem .6rem; border-radius: var(--r-sm);
  font-size: 1.02rem; font-weight: 700; color: var(--fg); text-decoration: none;
}
.drawer__links a:hover { background: var(--muted); }

/* Options past the fold. Every one is in the document, so they are crawlable
   and every link can earn; the button only decides what is on screen first. */
.book__item.is-folded { display: none; }
.book.is-open .book__item.is-folded { display: block; }
.book__more {
  display: flex; align-items: center; justify-content: center; gap: .4rem;
  width: 100%; margin-top: .75rem; padding: .7rem 1rem;
  border: 1px solid var(--primary); border-radius: var(--r-pill);
  background: #fff; color: var(--primary); cursor: pointer;
  font: inherit; font-size: .88rem; font-weight: 700;
  transition: background .18s var(--ease);
}
.book__more:hover { background: var(--accent-soft); }
.book__more svg { width: 15px; height: 15px; transition: transform .2s var(--ease); }
.book.is-open .book__more svg { transform: rotate(180deg); }

/* A product whose price the partner would not give us. The slot still exists so
   the cards line up, but it says what is true instead of "from" and a gap.

   The colour was --muted, which is a near-white surface token (#eef2f7) and not
   a text token: on the white card it measured about 1.1:1, so the line was
   legible only as a smear. --fg-soft is the quietest colour on the site that
   still passes AA on white, which is what this line needs to be, because on
   Popeye Village it is the ONLY thing the single booking card can say about
   price. */
.book__price--none { font-weight: 600; color: var(--fg-soft); font-size: .82rem; }

/* The search suggestion's thumbnail. Same footprint as the icon it replaces so
   the row height does not move, and the same 4:3 the cards use. */
.sug__thumb {
  width: 46px; height: 35px; flex: none; border-radius: 8px;
  object-fit: cover; background: var(--muted);
}


/* Display face. Headings and the wordmark only: the body face keeps
   its job, which is staying legible at 15px across a long article. */
h1, h2, .brand__name > :first-child, .hero h1, .ac__area + h3 {
  font-family: var(--font-display);
  letter-spacing: -0.015em;
}
.brand__name { font-family: var(--font-display); }

/* ==========================================================================
   HOMEPAGE REWORK (popeye-home-v3)
   The homepage used to open on a bulleted essay and a text sidebar, which read
   as a detail page rather than a homepage. This block styles the replacement:
   a tight hero CTA, a short lead, a photographic highlights row, the booking
   block presented as its own panel, a gallery strip and a "plan your visit"
   card row. Nothing here touches BRAND_SVG, a colour token or the display
   face; every rule below either reuses an existing token or introduces a
   purely structural one.
   ========================================================================== */

/* Hero call to action: sits under the chips, so it reaches for the same white
   text region a photographic hero already gives high contrast against. */
.guide-hero__cta { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: .35rem; }
.guide-hero__cta .btn { min-height: 46px; }

/* Short standfirst under the hero. Larger and quieter than the article body,
   so it reads as a two-sentence orientation rather than the start of a long
   article. */
.lead {
  max-width: 62ch; margin: clamp(1.75rem, 4vw, 2.5rem) auto 0;
  font-size: clamp(1.05rem, .98rem + .3vw, 1.22rem); line-height: 1.6;
  color: var(--fg-mid); text-wrap: pretty;
}

/* The booking block, presented as one panel rather than a sidebar column.
   book_facts() and book_block() still render their own "book" / "book--side"
   sections unchanged; this only wraps them. */
.book-panel {
  display: grid; gap: 1.5rem;
  background: #fff; border: 1px solid var(--border-soft); border-radius: var(--r-xl);
  box-shadow: var(--sh-lg); padding: clamp(1.25rem, 3vw, 2rem);
}
@media (min-width: 860px) {
  .book-panel { grid-template-columns: minmax(0,1fr) 300px; align-items: start; }
}
/* The at-a-glance facts sit inside the same panel now, so its own card chrome
   (background, border, shadow, sticky position) would double up with the
   panel's. A hairline divider between the two columns is enough. */
.book-panel .factbox {
  position: static; background: none; border: 0; box-shadow: none;
  padding: 1.25rem 0 0; border-top: 1px solid var(--border-soft);
}
@media (min-width: 860px) {
  .book-panel .factbox { padding: 0 0 0 1.75rem; border-top: 0; border-left: 1px solid var(--border-soft); }
}

/* Gallery strip. Reuses the .hrail scroller and its arrow controls; only the
   tile itself is new. tools/build.py renders one tile per photo variant that
   actually exists (see existing_variants()), so a single photo is a single
   tile rather than an empty frame padded out to a fixed count. */
.hrail--gallery { grid-auto-columns: minmax(240px, 78%); }
@media (min-width: 700px)  { .hrail--gallery { grid-auto-columns: minmax(320px, 55%); } }
@media (min-width: 1100px) { .hrail--gallery { grid-auto-columns: calc((100% - 2.5rem) / 2.4); } }
.gallery-tile { margin: 0; border-radius: var(--r-xl); overflow: hidden; box-shadow: var(--sh); background: var(--muted); }
.gallery-tile img {
  width: 100%; height: 100%; aspect-ratio: 4/3; object-fit: cover;
  transition: transform .5s var(--ease);
}
.gallery-tile:hover img { transform: scale(1.05); }

/* "Plan your visit": three link cards reusing the existing .trust box, which
   until now nothing on this site rendered. Made into a real link target with
   the same hover lift the reference gives every card. */
.plan-links .trust__box {
  display: block; text-decoration: none; color: inherit;
  transition: transform .22s var(--ease), border-color .22s var(--ease), box-shadow .22s var(--ease);
}
.plan-links .trust__box:hover, .plan-links .trust__box:focus-visible {
  transform: translateY(-4px); border-color: var(--primary); box-shadow: var(--sh-lg);
}

/* Staggered entrance on first paint: opacity and transform only, so nothing
   layout-shifts. --i is written per element by tools/build.py or inline in
   index.html, the same convention the attraction cards already use. */
@media (prefers-reduced-motion: no-preference) {
  .fade-up {
    animation: fadeUp .5s var(--ease) backwards;
    animation-delay: calc(min(var(--i, 0), 8) * 80ms);
  }
  @keyframes fadeUp {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }
}


/* ==========================================================================
   HERO SEARCH
   Ported from the sister site. The hero is the one place this site was
   missing the search the other two lead with, and every colour here is a
   token, so it takes this site's own palette rather than the sister's.
   ========================================================================== */
/* One long white pill: search field, a divider, the island selector, then the
   blue action. This is the hero's only interactive element by design. */
.searchbar {
  display: flex; align-items: center; gap: 0; margin-inline: auto;
  background: #fff; border: 1px solid transparent; border-radius: var(--r-pill);
  padding: .45rem .45rem .45rem 1.6rem;
  box-shadow: 0 2px 6px rgba(26,43,73,.09), 0 14px 34px rgba(26,43,73,.11);
  max-width: 46rem;
}
.searchbar:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 16%, transparent); }
.searchbar > svg { width: 20px; height: 20px; flex: none; color: var(--fg-soft); margin-right: .6rem; }
.searchbar input {
  flex: 1; min-width: 0; border: 0; background: none; font: inherit; font-size: 1.02rem;
  color: var(--fg); padding: .95rem .25rem;
}
.searchbar input::placeholder { color: var(--fg-soft); }
.searchbar input:focus { outline: none; }
.searchbar__sep { width: 1px; align-self: stretch; margin: .35rem .7rem; background: var(--border); flex: none; }
.searchbar__where {
  flex: none; border: 0; background: none; font: inherit; font-size: .95rem; font-weight: 600;
  color: var(--fg); padding: .55rem 1.6rem .55rem .35rem; cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2363687a' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .3rem center; background-size: 15px;
}
.searchbar__where:focus { outline: none; }
.searchbar .btn { min-height: 54px; padding-inline: 2rem; font-size: 1.02rem; flex: none; gap: 0; }
/* the hero bar is on a dark image, so its dropdown needs to sit above the scrim */
.searchbar { position: relative; }
.searchbar .sug { box-shadow: 0 22px 60px rgba(2,18,28,.42); }
.searchbar input::-webkit-search-cancel-button,
.rail__search input::-webkit-search-cancel-button { display: none; }
@media (max-width: 620px) {
  .searchbar { padding-left: 1rem; }
  .searchbar__sep, .searchbar__where { display: none; }
  .searchbar .btn span { display: none; }
  .searchbar .btn { padding-inline: 1.15rem; min-height: 48px; }
  /* Without this the placeholder is clipped flush against the button and the
     two read as one smudge. The bar is now tall enough that the collision is
     the first thing you see on a phone. */
  .searchbar input { padding-right: .7rem; }
}


/* The right rail is the booking card and nothing else now, the way the
   reference splits an activity page: detail on the left, the action on the
   right. With one product for sale the old split left a 500px hole under the
   options while the facts column ran on beside it. */
.book-panel__side { align-self: start; }
.book-panel__main .glance-h {
  font-size: .78rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--fg-soft); margin: 2rem 0 1rem;
}
.book-panel__main dl { display: grid; gap: .85rem; margin: 0 0 1rem; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.book-panel__main dt { font-size: .74rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--fg-soft); }
.book-panel__main dd { margin: .15rem 0 0; font-size: .93rem; color: var(--fg); font-weight: 500; }
.book-panel__main .factbox__note { font-size: .82rem; color: var(--fg-soft); line-height: 1.55; margin: 0; }


/* Sits where a price would sit, on the cards that have nothing to sell. A grid
   where some cards carry a price and others carry nothing reads as broken data,
   so each of those cards says plainly why instead of leaving a gap. Deliberately
   quieter than a price: it is information, not an offer. */
.ac__free {
  font-size: .78rem; font-weight: 700; letter-spacing: .01em;
  padding: .18rem .5rem; border-radius: 999px;
  background: var(--muted); color: var(--fg-mid); white-space: nowrap;
}
