/* ==========================================================================
   Baanpong Lodge — stylesheet
   ==========================================================================

   The site is hand-written static HTML. There is no page builder, no theme
   and no CMS: this file owns the whole design.

   The brand colours and the type scale were read out of the stylesheet the
   old site rendered with, so they are the values the lodge has looked like
   for years rather than a new invention. Everything that existed only to
   coexist with a page builder has gone: this file styles bare elements now,
   because nothing else does.

   Contents
     1. Tokens
     2. Reset and base elements
     3. Layout
     4. Components
     5. Utilities
     6. Site chrome — header, navigation, footer
     7. Page furniture — hero, prose, media
   ========================================================================== */

/* 1. TOKENS ================================================================ */

:root {
  /* -- Brand ------------------------------------------------------------- */
  /* The blue is the lodge's long-standing link and button colour; the navy
     pair was the footer. Both are kept so the site still looks like itself. */
  --color-primary: #2b7bb9;
  --color-primary-dark: #1f5a87;
  --color-primary-light: #95bddc;
  --color-navy: #1c3040;
  --color-navy-light: #30536e;

  /* -- Text -------------------------------------------------------------- */
  --color-text: #4a4f55;          /* body copy — darker than the old #757575,
                                     which failed contrast on muted panels */
  --color-heading: #1c2b36;
  --color-text-strong: #2f3640;
  /* #6b7280 passes on white but fails 4.5:1 on --color-surface-muted, which is
     exactly where breadcrumbs sit. This clears it on both. */
  --color-text-muted: #5c6572;
  --color-text-inverse: #ffffff;

  /* -- Surfaces ---------------------------------------------------------- */
  /* Warm off-whites rather than the old cold greys: the photography is all
     green garden and timber, and a blue-grey ground fought it. */
  --color-page: #ffffff;
  --color-surface: #ffffff;
  --color-surface-alt: #fcfbf9;
  --color-surface-muted: #f6f4f0;
  --color-surface-sand: #efe9e0;

  /* -- Borders ----------------------------------------------------------- */
  --color-border: #e4e0d9;
  --color-border-strong: #cfc9bf;
  --color-border-subtle: #eeebe5;

  /* -- Feedback ---------------------------------------------------------- */
  --color-error: #b3261e;
  --color-success: #005b06;

  /* -- Typography -------------------------------------------------------- */
  /* Roboto is served from /assets/fonts — no external font request. */
  --font-body: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-heading: var(--font-body);
  --font-alt: "Helvetica", Verdana, Arial, sans-serif;

  --text-base: 1.0625rem;         /* 17px — the old 16px was tight for prose */
  --text-sm: 0.9375rem;
  --text-xs: 0.8125rem;

  /* Fluid headings: the second value scales, the outer two clamp it. */
  --text-h1: clamp(2.125rem, 1.55rem + 2.6vw, 3.25rem);
  --text-h2: clamp(1.625rem, 1.35rem + 1.25vw, 2.25rem);
  --text-h3: clamp(1.3125rem, 1.18rem + 0.6vw, 1.625rem);
  --text-h4: 1.1875rem;
  --text-h5: 1.0625rem;
  --text-h6: 0.9375rem;

  --leading-body: 1.65;
  --leading-heading: 1.2;
  --weight-normal: 400;
  --weight-medium: 500;
  --weight-bold: 700;
  --tracking-tight: -0.015em;
  --tracking-wide: 0.08em;

  /* -- Spacing scale ----------------------------------------------------- */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 40px;
  --space-2xl: 64px;
  --space-3xl: 96px;

  /* -- Shape and depth --------------------------------------------------- */
  --radius-sm: 2px;
  --radius-md: 6px;
  --radius-lg: 12px;
  --radius-pill: 999px;
  --shadow-sm: 0 1px 2px rgba(28, 43, 54, .06), 0 2px 6px rgba(28, 43, 54, .05);
  --shadow-md: 0 2px 6px rgba(28, 43, 54, .07), 0 10px 24px rgba(28, 43, 54, .08);
  --shadow-lg: 0 18px 48px rgba(28, 43, 54, .16);

  --container-max: 1140px;
  --container-narrow: 720px;      /* prose measure */
  /* The header bar alone runs wider than the content column: eight top-level
     nav items, the logo and the search link do not fit in 1140px. Only in
     force once the bar is shown — see .bpl-header .bpl-container. */
  --container-header: 1280px;
  --header-height: 72px;
  --transition: .2s ease-in-out;

  /* Breakpoints are listed for reference only — custom properties cannot be
     used inside @media queries, so the literals below are repeated in the
     media blocks further down. Keep them in step.
       small  : 576px
       medium : 768px
       large  : 992px
       xlarge : 1200px
     The navigation is the one component that switches at xlarge rather than
     large — see the note above .bpl-nav-toggle for why. */
}

/* 2. RESET AND BASE ======================================================== */
/* This is the layer the mirror could never have: with the page builder gone,
   these selectors are unopposed. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The browser's own [hidden] rule is in the UA stylesheet, so any author rule
   that sets `display` beats it — `.bpl-button { display: inline-flex }` did,
   which would have left the call buttons visible and dead while the softphone
   widget was still deciding whether a call is possible at all. The whole point
   of those buttons shipping with `hidden` is that they appear only once a call
   can actually be placed, so this has to outrank the component that styles
   them. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchored sections must clear the sticky header. */
  scroll-padding-top: calc(var(--header-height) + var(--space-md));
}

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

body {
  margin: 0;
  background-color: var(--color-page);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-normal);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 var(--space-md);
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

h1 { font-size: var(--text-h1); }
h2 { font-size: var(--text-h2); }
h3 { font-size: var(--text-h3); }
h4 { font-size: var(--text-h4); }
h5 { font-size: var(--text-h5); }
h6 { font-size: var(--text-h6); }

p {
  margin: 0 0 var(--space-md);
  text-wrap: pretty;
}

a {
  color: var(--color-primary-dark);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  transition: color var(--transition);
}

a:hover { color: var(--color-primary); }

:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

img, picture, video, svg {
  display: block;
  max-width: 100%;
  height: auto;
}

ul, ol {
  margin: 0 0 var(--space-md);
  padding-left: 1.25em;
}

li { margin-bottom: var(--space-xs); }

blockquote {
  margin: 0 0 var(--space-md);
  padding-left: var(--space-lg);
  border-left: 3px solid var(--color-primary-light);
  color: var(--color-text-strong);
}

hr {
  height: 1px;
  margin-block: var(--space-xl);
  border: 0;
  background-color: var(--color-border);
}

table {
  width: 100%;
  margin-bottom: var(--space-md);
  border-collapse: collapse;
}

th, td {
  padding: var(--space-sm) var(--space-md);
  border-bottom: 1px solid var(--color-border);
  text-align: left;
  vertical-align: top;
}

th { color: var(--color-heading); font-weight: var(--weight-bold); }

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

strong, b { font-weight: var(--weight-bold); color: var(--color-text-strong); }

address { font-style: normal; }

/* 3. LAYOUT ================================================================ */

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

.bpl-container--narrow { max-width: var(--container-narrow); }

.bpl-section {
  padding-block: var(--space-2xl);
}

@media (min-width: 992px) {
  .bpl-section { padding-block: var(--space-3xl); }
}

.bpl-section--tight { padding-block: var(--space-xl); }

.bpl-section--muted {
  background-color: var(--color-surface-muted);
}

.bpl-section--sand {
  background-color: var(--color-surface-sand);
}

.bpl-section--navy {
  background-color: var(--color-navy);
  color: var(--color-text-inverse);
}

.bpl-section--navy h1,
.bpl-section--navy h2,
.bpl-section--navy h3,
.bpl-section--navy h4 {
  color: var(--color-text-inverse);
}

.bpl-section--navy a { color: var(--color-primary-light); }

/* A section's own heading block: centred, measure-limited, with room under. */
.bpl-section__head {
  max-width: 60ch;
  margin-inline: auto;
  margin-bottom: var(--space-xl);
  text-align: center;
}

.bpl-section__head > :last-child { margin-bottom: 0; }

.bpl-section__eyebrow {
  display: block;
  margin-bottom: var(--space-sm);
  color: var(--color-primary-dark);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.bpl-section--navy .bpl-section__eyebrow { color: var(--color-primary-light); }

.bpl-section__lede {
  margin-bottom: 0;
  font-size: 1.125rem;
  color: var(--color-text);
}

.bpl-section--navy .bpl-section__lede { color: rgba(255, 255, 255, .82); }

.bpl-stack > * + * {
  margin-top: var(--space-md);
}

.bpl-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .bpl-grid--2 { grid-template-columns: repeat(2, 1fr); }
  .bpl-grid--3 { grid-template-columns: repeat(2, 1fr); }
  .bpl-grid--4 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 992px) {
  .bpl-grid--3 { grid-template-columns: repeat(3, 1fr); }
  .bpl-grid--4 { grid-template-columns: repeat(4, 1fr); }
}

/* Two columns of unequal weight — copy beside a photograph. */
.bpl-split {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: 1fr;
  align-items: center;
}

@media (min-width: 992px) {
  .bpl-split { grid-template-columns: 1fr 1fr; gap: var(--space-2xl); }
  .bpl-split--reverse > :first-child { order: 2; }
}

/* Two columns of text are never the same height, and centring them leaves the
   shorter one floating against the other's middle. A photograph beside a
   paragraph still reads better centred, which is why this is a modifier and
   not a change to .bpl-split. */
.bpl-split--top { align-items: start; }

.bpl-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  align-items: center;
}

.bpl-actions--center { justify-content: center; }

/* 4. COMPONENTS ============================================================ */

.bpl-button,
.bpl-card,
.bpl-note {
  box-sizing: border-box;
}

/* -- Button ---------------------------------------------------------------- */
.bpl-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  min-height: 48px;                 /* a comfortable touch target */
  padding: var(--space-sm) var(--space-xl);
  border: 1px solid var(--color-primary-dark);
  border-radius: var(--radius-md);
  background-color: var(--color-primary);
  color: var(--color-text-inverse);
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition),
              transform var(--transition);
}

.bpl-button:active { transform: translateY(1px); }

.bpl-button:hover,
.bpl-button:focus-visible {
  background-color: var(--color-primary-dark);
  border-color: var(--color-primary-dark);
  color: var(--color-text-inverse);
  text-decoration: none;
}

/* The darker blue, not --color-primary: #2b7bb9 as button text on white is
   about 4.0:1, which fails AA. The filled button is fine because its text is
   white on that blue. */
.bpl-button--ghost {
  background-color: transparent;
  color: var(--color-primary-dark);
}

.bpl-button--ghost:hover,
.bpl-button--ghost:focus-visible {
  background-color: var(--color-primary);
  color: var(--color-text-inverse);
}

/* The same button on the navy band. It used to take its colour from
   `.bpl-section--navy a`, which is an element selector and so stopped reaching
   the call buttons the day they stopped being links: #1f5a87 on #1c3040 is
   1.86:1, which is not a button, it is a rumour. Said for the component
   instead, so it holds whichever element carries the class. 6.85:1. */
.bpl-section--navy .bpl-button--ghost {
  border-color: var(--color-primary-light);
  color: var(--color-primary-light);
}

.bpl-section--navy .bpl-button--ghost:hover,
.bpl-section--navy .bpl-button--ghost:focus-visible {
  background-color: var(--color-primary-light);
  color: var(--color-navy);
}

/* -- Card ------------------------------------------------------------------ */
.bpl-card {
  display: flex;
  flex-direction: column;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

/* A column, so that .bpl-card__meta can be pushed to the bottom and the
   "→" links line up across a row of cards with unequal copy. */
.bpl-card__body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}

.bpl-card__body {
  padding: var(--space-lg);
}

.bpl-card__title {
  margin: 0 0 var(--space-sm);
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-size: var(--text-h4);
  font-weight: var(--weight-bold);
  line-height: var(--leading-heading);
  transition: color var(--transition);
}

.bpl-card__text {
  margin: 0;
  color: var(--color-text);
  font-family: var(--font-body);
  line-height: var(--leading-body);
}

.bpl-card--featured {
  border-color: var(--color-primary);
  box-shadow: var(--shadow-md);
}

/* -- Note / callout -------------------------------------------------------- */
.bpl-note {
  padding: var(--space-md);
  border-left: 3px solid var(--color-primary);
  background-color: var(--color-surface-muted);
  color: var(--color-text);
}

.bpl-note--error { border-left-color: var(--color-error); }
.bpl-note--success { border-left-color: var(--color-success); }

/* The negotiable-rates line on the room pages. Rates are negotiable by design,
   so the site publishes no price — this says so, which an empty space does not.
   Slightly larger than body copy and measure-limited, because it is the first
   thing a visitor looks for and should read as a statement, not fine print. */
.bpl-note--rates {
  max-width: 62ch;
  margin-block: var(--space-md);
  margin-inline: auto;
  font-size: 1.05rem;
  line-height: var(--leading-body);
}

.bpl-note--rates p {
  margin: 0;
}

/* -- Contact form ---------------------------------------------------------- */
/* Used by /assets/js/contact-form.js on every page. These are the only
   unprefixed class names in this file, because they are ours rather than the
   page builder's. Previously duplicated as an inline <style> on all 54 pages. */
.relay-hp {
  position: absolute !important;
  left: -9000px !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status:empty {
  display: none;
}

.form-status {
  margin: var(--space-sm) 0 0;
  font-family: var(--font-body);
  font-size: .95rem;
  line-height: 1.4;
}

.form-status.is-error { color: #b3261e; }
.form-status.is-success { color: var(--color-success); }

/* -- Online menu ----------------------------------------------------------- */
/* The menu pages wear the site's header, nav and footer, so everything here
   styles only the content between them. They open with a full-bleed photo
   band, as the other pages do. */

/* -- the opening band, matching how the rest of the site opens ------------- */
.bpl-menu-hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 260px;
  padding-block: var(--space-2xl);
  background-color: var(--color-navy);
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  text-align: center;
}

/* A dark wash so the title holds up over any photograph. */
.bpl-menu-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgba(28, 48, 64, .55);
}

.bpl-menu-hero > * {
  position: relative;
}

.bpl-menu-hero .bpl-menu-title {
  color: var(--color-text-inverse);
  text-shadow: 0 1px 3px rgba(0, 0, 0, .45);
}

@media (min-width: 768px) {
  .bpl-menu-hero { min-height: 340px; }
}

.bpl-menu-title {
  margin: 0 0 var(--space-sm);
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-size: var(--text-h1);
  font-weight: var(--weight-normal);
  line-height: var(--leading-heading);
}

.bpl-menu-lede {
  max-width: 60ch;
  margin: 0;
}

/* -- the hub: one tile per section ----------------------------------------- */
.bpl-menu-sections {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: 1fr;
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (min-width: 576px) {
  .bpl-menu-sections { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 992px) {
  .bpl-menu-sections { grid-template-columns: repeat(3, 1fr); }
}

.bpl-menu-section-link {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-heading);
  text-decoration: none;
  transition: border-color var(--transition);
}

/* Each section tile leads with a photo of a dish from it, as the original
   menu's tiles did. */
/* The category pictures are the original site's own tiles and their shapes are
   all over the place — 1.47 (the spirits bottles) to 2.45 (the Western plate)
   against this box's 1.78. Cropping them to fill it took 27% off the width of
   one and 18% off the height of another, cutting the ends off the very thing
   the tile is meant to show. So they are contained rather than cropped: the
   whole picture, shrunk to fit, centred in the box.

   White, not the muted placeholder colour, because every one of the fifteen is
   a product shot on white — so the space contain leaves at the sides or above
   and below reads as part of the photograph rather than as a frame around it. */
.bpl-menu-section-link__media {
  display: block;
  aspect-ratio: 16 / 9;
  max-width: 100%;
  background-color: var(--color-surface);
}

.bpl-menu-section-link__media img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
}

.bpl-menu-section-link__text {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-md) var(--space-lg);
}

.bpl-menu-back {
  margin: 0 0 var(--space-sm);
  font-size: .95rem;
}

/* The back link is a strip beneath the hero, not a section in its own right.
   Given .bpl-section it took a full section's padding, and so did the grid
   under it, which put most of a screen of empty white between a section's
   title and its first dish. */
.bpl-menu-backbar {
  padding-block: var(--space-lg) 0;
}

.bpl-menu-backbar + .bpl-section {
  padding-block-start: var(--space-lg);
}

.bpl-menu-section-link:hover,
.bpl-menu-section-link:focus-visible {
  border-color: var(--color-primary);
  text-decoration: none;
}

.bpl-menu-section-link__name {
  font-size: var(--text-h4);
  line-height: var(--leading-heading);
}

.bpl-menu-section-link__count {
  color: var(--color-text);
  font-size: .875rem;
  font-variant-numeric: tabular-nums;
}

.bpl-menu-section-link.is-empty {
  border-left-color: var(--color-border-strong);
}

/* -- a section's items ----------------------------------------------------- */
.bpl-menu-group + .bpl-menu-group {
  margin-top: var(--space-xl);
}

.bpl-menu-group__title {
  margin: 0;
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  font-weight: var(--weight-normal);
}

/* Same size as the English section heading above it, sharing that rule's
   token rather than repeating its value. Extra leading only, for the tone
   marks. */
.bpl-menu-group__title-th {
  margin: 0;
  color: var(--color-text);
  font-size: var(--text-h3);
  line-height: 1.6;
}

.bpl-menu-group__title,
.bpl-menu-group__title-th {
  padding-bottom: var(--space-xs);
}

.bpl-menu-group__title-th {
  margin-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

/* an ungrouped section still wants the rule under its last heading */
.bpl-menu-group > .bpl-menu-group__title:last-of-type {
  margin-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

.bpl-menu-grid {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
}

@media (min-width: 576px) {
  .bpl-menu-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 992px) {
  .bpl-menu-grid { grid-template-columns: repeat(3, 1fr); }
}

.bpl-menu-item {
  display: flex;
  flex-direction: column;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

/* Contained, not cropped — the same call as the category cards above, for the
   same reason. The dish library is not one consistent shape: mostly 16/9 plated
   photographs, but also bottle shots as tall as 1.11 and as wide as 3.6, and
   cropping those to fill the box cut 51% off a Sangsom bottle and 38% off a San
   Miguel — taking the label off the very thing being sold.

   White, because 129 of the 199 are shot on white and the space contain leaves
   then reads as part of the photograph. The other 70 carry their own colour to
   the edge — brand logos, and a green studio backdrop on the wines and spirits
   — so those sit as a picture on a white card instead. That is the ordinary
   look of a product listing, and it beats cropping the label off. */
.bpl-menu-item__media {
  aspect-ratio: 16 / 9;
  max-width: 100%;
  background-color: var(--color-surface);
}

.bpl-menu-item__media img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
}

.bpl-menu-item__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-md);
}

.bpl-menu-item__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-sm);
}

.bpl-menu-item__name {
  margin: 0;
  color: var(--color-heading);
  font-family: var(--font-heading);
  font-size: var(--text-h4);
  font-weight: var(--weight-normal);
  line-height: var(--leading-heading);
}

.bpl-menu-item__desc {
  margin: 0;
  color: var(--color-text);
  font-size: var(--text-base);
  line-height: 1.6;
}

.bpl-menu-item__price {
  margin: 0;
  color: var(--color-text-strong);
  font-size: var(--text-h4);
  font-variant-numeric: tabular-nums;
}

.bpl-menu-item__prices {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-xs) var(--space-md);
  margin: 0;
}

.bpl-menu-item__prices dt {
  color: var(--color-text);
}

.bpl-menu-item__prices dd {
  margin: 0;
  color: var(--color-text-strong);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* The add-ons a dish can take. Set apart from the dish's own price by a rule
   and a label, because "+฿60" beside "฿180" is otherwise two prices for the
   same thing. The list itself reuses .bpl-menu-item__prices, which is already
   the label-left / price-right grid. */
.bpl-menu-item__extras {
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-border);
}

/* The label the site already uses for a small heading of this kind is the
   section eyebrow: bold, uppercase, tracked. Borrowed here so "Extras" reads
   as a heading for the rows beneath it rather than as another line of copy. */
.bpl-menu-item__extras-title {
  margin: 0 0 var(--space-sm);
  color: var(--color-heading);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.bpl-menu-item__extras .bpl-menu-item__prices {
  font-size: var(--text-sm);
}

.bpl-menu-item__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: auto;
  padding-top: var(--space-sm);
  border-top: 1px solid var(--color-border);
}

/* The item code is how a guest actually orders, so it stays legible rather
   than being treated as metadata. */
.bpl-menu-item__code {
  color: var(--color-text);
  font-family: var(--font-alt);
  font-size: .875rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
}

.bpl-menu-badge,
.bpl-menu-status {
  display: inline-block;
  padding: 2px var(--space-sm);
  border-radius: var(--radius-pill);
  font-size: .75rem;
  line-height: 1.6;
  white-space: nowrap;
}

.bpl-menu-badge--veg {
  background-color: var(--color-surface-muted);
  border: 1px solid var(--color-success);
  color: var(--color-success);
}

.bpl-menu-item.is-available .bpl-menu-status {
  background-color: var(--color-success);
  color: var(--color-text-inverse);
}

.bpl-menu-item.is-unavailable .bpl-menu-status {
  background-color: var(--color-error);
  color: var(--color-text-inverse);
}

/* Out of stock is dimmed but never hidden — a guest looking for a dish should
   find it and see that it is off, rather than wonder if they misremembered. */
.bpl-menu-item.is-unavailable .bpl-menu-item__media,
.bpl-menu-item.is-unavailable .bpl-menu-item__name,
.bpl-menu-item.is-unavailable .bpl-menu-item__price,
.bpl-menu-item.is-unavailable .bpl-menu-item__prices {
  opacity: .55;
}

.bpl-menu-loading,
.bpl-menu-empty {
  margin: 0;
  padding: var(--space-lg);
  background-color: var(--color-surface);
  border: 1px dashed var(--color-border-strong);
  border-radius: var(--radius-md);
  color: var(--color-text);
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .bpl-menu-section-link { transition: none; }
}

/* >>> menu hero backgrounds — generated by tools/build-menu-section-pages.js */
.bpl-menu-hero--breakfast {
  background-image: url('/online-menu/images/healthy-breakfast.webp');
}

.bpl-menu-hero--singledishes {
  background-image: url('/online-menu/images/kao-soi.webp');
}

.bpl-menu-hero--appetizers {
  background-image: url('/online-menu/images/deep-fried-shrimp-rolls.webp');
}

.bpl-menu-hero--stirfries {
  background-image: url('/online-menu/images/pud-thai.webp');
}

.bpl-menu-hero--curries-soups {
  background-image: url('/online-menu/images/gang-hang-leh.webp');
}

.bpl-menu-hero--salad {
  background-image: url('/online-menu/images/laab-isaan.webp');
}

.bpl-menu-hero--western {
  background-image: url('/online-menu/images/chicken-and-mushroom-sauce-with-fries.webp');
}

.bpl-menu-hero--burgers-hotdogs-sandwiches {
  background-image: url('/online-menu/images/100-beef-burger.webp');
}

.bpl-menu-hero--dessert {
  background-image: url('/online-menu/images/fried-banana.webp');
}

.bpl-menu-hero--fruitshakes-smoothies-juices {
  background-image: url('/online-menu/images/mango-shake.webp');
}

.bpl-menu-hero--softdrinks {
  background-image: url('/online-menu/images/water.webp');
}

.bpl-menu-hero--tea-coffee-chocolate {
  background-image: url('/online-menu/images/cappuccino.webp');
}

.bpl-menu-hero--beer-wine-ciders {
  background-image: url('/online-menu/images/craft-beer-lager.webp');
}

.bpl-menu-hero--spirits-liquers {
  background-image: url('/online-menu/images/irish-coffee.webp');
}

.bpl-menu-hero--shooters-cocktails-mocktails {
  background-image: url('/online-menu/images/blueberry-smoothie.webp');
}
/* <<< end menu hero backgrounds */

/* -- Closure notice banner ------------------------------------------------- */
/* A full-bleed band that sits at the top of the page content, above the hero,
   to announce a temporary closure. Navy so it reads as part of the site rather
   than as a browser warning, with the theme's own orange as the accent edge.
   Static by design — no dismiss control, so the notice cannot be missed on a
   second visit during the closure. Delete the markup once the dates pass. */
.bpl-notice {
  box-sizing: border-box;
  border-top: 4px solid var(--color-error);
  background-color: var(--color-navy);
  color: var(--color-text-inverse);
  font-family: var(--font-body);
  line-height: var(--leading-body);
}

.bpl-notice-inner {
  display: flex;
  gap: var(--space-md);
  align-items: flex-start;
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-md);
  padding-block: var(--space-lg);
}

.bpl-notice-icon {
  flex: none;
  width: 28px;
  height: 28px;
  margin-top: 2px;
  fill: var(--color-error);
}

.bpl-notice-body {
  min-width: 0;
}

/* The title is a paragraph, not a heading: the banner sits above the page h1,
   so a heading here would open the outline at the wrong level. The section is
   still reachable as a named region via aria-labelledby. */
.bpl-notice-title {
  margin: 0;
  color: var(--color-text-inverse);
  font-family: var(--font-heading);
  font-size: var(--text-h4);
  font-weight: var(--weight-bold);
  line-height: var(--leading-heading);
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* The two dated facts. A definition list keeps the label/value pairing in the
   markup itself, so it still reads correctly with styles off or to a screen
   reader; the grid only lays out what the list already says. */
.bpl-notice-dates {
  display: grid;
  gap: var(--space-xs) var(--space-sm);
  grid-template-columns: max-content 1fr;
  margin: var(--space-md) 0 0;
  font-size: var(--text-base);
}

.bpl-notice-dates dt {
  font-weight: var(--weight-bold);
}

.bpl-notice-dates dd {
  margin: 0;
}

.bpl-notice-apology {
  margin: var(--space-md) 0 0;
  color: var(--color-primary-light);
  font-size: 15px;
}

@media (min-width: 768px) {
  .bpl-notice-inner {
    gap: var(--space-lg);
    padding-block: var(--space-xl);
  }

  .bpl-notice-title {
    font-size: var(--text-h3);
  }

  /* Wide enough for the two pairs to sit side by side on one row. */
  .bpl-notice-dates {
    grid-template-columns: repeat(2, max-content 1fr);
    gap: var(--space-sm) var(--space-lg);
  }
}

/* 5. UTILITIES ============================================================= */

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

.bpl-text-center { text-align: center; }
.bpl-text-primary { color: var(--color-primary); }
.bpl-text-muted { color: var(--color-text); }
.bpl-bg-surface { background-color: var(--color-surface); }
.bpl-bg-navy { background-color: var(--color-navy); color: var(--color-text-inverse); }
.bpl-mt-0 { margin-top: 0; }
.bpl-mb-0 { margin-bottom: 0; }
.bpl-mt-xl { margin-top: var(--space-xl); }
.bpl-center { margin-inline: auto; }

/* 6. SITE CHROME =========================================================== */
/* Header, navigation and footer. Rendered from tools/partials/ into every
   page at build time, so these class names appear on every document. */

.bpl-skip {
  position: absolute;
  left: var(--space-md);
  top: var(--space-md);
  z-index: 200;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  background-color: var(--color-navy);
  color: var(--color-text-inverse);
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform var(--transition);
}

.bpl-skip:focus {
  transform: translateY(0);
  color: var(--color-text-inverse);
}

/* -- Header ---------------------------------------------------------------- */

.bpl-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background-color: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.bpl-header__inner {
  display: flex;
  /* The open drawer is a full-width row beneath the logo, so the row has to be
     allowed to wrap. Without this the nav stayed in the top row and flex shrank
     it to a 128px column pinned to the right — every label broke across two or
     three lines while two thirds of the screen sat empty. Above the nav
     breakpoint the bar is width:auto and fits on the first row regardless;
     tests/nav.spec.js holds it to that. */
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md);
  min-height: var(--header-height);
}

/* Only once the nav bar is shown, and only for the header: below this the
   header holds a logo, a search link and a button, which fit in 1140px and
   line up with the content column. Widening it at every width would push the
   logo out of line with the page beneath it for no gain. */
@media (min-width: 1280px) {
  .bpl-header .bpl-container { max-width: var(--container-header); }
}

.bpl-logo {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  margin-right: auto;
}

.bpl-logo img {
  width: auto;
  height: 40px;
}

@media (min-width: 992px) {
  .bpl-logo img { height: 46px; }
}

/* Search, as its own cluster. A tighter gap than the bar's own, because the
   bar has no spare room: see the note in partials/header.html and the width
   budget in tests/nav.spec.js. */
.bpl-header__actions {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
}

/* The header's search affordance is an icon linking to /search/, not a field.
   The bar has no room for an input a guest could actually type into — see the
   width budget in the note above .bpl-nav-toggle, and tests/nav.spec.js, which
   fails if this grows enough to wrap a nav label. 40px square, so it is still
   a real target on a phone. */
.bpl-header__search {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  color: var(--color-heading);
  text-decoration: none;
  transition: color var(--transition), border-color var(--transition);
}

.bpl-header__search:hover,
.bpl-header__search:focus-visible {
  color: var(--color-primary);
  border-color: var(--color-primary);
}

.bpl-nav-toggle {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: transparent;
  color: var(--color-heading);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
}

