/* ===== Home (index.html) — page-specific =====
   Everything that was in index.html's main <style> block, moved verbatim and
   in the same cascade position (site.css is linked immediately before this,
   so anything declared in both resolves here). The preloader block stays
   inline in the page: it has to paint before any external sheet is fetched. */

    html { scroll-behavior: smooth; overflow-x: hidden; overscroll-behavior-y: none; background: #081F17; }
    body { margin: 0; background: #F4EEE1; font-family: 'General Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; overflow-x: clip; overscroll-behavior-y: none; }
    :root { --gold: #D9A441; --nav-height: 60px; }
    /* Hero height: pure CSS, biased large and STABLE. 100lvh (largest
       viewport, toolbar collapsed) is a static value — it never changes while
       scrolling, so the hero never resizes mid-scroll. (The previous
       JS-measured height DID resize with the collapsing toolbar, which
       re-cropped the object-fit:cover images = the "zoom", shifted the
       bottom-anchored text with it, and let cream peek when the viewport grew
       faster than JS caught up.) 100lvh is also >= the visible height at
       every toolbar state, so the next section can never peek. Do NOT swap
       for dvh/svh or a JS var: dynamic height on this hero is the bug, not
       the fix. Nav is position:fixed (an overlay, not in flow) so nothing is
       subtracted; the hero's own top padding clears it. */
    #top { min-height: 100vh; min-height: 100lvh; }
    /* Keep the ever-running Ken Burns zoom + slide crossfades compositor-only
       so scrolling doesn't repaint five viewport-sized images. */
    #top .wl-slide { will-change: opacity; }
    #top .wl-slide img { will-change: transform; }
    /* ---- Hero ---- */
    #top { position: relative; display: flex; align-items: flex-end; overflow: hidden; background: #0B2A20; }
    .wl-slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1.2s ease; pointer-events: none; }
    .wl-slide[data-slide="0"] { opacity: 1; }
    .wl-slide img { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; transform-origin: 50% 45%; filter: saturate(1.06) sepia(0.05) contrast(1.03) brightness(0.92); animation: wpkb 28s ease-in-out infinite alternate; }
    .wl-hero-scrim { position: absolute; inset: 0; background: linear-gradient(to top right, rgba(11,42,32,0.92) 0%, rgba(11,42,32,0.55) 38%, rgba(11,42,32,0) 74%), radial-gradient(120% 90% at 50% 45%, rgba(0,0,0,0) 55%, rgba(8,31,23,0.35) 100%); animation: wpfadein 1.1s ease both; }
    /* --wl-bars-band is the space the absolutely-positioned slide indicators
       occupy at the bottom of the hero: their own 32px height plus the 30px
       they sit above the bottom edge plus the home-indicator inset they are
       pushed up by. The hero's bottom padding is that band plus 20px, so the
       CTA row clears the indicators by the same 20px at EVERY viewport instead
       of by whatever a per-breakpoint number happened to leave.

       This is what was overlapping on an iPhone 14 Pro Max: the indicators
       move up by the 34px inset, the padding did not, so a 20px gap became a
       14px overlap. It cannot be seen in a desktop browser, where the inset is
       0 — which is exactly why the number is derived here rather than typed. */
    .wl-herowrap { --wl-bars-band: calc(62px + var(--wl-bottom-safe)); position: relative; width: 100%; max-width: none; margin: 0 auto; padding: 150px var(--gutter) calc(var(--wl-bars-band) + 20px); display: flex; flex-direction: column; align-items: flex-start; gap: 26px; }
    .wl-hero-badge { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 12px; color: #F4EEE1; font-size: 11px; font-weight: 600; letter-spacing: 0.26em; text-transform: uppercase; animation: wpfadein 0.9s ease 0.5s both; }
    .wl-hero-rating { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
    .wl-hero-score { color: var(--gold); display: inline-flex; align-items: center; gap: 5px; }
    .wl-hero-score svg { width: 0.95em; height: 0.95em; }
    .wl-hero-hours { white-space: nowrap; }
    .wl-badge-sep { width: 36px; height: 1px; background: rgba(244,238,225,0.4); }
    .wl-hero-h1 { margin: 0; font-family: 'Fraunces',Georgia,serif; font-weight: 560; font-size: clamp(48px, 9.6vw, 136px); line-height: 0.95; color: #F4EEE1; letter-spacing: -0.015em; max-width: 11ch; }
    /* Each line is masked by its wrapper and rises into it; the three delays
       are what stagger the headline. */
    .wl-hero-h1 > span { display: block; overflow: hidden; padding-bottom: 0.06em; }
    .wl-hero-h1 > span:last-child { padding-bottom: 0.08em; }
    .wl-hero-h1 > span > span { display: block; animation: wpintro 0.9s cubic-bezier(0.16,1,0.3,1) both; }
    .wl-hero-h1 > span:nth-child(1) > span { animation-delay: 0.08s; }
    .wl-hero-h1 > span:nth-child(2) > span { animation-delay: 0.2s; }
    .wl-hero-h1 > span:nth-child(3) > span { animation-delay: 0.32s; font-style: italic; }
    .wl-hero-lede { margin: 0; max-width: 560px; font-size: clamp(15.5px, 1.9vw, 18px); line-height: 1.65; color: rgba(244,238,225,0.88); animation: wpfadein 0.9s ease 0.7s both; }
    .wl-hero-cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 6px; animation: wpfadein 0.9s ease 0.85s both; }
    .wl-btn-outline-light { border: 1px solid rgba(244,238,225,0.6); color: #F4EEE1; text-decoration: none; font-size: 12.5px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; padding: 17px 32px; border-radius: 999px; transition: background 0.25s ease, transform 0.25s cubic-bezier(0.16,1,0.3,1); }
    /* bottom is offset by --wl-bottom-safe (css/site.css) — the home-indicator
       inset, 0 on anything without one, so this is the original 30px on a
       desktop. The cookie card's separate lift is a transform on top and still
       composes. See the hero-CTA clearance rule further down for why the
       indicators also need the CTA row to stop short of them on a phone. */
    .wl-bars { position: absolute; right: 24px; bottom: calc(30px + var(--wl-bottom-safe)); z-index: 2; display: flex; gap: 4px; }
    .wl-bar { width: 44px; height: 32px; background: none; border: 0; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
    .wl-bar-track { position: relative; display: block; width: 36px; height: 2px; background: rgba(244,238,225,0.28); overflow: hidden; }
    .wl-bar-fill { position: absolute; inset: 0; transform: scaleX(0); transform-origin: left; background: var(--gold); }

    /* ---- Section head (label + self-drawing hairline) ----
       Shared pattern; only Visit uses the classes so far, the other sections
       still carry it inline. */
    .wl-sechead { display: flex; align-items: center; gap: 20px; margin-bottom: clamp(36px, 5vw, 56px); }
    .wl-sechead-label { font-size: 11px; font-weight: 700; letter-spacing: 0.3em; text-transform: uppercase; color: #F4EEE1; white-space: nowrap; }
    .wl-sechead-rule { flex: 1; height: 1px; background: rgba(244,238,225,0.18); transform-origin: left; animation: wpline 1s cubic-bezier(0.16,1,0.3,1) both; animation-timeline: view(); animation-range: entry 0% entry 40%; }

    /* ---- 06 Visit ---- */
    .wl-visit { position: relative; background: #0B2A20; padding: clamp(72px, 10vw, 130px) var(--gutter); scroll-margin-top: var(--nav-height); }
    .wl-visit-inner { max-width: none; margin: 0 auto; position: relative; z-index: 1; }
    .wl-visit-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: clamp(36px, 6vw, 72px); align-items: stretch; }
    .wl-visit-col { display: flex; flex-direction: column; gap: 22px; }
    .wl-visit h2 { margin: 0; font-family: 'Fraunces',Georgia,serif; font-weight: 520; font-size: clamp(34px, 5vw, 64px); line-height: 1; letter-spacing: -0.015em; color: #F4EEE1; }
    .wl-visit h2 em { font-style: italic; }
    .wl-visit-addr { margin: 0; font-size: 16px; line-height: 1.7; color: rgba(244,238,225,0.85); }
    .wl-visit-pill { margin: 0; display: inline-flex; align-self: flex-start; align-items: center; gap: 10px; border: 1px solid rgba(244,238,225,0.25); border-radius: 999px; padding: 9px 18px; font-size: 11.5px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; color: #F4EEE1; }
    .wl-visit-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--gold); }
    .wl-visit-contact { display: flex; flex-direction: column; gap: 4px; }
    .wl-visit-tel { color: #F4EEE1; text-decoration: none; font-family: 'Fraunces',serif; font-size: 24px; font-weight: 560; padding: 4px 0; font-variant-numeric: tabular-nums; }
    .wl-visit-email { color: rgba(244,238,225,0.75); text-decoration: none; font-size: 14.5px; font-weight: 500; padding: 6px 0; }
    .wl-visit-tags { display: flex; flex-wrap: wrap; gap: 10px; }
    .wl-tag { border: 1px solid rgba(244,238,225,0.3); border-radius: 999px; padding: 9px 18px; font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: #F4EEE1; }
    .wl-btn-gold.is-visit { align-self: flex-start; margin-top: 6px; }
    .wl-visit-map { position: relative; min-height: 380px; overflow: hidden; border: 1px solid rgba(244,238,225,0.16); background: #123B2F; }
    .wl-map-load { position: absolute; inset: 0; width: 100%; border: 0; background: transparent; cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; padding: 28px; text-align: center; color: #F4EEE1; }
    .wl-map-pin { display: block; width: 34px; height: 34px; color: var(--gold); }
    .wl-map-pin svg { width: 100%; height: 100%; }
    .wl-map-addr { font-size: 15px; line-height: 1.65; color: rgba(244,238,225,0.85); max-width: 30ch; }
    .wl-map-cta { display: inline-flex; align-items: center; gap: 9px; border: 1px solid rgba(244,238,225,0.5); border-radius: 999px; padding: 11px 22px; font-size: 11.5px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease; }
    /* Same grain overlay the footer uses; kept as its own class here rather
       than reaching for .wl-foot-noise, which is a footer name. */
    .wl-noise { position: absolute; inset: 0; pointer-events: none; opacity: 0.045; background-image: url("data:image/svg+xml,%3Csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20width=%27160%27%20height=%27160%27%3E%3Cfilter%20id=%27n%27%3E%3CfeTurbulence%20type=%27fractalNoise%27%20baseFrequency=%270.85%27%20numOctaves=%272%27%20stitchTiles=%27stitch%27/%3E%3C/filter%3E%3Crect%20width=%27160%27%20height=%27160%27%20filter=%27url(%23n)%27/%3E%3C/svg%3E"); }
    a { color: #0F362C; }
    /* <button> does not inherit font-family, and Chrome's UA default for it is
       Arial — which is how the six Feast Combos "See what's inside" toggles
       ended up rendering in Arial instead of General Sans. One rule at the
       control level, rather than a font-family on each label that needs it. */
    button { font-family: inherit; }
    /* Duplicate of site.css's rule, and it carries the same exclusion for the
       same reason: text links go amber on hover, pill buttons keep their own
       label colour. Amber on gold is unreadable. Change one, change both.
       The colour is --wl-link-hover: one amber cannot pass on both the cream
       and the dark green, so site.css sets the cream value on :root and the
       dark sections re-declare the lighter one. See the note there. */
    a:not([class*="wl-btn"]):not([class*="-dir"]):hover { color: var(--wl-link-hover); }
    @keyframes wpkb { from { transform: scale(1); } to { transform: scale(1.07); } }
    @keyframes wpintro { from { transform: translateY(112%); } to { transform: translateY(0); } }
    @keyframes wpfadein { from { opacity: 0; } to { opacity: 1; } }
    @keyframes wpmask { from { transform: translateY(112%); } to { transform: translateY(0); } }
    @keyframes wprise { from { opacity: 0; translate: 0 30px; } to { opacity: 1; translate: 0 0; } }
    @keyframes wpclip { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(0 0 0 0); } }
    @keyframes wpline { from { transform: scaleX(0); } to { transform: scaleX(1); } }
    /* Four copies, shifted by one copy (-25%). With two copies the shift was a
       full 50% and only ONE copy remained on screen after it: one copy measures
       ~2535px, so any viewport wider than that ran out of content and showed
       dead space at the tail. Three copies remain after the shift now, which
       covers past 7600px. Content count and this percentage are a pair: change
       one, change the other. */
    @keyframes wpmarquee { from { transform: translateX(0); } to { transform: translateX(-25%); } }
    @keyframes wpfill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
    @keyframes wpcard { 0% { transform: scale(0.94); opacity: 0.6; } 42% { transform: scale(1); opacity: 1; } 58% { transform: scale(1); opacity: 1; } 100% { transform: scale(0.94); opacity: 0.6; } }
    @keyframes wpnav { to { padding-top: 8px; padding-bottom: 8px; } }
    /* hover states (were style-hover) */
    /* ---- Sticky nav ----
       The nav's links are .wl-nav-a, NOT .wl-navlink. They used to share the
       class with the footer's links and only looked different because the
       nav's version carried inline styles that outranked the shared rule.
       With the inline styles gone that collision would silently restyle the
       whole nav, so the two roles get two classes. */
    .wl-nav { position: fixed; top: 0; left: 0; right: 0; z-index: 60; background: rgba(11,42,32,0.96); backdrop-filter: blur(10px); border-bottom: 1px solid rgba(244,238,225,0.14); }
    .wl-nav-bar { max-width: none; margin: 0 auto; padding: 18px var(--gutter); display: flex; align-items: center; gap: 32px; animation: wpnav linear both; animation-timeline: scroll(); animation-range: 0px 160px; }
    /* True logo ratio, no exception. WIDEPLATE is 21.5px rather than the old
       19px for one reason: at 0.35 that puts RESTAURANT at 7.53px, the same
       size it has always rendered at, so nothing got smaller in the process of
       becoming accurate. One knob — change this font-size and the rest follows.
       The wider mark is why the hamburger handover moved to 1030px. */
    .wl-nav-mark { text-decoration: none; color: #F4EEE1; display: flex; flex-direction: column; align-items: center; font-size: 21.5px; gap: 0.257em; }
    .wl-nav-mark b { font-family: 'New Baskerville FS','New Baskerville','Libre Baskerville',Baskerville,Georgia,serif; font-weight: 700; font-size: 1em; letter-spacing: 0.13em; margin-right: -0.13em; line-height: 1; white-space: nowrap; }
    .wl-nav-sub { display: flex; align-items: center; gap: 0.141em; width: 100%; }
    .wl-nav-rule { flex: 1; height: 1px; background: rgba(244,238,225,0.6); }
    .wl-nav-tag { font-family: 'New Baskerville FS','New Baskerville','Libre Baskerville',Baskerville,Georgia,serif; font-size: 0.35em; font-weight: 400; letter-spacing: 0.785em; margin-right: -0.785em; line-height: 1; }
    .wl-nav-links { display: flex; gap: clamp(16px, 2.2vw, 26px); }
    .wl-nav-a { color: rgba(244,238,225,0.8); text-decoration: none; font-size: 11.5px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; transition: color 0.2s ease; }
    .wl-nav-actions { margin-left: auto; display: flex; align-items: center; gap: 16px; }
    .wl-nav-tel { color: rgba(244,238,225,0.85); text-decoration: none; font-size: 13px; font-weight: 600; letter-spacing: 0.04em; white-space: nowrap; font-variant-numeric: tabular-nums; }
    .wl-mob span { display: block; width: 22px; height: 1.5px; background: #F4EEE1; }
    .wl-mobpanel-inner { display: flex; flex-direction: column; padding: 8px var(--gutter) 22px; gap: 2px; }
    .wl-mob-a { color: #F4EEE1; text-decoration: none; font-family: 'Fraunces',serif; font-size: 22px; font-weight: 500; padding: 12px 2px; }
    .wl-mob-dir { display: inline-flex; align-items: center; gap: 6px; color: var(--gold); text-decoration: none; font-size: 13px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; padding: 14px 2px; }
    .wl-mob-dir svg { width: 0.85em; height: 0.85em; }
    .wl-mob-call { color: var(--gold); text-decoration: none; font-size: 13px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; padding: 14px 2px; }
    /* No !important needed now that nothing inline is being fought. */
    .wl-nav-a:hover, .wl-navlink:hover { color: var(--gold); }

    /* ===== Interaction states (index) =====
       The shared focus ring and the :active vocabulary live in css/site.css;
       this block is the index-only controls. Two ground rules carried over:

       1. `translate`, never `transform`, for a press or a lift. The gold CTAs
          carry data-magnet, which writes an INLINE transform on mousemove, and
          inline beats a stylesheet — a transform here would be dead code on
          every desktop pointer. `translate` composes with it instead.
       2. `!important` where an element's own inline style declares the same
          property. The chips and the combo toggles both do (they are part of
          the ~900 inline attributes index.html deliberately still carries), and
          this is the same reason .wl-chip.is-active already needs it.

       Timing is ~180ms across the board, deliberately snappier than the house
       cubic-bezier(0.16,1,0.3,1) used for scroll reveals. A button should feel
       like it answered, not like it eased. */
    #top, #best-sellers, #combos, .wl-visit, .wl-nav, .wl-mobpanel { --wl-ring: var(--gold); }

    /* The gold pill's own states are NOT here — they live with its base rule
       below, because that rule re-declares `transition` and a later equal
       specificity declaration wins. Split across the two blocks, the new
       background and translate transitions were being dropped and the press
       shadow lost to the base :hover. One block, one cascade. */

    /* Ghost pills: outline-light (hero), seecombos (dark section), fb (cream
       section, and Facebook blue is deliberately NOT used here — this one is a
       cream-on-green outline that belongs to the site, not a brand button). */
    .wl-btn-outline-light { transition: background 0.18s ease, border-color 0.18s ease, translate 0.18s ease, transform 0.25s cubic-bezier(0.16,1,0.3,1); }
    /* Same `a:hover` amber problem, milder: the ghost pill's cream label was
       going amber against a dark green hero. It stays cream. */
    .wl-btn-outline-light:hover { background: rgba(244,238,225,0.12); border-color: #F4EEE1; color: #F4EEE1; }
    .wl-btn-outline-light:active { background: rgba(244,238,225,0.2); color: #F4EEE1; translate: 0 1px; }
    .wl-btn-seecombos:active { background: #E6DFCE !important; color: #0B2A20 !important; translate: 0 1px; }
    .wl-btn-fb:active { background: #082018 !important; color: #F4EEE1 !important; translate: 0 1px; }

    /* Nav + mobile panel links. The panel had no states at all, which on a
       phone meant a tap gave no confirmation it had registered. */
    .wl-nav-a:active, .wl-navlink:active { color: #C08F33; }
    .wl-nav-mark, .wl-nav-tel, .wl-mob-a, .wl-mob-dir, .wl-mob-call { transition: color 0.18s ease, opacity 0.18s ease; }
    .wl-nav-mark:hover, .wl-nav-tel:hover { color: var(--gold); }
    .wl-mob-a:hover { color: var(--gold); }
    /* These two are ALREADY gold, so hovering them to gold was a dead rule --
       nothing changed on hover. They step to the lighter gold instead, the same
       #E3B45C the gold pill uses, so the feedback is visible and the change
       still reads as the same button. Never hover a gold thing to gold. */
    .wl-mob-dir:hover, .wl-mob-call:hover { color: #E3B45C; }
    .wl-nav-mark:active, .wl-nav-tel:active, .wl-mob-a:active, .wl-mob-dir:active, .wl-mob-call:active { opacity: 0.6; }
    .wl-mob:active span { background: var(--gold); }

    /* Menu filter chips. Inline background/color/border-color on all 15, hence
       !important — the same fight .wl-chip.is-active already had to win. The
       active chip keeps its gold and only presses. */
    .wl-chip:hover { background: rgba(20,36,29,0.06) !important; border-color: rgba(20,36,29,0.55) !important; }
    .wl-chip:active { background: rgba(20,36,29,0.12) !important; translate: 0 1px; }
    .wl-chip.is-active:hover { background: #E3B45C !important; border-color: #E3B45C !important; }
    .wl-chip.is-active:active { background: #C08F33 !important; border-color: #C08F33 !important; }

    /* Combo "See what's inside" toggles: inline color, so !important again. */
    .wl-combo-toggle { transition: color 0.18s ease, border-color 0.18s ease; }
    .wl-combo-toggle:hover { color: var(--gold) !important; border-top-color: rgba(217,164,65,0.5) !important; }
    .wl-combo-toggle:active { color: #C08F33 !important; }
    .wl-combo-toggle:focus-visible { outline-offset: -2px; }

    /* Visit contact links and the hero's slide indicators. The bars are
       <button>s with no visual state whatsoever before this. */
    .wl-visit-tel, .wl-visit-email { transition: color 0.18s ease, opacity 0.18s ease; }
    .wl-visit-tel:hover, .wl-visit-email:hover { color: var(--gold); }
    .wl-visit-tel:active, .wl-visit-email:active { opacity: 0.6; }
    .wl-bar-track { transition: background 0.18s ease; }
    .wl-bar:hover .wl-bar-track { background: rgba(244,238,225,0.55); }
    .wl-bar:active .wl-bar-track { background: rgba(217,164,65,0.7); }

    /* Map facade: hover and focus already lit the pill. This is the press. */
    .wl-map-load:active .wl-map-cta { background: #C08F33; border-color: #C08F33; color: #1B1305; }

    /* Movement off, colour kept. The hover shadows are static, not motion. */
    @media (prefers-reduced-motion: reduce) {
      .wl-btn-gold:hover, .wl-btn-gold:active, .wl-btn-outline-light:active,
      .wl-btn-seecombos:active, .wl-btn-fb:active, .wl-chip:active,
      .wl-chip.is-active:active, .wl-map-load:active .wl-map-cta { translate: none !important; }
    }

    /* Gold pill. Four instances existed inline, identical except for their
       type size and padding, so the shared look is the base rule and the two
       that differ carry a modifier. */
    .wl-btn-gold { background: var(--gold); color: #1B1305; text-decoration: none; font-size: 12px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; padding: 17px 32px; border-radius: 999px; transition: background 0.18s ease, translate 0.18s ease, transform 0.25s cubic-bezier(0.16,1,0.3,1), box-shadow 0.18s ease; }
    .wl-btn-gold.is-nav { font-size: 11.5px; padding: 12px 22px; white-space: nowrap; }
    .wl-btn-gold.is-hero { font-size: 12.5px; padding: 18px 34px; }
    /* Hover and press: the deep shadow was already the house look for this
       button, so the lift keeps it and only the press steps it down.

       `color` is re-stated on BOTH states on purpose. site.css and this file
       both carry a global `a:hover { color: #C9862B }`, which is amber, and
       these pills are <a> elements — so hovering a gold button repainted its
       label amber ON gold and the text nearly vanished. Dark ink is what the
       label is at rest and it is what it stays: #1B1305 on #E3B45C is about
       10:1, while white on gold is under 2:1 and would fail outright. Any new
       filled button needs this line too. */
    .wl-btn-gold:hover { background: #E3B45C; color: #1B1305; translate: 0 -2px; box-shadow: 0 14px 38px rgba(217,164,65,0.4); }
    .wl-btn-gold:active { background: #C08F33; color: #1B1305; translate: 0 1px; box-shadow: 0 4px 12px rgba(217,164,65,0.28); }
    .wl-btn-outline-light:hover { background: rgba(244,238,225,0.12); }
    .wl-btn-seecombos:hover { background: #F4EEE1 !important; color: #0B2A20 !important; }
    .wl-btn-fb:hover { background: #0F362C !important; color: #F4EEE1 !important; }
    /* Map facade: the whole panel is the tap target, so the pill lights up on
       hover anywhere over it, not just directly on the pill. */
    .wl-map-load:hover .wl-map-cta, .wl-map-load:focus-visible .wl-map-cta { background: var(--gold); border-color: var(--gold); color: #1B1305; }
    .wl-map-load:focus-visible { outline: 2px solid var(--gold); outline-offset: -4px; }
    .wl-dish:hover { transform: scale(1.03) translateY(-4px); box-shadow: 0 24px 48px rgba(4,18,13,0.45); }
    .wl-combo:hover { transform: translateY(-5px); background: rgba(244,238,225,0.08) !important; }
    .wl-chip.is-active { background: var(--gold) !important; color: #1B1305 !important; border-color: var(--gold) !important; }
    /* horizontal scrollers: hide scrollbar, contain overscroll */
    [data-gallery], [data-chips] { -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
    [data-gallery]::-webkit-scrollbar, [data-chips]::-webkit-scrollbar { display: none; }
    /* mobile nav */
    .wl-mob { display: none; background: none; border: 0; cursor: pointer; padding: 10px; flex-direction: column; gap: 5px; min-width: 44px; min-height: 44px; justify-content: center; align-items: center; }
    .wl-mobpanel { border-top: 1px solid rgba(244,238,225,0.14); max-height: 0; overflow: hidden; transition: max-height 0.35s cubic-bezier(0.16,1,0.3,1); }
    /* Was 60vh, which clipped unreachably on short phones: the panel's content
       is ~430px of fixed-px type, so a 667px-tall iPhone SE got a 400px budget
       and the "Call 0966 965 9995" CTA was cut off with no way to scroll to it
       (the base rule's overflow:hidden made it unreachable, not just hidden).
       dvh instead of vh so browser chrome is accounted for, minus the bar's
       real height so the panel ends exactly at the bottom of the screen, and
       overflow-y:auto so content that outgrows the space scrolls instead of
       disappearing -- that last part is what stops this recurring the next
       time an item is added to the menu.

       --wl-bottom-safe is subtracted so the last item clears the iOS home
       indicator rather than sitting under it, which is the same class of bug
       this rule already exists to fix, and FAQ has made the panel one item
       taller. */
    .wl-mobpanel.open { max-height: calc(100dvh - var(--nav-bar-h, 60px) - var(--wl-bottom-safe)); overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
    /* ---- Seven desktop nav items: two compaction steps, then the hamburger ----
       MEASURED in a browser at 1920 with fonts loaded, not modelled. The parts
       are all fixed px and only the gutter and the link gap scale with vw:

         wordmark 175.55 · links text 442.43 (7 items, "FAQ" 29.86)
         nav tel 93.52 · Get Directions pill 164.3 · nav-bar gaps 2 x 32

       At default spacing that needs 0.212w + 955.79 <= w, i.e. 1213px before it
       fits at all, and ~1225 with a real margin. The old 1130 value came from
       arithmetic off six-item measurements and was simply wrong — it would have
       shipped an overflowing nav on every 1152 and 1200px laptop. Hence:

         > 1240   full spacing
         <= 1240  link gap 14, bar gap 24     (7 items fit to ~1131 with 16 spare)
         <= 1130  the nav phone number leaves (fits to 1024 with 27.8 spare)
         <= 1023  hamburger

       Hiding the tel is what buys 1024: seven links plus the number need
       1131px, so on a tablet the number gives way to the links. It is still in
       the footer, the Visit section and FAQ on every page, and the nav keeps
       its Get Directions pill throughout.

       iPad landscape (1024) keeps the desktop nav deliberately. Re-measure with
       b=document.querySelector('.wl-nav-bar'); b.scrollWidth - b.clientWidth
       at 1024, 1131 and 1241 after ANY change to a nav item, the wordmark size
       or the pill's label. Anything above 0 overflows. */
    @media (max-width: 1240px) {
      .wl-nav-bar { gap: 24px; }
      .wl-nav-links { gap: 14px; }
    }
    @media (max-width: 1130px) {
      .wl-nav-tel { display: none; }
    }
    @media (max-width: 1023px) {
      .wl-desk { display: none !important; }
      .wl-mob { display: flex !important; }
    }
    @media (max-width: 900px) {
      .wl-desk { display: none !important; }
      .wl-mob { display: flex !important; }
      .wl-storypin { position: static !important; top: auto !important; }
      /* padding-bottom is deliberately NOT overridden here any more: the base
         rule derives it from the indicators' own band. The old 56px was 6px
         less than the indicators occupy, which put the CTA row under them on
         an iPad in portrait. */
      .wl-herowrap { padding-top: 118px !important; }
    }
    /* Visit: fill the viewport below the sticky nav on desktop so the footer
       doesn't peek when landing on #visit (same pattern as the hero). */
    @media (min-width: 901px) {
      /* Visit fills the viewport below the nav so the footer never peeks on a
         #visit anchor jump, but the content sits at the TOP-LEFT (not floated
         in the vertical middle — justify-content:center was the "centered"
         look). The inner block grows to fill and the map stretches with it, so
         the tall section reads full instead of empty. */
      .wl-visit { min-height: calc(100vh - var(--nav-height)); min-height: calc(100lvh - var(--nav-height)); box-sizing: border-box; display: flex; flex-direction: column; }
      .wl-visit-inner { flex: 1 1 auto; display: flex; flex-direction: column; width: 100%; }
      .wl-visit-grid { flex: 1 1 auto; align-content: stretch; }
      /* Visit's heading goes larger on desktop. Authored with the rest of
         this block but never live, because the heading's inline font-size
         outranked it; extracting that into a class made it apply, and it is
         now deliberately kept. Adds ~34px to the section's height. */
      .wl-visit h2 { font-size: clamp(40px, 5.6vw, 82px); }
    }
    @media (max-width: 600px) {
      /* Same as the 900px block: the bottom is the base rule's derived band. */
      .wl-herowrap { padding-top: 104px !important; }
      .wl-drag-hint { display: none !important; }
      /* Hero headline: keep the signature 3-line stagger but shrink it so it
         reads comfortably and stops dominating the phone screen. */
      .wl-hero-h1 { font-size: clamp(33px, 9vw, 46px) !important; }
      /* Stronger bottom-up scrim: mobile crops sit tighter to the food, so
         text needs more protection against all 5 rotating photos. */
      .wl-hero-scrim { background: linear-gradient(to top, rgba(11,42,32,0.96) 0%, rgba(11,42,32,0.78) 26%, rgba(11,42,32,0.35) 55%, rgba(11,42,32,0) 82%) !important; }
      /* Badge row: wrap predictably into two clean lines, drop the divider.
         This rule was authored long ago but never took effect, because the
         badge's inline styles outranked it; extracting them into a class made
         it live, and it is now deliberately kept. Phones get a 10px badge. */
      .wl-hero-badge { font-size: 10px; letter-spacing: 0.2em; gap: 6px 12px; }
      .wl-badge-sep { display: none !important; }
      /* Slide indicators: smaller and centered on mobile. */
      .wl-bars { left: 0 !important; right: 0 !important; justify-content: center !important; }
      .wl-bars .wl-bar { width: 34px !important; }
      .wl-bars .wl-bar > span { width: 22px !important; }
      /* Footer: center the whole content block on phones (desktop untouched). */
      .wl-foot-cols { justify-items: center !important; }
      .wl-foot-col { align-items: center !important; text-align: center !important; }
    }
    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; animation-delay: 0s !important; transition-delay: 0s !important; }
    }

    /* ===== One-time scroll reveals (Round 5) =====
       app.js stamps .js on <html>, then a one-shot IntersectionObserver adds
       .is-in to each [data-reveal] section on FIRST entry and unobserves it —
       so every reveal below plays exactly once per page load and never
       replays on scroll-up. Pre-reveal hidden states are gated on html.js so
       a no-JS visitor sees everything. Reduced motion: the global 0.01ms
       rule above collapses all of these to instant appearance. Elements with
       inline transition lists (.wl-chip/.wl-combo/.wl-dish) are animated with
       KEYFRAMES on purpose — a stylesheet transition would lose to their
       inline transition declaration. */
    @keyframes wpdeal { from { opacity: 0; transform: translateX(var(--ddist, 110px)) rotate(var(--drot, -7deg)) scale(1.05); } to { opacity: 1; transform: none; } }
    @keyframes wpchipin { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
    @keyframes wpfroml { from { opacity: 0; transform: translateX(calc(-1 * var(--cdist, 72px))); } to { opacity: 1; transform: none; } }
    @keyframes wpfromr { from { opacity: 0; transform: translateX(var(--cdist, 72px)); } to { opacity: 1; transform: none; } }
    @keyframes wpsweep { to { transform: translateX(130%); } }
    @keyframes wpstar { 0% { stroke-dashoffset: 1; fill-opacity: 0; } 62% { stroke-dashoffset: 0; fill-opacity: 0; } 100% { stroke-dashoffset: 0; fill-opacity: 1; } }
    /* 01 Promise: cloche reveal + hairline-drawn pillars */
    /* Round 6: every duration and delay in this block was scaled by 1.2 so the
       reveals play ~20% slower. Scale, do not flat-add, or the staggers and the
       combos' converging timing stop lining up. */
    .wl-cloche { transition: clip-path 1.26s cubic-bezier(0.3, 0, 0.2, 1); }
    html.js .wl-promise:not(.is-in) .wl-cloche { clip-path: circle(6% at 50% 55%); }
    .wl-promise.is-in .wl-cloche { clip-path: circle(125% at 50% 55%); }
    .wl-pillar { position: relative; }
    html.js .wl-pillar { border-top-color: transparent !important; }
    .wl-pillar::before { content: ''; position: absolute; top: -1px; left: 0; width: 100%; height: 1px; background: rgba(20,36,29,0.16); transform: scaleX(0); transform-origin: left; transition: transform 0.84s cubic-bezier(0.16, 1, 0.3, 1); }
    html:not(.js) .wl-pillar::before { display: none; }
    .wl-pillar > * { transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
    html.js .wl-promise:not(.is-in) .wl-pillar > * { opacity: 0; transform: translateY(12px); }
    /* The whole pillar sequence is shifted 0.18s EARLIER than it was, so
       pillar 1's text starts at 1.02s against the cloche's 1.26s finish: 240ms
       of overlap, which reads as one gesture instead of two events. A flat
       shift, not a scale, on purpose — it keeps the 0.30s inter-pillar stagger
       and the 0.18s hairline-then-text order exactly as signed off. Do not
       touch anything below this block; Best Sellers, Combos, Our Story and the
       rating stat are signed off and their timings are independent. */
    .wl-promise.is-in .wl-pillar::before { transform: scaleX(1); }
    .wl-promise.is-in .wl-pillar:nth-child(1)::before { transition-delay: 0.84s; }
    .wl-promise.is-in .wl-pillar:nth-child(2)::before { transition-delay: 1.14s; }
    .wl-promise.is-in .wl-pillar:nth-child(3)::before { transition-delay: 1.44s; }
    .wl-promise.is-in .wl-pillar:nth-child(1) > * { transition-delay: 1.02s; }
    .wl-promise.is-in .wl-pillar:nth-child(2) > * { transition-delay: 1.32s; }
    .wl-promise.is-in .wl-pillar:nth-child(3) > * { transition-delay: 1.62s; }
    /* last pillar's closing (bottom) hairline draws with the same choreography */
    /* Below ~420px the pillar's three inline columns (48-90px number, 1fr
       title, 150-300px copy, 20px gaps) cannot fit: the 1fr track's automatic
       minimum is its own min-content, so the row overflowed the gutter and the
       copy was clipped by the body's overflow-x. Measured 12px past a 360px
       viewport and 52px past 320px. Number and title share the first row, the
       copy takes the full width beneath. !important because the grid is
       declared inline on all three pillars. */
    @media (max-width: 420px) {
      .wl-pillar { grid-template-columns: auto minmax(0, 1fr) !important; gap: 6px 14px !important; }
      .wl-pillar > :last-child { grid-column: 1 / -1; }
    }
    html.js .wl-pillar:last-child { border-bottom-color: transparent !important; }
    .wl-pillar:last-child::after { content: ''; position: absolute; bottom: -1px; left: 0; width: 100%; height: 1px; background: rgba(20,36,29,0.16); transform: scaleX(0); transform-origin: left; transition: transform 0.84s cubic-bezier(0.16, 1, 0.3, 1); }
    html:not(.js) .wl-pillar:last-child::after { display: none; }
    .wl-promise.is-in .wl-pillar:last-child::after { transform: scaleX(1); transition-delay: 1.62s; }
    /* 03 Best Sellers: cards dealt onto the table (lighter on phones) */
    html.js #best-sellers:not(.is-in) .wl-dish { opacity: 0; }
    /* Best Sellers only: scaled by 1.2 again this round (0.78s -> 0.94s), and
       the stagger scaled with it so the cards keep their spacing. */
    #best-sellers.is-in .wl-dish { animation: wpdeal 0.94s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; }
    #best-sellers.is-in [data-gallery] > div:nth-child(2) .wl-dish { animation-delay: 0.13s; }
    #best-sellers.is-in [data-gallery] > div:nth-child(3) .wl-dish { animation-delay: 0.26s; }
    #best-sellers.is-in [data-gallery] > div:nth-child(4) .wl-dish { animation-delay: 0.39s; }
    #best-sellers.is-in [data-gallery] > div:nth-child(5) .wl-dish { animation-delay: 0.52s; }
    #best-sellers.is-in [data-gallery] > div:nth-child(6) .wl-dish { animation-delay: 0.65s; }
    #best-sellers.is-in [data-gallery] > div:nth-child(n+7) .wl-dish { animation-delay: 0.72s; }
    @media (max-width: 600px) { #best-sellers { --ddist: 48px; --drot: -3deg; } }
    /* 02 Menu: deliberately minimal — header fade-up + light chip stagger */
    .wl-menuhead { transition: opacity 0.55s ease, transform 0.55s cubic-bezier(0.16, 1, 0.3, 1); }
    html.js #menu:not(.is-in) .wl-menuhead { opacity: 0; transform: translateY(20px); }
    html.js #menu:not(.is-in) .wl-chip { opacity: 0; }
    #menu.is-in .wl-chip { animation: wpchipin 0.45s ease backwards; }
    #menu.is-in .wl-chip:nth-child(2) { animation-delay: 0.04s; }
    #menu.is-in .wl-chip:nth-child(3) { animation-delay: 0.08s; }
    #menu.is-in .wl-chip:nth-child(4) { animation-delay: 0.12s; }
    #menu.is-in .wl-chip:nth-child(5) { animation-delay: 0.16s; }
    #menu.is-in .wl-chip:nth-child(6) { animation-delay: 0.2s; }
    #menu.is-in .wl-chip:nth-child(7) { animation-delay: 0.24s; }
    #menu.is-in .wl-chip:nth-child(8) { animation-delay: 0.28s; }
    #menu.is-in .wl-chip:nth-child(n+9) { animation-delay: 0.32s; }
    /* 04 Combos: converging rows — All-Meat from the left, Meat & Seafood
       from the right, middle pair settles last so they "meet". */
    html.js #combos:not(.is-in) .wl-combo { opacity: 0; }
    #combos.is-in .wl-combo { animation: wpfroml 0.84s cubic-bezier(0.16, 1, 0.3, 1) backwards; }
    #combos.is-in .wl-combo:nth-child(n+4) { animation-name: wpfromr; }
    #combos.is-in .wl-combo:nth-child(2) { animation-delay: 0.14s; }
    #combos.is-in .wl-combo:nth-child(3) { animation-delay: 0.29s; }
    #combos.is-in .wl-combo:nth-child(4) { animation-delay: 0.29s; }
    #combos.is-in .wl-combo:nth-child(5) { animation-delay: 0.14s; }
    @media (max-width: 600px) { #combos { --cdist: 40px; } }
    /* 05 Story: one-time diagonal light sweep + pull-quote mask reveal */
    .wl-storyimg { position: relative; }
    .wl-storyimg::after { content: ''; position: absolute; inset: -25% -35%; background: linear-gradient(115deg, rgba(255,246,220,0) 42%, rgba(255,246,220,0.3) 50%, rgba(255,246,220,0) 58%); transform: translateX(-130%); pointer-events: none; }
    #story.is-in .wl-storyimg::after { animation: wpsweep 1.8s cubic-bezier(0.4, 0, 0.2, 1) 0.36s both; }
    .wl-squote { transition: clip-path 1.2s cubic-bezier(0.16, 1, 0.3, 1) 0.42s; }
    html.js #story:not(.is-in) .wl-squote { clip-path: inset(0 100% 0 0); }
    #story.is-in .wl-squote { clip-path: inset(0 0 0 0); }
    /* 06 Visit: deliberately minimal — single fade/slide-up */
    #visit > div { transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
    html.js #visit:not(.is-in) > div { opacity: 0; transform: translateY(24px); }
    /* The grain layer is also a direct child of #visit, so the reveal above
       used to catch it too — except it carried opacity:0.045 inline, which
       outranked the rule. With the inline style extracted the class alone
       loses, and the grain would start fading in with the section. Matching
       the reveal rule's specificity (and coming after it) restores exactly
       what shipped: the grain's opacity is pinned, its transform still
       animates with everything else. */
    html.js #visit:not(.is-in) > .wl-noise { opacity: 0.045; }
    /* Social proof stat: star outline draws itself, then fills gold */
    html.js .wl-rate:not(.is-in) .wl-star-big path { fill-opacity: 0; }
    /* Star draw and the 4.8/198 count-up are a pair: this duration and the
       count-up's `dur` in app.js reveals() were both scaled by 1.2, so they
       still finish in the same relationship to each other. Change one, change
       the other. */
    .wl-rate.is-in .wl-star-big path { stroke: var(--gold); stroke-width: 1.4; stroke-dasharray: 1; animation: wpstar 1.8s ease 0.12s both; }
    /* Reviews: 3 groups stacked in one grid cell, opacity crossfade (JS).
       ponytail: container height = tallest group; short groups leave some
       whitespace — switch to measured-height animation only if it ever
       bothers on mobile. Without JS the groups just stack, all 9 visible. */
    html.js .wl-revstack { display: grid; }
    html.js .wl-revstack .wl-revgroup { grid-area: 1 / 1; }
    .wl-revgroup { display: flex; flex-direction: column; transition: opacity 1.2s ease; }
    .wl-revgroup[aria-hidden="true"] { pointer-events: none; }
    .wl-revbars { display: none; }
    html.js .wl-revbars { display: flex; gap: 4px; padding-top: 14px; }
    .wl-revbar span { display: block; width: 28px; height: 2px; background: rgba(20,36,29,0.22); transition: background 0.3s ease; }
    .wl-revbar.is-on span { background: var(--gold); }
    /* Hamburger -> X morph (snappy back-ease; reduced-motion handled above) */
    .wl-mob span { transition: transform 0.34s cubic-bezier(0.34, 1.8, 0.5, 1), opacity 0.18s ease; }
    .wl-mob[aria-expanded="true"] span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
    .wl-mob[aria-expanded="true"] span:nth-child(2) { opacity: 0; transform: scaleX(0); }
    .wl-mob[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }
