/*
  Smith & Wollensky — Vietnam mockup stylesheet

  Typography, colour and layout geometry are taken verbatim from the live
  Smith & Wollensky compiled stylesheet (see mockups/_source/style.min.css).
  The four brand faces — griffon, gill-sans-nova, schoolbook and
  schoolbook-condensed — are the real Adobe Typekit webfonts, declared in
  assets/css/hei2crz.css and served from Adobe's CDN. Noto Sans TC is the
  CJK fallback in every stack, exactly as on the live site.

  The live type utility class names (.f__*) and colour utility class names
  (.u__c-*) are preserved so the markup stays directly comparable to
  mockups/_source/*.html.
*/

:root {
  /* ---- Brand colour tokens (live values) ---- */
  --color-gold: #976f41;
  --color-gold-light: #bc9b67;
  --color-green: #005b28;
  --color-green-deep: #013d1b;
  --color-cream: #feead8;
  --color-cream-soft: #fff7ef;
  --color-ink: #212529;
  --color-muted: #666263;
  --color-brown: #522a31;
  --color-wine: #522a31;
  --color-white: #ffffff;
  --color-black: #2d1a10;

  /* ---- Type stacks (live values) ---- */
  --font-display: griffon, "Noto Sans TC", Copperplate, serif;
  --font-sans: gill-sans-nova, "Noto Sans TC", "Gill Sans", "Segoe UI", -apple-system, sans-serif;
  --font-body: schoolbook, "Noto Sans TC", Georgia, serif;
  --font-section: schoolbook-condensed, "Noto Sans TC", Georgia, serif;

  /* ---- Spacing scale (4pt) ---- */
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2.5rem;
  --space-2xl: 4rem;
  --space-3xl: 6rem;
  --space-4xl: 8.5rem;

  /* ---- Motion ---- */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 160ms;
  --dur-med: 250ms;
  --dur-slow: 900ms;

  --radius-sm: 2px;
  --rule-hairline: 1px solid rgba(151, 111, 65, 0.25);

  /* Fixed-header offset — matches live `body { padding-top: 137px }` */
  --header-offset: 137px;

  /* One spacing value shared by both header nav columns, so every gap between
     nav items is identical regardless of how many items a column holds. The
     live site relies on space-around/space-between instead, which only looks
     even because it carries three items per column; the language switcher
     makes four on the right and that collapsed the gaps to 12px. A flat 40px
     applies throughout, since the desktop nav now only renders above 1400px
     where it fits. */
  --nav-gap: 40px;

  color-scheme: light;
}

/* ---------------------------------------------------------------- */
/* Reset                                                             */
/* ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  max-width: 100vw;
  overflow-x: hidden;
  /* Live site: white page background. Cream is an accent, never the ground. */
  background-color: var(--color-white);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 180%;
  padding-top: var(--header-offset);
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }
button { font-family: inherit; }

h1, h2, h3, h4 {
  margin: 0;
  font-style: normal;
  font-weight: 700;
  overflow-wrap: anywhere;
}

/* Live wrapper width. The live site puts the 48px gutter on .header/.footer
   themselves and runs modules edge-to-edge inside a 1344px wrap; here the
   gutter lives on the wrap so every page section shares one measure. */
.u__wrap,
.wrap {
  width: 100%;
  max-width: 1344px;
  margin-left: auto;
  margin-right: auto;
  position: relative;
  padding-inline: 48px;
}
@media only screen and (max-width: 1200px) {
  .u__wrap,
  .wrap { padding-inline: 24px; }
}

.u__img-cover { position: relative; }
.u__img-cover img { width: 100%; height: 100%; object-fit: cover; }
.u__img-contain { position: relative; }
.u__img-contain img { width: 100%; height: 100%; object-fit: contain; }

/* Visually hidden but announced by screen readers — used to state that a
   card link opens a PDF or a new tab. */
.u__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;
}

/* Skip link */
.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--color-green);
  color: var(--color-white);
  padding: var(--space-sm) var(--space-md);
  z-index: 2000;
  font-family: var(--font-sans);
}
.skip-link:focus {
  left: var(--space-md);
  top: var(--space-md);
}

:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 3px;
}

/* ---------------------------------------------------------------- */
/* Typography utilities — verbatim from the live stylesheet           */
/* ---------------------------------------------------------------- */
.f__link-sans {
  font-family: gill-sans-nova, "Noto Sans TC";
  font-style: normal;
  font-weight: 500;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: .2em;
  text-transform: uppercase;
}

.f__section-header {
  font-family: gill-sans-nova, "Noto Sans TC";
  font-style: normal;
  font-weight: 700;
  font-size: 21px;
  line-height: 32px;
  letter-spacing: .1em;
  text-transform: uppercase;
}

.f__poster {
  font-family: griffon, "Noto Sans TC", Copperplate;
  font-style: normal;
  font-weight: 700;
  font-size: 54px;
  line-height: 140%;
  text-transform: lowercase;
}

.f__headline-h2 {
  font-family: schoolbook-condensed, "Noto Sans TC";
  font-style: normal;
  font-weight: 700;
  font-size: 32px;
  line-height: 120%;
  letter-spacing: .05em;
  text-transform: uppercase;
}

.f__headline-h3 {
  font-family: schoolbook-condensed, "Noto Sans TC";
  font-style: normal;
  font-weight: 700;
  font-size: 24px;
  line-height: 120%;
  letter-spacing: .05em;
  text-transform: uppercase;
}

.f__paragraph-medium {
  font-family: schoolbook, "Noto Sans TC";
  font-style: normal;
  font-weight: 400;
  font-size: 19px;
  line-height: 180%;
}

.f__paragraph-regular {
  font-family: schoolbook, "Noto Sans TC";
  font-style: normal;
  font-weight: 400;
  font-size: 16px;
  line-height: 180%;
}

/* Responsive step-downs (live breakpoints: 1200px, then 576px) */
@media only screen and (max-width: 1200px) {
  .f__poster { font-size: 36px; }
}
@media only screen and (max-width: 576px) {
  .f__poster { font-size: 28px; }
  .f__section-header { font-size: 16px; line-height: 20px; }
  .f__paragraph-regular { font-size: 15px; }
  .f__headline-h2 { font-size: 24px; }
}

/* ---------------------------------------------------------------- */
/* Colour utilities — verbatim from the live stylesheet               */
/* ---------------------------------------------------------------- */
.u__c-gold  { color: #976f41; }
.u__c-green { color: #005b28; }
.u__c-brown { color: #522a31; }
.u__c-white { color: #fff; }
.u__c-cream { color: #feead8; }
.u__c-grey  { color: #666263; }

/* ---------------------------------------------------------------- */
/* Placeholder / TBD styling                                        */
/* ---------------------------------------------------------------- */
.tbd {
  font-style: italic;
  color: var(--color-brown);
  border-bottom: 1px dashed var(--color-brown);
  padding-bottom: 1px;
}
/* On the dark green footer the wine tone is illegible — placeholders read
   as a dimmed white there instead. */
.site-footer .tbd,
.footer .tbd {
  color: rgba(255, 255, 255, 0.85);
  border-bottom-color: rgba(255, 255, 255, 0.5);
}

/* ---------------------------------------------------------------- */
/* Buttons (live `.button` treatment: gold outline, gold fill wipe)   */
/* ---------------------------------------------------------------- */
.button,
.btn {
  display: inline-block;
  position: relative;
  z-index: 2;
  font-size: 0;
  padding: 0;
  border: 0;
  background-color: transparent;
  cursor: pointer;
}
.button > span,
.btn > span {
  pointer-events: none;
  padding: 16px;
  border: 1px solid var(--color-gold);
  background: transparent;
  overflow: hidden;
  position: relative;
  display: inline-block;
  font-family: gill-sans-nova, "Noto Sans TC";
  font-style: normal;
  font-weight: 500;
  font-size: 14px;
  line-height: 23px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--color-gold);
  transition: color .25s ease;
}
.button > span::after,
.btn > span::after {
  content: "";
  width: 100%;
  height: 100%;
  display: block;
  background-color: var(--color-gold);
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  transform: translateY(100%);
  transition: transform .25s ease;
}
.button:hover > span,
.button:focus-visible > span,
.btn:hover > span,
.btn:focus-visible > span { color: var(--color-white); }
.button:hover > span::after,
.button:focus-visible > span::after,
.btn:hover > span::after,
.btn:focus-visible > span::after { transform: translateY(0); }

/* The gold wipe-up fill is the button's identity on the live site. Under
   reduced motion it is shortened rather than removed — `transition: none`
   turned it into an instant colour flip that read as "no hover effect". */
@media (prefers-reduced-motion: reduce) {
  .button > span, .button > span::after,
  .btn > span, .btn > span::after { transition-duration: 120ms; }
}

/* ---------------------------------------------------------------- */
/* Header — live geometry                                             */
/* ---------------------------------------------------------------- */
.header,
.site-header {
  /* Live padding is 40px top; the social row above the nav now occupies part
     of that space, so the top is trimmed to 24px to keep the wordmark at the
     same height it sits at on the live site. */
  padding: 24px 48px 40px;
  z-index: 20;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  transition: transform .25s ease;
  background-color: #fff;
}
.header.hidden,
.site-header.hidden { transform: translateY(-100%); }

/* The header slide is essential motion, not decoration: it matches the live
   site's `transition: transform .25s ease` and is deliberately kept under
   prefers-reduced-motion. Windows "Show animations: off" reports reduce, and
   zeroing this made the header snap instead of glide. */

.header .u__wrap,
.site-header .u__wrap { padding-inline: 0; }

/* 12-column grid, vertically centred — mirrors the live Bootstrap row */
.header__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
.header__col {
  min-width: 0;
}
.header__col--mobile-logo { flex: 0 0 25%; display: flex; text-align: left; }

/* Nav spacing.

   The live site uses space-around on the left column and space-between on the
   right, which happens to look even there because each column holds three
   items of similar width. Adding the language switcher makes four items in the
   right column, and space-between hands the leftover width to the outer edges
   — measured 12px between NEWS / CONTACT US / RESERVATIONS against 71px on the
   left. So the distribution is replaced with one explicit gap shared by both
   columns: spacing no longer depends on how many items a column holds, and the
   centre column still owns its third of the row, keeping the logo centred. */
/* Each side column takes exactly the width its own items need (flex-basis
   auto), and the centre column absorbs all remaining space. Equal thirds — or
   an equal flex-basis — split the surplus evenly, which the right column
   cannot use: it carries four items (~450px with the language switcher)
   against the left's three (~283px), so its content ran past its share and
   under the 288px logo below ~1300px. Sizing to content removes that overrun,
   and centring the logo inside the elastic middle column keeps it on the
   viewport's centre line at every width. */
/* Both side columns take an equal half of the row minus the logo's reserved
   width, and centre their own items within it. Pinning each group to the outer
   edge instead (flex-start / flex-end) leaves lopsided voids beside the
   wordmark — measured 234px to its left against 33px to its right, because the
   heavier group reaches much closer to the centre. Centring each group
   balances the whitespace while the shared column-gap keeps every gap between
   items identical. The left column now carries the four items (MENUS, PRIVATE
   EVENTS, NEWS, OUR STORY) and the right three (LOCATIONS, RESERVATIONS, the
   language switcher), the wordmark itself standing in for HOME. */
.header__col--left,
.header__col--right {
  flex: 1 1 0;
  min-width: 0;
  display: none;
  align-items: center;
  justify-content: center;
  column-gap: var(--nav-gap);
}

/* The middle column absorbs the leftover width, but the side columns are
   unequal (four items left, three right), so the column's own centre is not
   the viewport's. The logo is therefore taken out of flow and pinned to the
   header row's true centre line, which is what the design calls for; the
   column still reserves the horizontal space in flow, so neither nav group
   can encroach on the wordmark. */
.header__col--centre {
  flex: 1 1 auto;
  display: none;
  text-align: center;
}
/* The centre column never shrinks below the wordmark plus breathing room, so
   the two nav groups can never reach it. With both side columns on an equal
   flex basis, the logo's own column sits on the row's centre line and the
   wordmark is centred within it — no absolute positioning needed. */
@media (min-width: 1400px) {
  .header__col--centre {
    flex: 0 0 auto;
    /* 288px wordmark + 16px clear either side. */
    min-width: 320px;
  }
}

.header__link {
  font-size: 16px;
  line-height: 23px;
  letter-spacing: .1em;
  display: inline-block;
  /* "CONTACT US" and "OUR STORY" must never break across two lines — the
     columns shrink before the labels do. */
  white-space: nowrap;
  transition: all .25s ease;
  /* Flex item in .header__col--left / --right: centre on the row's cross axis
     so the links and the RESERVATIONS button share one centre line. */
  align-self: center;
}
.header__link:hover { color: #976f41; }
.header__link[aria-current="page"] { text-decoration: underline; text-underline-offset: 5px; }

/* Live width, held at every desktop width. */
.header__logo { width: 288px; display: inline-block; z-index: 25; }
.header__logo img { width: 100%; height: auto; }
.header__logo-mobile { width: 50px; display: inline-block; }
.header__logo-mobile-open { width: auto; height: 18px; display: inline-block; }
.header__logo-mobile-open img { width: auto; height: 100%; }

/* Live rule is `margin-right: 24px`, which clears the hamburger sitting to its
   right. The hamburger is hidden at desktop, so the margin only pushes the
   button out of line with the nav links — applied at mobile only. */
/* The .button wrapper is inline-block with font-size:0 and an inline-block
   span, so the span's own baseline lands on the header row's text baseline and
   drags the button below the NEWS / CONTACT US links. The row is flex, so make
   the button a flex item that centres its span instead of baseline-aligning it. */
.header__reservations {
  display: inline-flex;
  align-items: center;
  align-self: center;
}
@media (max-width: 1199.98px) {
  .header__reservations { margin-right: 24px; }
}
.header__reservations > span {
  padding-top: 8px;
  padding-bottom: 10px;
  font-size: 16px;
}

/* nav-open/close SVGs each draw their own 48x48 circle: the default is a gold
   disc with a white glyph, the __hover variant inverts to a white disc with a
   gold glyph and gold ring. The crossfade performs the whole inversion, so the
   icons must fill the button edge-to-edge with no padding of their own and the
   button's own background must stay transparent — otherwise the CSS gold disc
   shows through and the hover state looks like nothing happened. */
.header__hamburger {
  display: inline-block;
  vertical-align: middle;
  border-radius: 50%;
  position: relative;
  width: 48px;
  height: 48px;
  padding: 0;
  background-color: transparent;
  border: 0;
  cursor: pointer;
  flex-shrink: 0;
}
.header__hamburger-icon {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  transition: opacity .25s ease;
  pointer-events: none;
}
.header__hamburger-icon-hover { opacity: 0; }
/* Icon swap is hover-only, matching the live site. It previously also fired on
   :focus-visible, which made a programmatically-focused button (the modal's
   close X) render in its hover state before the pointer ever touched it — so a
   real hover appeared to do nothing. Keyboard users still get the global
   :focus-visible outline ring defined further up. */
.header__hamburger:hover .header__hamburger-icon { opacity: 0; }
.header__hamburger:hover .header__hamburger-icon-hover { opacity: 1; }

/* Language switcher. Reuses the .header__hamburger gold circle and its
   icon-crossfade, exactly as smithandwollensky.com.tw does — the icon shows the
   language you switch TO, and the __hover variant inverts to a white disc with
   gold letters and a gold ring. Desktop only (the live TW markup is
   `d-none d-xl-block`); the mobile nav carries a text link instead. */
/* The switcher sits in the shared column gap like every other nav item, so it
   needs no margin of its own. */
.header__lang { display: none; }

/* ---------------------------------------------------------------- */
/* Social row — one line of icons pinned to the top-right corner,     */
/* directly above the nav row and the language switcher.              */
/* ---------------------------------------------------------------- */
/* The header's own 40px top padding already supplies the clearance above
   the icons, so the row adds only the gap beneath itself. It is trimmed to
   16px at desktop so the wordmark does not drop further down the viewport
   than it does on the live site. */
/* The icons must line up over the language switcher, which is the last item in
   a centre-justified column — so its right edge sits inboard of the wrap by an
   amount that changes with the nav's content width. A fixed inset cannot track
   that (measured 61px at 1920px but 51px at 1400px). Instead the row mirrors
   the header's own three-column structure: two spacer cells matching
   .header__col--left and --centre, then a right cell that repeats the real
   column's `flex: 1 1 0` basis and centre justification. The icons therefore
   land on the switcher's centre line at every width, with no magic numbers. */
.header__social-row {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}
/* Matches .header__col--left. */
.header__social-spacer {
  flex: 1 1 0;
  min-width: 0;
}
/* Matches .header__col--centre at the desktop breakpoint, where it is
   `flex: 0 0 auto; min-width: 320px`. */
.header__social-spacer--centre {
  flex: 0 0 auto;
  min-width: 320px;
}
/* Matches .header__col--right's flex basis, so the cell spans exactly the same
   horizontal band as the nav group beneath it (measured: both edges align to
   0.00px at every width from 1400px to 2560px).

   The nav column is centre-justified, so the switcher — its last item — stops
   short of the band's right edge by half the column's leftover space. The nav
   group's content measures 389.74px, so that offset is
   (band width - 389.74) / 2, which CSS can express directly against the band
   because the cell and the column always share a width. Pinning the icons to
   the band's end and pushing them back by that amount lands the last icon's
   right edge on the switcher's, at every width — no measured constant, and it
   still holds at 1400px where the column narrows from 512px to 492px. */
.header__social-cell {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.header__social {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0;
  list-style: none;
  flex: 0 0 auto;
  /* Half the nav column's leftover space; see above. The percentage resolves
     against .header__social-cell, which always matches the nav column's width,
     so this tracks the 1400px narrowing without a media query. */
  margin: 0 calc((100% - 389.74px) / 2) 0 0;
}
/* The icons are a framed outline set — Facebook and Instagram are drawn as
   rounded squares, WhatsApp and Zalo as speech bubbles — so each already
   carries its own frame. Wrapping them in the nav's gold disc would nest one
   frame inside another, so the link is a plain hit area instead and the mark
   stands on its own. The 28px box is kept as the row's rhythm and as a
   comfortable pointer target. */
.header__social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  color: var(--color-gold);
  flex-shrink: 0;
  transition: color .25s ease, opacity .25s ease;
}
/* No disc to invert, so hover shifts the stroke to the darker brand gold —
   the same cue the nav links use. */
.header__social-link:hover,
.header__social-link:focus-visible {
  color: var(--color-brown);
}
/* The source set hardcodes `stroke:#000000` in a <style> block, which would
   beat inherited colour, so the markup drops that and sets
   stroke="currentColor" instead; the width is set here rather than in the
   artwork. With vector-effect="non-scaling-stroke" on every shape this is a
   device-pixel width, so the hairline stays identical across all four icons
   even though each renders at a different size (25.3px down to 21.8px, to
   equalise the painted artwork inside the shared 48-unit viewBox). Without it
   the 1-unit source stroke would resolve to about 0.5px and render pale and
   blurry. */
.header__social-icon {
  display: block;
  stroke-width: 1.25px;
  overflow: visible;
}

/* The desktop nav switches to the hamburger at 1400px rather than the live
   site's 1200px. Measured: the widest nav column carries four items at a flat
   40px gap, which needs a wider viewport than 1200px to clear the centred
   288px wordmark. Raising the breakpoint keeps every desktop width correct
   instead of letting the nav run under the logo, and avoids shrinking the gaps
   to compensate; the live site can sit at 1200px only because it carries fewer
   nav items overall. */
@media (min-width: 1400px) {
  .header__col--mobile-logo { display: none; }
  .header__col--left { display: flex; }
  .header__col--centre { display: block; }
  .header__col--right { display: flex; }
  .header__hamburger.open { display: none; }
  .header__lang { display: inline-block; }
}
@media only screen and (max-width: 1399.98px) {
  .header, .site-header { padding: 5px 24px; }
  /* No room for a second row once the header collapses to 58px tall; the
     mobile nav panel carries the social links instead. */
  .header__social-row { display: none; }
  /* Below the desktop breakpoint both nav columns are hidden outright, so only
     the RESERVATIONS button and the hamburger remain; the right column takes
     the row's full remaining width and pins them to the end. */
  .header__col--right { flex: 0 0 75%; display: flex; justify-content: flex-end; }
  .header__reservations > span { font-size: 12px; padding: 10px 12px; }
  body { padding-top: 58px; }
}

/* ---------------------------------------------------------------- */
/* Popup overlays — one shared base for the mobile nav and the        */
/* exclusive-offers modal, matching the live `.popup`                 */
/* ---------------------------------------------------------------- */
.popup {
  z-index: 30;
  width: 100vw;
  height: 100vh;
  overflow: auto;
  position: fixed;
  top: 0;
  left: 0;
  background-color: #feead8;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s ease;
  padding-left: 48px;
  padding-right: 48px;
  display: none;
}
.popup.active {
  pointer-events: all;
  opacity: 1;
  display: block;
}
@media (prefers-reduced-motion: reduce) {
  .popup { transition-duration: 0s; }
}
@media only screen and (max-width: 1200px) {
  .popup { padding-left: 24px; padding-right: 24px; }
}

.popup__header {
  position: relative;
  background-color: transparent;
  padding-left: 0;
  padding-right: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 24px;
  padding-bottom: 24px;
}
.popup .header__hamburger.close { display: inline-block; z-index: 25; }

.main-nav { padding-block: var(--space-2xl); }
.main-nav__links { padding-left: 0; text-align: center; }
.main-nav__link { margin-bottom: 24px; }
.main-nav__link a { transition: opacity .25s ease; display: inline-block; }
.main-nav__link a:hover { color: var(--color-gold); }
@media only screen and (max-width: 576px) {
  .main-nav__link a { font-size: 22px; line-height: 22px; }
}

/* The mobile nav's language link, set off from the page links by a hairline
   rule so it reads as a mode switch rather than a sixth destination. The live
   TW mobile nav lists it as a plain text link in the same style. */
.main-nav__links--lang {
  margin-top: var(--space-lg);
  padding-top: var(--space-lg);
  border-top: var(--rule-hairline);
}
.main-nav__links--lang .main-nav__link { margin-bottom: 0; }
.main-nav__links--lang a { color: var(--color-gold); }

.main-nav__social {
  display: flex;
  gap: 15px;
  justify-content: center;
  padding-bottom: 30px;
}
/* The nav panel carries the same outline set as the header row, and those
   marks are self-framed (rounded squares and speech bubbles), so the link is a
   plain 44px hit area rather than a gold disc — nesting a frame inside a circle
   reads as two competing frames. 44px keeps the comfortable touch target. */
.main-nav__social .social-link {
  display: inline-flex;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  color: var(--color-gold);
  transition: color .25s ease;
}
/* No disc to invert, so the stroke shifts to the darker brand gold on hover,
   matching the header row. */
.main-nav__social .social-link:hover,
.main-nav__social .social-link:focus-visible {
  color: var(--color-brown);
}
/* The source set hardcodes stroke:#000000 in a <style> block; the markup drops
   that and uses stroke="currentColor", with the width set here. Every shape
   carries vector-effect="non-scaling-stroke", so this is a device-pixel width
   and stays identical across all four icons even though each renders at its own
   size (30.3px down to 26.2px) to equalise the painted artwork inside the
   shared 48-unit viewBox. */
.main-nav__social .social-link__icon {
  display: block;
  stroke-width: 1.5px;
  overflow: visible;
}

/* The footer still carries the filled brand marks, which size by height and
   need Facebook's tall "f" kept narrower than Instagram's square glyph. */
.footer__social .social-link__icon { display: block; width: 18px; height: 18px; }
.footer__social .social-link__icon--fb { width: 11px; }

/* Exclusive-offers modal — the live `.popup.exclusive` panel geometry */
/* The dialog takes focus on open (tabindex="-1") so the modal is announced
   without any control looking pre-activated. It is a container, not a control,
   so it does not draw the global focus ring — the ring would frame the whole
   600px panel. Focus is still trapped inside and Tab reaches the close button. */
.popup[tabindex]:focus,
.popup[tabindex]:focus-visible { outline: none; }

.popup.exclusive {
  max-width: 600px;
  max-height: 90vh;
  height: auto;
  width: 100%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding-bottom: 30px;
  z-index: 40;
}
/* Backdrop behind the exclusive panel, so clicking outside can close it. */
.popup-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(45, 26, 16, 0.55);
  z-index: 35;
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease;
}
.popup-backdrop.active { opacity: 1; pointer-events: all; }
@media (prefers-reduced-motion: reduce) {
  .popup-backdrop { transition-duration: 0s; }
}

.cross-area { text-align: right; margin-top: 1.5rem; margin-bottom: 1rem; }
.custom-popup__title { margin-bottom: var(--space-lg); }
.custom-popup__content { margin: 0; }

.sw-fade {
  position: relative;
  width: 100%;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 12px;
}
.sw-fade .sw-fade__item {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .9s ease-in-out;
  pointer-events: none;
}
.sw-fade .sw-fade__item.is-active { opacity: 1; pointer-events: auto; }
.sw-fade img { width: 100%; height: auto; display: block; }
@media (prefers-reduced-motion: reduce) {
  .sw-fade .sw-fade__item { transition-duration: 0s; }
}

/* ---------------------------------------------------------------- */
/* Hero                                                              */
/* ---------------------------------------------------------------- */
/* The hero is NOT full-bleed on the live site. Measured against
   smithandwollensky.com.ph: 1806px hero inside an 1887px viewport, inset 48px
   each side. The hero itself has no width limit — the inset comes from the
   live `main.ignite-modules` padding (48px, 24px on mobile), which matches the
   header's own 48px gutter so the hero lines up with the nav above it.
   Height is 100vh capped at 756px on every page, home and inner alike. */
main {
  padding-left: 48px;
  padding-right: 48px;
}
@media only screen and (max-width: 1200px) {
  main { padding-left: 24px; padding-right: 24px; }
}

.m__hero {
  position: relative;
  margin-bottom: 68px;
  height: 100vh;
  max-height: 756px;
  overflow: hidden;
}
/* No short-hero variant on the live site: story, menu, news, contact, about and
   privacy all measure the same 756px as home. Kept as a no-op so the class in
   the markup stays harmless. */

.m__hero__background { width: 100%; height: 100%; position: absolute; top: 0; left: 0; }
.m__hero__background-image { width: 100%; height: 100%; z-index: 1; position: absolute; top: 0; left: 0; }
.m__hero__background-image .hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-in-out);
}
.m__hero__background-image .hero__slide.is-active { opacity: 1; }
.m__hero__background-image img { width: 100%; height: 100%; object-fit: cover; }
@media (prefers-reduced-motion: reduce) {
  .m__hero__background-image .hero__slide { transition: opacity 150ms linear; }
}
.m__hero__background-tint {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  background-color: rgba(0, 0, 0, 0.4);
}

.m__hero__overlay {
  position: relative;
  z-index: 3;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Live inner container is max-width:100%, so it fills u__wrap's 1344px and the
   72px titles ("America's Steakhouse", "OUR AMERICAN DREAM") stay on one line.
   A 900px cap here was forcing them to wrap onto two. */
.m__hero__overlay-inner { text-align: center; max-width: 100%; }
.m__hero__overlay-subtitle {
  display: block;
  letter-spacing: .25em;
  font-size: 20px;
  margin: 0 0 var(--space-md);
}
.m__hero__overlay-title {
  display: block;
  margin-bottom: 40px;
  font-size: 72px;
}
@media only screen and (max-width: 1200px) {
  .m__hero {
    min-height: 400px;
    height: calc(100vh - 105px);
    margin-bottom: 48px;
  }
  .m__hero__overlay-subtitle { font-size: 18px; }
  .m__hero__overlay-title { font-size: 36px; }
}

/* ---------------------------------------------------------------- */
/* Feature media (image + copy row) — live m__fmedia                 */
/* ---------------------------------------------------------------- */
.m__fmedia { margin-bottom: 100px; }
.m__fmedia__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin-inline: -12px;
}
.m__fmedia__row--reverse { flex-direction: row-reverse; }
.m__fmedia__col { flex: 0 0 100%; padding-inline: 12px; min-width: 0; }
.m__fmedia__content { padding-left: 70px; padding-right: 70px; }
.m__fmedia__title { margin-bottom: 48px; }
.m__fmedia__divider {
  width: 90px;
  height: 1px;
  background-color: #976f41;
  margin-bottom: 50px;
}
.m__fmedia__copy { margin-bottom: 0; }
.m__fmedia__media { position: relative; }
.m__fmedia__media img { width: 100%; height: auto; display: block; }

@media (min-width: 1200.01px) {
  .m__fmedia__col { flex: 0 0 50%; }
  /* Live rule: the hairline sits on the side the copy column faces. */
  .m__fmedia__right .m__fmedia__row { border-left: 1px solid #976f41; }
  .m__fmedia__left .m__fmedia__row { border-right: 1px solid #976f41; }
}
@media only screen and (max-width: 1400px) {
  .m__fmedia__content { padding-left: 25px; padding-right: 25px; }
}
@media only screen and (max-width: 1200px) {
  .m__fmedia {
    padding-bottom: 48px;
    margin-bottom: 48px;
    border-bottom: 1px solid #976f41;
  }
  .m__fmedia__content { padding-left: 0; padding-right: 0; }
  .m__fmedia__title { margin-bottom: 24px; }
  .m__fmedia__divider { margin-bottom: 24px; }
  .m__fmedia__media { height: 342px; margin-bottom: 24px; }
  .m__fmedia__media img { height: 100%; object-fit: cover; }
}

/* Auto-rotating fade slider (home article column) */
.sw-slider {
  position: relative;
  width: 100%;
  margin: auto;
  overflow: hidden;
  background: #000;
  aspect-ratio: 3 / 2;
}
.sw-slider .sw-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1s ease-in-out;
}
.sw-slider .sw-slide.is-active { opacity: 1; }
/* Live inline rule: contain, so each frame shows in full and is never cropped. */
.sw-slider .sw-slide img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
@media (prefers-reduced-motion: reduce) {
  .sw-slider .sw-slide { transition-duration: 0s; }
}

/* ---------------------------------------------------------------- */
/* Carousel slider section — live m__cslider (Swiper 8)              */
/* ---------------------------------------------------------------- */
.m__cslider { margin-bottom: 120px; }
.m__cslider__title { margin-bottom: 50px; }
.m__cslider__swiper { position: static; overflow: hidden; }
.m__cslider__swiper-slide-content {
  margin-right: 44px;
  padding-right: 44px;
  border-right: 1px solid #976f41;
  height: 100%;
}
.m__cslider__swiper-slide-highlight { height: 470px; }
.m__cslider__swiper-slide-title {
  padding-top: 32px;
  margin-bottom: 24px;
  font-size: 20px;
  display: block;
}
.m__cslider__swiper-slide-divider {
  background-color: #976f41;
  height: 1px;
  width: 114px;
  max-width: 50%;
  margin-bottom: 24px;
}
.m__cslider__swiper-slide-copy { margin-bottom: 24px; margin-top: 0; }

/* The live site uses bare divs here; this repo requires keyboard access, so
   these are real <button>s — hence the appearance reset. */
.m__cslider__swiper-arrow {
  position: absolute;
  right: 3px;
  top: 45%;
  width: 80px;
  height: 80px;
  background-color: #976f41;
  border: 1px solid #976f41;
  border-radius: 50%;
  z-index: 5;
  padding: 26px 31px;
  cursor: pointer;
  transition: all .25s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-appearance: none;
  appearance: none;
}
.m__cslider__swiper-arrow:hover,
.m__cslider__swiper-arrow:focus-visible { transform: scale(1.05); }
.m__cslider__swiper-arrow.hidden { opacity: 0; pointer-events: none; }
.m__cslider__swiper-arrow img { width: 15px; height: auto; }
.m__cslider__swiper-prev { left: -45px; right: auto; }
.m__cslider__swiper-next { right: 3px; }
/* The live hover is `transform: scale(1.05)` with `transition: all .25s ease`.
   Under reduced motion the scale is kept (it is the hover affordance, not
   decoration) and only the duration is shortened — `transform: none` here
   removed the effect entirely. */
@media (prefers-reduced-motion: reduce) {
  .m__cslider__swiper-arrow { transition-duration: 120ms; }
}

@media only screen and (max-width: 1200px) {
  .m__cslider { margin-bottom: 48px; }
  .m__cslider__title { margin-bottom: 24px; }
  .m__cslider__swiper-slide-content { padding-right: 0; margin-right: 0; border-right: 0; }
  .m__cslider__swiper-slide-highlight { height: 342px; }
  .m__cslider__swiper-arrow {
    top: 32%;
    width: 40px;
    height: 40px;
    padding: 3px 10px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .m__cslider__swiper-arrow img { width: 15px; }
  .m__cslider__swiper-prev { left: -20px; }
  .m__cslider__swiper-next { right: -20px; }
}

/* Fallback layout if the Swiper CDN script does not load — the slides
   still read as a legible responsive grid rather than a stack of images. */
.m__cslider__swiper:not(.swiper-initialized) .swiper-wrapper {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-lg);
}
@media only screen and (max-width: 1200px) {
  .m__cslider__swiper:not(.swiper-initialized) .swiper-wrapper {
    grid-template-columns: minmax(0, 1fr);
  }
  .m__cslider__swiper:not(.swiper-initialized) ~ .m__cslider__swiper-arrow { display: none; }
}

/* ---------------------------------------------------------------- */
/* Location slider — live m__pslider (Swiper 8)                      */
/* Used by the Home "S&W Regional Footprint" section. The live       */
/* module lays each slide out on Bootstrap's grid; this repo carries  */
/* no Bootstrap, so the row is a plain flex row with the same 50/50   */
/* split and the same reversed order (text left, image right).       */
/* ---------------------------------------------------------------- */
.m__pslider { margin-bottom: 150px; position: relative; }
/* One hairline for the whole section, drawn as a pseudo-element on the wrap
   rather than a border on each slide. Putting it on the slide (as the live
   module does) repeats it six times, and the neighbouring slides' copies are
   visible inside the viewport because the swiper track is far wider than the
   wrap. Positioned to land on the same vertical as .m__fmedia__row's
   border-left beside HOME. */
.m__pslider .u__wrap { position: relative; }
.m__pslider .u__wrap::before {
  content: "";
  position: absolute;
  left: 36px;   /* wrap padding 48px - 12px row gutter, as .m__fmedia__row */
  width: 1px;
  /* Spans the slide area only, so it starts below the tab strip rather than
     alongside the section title. The tabs are a fixed block (title 50px
     margin + tab row + 45px margin), so the offset is a constant. */
  top: 166px;
  bottom: 0;
  background-color: var(--color-gold);
  z-index: 1;
  pointer-events: none;
}
.m__pslider__title { margin-bottom: 50px; }
/* No `overflow: hidden` on the wrap. The live module clips here to contain
   its tab strip, but .m__pslider__nav-wrapper already scrolls itself, and the
   clip cut 45px off the prev arrow, which hangs into the page gutter exactly
   as .m__cslider's does. */

/* Tab strip -------------------------------------------------------- */
.m__pslider__nav-wrapper { max-width: 100%; position: relative; }
.m__pslider__nav {
  margin: 0 0 45px;
  padding-left: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
}
.m__pslider__nav > li { display: inline-block; }

/* The live tabs are bare <li>s; these are real buttons for keyboard
   access, hence the appearance reset back to plain text. */
.m__pslider__nav-item {
  display: inline-block;
  width: auto;
  margin-right: 25px;
  margin-bottom: 15px;
  padding: 0;
  border: 0;
  background: none;
  /* Not `color: inherit`: like `font: inherit` before it, that beat the
     u__c-brown class on the same element and rendered the unselected tabs in
     the body ink (#212529) where Taiwan renders them brown (#522a31). The
     .active rule below still overrides this to gold. */
  color: var(--color-brown);
  text-align: left;
  cursor: pointer;
  transition: all .25s ease;
  -webkit-appearance: none;
  appearance: none;
  /* Restate .f__link-sans rather than `font: inherit`. These tabs are
     <button>s for keyboard access, and `font: inherit` took the body serif,
     overriding the class on the same element: measured against the live
     Taiwan tabs, ours rendered schoolbook 16px/400 at 3.2px tracking where
     Taiwan renders gill-sans-nova 14px/500 at 2.8px. */
  font-family: var(--font-sans);
  font-style: normal;
  font-weight: 500;
  font-size: 14px;
  line-height: 20px;
  letter-spacing: .2em;
  text-transform: uppercase;
}
.m__pslider__nav-item.active {
  color: var(--color-gold);
  text-decoration: underline;
}
.m__pslider__nav-item:hover { text-decoration: underline; }
.m__pslider__nav-item:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 3px;
  text-decoration: underline;
}

/* Slides ----------------------------------------------------------- */
/* Static, not relative: the arrows are positioned against .m__pslider (the
   section) exactly as .m__cslider's are, so both sections' controls land on
   the same vertical. When this was `relative` the offsets resolved against
   this wrapper inside u__wrap instead, putting every arrow 48px — one page
   gutter — inboard of the ones below. */
.m__pslider__swiper-wrapper { position: static; }
/* The track is clipped, not left visible. The photograph's 22.5% pull-back is
   a decorative bleed inside the active slide, but on wide viewports the same
   pull let the INACTIVE neighbour's image show in the page gutter (measured:
   reaching x=169 at 1920px, though not at 1440px where the gutter is small).
   Both axes must be clipped: `overflow-x: clip` with `overflow-y: visible` is
   not a valid pairing — the visible axis is coerced and no clipping happens,
   which is exactly what the measurement showed. Nothing needs to escape
   vertically, and the arrows are children of .m__pslider, outside this box,
   so they still hang into the gutter. */
.m__pslider__swiper { overflow: clip; }
.m__pslider .swiper-backface-hidden .swiper-slide { backface-visibility: visible; }

.m__pslider__swiper-slide {
  transition: opacity .25s ease;
  height: auto;
}

/* Text left, image right — the live module gets this from
   .flex-xl-row-reverse on a Bootstrap row. */
.m__pslider__swiper-slide-row {
  display: flex;
  align-items: flex-start;
  margin-inline: -12px;
}
/* The photograph is pulled 22.5% left of its own column (10% once its slide
   is active), so it reaches back past the 50% line into this column. The text
   sits above it on z-index, which means an overlap does not clip — it just
   prints the address over the picture. Capping the text at the width that
   stays clear of the active image is what actually prevents that; the column
   itself keeps the live module's flex basis so the layout rhythm is intact. */
.m__pslider__swiper-slide-left {
  z-index: 2;
  flex: 0 0 41.6667%;   /* live col-xxl-5 of 12 */
  max-width: 41.6667%;
  /* 12px cancels the row's negative inline margin, 70px matches
     .m__fmedia__content, and 1px stands in for the border-left that shifts
     .m__fmedia__row's content — the hairline here is a pseudo-element, which
     takes no layout space, so without it this heading sat 1px left of HOME. */
  padding-left: 83px;
  padding-right: 12px;
}
/* Active image starts at 50% - 10% of the right column (= 45% of the row),
   so the text stops just short of that, measured from the row rather than
   from its own column. */
.m__pslider__swiper-slide-title,
.m__pslider__swiper-slide-divider,
.m__pslider__swiper-slide-content { max-width: min(100%, 380px); }
.m__pslider__swiper-slide-right {
  z-index: 1;
  flex: 0 0 50%;
  max-width: 50%;
  padding-inline: 12px;
  padding-bottom: 75px;
  position: relative;
}

/* The slide content slides in from the right once its slide is active.
   Held at opacity 0 until then, matching the live staggered reveal. */
.m__pslider__swiper-slide-title {
  /* The live module pads this title 51px/57px, which gave it a 178px box
     against HOME's 75.6px and left far too much air above the rule. HOME's
     title is plain .f__poster with no padding at all — the divider's own 50px
     margin is the entire gap — so this matches that. Font size stays at the
     module's 50px rather than .f__poster's 54px, because 54px pushes
     "HO CHI MINH CITY" back over the photograph. */
  padding-top: 0;
  padding-bottom: 0;
  font-size: 50px;
  display: block;
  /* The live module sets `overflow-wrap: normal` because its longest title is
     "TAICHUNG". "HO CHI MINH CITY" is far longer and overflowed the column
     into the photograph, so the title wraps between words here. It is a
     multi-word place name, so there is always a break point available and the
     mid-word breaking `anywhere` would allow is never needed. */
  overflow-wrap: break-word;
  margin: 0;
  opacity: 0;
  transform: translateX(50px);
  transition: opacity .5s ease, transform .5s ease;
  transition-delay: .1s;
}
/* 90px wide with 50px beneath it, matching .m__fmedia__divider rather than
   the live module's 114px/39px, so the rule under the location name reads the
   same as the one under HOME. */
.m__pslider__swiper-slide-divider {
  background-color: var(--color-gold);
  height: 1px;
  width: 90px;
  margin-bottom: 50px;
  opacity: 0;
  transform: translateX(50px);
  transition: opacity .5s ease, transform .5s ease;
  transition-delay: .1s;
}
.m__pslider__swiper-slide-content {
  margin-bottom: 35px;
  opacity: 0;
  transform: translateX(50px);
  transition: opacity .5s ease, transform .5s ease;
  transition-delay: .2s;
}
/* The Vietnam slides carry the same ADDRESS / PHONE / EMAIL rows as
   locations.html, so they reuse .m__map__content-row* wholesale rather than
   duplicating that styling here. Only the reveal and the row rhythm differ. */
.m__pslider__swiper-slide-content .m__map__content-row { margin-bottom: 24px; }
.m__pslider__swiper-slide-content .m__map__content-row:last-child { margin-bottom: 0; }
/* The email is a single unbreakable token (454px) wider than the live
   module's 80% text column (384px), so on the two Vietnam slides it ran out
   over the photograph. Breaking it mid-word reads badly, so the address
   block gets the full column width instead and the address keeps its 80%.
   The overseas slides carry no email and were never affected. */
/* The email is one unbreakable token wider than the 380px text cap. Breaking
   it mid-word reads badly, so it goes on its own line at a smaller size that
   fits whole. It sits below the phone number with only the photograph's blank
   lower half beside it, so the extra width costs nothing. */
.m__pslider__swiper-slide-mail {
  color: inherit;
  text-decoration: underline;
  display: inline-block;
  white-space: nowrap;
  font-size: 0.72em;
  letter-spacing: 0.01em;
}
.m__pslider__swiper-slide-mail:hover,
.m__pslider__swiper-slide-mail:focus-visible { color: var(--color-green); }

.m__pslider__swiper-slide-highlight {
  height: 617px;
  width: 112.5%;
  margin-left: -22.5%;
  transition: all .5s ease;
  transition-delay: .25s;
}

/* The cream card overlaps the bottom of the photograph. */
.m__pslider__swiper-slide-card {
  opacity: 0;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1;
  margin: 0 auto;
  width: 480px;
  background-color: var(--color-cream);
  padding: 66px 80px 65px;
  transform: translateY(20px);
  transition: opacity .5s ease, transform .5s ease;
  transition-delay: .5s;
}
.m__pslider__swiper-slide-card-title { margin-bottom: 24px; }
.m__pslider__swiper-slide-card-divider {
  width: 90px;
  height: 1px;
  background-color: var(--color-gold);
  margin-bottom: 24px;
}
.m__pslider__swiper-slide-excerpt { margin-bottom: 24px; margin-top: 0; }

/* Active slide: everything settles into place and the photo widens. */
.m__pslider__swiper-slide.swiper-slide-active .m__pslider__swiper-slide-title,
.m__pslider__swiper-slide.swiper-slide-active .m__pslider__swiper-slide-divider,
.m__pslider__swiper-slide.swiper-slide-active .m__pslider__swiper-slide-content,
.m__pslider__swiper-slide.swiper-slide-active .m__pslider__swiper-slide-card {
  opacity: 1;
  transform: translateX(0);
}
.m__pslider__swiper-slide.swiper-slide-active .m__pslider__swiper-slide-card {
  transform: translateY(0);
}
.m__pslider__swiper-slide.swiper-slide-active .m__pslider__swiper-slide-highlight {
  width: 110%;
  margin-left: -10%;
}

/* Arrows. As with .m__cslider these are real <button>s, so the
   appearance is reset. */
.m__pslider__swiper-arrow {
  position: absolute;
  top: 45%;
  width: 80px;
  height: 80px;
  background-color: var(--color-gold);
  border: 1px solid var(--color-gold);
  border-radius: 50%;
  z-index: 5;
  padding: 26px 31px;
  cursor: pointer;
  transition: all .25s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-appearance: none;
  appearance: none;
}
.m__pslider__swiper-arrow:hover,
.m__pslider__swiper-arrow:focus-visible { transform: scale(1.05); }
.m__pslider__swiper-arrow.hidden { opacity: 0; pointer-events: none; }
.m__pslider__swiper-arrow img { width: 18px; height: auto; }
/* Same offsets as .m__cslider's arrows so both sections' controls sit on the
   identical vertical: -45px hangs the prev arrow into u__wrap's gutter and
   3px keeps the next arrow inside the wrap's right edge. The earlier -88px
   pushed the next arrow 43px past the wrap, where the viewport clipped it. */
.m__pslider__swiper-prev { left: -45px; right: auto; }
.m__pslider__swiper-next { right: 3px; left: auto; }

/* As with .m__cslider, the scale is the hover affordance rather than
   decoration, so reduced motion shortens the durations instead of
   removing the transforms — which would kill the reveal outright and
   leave every slide stuck at opacity 0. */
@media (prefers-reduced-motion: reduce) {
  .m__pslider__swiper-arrow,
  .m__pslider__swiper-slide-highlight,
  .m__pslider__swiper-slide-title,
  .m__pslider__swiper-slide-divider,
  .m__pslider__swiper-slide-content,
  .m__pslider__swiper-slide-card { transition-duration: 150ms; transition-delay: 0s; }
}

/* Narrower columns below 1400px: the left column's padding tracks
   .m__fmedia__content's own 1400px value, and the title scales down so the
   longest name still clears the photograph. */
@media only screen and (max-width: 1400px) {
  /* 12px gutter + 25px (.m__fmedia__content's own 1400px padding) + 1px for
     the border, so the heading stays on HOME's vertical here too. */
  .m__pslider__swiper-slide-left { padding-left: 38px; }
  /* At 50px the word "MINH" alone is wider than the text cap, so the longest
     title still reached the photograph. The type scales down with the column
     rather than the cap growing, which would put the text back over the
     image. */
  .m__pslider__swiper-slide-title { font-size: 42px; }
}

/* Below the live xl breakpoint the slide stacks: photo first at full
   width, then the card in normal flow, then the address block. */
@media only screen and (max-width: 1200px) {
  .m__pslider { margin-bottom: 48px; }
  .m__pslider__title { margin-bottom: 27px; }
  /* Below the desktop breakpoint the six tabs cannot fit, so the strip
     becomes a single horizontally scrollable row. Both `nowrap` and
     `flex-shrink: 0` are required: without the latter, a nowrap flex
     container still squeezes the items until their labels wrap, which
     produced a second row that horizontal scrolling could never reach.
     The live site forces this with a flat `min-width: 1500px`, which
     overshoots and leaves dead space at the end of the scroll; sizing
     to the content instead scrolls exactly as far as there are tabs. */
  .m__pslider__nav {
    margin-bottom: 27px;
    flex-wrap: nowrap;
    width: max-content;
    min-width: 100%;
  }
  .m__pslider__nav > li { flex: 0 0 auto; }
  .m__pslider__nav-item { white-space: nowrap; }
  .m__pslider__nav-wrapper {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    /* Fade the right edge so it reads as scrollable rather than cut off. */
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
  }
  /* Once scrolled to the end there is nothing more to hint at. */
  .m__pslider__nav-wrapper.is-scroll-end {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .m__pslider__swiper-slide { margin-left: auto; border-left: 0; }
  .m__pslider__swiper-slide.swiper-slide-active { border: 0; }

  /* Stacked: no columns, so the row drops its negative gutter margin. Left in
     place, that -12px made every slide's content wider than its slide, which
     spilled past the track's clip and showed the next slide's photo as a
     strip down the right edge. */
  .m__pslider__swiper-slide-row {
    display: block;
    margin-inline: 0;
  }
  /* The section hairline goes too, exactly as .m__fmedia's border-left does
     below this breakpoint — there is no side-by-side column for it to edge. */
  .m__pslider .u__wrap::before { display: none; }
  .m__pslider__swiper-slide-left,
  .m__pslider__swiper-slide-right {
    flex: none;
    max-width: 100%;
    padding-left: 0;
  }
  .m__pslider__swiper-slide-right { padding-bottom: 0; }

  .m__pslider__swiper-slide-title { width: 100%; padding-top: 0; padding-bottom: 24px; font-size: 36px; }
  .m__pslider__swiper-slide-divider { margin-bottom: 24px; }
  .m__pslider__swiper-slide-content { max-width: 100%; margin-bottom: 24px; }
  .m__pslider__swiper-slide-highlight { height: 342px; width: 100%; margin-left: 0; margin-bottom: 24px; }
  .m__pslider__swiper-slide.swiper-slide-active .m__pslider__swiper-slide-highlight {
    width: 100%;
    margin-left: 0;
  }
  .m__pslider__swiper-slide-card {
    max-width: 100%;
    width: 100%;
    position: relative;
    margin: 0 auto 24px;
    padding: 24px 24px 28px;
  }
  /* `overflow: visible` on the swiper lets the desktop photo bleed past its
     column, but once the slides stack it also lets the NEXT slide's image
     show as a strip down the right edge. Clipping the track (not .u__wrap,
     which must stay visible for the arrows) hides it. */
  .m__pslider__swiper { overflow: hidden; }
  .m__pslider__swiper-arrow {
    top: 38%;
    width: 40px;
    height: 40px;
    padding: 3px 10px;
  }
  .m__pslider__swiper-arrow img { width: 15px; }
  .m__pslider__swiper-prev { left: -20px; }
  .m__pslider__swiper-next { right: -20px; }
}

/* Fallback if the Swiper CDN script does not load: show only the first
   slide (the tabs and arrows cannot work without Swiper) and drop the
   reveal offsets so the content is actually legible. */
.m__pslider__swiper:not(.swiper-initialized) .m__pslider__swiper-slide + .m__pslider__swiper-slide { display: none; }
.m__pslider__swiper:not(.swiper-initialized) .m__pslider__swiper-slide-title,
.m__pslider__swiper:not(.swiper-initialized) .m__pslider__swiper-slide-divider,
.m__pslider__swiper:not(.swiper-initialized) .m__pslider__swiper-slide-content,
.m__pslider__swiper:not(.swiper-initialized) .m__pslider__swiper-slide-card {
  opacity: 1;
  transform: none;
}
.m__pslider__swiper:not(.swiper-initialized) ~ .m__pslider__swiper-arrow,
.m__pslider__swiper:not(.swiper-initialized) .m__pslider__nav-wrapper { display: none; }

/* ---------------------------------------------------------------- */
/* Generic content sections                                          */
/* ---------------------------------------------------------------- */
.section { padding-block: var(--space-3xl); }
.section--tight { padding-block: var(--space-2xl); }
.section--cream-soft { background: var(--color-cream-soft); }
.section--green { background: var(--color-green); }
.section-heading { margin-bottom: var(--space-2xl); text-align: center; }
.section-lead { margin: 0 auto var(--space-xl); max-width: 620px; text-align: center; }

/* ---------------------------------------------------------------- */
/* Home business-lunch promo re-entry link                            */
/* ---------------------------------------------------------------- */
.promo-strip {
  display: grid;
  grid-template-columns: minmax(0, 200px) minmax(0, 1fr);
  gap: var(--space-xl);
  align-items: center;
  border: 1px solid rgba(151, 111, 65, 0.35);
  padding: var(--space-xl);
}
.promo-strip__media img { width: 100%; }
.promo-strip__title { margin-bottom: var(--space-sm); }
.promo-strip__copy { margin: 0 0 var(--space-lg); }
@media (max-width: 720px) {
  .promo-strip { grid-template-columns: minmax(0, 1fr); text-align: center; }
  .promo-strip__media { max-width: 200px; margin: 0 auto; }
}

/* ---------------------------------------------------------------- */
/* story-card-small — shared PDF/article card (menu + news pages)    */
/* Geometry and transitions are the live values from                  */
/* mockups/_source/style.min.css.                                     */
/* ---------------------------------------------------------------- */
.story-card-small-highlight {
  width: 100%;
  overflow: hidden;
  height: 285px;
  margin-bottom: 35px;
}
.story-card-small-highlight-wrapper {
  height: 100%;
  transition: transform .5s ease;
}
.story-card-small-title {
  transition: all .25s ease;
  padding-bottom: 20px;
  min-height: 133px;
  font-size: 20px;
}
.story-card-small a { display: block; }
.story-card-small a:hover { text-decoration: none; }
.story-card-small a:hover img { transform: scale(1.05); }
.story-card-small a:hover .story-card-small-title { color: var(--color-gold); }
.story-card-small img { transition: transform .25s ease; }
/* The link wraps image and title, so the ring must be visible around the
   whole card rather than only the text node. */
.story-card-small a:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 4px;
}

/* The hover scale is the affordance that tells a reviewer the card is
   interactive, so reduced motion shortens the duration instead of
   removing the transform. */
@media (prefers-reduced-motion: reduce) {
  .story-card-small-highlight-wrapper,
  .story-card-small img { transition-duration: 120ms; }
  .story-card-small-title { transition-duration: 120ms; }
}

@media only screen and (max-width: 1200px) {
  .story-card-small-highlight { height: 324px; margin-bottom: 24px; }
  .story-card-small-title { min-height: 0; }
}

/* ---------------------------------------------------------------- */
/* Card grids — the live Bootstrap row-cols behaviour, hand-rolled   */
/* since this build ships no Bootstrap. xl is 1200px throughout.     */
/* ---------------------------------------------------------------- */
.card-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-xl);
  justify-content: center;
}
/* row-cols-md-2 */
@media only screen and (min-width: 768px) {
  .card-grid--4up { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* row-cols-lg-3 */
@media only screen and (min-width: 992px) {
  .card-grid--4up { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* row-cols-xl-4 */
@media only screen and (min-width: 1200px) {
  .card-grid--4up { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* col-12 col-xl-4 — news page is 1-up below xl, 3-up at xl. */
  .card-grid--3up { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* justify-content-center with a short final row: the two Special Offer
   cards centre rather than stretching to fill four tracks. */
.card-grid--centred {
  justify-content: center;
  grid-auto-flow: row;
}
@media only screen and (min-width: 768px) {
  .card-grid--centred { grid-template-columns: repeat(2, minmax(0, min(100%, 320px))); }
}

/* ---------------------------------------------------------------- */
/* Menu page                                                         */
/* ---------------------------------------------------------------- */
.m__menus { padding-block: var(--space-3xl); }
.m__menus__title { text-align: center; margin-bottom: var(--space-2xl); }
.m__menus__title + .card-grid { margin-bottom: var(--space-3xl); }
.m__menus .card-grid:last-child { margin-bottom: 0; }
@media only screen and (max-width: 1200px) {
  .m__menus { padding-block: var(--space-2xl); }
  .m__menus__title { margin-bottom: var(--space-xl); }
  .m__menus__title + .card-grid { margin-bottom: var(--space-2xl); }
}

/* Menu content now lives entirely in the linked PDFs, matching the live
   site; the note below the grids explains that to reviewers. */
.menu-pdf-note {
  max-width: 720px;
  margin: var(--space-2xl) auto 0;
  text-align: center;
  color: var(--color-muted);
  font-size: 15px;
}

/* ---------------------------------------------------------------- */
/* News page                                                         */
/* ---------------------------------------------------------------- */
.m__pfeed { padding-block: var(--space-3xl); }
@media only screen and (max-width: 1200px) {
  .m__pfeed { padding-block: var(--space-2xl); }
}
/* The live site draws a 16:9 background-image div; a real <img> with the
   same ratio is used instead so the artwork is content, not decoration.
   The frame therefore takes its height from the ratio rather than the
   fixed 285px the menu cards use. */
.story-card-small-highlight--ratio {
  height: auto;
  aspect-ratio: 16 / 9;
  margin-bottom: var(--space-md);
}
.story-card-small-highlight--ratio .story-card-small-highlight-wrapper { height: 100%; }
@media only screen and (max-width: 1200px) {
  .story-card-small-highlight--ratio { height: auto; margin-bottom: var(--space-md); }
}
.news-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.news-card__meta { margin: 0 0 var(--space-xs); }

/* ---------------------------------------------------------------- */
/* Locations page — live m__map, m__llist and m__ltext modules       */
/* Geometry is taken verbatim from mockups/_source/tw-style.min.css  */
/* except where noted. The live page pairs each location with a      */
/* Google map; this build has no Maps API key and the Vietnam        */
/* addresses are unconfirmed, so that half carries a featured venue  */
/* image instead (explicit client request).                          */
/* ---------------------------------------------------------------- */

/* Full-bleed out of the wrap. The live rule is -49px/-24px, which cancels
   the live 48px/24px main.ignite-modules padding plus 1px of Bootstrap
   gutter rounding. This build has no Bootstrap gutters, so the negatives
   are exactly -48px/-24px to match main's own padding — -49px would leave
   a 1px sliver past the viewport edge and trip horizontal overflow. */
/* The live Taiwan page runs these rows full-bleed via negative side margins.
   Here they sit inside the page gutter instead, matching the Our Story
   (.m__fmedia) sections, so the image and the information panel both keep a
   margin from the viewport edges. */
.m__map {
  margin-bottom: 81px;
}
.m__map__row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
}
/* Image right / cream panel left on the second location, for rhythm.
   Follows the .m__fmedia__row--reverse approach already in this sheet. */
.m__map__row--reverse { flex-direction: row-reverse; }
/* Live rule is `.m__map .col-xl-6 { padding: 0 }` — the two halves butt
   together with no gutter. */
.m__map__col {
  flex: 0 0 100%;
  padding: 0;
  min-width: 0;
}

/* The live panel and map are both a fixed 850px. Measured in the browser, the
   Vietnam panel content (four info rows, three hours sub-headings and a
   directions block per location) needs 865px at every desktop width, so a
   fixed 850px would clip it — the live Taiwan copy is shorter and happens to
   fit. `min-height: 850px` plus `align-items: stretch` keeps the live
   proportion and the equal-height pairing, and lets the row grow instead of
   scrolling internally if the client's final copy runs longer still.
   The image half stretches to whatever the panel settles on. */
.m__map__media {
  width: 100%;
  min-height: 850px;
  overflow: hidden;
}
.m__map__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.m__map__content {
  background-color: #feead8;
  min-height: 850px;
  padding: 45px 60px;
}
.m__map__content-title { margin-bottom: 24px; }
.m__map__content-divider {
  height: 1px;
  width: 90px;
  background-color: #976f41;
  margin-bottom: 48px;
}
.m__map__content-row { margin-bottom: 20px; }
.m__map__content-row:last-child { margin-bottom: 0; }
.m__map__content-row-title { margin-bottom: 10px; }
.m__map__content-row-content p { margin: 0 0 var(--space-md); }
.m__map__content-row-content p:last-child { margin-bottom: 0; }
.m__map__content-row-content a {
  color: inherit;
  text-decoration: underline;
}
.m__map__content-row-content a:hover { color: #005b28; }

/* Boundary is 1201px, not the 1200.01px used elsewhere in this sheet: Chrome
   rounds a fractional min-width down, so `min-width: 1200.01px` and
   `max-width: 1200px` BOTH match at exactly 1200px. That overlap put a 342px
   mobile image frame beside a full-height desktop panel at 1200px. Integer
   bounds make the switch exclusive. */
@media (min-width: 1201px) {
  .m__map__col { flex: 0 0 50%; }
  /* Both halves fill whichever height the taller one settles on. Scoped to
     desktop: this child selector outranks the 342px mobile rule below, so
     leaving it unscoped let the image keep its intrinsic ratio on mobile. */
  .m__map__col > .m__map__media,
  .m__map__col > .m__map__content { height: 100%; }
}
@media only screen and (max-width: 1200px) {
  .m__map {
    margin-bottom: 48px;
  }
  /* Live mobile values: 342px map frame, auto-height panel. */
  .m__map__media { min-height: 0; height: 342px; }
  .m__map__content {
    min-height: 0;
    height: auto;
    padding: 27px 24px;
  }
  .m__map__content-divider { margin-bottom: 24px; }
  /* row-reverse would put the image below the panel once the row collapses
     to a single column, which reads as the wrong order. */
  .m__map__row--reverse { flex-direction: row; }
}

/* ---- Explore More Asia Locations — live m__llist ---- */
.m__llist { padding-bottom: var(--space-xl); }
/* The live site gives this line body-copy styling (f__paragraph-medium on a
   bare <p>), which reads as a stray label rather than the heading for three
   cards. It keeps the live class in the markup for comparability but is
   restyled here to the section-header treatment used elsewhere on the site. */
.m__llist__heading {
  margin-bottom: var(--space-xl);
  font-family: gill-sans-nova, "Noto Sans TC";
  font-weight: 700;
  font-size: 21px;
  line-height: 32px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--color-gold);
}
.m__llist__heading p { margin: 0; }
@media only screen and (max-width: 576px) {
  .m__llist__heading { font-size: 16px; line-height: 20px; }
}
.m__llist__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-xl);
}
/* col-12 col-xl-4 — 1-up below xl, 3-up at xl. This build's xl is 1200px, and
   the down-level rules on this page are `max-width: 1200px`, so the up-level
   bound is 1201px — see the note on .m__map__col above. A bare 1200px (or a
   fractional 1200.01px, which Chrome rounds down) makes both queries match at
   exactly 1200px and leaves this grid 3-across while the location rows have
   already stacked. */
@media only screen and (min-width: 1201px) {
  .m__llist__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Live height is 540px. The three source images are 960x540, 960x540 and
   933x700, so a 540px frame upscales two of them. 360px is the equal height
   used across all three cards instead — it holds a 16:9 crop at the ~400px
   column width these cards occupy inside the 1344px wrap. */
.m__llist__card-highlight {
  width: 100%;
  height: 360px;
  overflow: hidden;
  margin-bottom: 32px;
}
/* The scale target is the wrapper, not the img, so the 1.1 scale is clipped
   by the overflow:hidden frame above it. */
.m__llist__card-highlight-wrapper {
  width: 100%;
  height: 100%;
  transition: transform .5s ease;
}
.m__llist__card-highlight-wrapper img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.m__llist__card a { display: block; }
.m__llist__card-title {
  transition: color .25s ease;
  margin-bottom: 16px;
}
.m__llist__card-divider {
  height: 1px;
  width: 115px;
  background-color: #976f41;
  margin-bottom: 16px;
}
.m__llist__card-excerpt { margin-bottom: 16px; }
.m__llist__card-link { margin-bottom: 0; }
.m__llist__card a:hover { text-decoration: none; }
.m__llist__card a:hover .m__llist__card-title { color: #976f41; }
.m__llist__card a:hover .m__llist__card-highlight-wrapper { transform: scale(1.1); }
/* The link wraps image, title and excerpt, so the ring must surround the
   whole card rather than only the text node. */
.m__llist__card a:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 4px;
}

/* The image scale and title colour shift are the affordance that tells a
   reviewer the card is interactive, so reduced motion shortens the
   durations rather than removing the transform. */
@media (prefers-reduced-motion: reduce) {
  .m__llist__card-highlight-wrapper { transition-duration: 150ms; }
  .m__llist__card-title { transition-duration: 120ms; }
}

@media only screen and (max-width: 1200px) {
  .m__llist__card-highlight { height: 342px; margin-bottom: 24px; }
}

/* ---- MENUS page: chef statement and the two-up location index ---- */
/* The reference menu page opens with a culinary-officer statement, then lists
   one card per venue. Both reuse modules already in this sheet rather than
   adding new ones: m__ltext for the statement, m__llist for the cards. */

/* m__ltext holds its copy to one line with nowrap for the contact-page
   sentence. A multi-sentence quote cannot do that, so this variant wraps
   normally and takes the narrower measure the reference uses. */
.m__ltext--quote { padding-top: 80px; padding-bottom: 80px; }
/* .m__ltext .u__wrap sets 1344px later in this file at equal specificity,
   so the variant is qualified with .m__ltext to outrank it in place. */
.m__ltext.m__ltext--quote .u__wrap { max-width: 1000px; }
.m__ltext.m__ltext--quote .m__ltext__copy {
  white-space: normal;
  text-align: center;
  margin-bottom: 24px;
}
.m__ltext__quote { margin: 0; }
.m__ltext__cite { text-align: center; }
/* <footer> inside <blockquote> is the attribution; the em dash is decorative
   so it lives in CSS rather than the text node a screen reader announces. */
.m__ltext__cite::before { content: "\2014\00a0 "; }

/* m__llist__grid is 3-up at xl. Two cards in a 3-up track would each sit at a
   third of the wrap with a third left empty, so this variant switches to two
   equal columns. The same 1201px bound as the base grid: see the note there on
   why a bare 1200px double-matches. */
@media only screen and (min-width: 1201px) {
  .m__llist--2up .m__llist__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Wider columns than the 3-up case, so the frame gets proportionate height. */
@media only screen and (min-width: 1201px) {
  .m__llist--2up .m__llist__card-highlight { height: 440px; }
}
/* The section heading here is .m__menus__title, whose spacing rule targets
   `+ .card-grid`. This grid is m__llist__grid, so it needs its own. */
.m__menus__title + .m__llist__grid { margin-bottom: 0; }

/* Back to the location index from a venue's menu page. */
.menu-back-link { margin-top: -32px; margin-bottom: 16px; }
.menu-back-link a { text-decoration: none; }
.menu-back-link a:hover,
.menu-back-link a:focus-visible { text-decoration: underline; }

@media only screen and (max-width: 1200px) {
  .m__ltext--quote { padding-top: 48px; padding-bottom: 48px; }
}

/* ---- PRIVATE EVENTS pages ---- */
/* The reference event pages reuse the modules already in this sheet
   (m__hero, m__ltext, m__fmedia, m__pslider, m__map, contact-form). Only the
   pieces below have no existing equivalent: the standalone section heading,
   the venue at-a-glance list, the capability strip and the two list blocks. */

/* Standalone section heading. .m__menus__title centres its heading and owns a
   `+ .card-grid` spacing rule, which does not apply where the next element is
   a plain list or an fmedia row, so these pages use their own. */
/* Left-aligned, matching "S&W Regional Footprint" on the home page: every
   section heading on these pages sits on the same vertical as the page
   gutter rather than some centred and some not. */
.pe__section-title { text-align: left; margin-bottom: var(--space-2xl); }

/* These two use the shared .m__menus__title, which stays centred for the
   per-venue menu pages' own headings. Scoped by id so only these move. */
#menus-by-location,
#our-locations { text-align: left; }
/* The slider headings use .m__pslider__title's own 50px margin instead, so
   they sit identically to the home page's "S&W Regional Footprint". */
.pe__section-head { padding-top: 40px; }

/* Venue at-a-glance: the reference page sets three short points under the
   hero, then a capability strip. Both are lists, so list-style is dropped
   rather than the markup being flattened to paragraphs. */
.pe__venue-intro { padding-top: 40px; padding-bottom: 80px; text-align: center; }
.pe__venue-points,
.pe__venue-specs,
.pe__functions-list { list-style: none; margin: 0; padding: 0; }
.pe__venue-points { max-width: 760px; margin: 0 auto var(--space-xl); }
/* The at-a-glance figures are all still pending, so this framing sentence —
   the reference page's own — carries the section while they are TBD. */
.pe__venue-lede { max-width: 820px; margin: 0 auto var(--space-lg); }
.pe__venue-points li { margin-bottom: var(--space-sm); }
.pe__venue-points li:last-child { margin-bottom: 0; }

/* Capability strip — A/V, WI-FI, CAPACITY. Inline row on wide screens,
   separated by gold rules; flex-wrap lets it fall to two lines rather than
   overflow, and the rules are drawn on the item so a wrapped line does not
   start with a stray divider. */
.pe__venue-specs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-md) 0;
  margin-bottom: var(--space-xl);
}
.pe__venue-specs li { padding: 0 var(--space-lg); }
.pe__venue-specs li + li { border-left: 1px solid #976f41; }
.pe__venue-cta { margin: 0; }

/* Events and functions: each occasion type with its own line icon. Two
   columns from the 1200px tier up so seven items do not run as one long
   ladder. The list was `columns: 2`, which cannot hold an icon in line with
   wrapping text — a fragmented item would leave its mark stranded in the
   other column — so it is a grid instead, filled down the first column then
   the second to keep reading order. */
.pe__functions { padding-bottom: 100px; }
/* The heading stays left-aligned on the page gutter; the grid itself is
   centred under it (client direction). Items keep a shared left edge inside
   each column rather than each being centred independently — the labels are
   unequal widths ("Bar" against "Conference Meetings"), so per-item centring
   would make the icon column zigzag. */
.pe__functions-list { margin-inline: auto; width: fit-content; }
.pe__functions-list li {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-sm);
}
.pe__functions-list li:last-child { margin-bottom: 0; }
/* The mark is decorative and the label carries the meaning, so the icon is
   the item that gives way: it never shrinks, and the text wraps beside it. */
.pe__functions-icon {
  flex: 0 0 auto;
  color: var(--color-gold);
  stroke-width: 1.5px;
}

/* Card eyebrow on the OUR LOCATIONS cards — the reference sets a short label
   above the venue name. Sits tight to the title it introduces. */
.m__llist__card-eyebrow { margin-top: var(--space-md); margin-bottom: var(--space-2xs); }

/* Back to the venue index from a venue's event page. .u__wrap carries the page
   gutter, so this only needs its own vertical rhythm. */
.pe__back-link { margin-top: 0; margin-bottom: 80px; }
.pe__back-link a { text-decoration: none; }
.pe__back-link a:hover,
.pe__back-link a:focus-visible { text-decoration: underline; }

/* LOCATION DETAILS heading sits inside m__map, whose own row is full-bleed;
   the heading takes the wrap, so it needs the gap to the row below. */
.m__map .pe__section-title { margin-bottom: var(--space-xl); }

@media only screen and (min-width: 1201px) {
  /* Three columns. Measured against the widest item (226px, "Conference
     Meetings"): 3 columns need 871px and the wrap offers 1104px at the
     1200px tier, so it fits with room to spare, where 4 columns would need
     1193px and overflow there. Rows are explicit so the flow runs down each
     column and the reading order stays vertical.
     Note this block is inside the sheet's `min-width: 1201px` tier, so the
     1101-1200px band is covered by its own rule below. */
  .pe__functions-list {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(3, auto);
    column-gap: var(--space-3xl);
  }
}

/* 1101-1200px: still wide enough for three columns (871px needed against
   1104px usable at 1200px), which the sheet's 1201px tier does not reach. */
@media only screen and (min-width: 1101px) and (max-width: 1200px) {
  .pe__functions-list {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(3, auto);
    column-gap: var(--space-3xl);
  }
}

/* Between 641px and 1200px three columns overflow (871px needed, 672px
   available at 768px), so the strip drops to two; below 641px a single
   column, since two need 548px. */
@media only screen and (min-width: 641px) and (max-width: 1100px) {
  .pe__functions-list {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(4, auto);
    column-gap: var(--space-xl);
  }
}

@media only screen and (max-width: 1200px) {
  .pe__section-title { margin-bottom: var(--space-xl); }
  .pe__section-head { padding-top: 24px; }
  .pe__venue-intro { padding-top: 24px; padding-bottom: 48px; }
  .pe__functions { padding-bottom: 48px; }
  .pe__back-link { margin-bottom: 48px; }
  /* Stacked, the vertical rules would read as stray marks, so the strip
     becomes a column and the dividers are dropped. */
  .pe__venue-specs { flex-direction: column; }
  .pe__venue-specs li { padding: 0; }
  .pe__venue-specs li + li { border-left: 0; }
}


/* ---- PRIVATE EVENTS: the HOSPITALITY IS IN OUR BLOOD slider ---- */
/* Same m__pslider module as the home page's regional footprint. The section
   heading now uses the same .f__section-header + .u__c-gold convention as
   "S&W Regional Footprint", so the module's own hairline offset (top: 166px,
   calibrated to that title) applies unchanged and needs no override here. */

/* The tab strip is a horizontal scroller below 1200px: the module sets
   flex-wrap: nowrap and white-space: nowrap there, and .m__pslider__nav-wrapper
   scrolls with its own scroll-end affordance. These three labels are longer
   than the home page's city names, so the strip does need dragging at 390px —
   left as the module intends rather than overridden, since the home page's six
   tabs rely on the same behaviour and the arrows switch panels too. */
/* The module's 50px slide title is sized for the home page's city names.
   "DEDICATED EVENT PROFESSIONALS" is far longer and broke mid-word
   ("PROFESSION / ALS") in the 530px copy column, so this section's titles
   step down and are allowed to break only between words. */
.pe__hosp .m__pslider__swiper-slide-title {
  font-size: 34px;
  overflow-wrap: normal;
  word-break: keep-all;
  hyphens: none;
}
@media only screen and (max-width: 1200px) {
  .pe__hosp .m__pslider__swiper-slide-title { font-size: 30px; }
}

/* Without a cream card overlapping it, a bare 617px frame left the copy
   column short by 152px. Matching the frame to the copy keeps the pair
   balanced; the reference panels are image-above-text, so no card is
   expected here. */
@media only screen and (min-width: 1201px) {
  .pe__hosp .m__pslider__swiper-slide-highlight { height: 470px; }
}

/* ---- Form intro line — live m__ltext ---- */
/* Live section padding: 25px top / 80px bottom, 0 / 48px below 1200px. */
.m__ltext {
  padding-top: 25px;
  padding-bottom: 80px;
}
/* The live rule narrows this wrap to 888px, which is enough for the Taiwan
   page because its own copy is shorter. At 28px our sentence wrapped onto two
   lines there, so the wrap keeps the page measure and the line is held to one
   with nowrap; it drops back to normal wrapping on mobile, where one line is
   impossible. */
.m__ltext .u__wrap { max-width: 1344px; }
.m__ltext__copy { white-space: nowrap; }
@media only screen and (max-width: 1200px) {
  .m__ltext__copy { white-space: normal; }
}
.m__ltext__copy { margin: 0; }
/* .f__paragraph-large did not exist in this sheet — added from the live
   stylesheet (28px, stepping to 22px below 1200px). */
.f__paragraph-large {
  font-family: schoolbook, "Noto Sans TC";
  font-style: normal;
  font-weight: 400;
  font-size: 28px;
  line-height: 180%;
}
@media only screen and (max-width: 1200px) {
  .m__ltext {
    padding-top: 0;
    padding-bottom: 48px;
  }
  .f__paragraph-large { font-size: 22px; }
}

.text-center { text-align: center; }

/* ---------------------------------------------------------------- */
/* Contact form (Locations page, bottom)                             */
/* ---------------------------------------------------------------- */
.contact-form { padding-bottom: var(--space-3xl); }
/* Live layout: fields 1-3 are col-xl-4 (three across), 4-5 are col-xl-6
   (two across), comments and submit col-12. Hand-rolled on a 6-track grid
   so both splits share one container; xl is 1200px throughout this build. */
.contact-form__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--space-lg);
}
@media only screen and (min-width: 1201px) {
  .contact-form__grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .form-field--third { grid-column: span 2; }
  .form-field--half { grid-column: span 3; }
  .form-field--full { grid-column: 1 / -1; }
}
.form-required-note {
  font-size: 15px;
  color: var(--color-brown);
  margin: 0 0 var(--space-lg);
}
.form-field { margin-bottom: var(--space-lg); }
/* Uppercase, matching the live Taiwan form labels. Applied in CSS rather than
   by rewriting the markup, so the accessible name keeps its natural casing for
   screen readers. */
.form-field label {
  display: block;
  font-family: schoolbook, "Noto Sans TC";
  font-size: 16px;
  color: var(--color-ink);
  margin-bottom: var(--space-xs);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.form-field .req { color: var(--color-brown); }
.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  font-family: schoolbook, "Noto Sans TC";
  font-size: 16px;
  padding: 0.7rem 0.85rem;
  border: 1px solid rgba(33, 37, 41, 0.3);
  background: var(--color-white);
  color: var(--color-ink);
}
.form-field input:focus-visible,
.form-field select:focus-visible,
.form-field textarea:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 1px;
}
.form-field textarea { resize: vertical; min-height: 130px; }
.form-note {
  font-size: 14px;
  color: var(--color-muted);
  font-style: italic;
  margin-bottom: var(--space-lg);
}
.contact-form__submit { text-align: center; }

/* "Get Directions" — a secondary gold text link, deliberately lighter than
   the RESERVATIONS button so it does not compete with the primary CTA.
   Until each Vietnam address is confirmed it renders as a non-focusable
   pending state rather than a link to an empty map. */
/* The pending link and its note stack, but as normal flow rather than flex:
   a flex item stretches to the line and .tbd would then underline the whole
   width. ::before on the note forces the break instead. */
.directions { margin-top: var(--space-xs); }
.directions__note::before { content: ""; display: block; }
.directions__link,
.directions__pending {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-sans);
  font-size: 13px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--color-gold);
}
.directions__link {
  text-decoration: underline;
  text-decoration-color: rgba(151, 111, 65, 0.45);
  text-underline-offset: 3px;
  transition: text-decoration-color var(--dur-fast) var(--ease-out);
}
.directions__link:hover { text-decoration-color: var(--color-gold); }
@media (prefers-reduced-motion: reduce) {
  .directions__link { transition-duration: 80ms; }
}
.directions__pending {
  opacity: 0.7;
  cursor: default;
  text-decoration: line-through;
  text-decoration-color: rgba(151, 111, 65, 0.5);
}
.directions__icon { flex-shrink: 0; }
/* This element is both the flex item and the .tbd carrier, so it must not be
   given a full-width box: the dashed border-bottom would paint right across
   it and read as a second separator competing with the gold rule above.
   `width: fit-content` keeps the underline on the text, while the forced line
   break before it puts the note under "Get Directions" rather than beside
   it. */
.directions__note {
  width: fit-content;
  max-width: 100%;
  margin-top: var(--space-2xs);
  font-size: 14px;
}

/* ---------------------------------------------------------------- */
/* About / Privacy — long document pages                             */
/* ---------------------------------------------------------------- */
.doc-page { max-width: 888px; margin: 0 auto; }
.doc-page h1 { margin-bottom: var(--space-xl); }
.doc-page h2 { margin: var(--space-2xl) 0 var(--space-md); }
.doc-page p { color: var(--color-muted); margin: 0 0 var(--space-md); }
.doc-page ol { margin: 0 0 var(--space-lg); padding-left: 1.4rem; color: var(--color-muted); }
.doc-page ol li { margin-bottom: var(--space-sm); }
.doc-page strong { color: var(--color-ink); }

/* ---------------------------------------------------------------- */
/* Footer — live geometry                                             */
/* ---------------------------------------------------------------- */
/* Live padding is `80px 48px 150px`, where the 150px bottom reserved room for
   the absolutely-positioned watermark. The watermark is now in flow, so that
   reserve is no longer needed. */
.footer,
.site-footer {
  width: 100%;
  background-color: #005b28;
  padding: 80px 48px 40px;
  overflow: hidden;
  position: relative;
}
.footer .u__wrap,
.site-footer .u__wrap { padding-inline: 0; }

.footer__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  margin-inline: -12px;
}
.footer__col {
  flex: 0 0 100%;
  padding-inline: 12px;
  min-width: 0;
}
.footer__col-item { margin-bottom: 15px; }
.footer__col-item p { margin: 0 0 15px; }
.footer__col-item p:last-child { margin-bottom: 0; }
.footer__col-link { display: inline-block; }
/* Address / telephone / email each own a line. */
.footer__col-link-ignite { display: block; }
.footer__col-link { transition: opacity .25s ease; }
.footer__col-link:hover { color: #fff; opacity: 0.75; }
.footer__col-inline { display: inline-block; padding-right: 1rem; }

.footer__social {
  display: flex;
  justify-content: flex-start;
  gap: 0;
}
.footer__social .social-link {
  color: #fff;
  margin-right: 15px;
  display: inline-flex;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 50%;
  transition: background .25s ease, color .25s ease;
}
.footer__social .social-link:hover,
.footer__social .social-link:focus-visible {
  background: #fff;
  color: var(--color-green);
}

/* The live site absolutely positions this watermark at bottom:-35px and relies
   on the footer's 150px bottom padding to clear it. That breaks once the
   content column wraps taller than the padding reserves — the copyright then
   sits on top of the wordmark (measured 68px of overlap at 1600px wide).
   Keeping it in normal flow after the content guarantees it always sits below
   the footer text and stays fully visible, at any width. */
.footer__logo {
  width: 100%;
  max-width: 1344px;
  margin: var(--space-xl) auto 0;
  height: 103px;
  pointer-events: none;
}
.footer__logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center bottom;
  display: block;
}

@media (min-width: 1200.01px) {
  .footer__col--links { flex: 0 0 75%; }
  .footer__col--social { flex: 0 0 25%; }
  .footer__col--social .footer__social { justify-content: flex-end; }
  .footer__col--copyright { flex: 0 0 25%; }
}
@media (max-width: 1200px) {
  .footer, .site-footer { padding-left: 24px; padding-right: 24px; }
  .footer__col { margin-bottom: 30px; }
  .footer__logo { height: 56px; width: 100%; margin-top: var(--space-lg); }
  .footer__col-inline { display: block; padding-right: 0; }
}