.bpl-nav-toggle__bars {
  position: relative;
  display: block;
  width: 18px;
  height: 2px;
  background-color: currentColor;
}

.bpl-nav-toggle__bars::before,
.bpl-nav-toggle__bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 18px;
  height: 2px;
  background-color: currentColor;
}

.bpl-nav-toggle__bars::before { top: -6px; }
.bpl-nav-toggle__bars::after { top: 6px; }

/* The navigation switches at 1280px, not at the 992px the rest of the site
   uses. Eight top-level items plus the logo and the search link need 795px of
   bar, and a 1140px container leaves only 723px between them — a limit set by
   the container, not the viewport, so it bit at 1920px exactly as hard as at
   1200px and the labels wrapped onto two lines. The header gets its own wider
   container above, and the bar appears only once that container is at full
   width. Below here the button and its drawer do the job with room to spare.
   1280 is repeated in the two blocks below and in assets/js/site.js. */
@media (min-width: 1280px) {
  .bpl-nav-toggle { display: none; }
}

/* -- Navigation ------------------------------------------------------------ */

.bpl-nav {
  display: none;
  width: 100%;
  /* Last on the wrapped row, so the open drawer lands BELOW the logo, the
     search icon and the toggle rather than between them.

     Without this the flex lines pack in DOM order — logo, then a 100%-wide
     nav, then search and toggle — which put the toggle on a third line
     underneath the whole menu. Measured on a Pixel 7: with the drawer open the
     toggle sat at y=986 in an 839px viewport, i.e. off the bottom of the
     screen. You tapped a button to open the menu and the button to close it
     was gone. Ordering the nav last makes the open row identical to the closed
     one, which is where the hand already is.

     It is a visual reorder only; the markup is untouched, because the nav has
     to stay before the search link and the toggle in the DOM for the reading
     and tab order to make sense. Reset above the nav breakpoint, where the bar
     sits between the logo and the search as it always has. */
  order: 1;
}

.bpl-nav.is-open {
  display: block;
  padding-bottom: var(--space-md);
}

.bpl-nav__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.bpl-nav__item { margin: 0; position: relative; }

.bpl-nav__link {
  display: block;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  color: var(--color-heading);
  font-weight: var(--weight-medium);
  text-decoration: none;
}

.bpl-nav__link:hover,
.bpl-nav__link:focus-visible {
  background-color: var(--color-surface-muted);
  color: var(--color-primary-dark);
}

.bpl-nav__link[aria-current="page"] {
  color: var(--color-primary-dark);
  box-shadow: inset 3px 0 0 var(--color-primary);
}

/* Contact, last in the bar. In the stacked menu it is a full-width row like
   any other link; in the bar it is the only item wearing a filled background,
   and it takes wider side padding there so the fill reads as a button rather
   than as highlighted text — see the rule in the 1280px block below. */
.bpl-nav__link--cta,
.bpl-nav__link--cta[aria-current="page"] {
  background-color: var(--color-primary);
  color: #fff;
  box-shadow: none;
}

.bpl-nav__link--cta:hover,
.bpl-nav__link--cta:focus-visible {
  background-color: var(--color-primary-dark);
  color: #fff;
}

/* Sub-navigation. On small screens it is always expanded — a nested
   disclosure inside an already-open menu is more chrome than it is worth. */
.bpl-nav__sub {
  margin: 0 0 var(--space-sm) var(--space-md);
  padding: 0;
  list-style: none;
}

.bpl-nav__sub .bpl-nav__link {
  color: var(--color-text);
  font-size: var(--text-sm);
  font-weight: var(--weight-normal);
}

@media (min-width: 1280px) {
  .bpl-nav {
    display: block;
    width: auto;
    margin-right: var(--space-lg);
    /* Back between the logo and the search cluster. The order above is only
       for the wrapped, drawer layout below this breakpoint. */
    order: 0;
  }

  .bpl-nav.is-open { padding-bottom: 0; }

  .bpl-nav__list {
    flex-direction: row;
    align-items: center;
    gap: var(--space-xs);
  }

  .bpl-nav__link { padding: var(--space-sm) var(--space-sm); }

  /* Twice the side padding of a plain link. At 8px the fill sat hard against
     the word on both sides and read as a highlight; the bar has 237px spare at
     the breakpoint, so the 16px this costs is inside the budget
     tests/nav.spec.js holds. */
  .bpl-nav__link--cta { padding-inline: var(--space-md); }

  /* The current page is marked with an underline, which means squaring the
     link off so the rule sits flush. The Contact pill is excluded: it is a
     filled button, and squaring it turned it into a rectangle with a second
     line under it on the one page it matters most.

     :not() is what does it rather than a later override, because
     `.bpl-nav__link--cta[aria-current="page"]` and
     `.bpl-nav__link[aria-current="page"]` have identical specificity — the
     `box-shadow: none` up at the pill's own rule was already losing this
     fight on source order alone, silently. */
  .bpl-nav__link[aria-current="page"]:not(.bpl-nav__link--cta) {
    box-shadow: inset 0 -2px 0 var(--color-primary);
    border-radius: 0;
  }

  /* Desktop dropdown: hidden until the item is hovered or something inside
     it has focus, so it is reachable by keyboard without any JavaScript. */
  .bpl-nav__sub {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 10;
    display: none;
    min-width: 230px;
    margin: 0;
    padding: var(--space-sm);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-md);
  }

  .bpl-nav__item:hover > .bpl-nav__sub,
  .bpl-nav__item:focus-within > .bpl-nav__sub {
    display: block;
  }

  /* The parent items carried a small chevron here. It is gone on the owner's
     instruction: the sub-menus still open on hover and focus, and the class
     stays because build-site.js and the sub-menu rules above both key off it. */
}

/* -- Footer ---------------------------------------------------------------- */

.bpl-footer {
  background-color: var(--color-navy);
  color: rgba(255, 255, 255, .78);
}

.bpl-footer a { color: #ffffff; }
.bpl-footer a:hover { color: var(--color-primary-light); }

.bpl-footer h2,
.bpl-footer h3 {
  margin-bottom: var(--space-md);
  color: var(--color-text-inverse);
  font-size: var(--text-h4);
}

.bpl-footer__grid {
  display: grid;
  gap: var(--space-xl);
  grid-template-columns: 1fr;
  padding-block: var(--space-2xl);
}

@media (min-width: 768px) {
  .bpl-footer__grid { grid-template-columns: 1fr 1fr; }
}

@media (min-width: 992px) {
  .bpl-footer__grid { grid-template-columns: 1.2fr 1fr 1fr; gap: var(--space-2xl); }
}

.bpl-footer__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.bpl-footer__list li { margin-bottom: var(--space-sm); }

.bpl-footer__bottom {
  padding-block: var(--space-md);
  border-top: 1px solid rgba(255, 255, 255, .14);
  font-size: var(--text-sm);
}

.bpl-footer__bottom .bpl-container {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-lg);
  justify-content: space-between;
}

.bpl-footer__bottom p { margin: 0; }

/* 7. PAGE FURNITURE ======================================================== */

/* -- Hero ------------------------------------------------------------------ */

.bpl-hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 420px;
  padding-block: var(--space-2xl);
  background-color: var(--color-navy);
  color: var(--color-text-inverse);
  isolation: isolate;
  overflow: hidden;
}

.bpl-hero--tall { min-height: min(78vh, 680px); }

.bpl-hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* For a photograph whose subject sits above its middle. A 420px band crops
   hard from the centre of a tall frame: on the pool shot that threw away the
   pool edge, the tables and the building and kept 1920x420 of open water,
   which read as an abstract blue field rather than as a pool. Which slice is
   the right one is a property of the picture, so look at the rendered crop
   before putting this on a hero — it is not a default. */
.bpl-hero__media--high { object-position: 50% 15%; }

/* Dark at the foot, thinner at the head, so the photograph survives and the
   text still passes contrast over it.

   These stops used to be much lighter — .25 at 72% and .15 at the top — which
   only worked because the hero then held a dark, dusky collage. Put a bright
   midday photograph behind it and the eyebrow measured 1.6:1 on a phone
   against a 4.5:1 requirement: the gradient was tuned to one picture rather
   than to the job. A hero has to hold up over whatever photograph it is given,
   so every stop here is heavy enough for a daylit one, checked against the
   rendered pixels rather than by eye. .bpl-hero--post overrides this with a
   heavier profile again, for a shorter band and twelve arbitrary pictures. */
.bpl-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to top,
    rgba(15, 26, 34, .9) 0%,
    rgba(15, 26, 34, .82) 40%,
    rgba(15, 26, 34, .7) 70%,
    rgba(15, 26, 34, .56) 100%
  );
}

.bpl-hero__inner { max-width: 44rem; }

.bpl-hero__eyebrow {
  display: block;
  margin-bottom: var(--space-md);
  color: #ffffff;
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.bpl-hero__title {
  margin-bottom: var(--space-md);
  color: var(--color-text-inverse);
  text-shadow: 0 1px 24px rgba(0, 0, 0, .35);
}

.bpl-hero__lede {
  margin-bottom: var(--space-xl);
  color: rgba(255, 255, 255, .92);
  font-size: clamp(1.0625rem, 1rem + .4vw, 1.3125rem);
  text-shadow: 0 1px 16px rgba(0, 0, 0, .3);
}

.bpl-hero__lede > :last-child { margin-bottom: 0; }

/* The homepage hero puts buttons under its lede, and that gap separates the
   two. An interior page's hero ends on the lede, where the same gap is just
   dead space hanging below the last line. */
.bpl-hero__inner > .bpl-hero__lede:last-child { margin-bottom: 0; }

/* A hero button has to hold its own over a photograph. */
.bpl-hero .bpl-button--ghost {
  border-color: rgba(255, 255, 255, .8);
  background-color: rgba(255, 255, 255, .12);
  color: #ffffff;
  backdrop-filter: blur(2px);
}

.bpl-hero .bpl-button--ghost:hover,
.bpl-hero .bpl-button--ghost:focus-visible {
  background-color: #ffffff;
  border-color: #ffffff;
  color: var(--color-heading);
}

/* A blog post's head, over the post's own photograph. The articles used to
   carry their photographs as .bpl-gallery blocks inside .bpl-prose, where a
   four-column collage in a narrow column dealt two images as two small squares
   against an empty half. The one worth keeping runs here instead, at the size
   a lead photograph wants. Shorter than the homepage's hero, because it heads
   an article rather than making the case for the place. */
.bpl-hero--post {
  min-height: 300px;
  padding-block: var(--space-xl);
}

@media (min-width: 768px) {
  .bpl-hero--post { min-height: 380px; }
}

/* The h2 step, not the h1 one: at --text-h1 a title as long as "San Kamphaeng
   Hot Springs" took four lines of the photograph on a phone. */
.bpl-hero--post .bpl-hero__title {
  font-size: var(--text-h2);
}

/* Its own wash, because the band is short. The homepage hero is one chosen
   photograph 680px tall, so its text sits deep in the dark end of the
   gradient. These are twelve arbitrary photographs in a band barely 380px
   tall — waterfalls, bright foliage, white sky — and the breadcrumb rides near
   the top of it, where the wash is thinnest.

   The top two stops used to be .46 and .54, set by measuring the worst
   photograph in the set, and they did not survive a phone:
   tests/hero-contrast.spec.js put the breadcrumb at 4.33:1 on
   /alpine-golf-course/, 4.39:1 on /muang-on-cave/ and 4.48:1 on
   /san-kamphaeng-hot-springs/, against the 4.5:1 that 15px white needs. A
   phone crops a landscape picture to its middle, and in those three that is
   where the bright water and sky sit, so the pixel behind the breadcrumb is
   lighter there than in any desktop crop of the same file. Measuring one
   viewport is what missed it.

   Raised until the worst of the twelve has real room: the tightest is now
   /san-kamphaeng-hot-springs/ at 5.46:1 on a phone, and the rest are 5.8:1 or
   better. The photographs still read as photographs — checked on the rendered
   band rather than trusting the number. The spec measures every hero at both
   viewports, so the next picture that does not clear it fails by name. */
.bpl-hero--post::after {
  background: linear-gradient(
    to top,
    rgba(15, 26, 34, .88) 0%,
    rgba(15, 26, 34, .78) 40%,
    rgba(15, 26, 34, .70) 75%,
    rgba(15, 26, 34, .65) 100%
  );
}

/* The breadcrumb came up with the head, so it needs the light treatment the
   interior band never had to give it. */
.bpl-hero .bpl-breadcrumb { color: rgba(255, 255, 255, .82); }

.bpl-hero .bpl-breadcrumb a { color: #ffffff; }

.bpl-hero .bpl-breadcrumb li + li::before { color: rgba(255, 255, 255, .55); }

/* -- Interior page head ---------------------------------------------------- */

.bpl-pagehead {
  padding-block: var(--space-2xl);
  background-color: var(--color-surface-muted);
  border-bottom: 1px solid var(--color-border);
}

.bpl-pagehead__inner { max-width: 52rem; }

.bpl-pagehead h1 { margin-bottom: var(--space-md); }

.bpl-pagehead__lede {
  margin-bottom: 0;
  font-size: 1.1875rem;
  color: var(--color-text);
}

.bpl-breadcrumb {
  margin-bottom: var(--space-md);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.bpl-breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.bpl-breadcrumb li { margin: 0; }

.bpl-breadcrumb li + li::before {
  content: "/";
  margin-right: var(--space-sm);
  color: var(--color-border-strong);
}

/* -- Prose ----------------------------------------------------------------- */
/* Long-form body content on the hub and information pages. */

.bpl-prose {
  max-width: var(--container-narrow);
}

.bpl-prose > h2 { margin-top: var(--space-2xl); }
.bpl-prose > h3 { margin-top: var(--space-xl); }
.bpl-prose > :first-child { margin-top: 0; }
.bpl-prose > :last-child { margin-bottom: 0; }

.bpl-prose--wide { max-width: 62rem; }

.bpl-lead {
  font-size: 1.1875rem;
  color: var(--color-text-strong);
}

/* -- Media ----------------------------------------------------------------- */

.bpl-figure {
  margin: 0 0 var(--space-lg);
}

.bpl-figure img {
  width: 100%;
  border-radius: var(--radius-lg);
}

.bpl-figure figcaption {
  margin-top: var(--space-sm);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* A photograph in a band. The library is a mix of landscape and portrait —
   some of it 2:3 — so nothing here may depend on an image's own proportions:
   an unconstrained portrait ran a screen and a half on a phone before the copy
   it belonged to even started.

   The box carries the shape and the crop; the image fills it. overflow:hidden
   is what keeps the crop inside the rounded corner, and because the box is a
   grid child it cannot reach past the container's padding or the page gutter
   however the picture is shaped. */
.bpl-media {
  aspect-ratio: 3 / 2;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  background-color: var(--color-surface-muted);
}

.bpl-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 992px) {
  /* Beside a column of text, the photograph is as tall as the band and no
     taller: it stretches to the row rather than setting it, so the words
     decide the height and the picture is cropped to match. Taking the image
     out of flow is what stops it pushing the row taller than the copy needs —
     without that, height:100% resolves against its own natural height and the
     two columns argue.

     The floor is for a band of three or four lines, where a photograph
     stretched to the text alone would be a letterbox strip. */
  .bpl-split > .bpl-media {
    aspect-ratio: auto;
    align-self: stretch;
    position: relative;
    min-height: 22rem;
  }

  .bpl-split > .bpl-media img {
    position: absolute;
    inset: 0;
  }

  /* On its own, across the full width of the container, a photograph has no
     column to match, so it takes a wider crop than the 3:2 a phone gets —
     1232px of 3:2 is 821px tall, which is most of a laptop screen for one
     picture. */
  .bpl-container > .bpl-media {
    aspect-ratio: 16 / 9;
  }
}

.bpl-card__media {
  aspect-ratio: 3 / 2;
  background-color: var(--color-surface-muted);
}

.bpl-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A card that is entirely a link. */
.bpl-card--link { display: block; text-decoration: none; color: inherit; transition: transform var(--transition), box-shadow var(--transition); }
.bpl-card--link:hover,
.bpl-card--link:focus-visible { transform: translateY(-2px); box-shadow: var(--shadow-md); color: inherit; }
.bpl-card--link:hover .bpl-card__title { color: var(--color-primary-dark); }

.bpl-card__meta {
  margin-top: auto;
  padding-top: var(--space-md);
  color: var(--color-primary-dark);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

/* -- Gallery --------------------------------------------------------------- */

.bpl-gallery {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 768px) { .bpl-gallery { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1200px) { .bpl-gallery { grid-template-columns: repeat(4, 1fr); } }

/* A four-photograph collage that stands where a single photograph would — in
   one column of a .bpl-split. Two columns at every width, because the widening
   grid above would deal a set of four as 3+1 on a tablet, and half a row of one
   reads as a mistake rather than a composition. Declared after those two media
   queries so it outranks them on source order alone. */
.bpl-gallery--2 { grid-template-columns: repeat(2, 1fr); }

.bpl-gallery img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-md);
}

/* -- Facts list ------------------------------------------------------------ */
/* Amenity and room-fact lists: a tick, a fact, no prose. */

.bpl-facts {
  display: grid;
  gap: var(--space-sm) var(--space-lg);
  margin: 0 0 var(--space-lg);
  padding: 0;
  list-style: none;
  grid-template-columns: 1fr;
}

@media (min-width: 576px) { .bpl-facts--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 768px) { .bpl-facts--3 { grid-template-columns: repeat(3, 1fr); } }

.bpl-facts li {
  position: relative;
  margin: 0;
  padding-left: 1.6em;
}

.bpl-facts li::before {
  content: "";
  position: absolute;
  left: .25em;
  top: .5em;
  width: .45em;
  height: .8em;
  border-right: 2px solid var(--color-primary);
  border-bottom: 2px solid var(--color-primary);
  transform: rotate(40deg);
}

/* -- Questions and answers ------------------------------------------------- */
/* The honest-answers block. A <dl>, because that is what it is. */

.bpl-qa {
  max-width: 46rem;
  margin: 0;
}

.bpl-qa > div + div {
  margin-top: var(--space-lg);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--color-border);
}

.bpl-qa dt {
  margin-bottom: var(--space-xs);
  color: var(--color-heading);
  font-size: var(--text-h4);
  font-weight: var(--weight-bold);
}

.bpl-qa dd { margin: 0; }
.bpl-qa dd > :last-child { margin-bottom: 0; }

/* -- Testimonial ----------------------------------------------------------- */

.bpl-quote {
  margin: 0;
  padding: var(--space-lg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background-color: var(--color-surface);
}

.bpl-quote blockquote {
  margin: 0 0 var(--space-md);
  padding: 0;
  border: 0;
  color: var(--color-text-strong);
}

.bpl-quote blockquote > :last-child { margin-bottom: 0; }

.bpl-quote figcaption {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

/* -- Price tag ------------------------------------------------------------- */
/* The day-visitor fee is the only published price on the site. It gets to
   look like a price. */

.bpl-price {
  display: block;
  margin-bottom: var(--space-sm);
  color: var(--color-heading);
  font-size: clamp(1.75rem, 1.5rem + 1vw, 2.25rem);
  font-weight: var(--weight-bold);
  line-height: 1.1;
  letter-spacing: var(--tracking-tight);
}

.bpl-price small {
  display: block;
  margin-top: var(--space-xs);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-normal);
  letter-spacing: normal;
}

/* No page prices anything in figures at the moment — room rates are quoted by
   enquiry and the day-visitor fee was taken off the site — so this currently
   stands only on /style-sheet/. The navy override stays with it: the component
   was written for a navy panel, where heading-coloured text is unreadable. */
.bpl-section--navy .bpl-price { color: var(--color-text-inverse); }
.bpl-section--navy .bpl-price small { color: rgba(255, 255, 255, .8); }

/* -- Forms ----------------------------------------------------------------- */
/* One form component, used by the footer enquiry form and /contact-us/.
   The old markup came from a forms plugin that styled itself; this is the
   replacement, and it is the only form styling on the site. */

.bpl-form__row {
  margin-bottom: var(--space-md);
}

.bpl-form label {
  display: block;
  margin-bottom: var(--space-xs);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

/* A real colour, not opacity: dimming the label to 70% drops it under 4.5:1
   against white, and "optional" is exactly the word someone needs to read. */
.bpl-form__optional {
  font-weight: var(--weight-normal);
  color: var(--color-text-muted);
}

.bpl-footer .bpl-form__optional { color: rgba(255, 255, 255, .75); }

.bpl-form input,
.bpl-form textarea {
  width: 100%;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: var(--color-surface);
  color: var(--color-text);
  font-size: var(--text-base);
  transition: border-color var(--transition), box-shadow var(--transition);
}

.bpl-form textarea { resize: vertical; min-height: 7rem; }

.bpl-form input:focus,
.bpl-form textarea:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(43, 123, 185, .18);
  outline: none;
}

/* The line under the submit button pointing at /privacy/. Small and quiet: it
   is a disclosure, not a selling point, and it sits on every page because the
   enquiry band does. */
.bpl-form__privacy {
  margin: var(--space-md) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.bpl-form button[type="submit"] { width: 100%; }

@media (min-width: 576px) {
  .bpl-form button[type="submit"] { width: auto; }
}

/* The footer form sits on navy. */
.bpl-footer .bpl-form label { color: rgba(255, 255, 255, .88); }

.bpl-footer .bpl-form input,
.bpl-footer .bpl-form textarea {
  border-color: rgba(255, 255, 255, .28);
  background-color: rgba(255, 255, 255, .08);
  color: #ffffff;
}

.bpl-footer .bpl-form input::placeholder,
.bpl-footer .bpl-form textarea::placeholder { color: rgba(255, 255, 255, .55); }

.bpl-footer .bpl-form input:focus,
.bpl-footer .bpl-form textarea:focus {
  border-color: var(--color-primary-light);
  background-color: rgba(255, 255, 255, .14);
}

.bpl-footer .form-status.is-error { color: #ffb4ab; }
.bpl-footer .form-status.is-success { color: #9be39f; }

/* -- Search ---------------------------------------------------------------- */
/* The /search/ page only. The header carries an icon linking here, and the
   results are drawn by assets/js/search-page.js out of the Pagefind index in
   /pagefind/ — see tools/build-search-index.js for how that is built. */

.bpl-search-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  max-width: 36rem;
  margin-bottom: 0;
}

/* Matches .bpl-form's fields rather than inheriting from them: this form is
   not a .bpl-form — it posts nothing, it has one field, and it needs to grow
   into the row rather than fill it. */
.bpl-search-form__input {
  flex: 1 1 14rem;
  min-width: 0;
  padding: var(--space-sm) var(--space-md);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: var(--color-surface);
  color: var(--color-text);
  font-family: inherit;
  font-size: var(--text-base);
  transition: border-color var(--transition), box-shadow var(--transition);
}

.bpl-search-form__input:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(43, 123, 185, .18);
  outline: none;
}

/* One column on a phone, with the filters above the results: the section
   counts are the thing that tells a guest whether to bother scrolling. */
.bpl-search-layout {
  display: grid;
  gap: var(--space-xl);
}

@media (min-width: 992px) {
  .bpl-search-layout {
    grid-template-columns: 15rem minmax(0, 1fr);
    align-items: start;
  }
}

.bpl-search-filters__heading {
  margin-bottom: var(--space-sm);
  font-size: var(--text-h6);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.bpl-search-filters__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.bpl-search-filters__item {
  margin: 0;
}

.bpl-search-filters__item label {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) 0;
  cursor: pointer;
}

/* Dimmed, not hidden and not disabled: a section with nothing in it for this
   query still has to be visible for the guest to untick it again. */
.bpl-search-filters__item.is-empty .bpl-search-filters__label {
  color: var(--color-text-muted);
}

.bpl-search-filters__count {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.bpl-search-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-md);
  margin-bottom: var(--space-lg);
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--color-border);
}

.bpl-search-status {
  margin: 0;
  color: var(--color-text-muted);
}

.bpl-search-sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.bpl-search-sort select {
  padding: var(--space-xs) var(--space-sm);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background-color: var(--color-surface);
  color: var(--color-text);
  font-family: inherit;
  font-size: var(--text-sm);
}

.bpl-search-results {
  display: grid;
  gap: var(--space-md);
}

.bpl-search-result {
  display: block;
  padding: var(--space-lg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background-color: var(--color-surface);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.bpl-search-result:hover,
.bpl-search-result:focus-visible {
  border-color: var(--color-primary-light);
  box-shadow: var(--shadow-sm);
}

.bpl-search-result__title {
  margin-bottom: var(--space-xs);
  font-size: var(--text-h4);
  color: var(--color-primary);
}

.bpl-search-result__meta {
  margin-bottom: var(--space-sm);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.bpl-search-result__excerpt {
  margin-bottom: 0;
  color: var(--color-text);
}

/* Pagefind marks the matched words. A yellow highlight on this palette reads
   as a defect, so the emphasis is weight and the brand blue instead. */
.bpl-search-result__excerpt mark {
  background-color: transparent;
  color: var(--color-heading);
  font-weight: var(--weight-bold);
}

/* 8. STYLE SHEET PAGE ====================================================== */
/* Presentation for /style-sheet/ — the living reference for everything above.
   It is the one page that has to *show* a token rather than use it, and a
   swatch cannot carry its colour in a style attribute: verify-site.js fails
   the build on one, and rightly. So each chip names its token in a class.

   This block ships to every page for the sake of one hidden page, which is a
   real if small cost — about 90 lines of a 2,000-line file. The alternative is
   a third stylesheet, and the rule that a page loads exactly fonts.css and
   main.css is worth more than those lines: it is what proves no page builder
   ever comes back. Delete this section and /style-sheet/ together, or not at
   all. */

.bpl-specimen {
  margin-bottom: var(--space-xl);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.bpl-specimen__label {
  display: block;
  padding: var(--space-xs) var(--space-md);
  border-bottom: 1px solid var(--color-border);
  background-color: var(--color-surface-muted);
  font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* Specimens sit on white whatever band they land in — a component shown
   against a muted section is a component shown wrong. */
.bpl-specimen__body {
  padding: var(--space-lg);
  background-color: var(--color-surface);
}

/* A specimen of something that paints its own full-bleed background — a
   section modifier, a hero — has no padding of its own to fight with. */
.bpl-specimen__body--flush {
  padding: 0;
}

.bpl-swatches {
  display: grid;
  margin-bottom: var(--space-xl);
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: var(--space-md);
  padding: 0;
  list-style: none;
}

.bpl-swatch { margin: 0; }

.bpl-swatch__chip {
  display: block;
  height: 3.5rem;
  margin-bottom: var(--space-xs);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background-color: var(--swatch, transparent);
}

.bpl-swatch__name {
  display: block;
  font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

/* One line per token. Tedious, and the point: a token added above with no line
   here is simply missing from the reference, which is visible at a glance. */
.bpl-swatch--primary       { --swatch: var(--color-primary); }
.bpl-swatch--primary-dark  { --swatch: var(--color-primary-dark); }
.bpl-swatch--primary-light { --swatch: var(--color-primary-light); }
.bpl-swatch--navy          { --swatch: var(--color-navy); }
.bpl-swatch--navy-light    { --swatch: var(--color-navy-light); }
.bpl-swatch--text          { --swatch: var(--color-text); }
.bpl-swatch--heading       { --swatch: var(--color-heading); }
.bpl-swatch--text-strong   { --swatch: var(--color-text-strong); }
.bpl-swatch--text-muted    { --swatch: var(--color-text-muted); }
.bpl-swatch--page          { --swatch: var(--color-page); }
.bpl-swatch--surface-alt   { --swatch: var(--color-surface-alt); }
.bpl-swatch--surface-muted { --swatch: var(--color-surface-muted); }
.bpl-swatch--surface-sand  { --swatch: var(--color-surface-sand); }
.bpl-swatch--border        { --swatch: var(--color-border); }
.bpl-swatch--border-strong { --swatch: var(--color-border-strong); }
.bpl-swatch--border-subtle { --swatch: var(--color-border-subtle); }
.bpl-swatch--error         { --swatch: var(--color-error); }
.bpl-swatch--success       { --swatch: var(--color-success); }

/* The spacing scale, drawn at its own size so the steps are comparable. */
.bpl-scale {
  margin: 0;
  padding: 0;
}

/* The modifier sits on the row, not on the list: on the list it would set
   --step for every row at once and draw seven identical bars. */
.bpl-scale__row {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  margin-bottom: var(--space-sm);
}

.bpl-scale dt {
  flex: 0 0 11rem;   /* the monospace label wraps under about 11rem */
  font-family: ui-monospace, "Cascadia Mono", "Consolas", monospace;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.bpl-scale dd {
  margin: 0;
}

.bpl-scale dd::before {
  content: "";
  display: block;
  height: 1rem;
  width: var(--step);
  border-radius: var(--radius-sm);
  background-color: var(--color-primary-light);
}

.bpl-scale__row--xs  { --step: var(--space-xs); }
.bpl-scale__row--sm  { --step: var(--space-sm); }
.bpl-scale__row--md  { --step: var(--space-md); }
.bpl-scale__row--lg  { --step: var(--space-lg); }
.bpl-scale__row--xl  { --step: var(--space-xl); }
.bpl-scale__row--2xl { --step: var(--space-2xl); }
.bpl-scale__row--3xl { --step: var(--space-3xl); }
