/* ============================================================
   Single Fin & Twinny — main.css
   Speculative pitch site. Tokens-only; nothing hard-coded below §2.
   One venue, two brands: Single Fin leads, Twinny is a scoped
   colour world (.brand-tw) applied to its own sections/pages.
   Order: 1 Fonts · 2 Tokens · 3 Reset/base · 4 Type · 5 Layout
   primitives · 6 Components · 7 Motion
   ============================================================ */

/* ---------- 1. Fonts (self-hosted, subset woff2) ---------- */
@font-face {
  font-family: "Bagel Fat One";
  src: url("/assets/css/fonts/BagelFatOne-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito";
  src: url("/assets/css/fonts/Nunito-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito";
  src: url("/assets/css/fonts/Nunito-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito";
  src: url("/assets/css/fonts/Nunito-800.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}

/* The two faces of Twinny's printed cocktail menu, read off the PDF's own
   embedded fonts. Both are on Google Fonts under the OFL, so they are
   self-hosted here like the rest. Loaded for the cocktail cards only: the
   venue's own type stack is still Bagel Fat One and Nunito. */
@font-face {
  font-family: "Barrio";
  src: url("/assets/css/fonts/Barrio-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Averia Sans Libre";
  src: url("/assets/css/fonts/AveriaSansLibre-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Averia Sans Libre";
  src: url("/assets/css/fonts/AveriaSansLibre-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
/* The burger menu's body face. Single Fin sets every card's description and
   price block in one serif; Bitter stands in for it. */
@font-face {
  font-family: "Bitter";
  src: url("/assets/css/fonts/Bitter-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Bitter";
  src: url("/assets/css/fonts/Bitter-700italic.woff2") format("woff2");
  font-weight: 700; font-style: italic; font-display: swap;
}

/* PROVISIONAL — one display face per burger.
   Every card is titled in its own face in print, and the layouts arrived as
   PNGs, which carry no font metadata (unlike Twinny's PDF, where the names were
   simply read off). Each face below is the closest free stand-in, NOT an
   identification, and each is SUBSETTED to its own title: fourteen faces come
   to 23 KB because none of them carries a glyph it will never draw. Swap them
   the day a PDF or SVG export names the real ones. See §6.12. */
@font-face { font-family: "Abril Fatface";    src: url("/assets/css/fonts/AbrilFatface-Regular.woff2") format("woff2");    font-weight: 400; font-display: swap; }
@font-face { font-family: "Bevan";            src: url("/assets/css/fonts/Bevan-Regular.woff2") format("woff2");            font-weight: 400; font-display: swap; }
@font-face { font-family: "Titan One";        src: url("/assets/css/fonts/TitanOne-Regular.woff2") format("woff2");         font-weight: 400; font-display: swap; }
@font-face { font-family: "Pirata One";       src: url("/assets/css/fonts/PirataOne-Regular.woff2") format("woff2");        font-weight: 400; font-display: swap; }
@font-face { font-family: "Rye";              src: url("/assets/css/fonts/Rye-Regular.woff2") format("woff2");              font-weight: 400; font-display: swap; }
@font-face { font-family: "Playfair Display"; src: url("/assets/css/fonts/PlayfairDisplay-700.woff2") format("woff2");      font-weight: 700; font-display: swap; }
@font-face { font-family: "Rock Salt";        src: url("/assets/css/fonts/RockSalt-Regular.woff2") format("woff2");         font-weight: 400; font-display: swap; }
@font-face { font-family: "Archivo Black";    src: url("/assets/css/fonts/ArchivoBlack-Regular.woff2") format("woff2");     font-weight: 400; font-display: swap; }
@font-face { font-family: "Anton";            src: url("/assets/css/fonts/Anton-Regular.woff2") format("woff2");            font-weight: 400; font-display: swap; }
@font-face { font-family: "Permanent Marker"; src: url("/assets/css/fonts/PermanentMarker-Regular.woff2") format("woff2");  font-weight: 400; font-display: swap; }
@font-face { font-family: "Cinzel";           src: url("/assets/css/fonts/Cinzel-700.woff2") format("woff2");               font-weight: 700; font-display: swap; }
@font-face { font-family: "Fredoka";          src: url("/assets/css/fonts/Fredoka-600.woff2") format("woff2");              font-weight: 600; font-display: swap; }
@font-face { font-family: "Luckiest Guy";     src: url("/assets/css/fonts/LuckiestGuy-Regular.woff2") format("woff2");      font-weight: 400; font-display: swap; }
@font-face { font-family: "Poiret One";       src: url("/assets/css/fonts/PoiretOne-Regular.woff2") format("woff2");        font-weight: 400; font-display: swap; }

/* ---------- 2. Tokens ---------- */
:root {
  /* --- Shared venue ink (both brands sit on this) --- */
  --navy:      #272E59;  /* sampled from the Single Fin logo linework */
  --navy-deep: #1B2043;  /* footer / gradient end */

  /* --- Single Fin: sampled from the logo (brand_extract) --- */
  --sf-sky:        #ADE1F9;  /* 28.4% — light ground */
  --sf-ocean:      #35A5CB;  /* 27.4% — letterform fill */
  --sf-ocean-deep: #216E8C;  /* text-safe variant of ocean */
  --sf-ocean-wash: color-mix(in srgb, #35A5CB 65%, #ffffff);  /* door scrim: ocean lightened so navy text clears 6.9:1 */
  --sf-lemon:      #FEF36A;  /* 14.8% — highlight ground */
  --sf-lemon-700:  #F2D45C;  /* lemon as small TEXT on a dark ground. The lemon above is a
                                GROUND colour: as 13-14px type on navy it measures 11.25:1
                                but halates -- a light, fully saturated yellow on dark bleeds
                                at the letterform edges and reads fuzzy no matter what the
                                ratio says. This is the same hue with the glare taken out,
                                still 8.85:1 on navy and 11.1:1 on Twinny's indigo, so AAA on
                                both. Use it for nav labels and controls; leave the plain
                                lemon on grounds, borders and display headings, where the
                                effect does not appear. Added 31 Aug 2026. */
  --sf-red:        #E73D33;  /*  2.2% — outer ring accent */
  --sf-red-700:    #BE2A21;  /* CTA fill: white label passes AA-normal */
  --sf-red-800:    #94211A;  /* red as TEXT. The 700 is a fill and was never meant to
                                be read at 13px: as a label it is 5.93:1 on white and
                                drops to 5.59:1 the moment a cream hover ground slides
                                under it. This clears 8.43:1 and 7.94:1 on the same
                                two grounds -- AAA on both. Added 31 Aug 2026. */

  /* --- Twinny: carried over from the approved v1 palette --- */
  --tw-indigo:     #211454;
  --tw-butter:     #FCC56A;
  --tw-sunset:     #E0592F;
  --tw-sunset-700: #C6461C;

  /* --- Build colours. Lifted from the printed menu cards (amber / dark red /
         gold / purple, then the three veggie greens) and darkened where a white
         label needed to clear AA. One set, shared by the price cells and the
         build cards, so a build reads as the same colour everywhere. --- */
  --build-third-pounder: #A85416;
  --build-doble-smash:   #96242B;
  --build-pollo:         #F2D45C;  /* the one light fill: takes navy text */
  --build-gourmet:       #574B86;
  --build-espinacas:     #3E7B3A;
  --build-tempeh:        #2F6B4F;
  --build-alcachofas:    #1F5638;

  /* --- Neutrals --- */
  --paper:    #ffffff;
  --cream:    #FFF8E3;  /* warm off-white, keyed to the lemon */
  --ink:      #221F2E;
  --ink-soft: #55506A;

  /* --- Brand-slot aliases. Default = Single Fin. Re-pointed by .brand-tw --- */
  --brand-dark:    var(--navy);
  --brand-warm:    var(--sf-lemon);
  --brand-accent:  var(--sf-red);
  --brand-cta:     var(--sf-red-700);
  --brand-cool:    var(--sf-ocean);

  /* Hairlines / scrims */
  --line:         color-mix(in srgb, var(--ink) 14%, transparent);
  --line-on-dark: color-mix(in srgb, var(--brand-warm) 26%, transparent);

  /* Type */
  --font-display: "Bagel Fat One", system-ui, sans-serif;
  --font-ui: "Nunito", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  /* Cocktail menu only. Barrio sets the drink name, the calorie figure, the
     three descriptor words and the section titles; Averia sets the price,
     the small labels and the ingredient line. Same split as the print menu. */
  --font-menu-display: "Barrio", "Bagel Fat One", system-ui, sans-serif;
  --font-menu-ui: "Averia Sans Libre", "Nunito", system-ui, sans-serif;

  --fs-hero:      clamp(2.4rem, 8vw, 4.5rem);
  --fs-statement: clamp(1.8rem, 5vw, 3rem);
  --fs-h2:        clamp(1.6rem, 4vw, 2.4rem);
  --fs-h3:        1.25rem;
  --fs-body:      1.0625rem;
  --fs-small:     0.875rem;
  --fs-eyebrow:   0.8rem;

  --lh-display: 1.05;
  --lh-head:    1.15;
  --lh-body:    1.6;
  --ls-caps:    0.12em;
  --ls-name:    0.08em;

  /* Spacing — one 4px-based scale */
  --s1: 0.25rem; --s2: 0.5rem; --s3: 0.75rem; --s4: 1rem;
  --s5: 1.5rem;  --s6: 2rem;   --s7: 3rem;    --s8: 4rem;
  --section-y: clamp(2.75rem, 7vw, 6rem);

  /* Structure */
  --measure: 62ch;
  --container: 1200px;
  --radius: 16px;
  --radius-btn: 999px;
  --gutter: clamp(1.1rem, 4vw, 2.5rem);
}

/* Twinny's colour world. Put it on any section, or on <body> for the bar pages. */
.brand-tw {
  --brand-dark:   var(--tw-indigo);
  --brand-warm:   var(--tw-butter);
  --brand-accent: var(--tw-sunset);
  --brand-cta:    var(--tw-sunset-700);
  --brand-cool:   #86BED5;
}

/* ---------- 3. Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, picture, svg { display: block; max-width: 100%; }
a { color: inherit; }
ul[role="list"] { list-style: none; padding: 0; }

.skip-link {
  position: absolute; left: var(--s3); top: -3rem;
  background: var(--navy); color: var(--sf-lemon);
  padding: var(--s2) var(--s4); border-radius: 0 0 var(--radius) var(--radius);
  z-index: 100; transition: top .15s ease;
}
.skip-link:focus { top: 0; }
}

:where(a, button, [tabindex], input, summary, [role="tab"]):focus-visible {
  outline: 3px solid var(--brand-accent);
  outline-offset: 2px;
  border-radius: 4px;
}
.on-dark :where(a, button, [tabindex], summary, [role="tab"]):focus-visible {
  outline-color: var(--brand-warm);
}

/* ---------- 4. Type ---------- */
h1, h2, h3 { font-family: var(--font-display); font-weight: 400; line-height: var(--lh-head); }
h1 { font-size: var(--fs-hero); line-height: var(--lh-display); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); font-family: var(--font-ui); font-weight: 800; letter-spacing: var(--ls-name); }
p  { max-width: var(--measure); }

.eyebrow {
  font-size: var(--fs-eyebrow); font-weight: 800; text-transform: uppercase;
  letter-spacing: var(--ls-caps); color: var(--ink-soft);
}
.on-dark .eyebrow { color: var(--brand-warm); }
.small { font-size: var(--fs-small); }
.on-dark { color: var(--paper); }
.on-dark h1, .on-dark h2 { color: var(--brand-warm); }

.text-link {
  font-weight: 800;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  text-decoration-color: var(--brand-accent);
}
.text-link:hover { color: var(--brand-cta); }

/* ---------- 5. Layout primitives ---------- */
.container { width: min(100% - (var(--gutter) * 2), var(--container)); margin-inline: auto; }
.section   { padding-block: var(--section-y); }
.stack   > * + * { margin-top: var(--s4); }
.stack-lg > * + * { margin-top: var(--s5); }

.ground-navy  { background: var(--navy); }
.ground-cream { background: var(--cream); }
.ground-sky   { background: var(--sf-sky); }
.ground-lemon { background: var(--sf-lemon); }

.split {
  display: grid; gap: var(--s7); align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 860px) { .split { grid-template-columns: 1fr 1fr; } }

/* ---------- 6. Components ---------- */

/* 6.1 Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  font-family: var(--font-ui); font-weight: 800; font-size: var(--fs-body);
  padding: var(--s3) var(--s5); border-radius: var(--radius-btn);
  text-decoration: none; border: 2px solid transparent;
  transition: transform .12s ease, background-color .12s ease, color .12s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn--primary { background: var(--brand-cta); color: var(--paper); }
.btn--primary:hover { background: var(--brand-accent); }
.btn--ghost { border-color: currentColor; }
.btn--ghost:hover { background: var(--brand-warm); color: var(--navy); border-color: var(--brand-warm); }
/* WhatsApp green, darkened so a white bold label clears AA-normal (5.42:1).
   The stock #25D366 sits at 2.2:1 and fails. */
.btn--wa { background: #0F7A3D; color: var(--paper); }
.btn--wa:hover { background: #0B6E33; }
.btn__icon { width: 1.15em; height: 1.15em; flex: none; }

/* 6.2 Header */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--navy); color: var(--paper);
  border-bottom: 3px solid var(--sf-lemon);
}
.site-header > .container {
  display: flex; align-items: center; gap: var(--s4);
  padding-block: var(--s3); flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: var(--s3); text-decoration: none; margin-right: auto; }
/* Never let the logo be the thing that gives way when the bar runs out of room:
   with the language switcher in the row and the longer Spanish nav words, it was
   being squashed to a 6px sliver on phones. */
.brand img { border-radius: 50%; flex: 0 0 auto; }
.brand-word { font-family: var(--font-display); font-size: 1.35rem; color: var(--sf-lemon); }
.site-nav { display: flex; gap: var(--s5); align-items: center; }
.site-nav a { text-decoration: none; font-weight: 600; }
.site-nav a:hover { color: var(--sf-lemon-700); }
.site-header .btn { padding-block: var(--s2); }

/* Phones: logo plus nav, nothing else. The reserve button is dropped from the
   sticky bar entirely (it was the least useful thing there, and every page still
   leads with a full-width WhatsApp CTA), which frees the width to set the nav at
   full body size instead of shrinking it. */
@media (max-width: 40rem) {
  .site-header > .container { flex-wrap: nowrap; gap: var(--s2); }
  .brand-word { display: none; }
  .site-header .btn--wa { display: none; }
  /* Measured on 31 Aug 2026, and this bar has no slack to spare. At 390px the
     three nav words already wanted 242px against 235 available, so "Vis&iacute;tanos" was
     running under the language switcher BEFORE any dropdown existed; the two
     carets then added 26px more. At 375px (iPhone mini/SE, common) it was still
     3px over after the first pass. What claws it back, in order of how much it
     buys: the container gap above, this gap and font-size pair, and the smaller
     caret below. Do not widen any of them without re-measuring at 360 AND 375. */
  .site-nav {
    margin-left: auto;
    gap: clamp(.3rem, 2.4vw, var(--s5));
    font-size: clamp(.78rem, 3.15vw, var(--fs-small));
    min-width: 0;
  }
  .navdrop__toggle, .navdrop__link { gap: .15em; }
  .navdrop__caret { width: 8px; height: 8px; }
  .site-nav a { font-weight: 700; }
  /* The switcher stays (language matters more here than a second Reserve CTA),
     so the nav gives up the width instead: Spanish sets three longer words. */
  .site-header__right { gap: var(--s2); }
  .langswitch__toggle { padding-inline: var(--s1); }
}

/* Below ~375px the bar simply runs out of room: measured by shrinking the header
   container in 20px steps, clearance is +8px at 375 and above (a constant, because
   the brand's auto margin keeps the nav flush against the switcher) and goes
   negative at 360 and worse below. Rather than shrink the type further on the
   smallest phones, the nav drops to its own full-width row there. Costs ~26px of
   header height on those devices only, and nothing anywhere else.
   The dropdown panel is unaffected: it is positioned against the header container,
   not the nav, so it still opens under the whole bar. */
@media (max-width: 23.4rem) {
  .site-header > .container { flex-wrap: wrap; row-gap: var(--s2); }
  .brand { margin-right: 0; }
  .site-header__right { margin-left: auto; }
  .site-nav {
    order: 3; flex-basis: 100%;
    margin-left: 0; justify-content: space-between;
    font-size: var(--fs-small);
  }
}

/* 6.2b Language switcher. A native <details> disclosure: no JS, so it cannot be
   broken by a page whose own scripts fail, and it works before hydration on any
   page. Collapsed it is just the current language code; open it lists both. No
   flags — a flag is a country, not a language, and Spanish here is not Spain's
   alone. Kept visible on phones where the Reserve button is dropped: switching
   language matters more in the sticky bar than a second CTA. */
.site-header__right { display: flex; align-items: center; gap: var(--s3); }

.langswitch { position: relative; }
.langswitch__toggle {
  display: inline-flex; align-items: center; gap: .3em;
  min-height: 44px; padding: 0 var(--s2); cursor: pointer;
  font-weight: 700; font-size: var(--fs-small); letter-spacing: var(--ls-caps);
  color: var(--paper); list-style: none; transition: color .15s ease;
}
.langswitch__toggle::-webkit-details-marker { display: none; }  /* Safari's triangle */
.langswitch__toggle:hover { color: var(--sf-lemon-700); }
.langswitch__caret { width: 11px; height: 11px; flex: 0 0 auto; transition: transform .2s ease; }
.langswitch[open] .langswitch__caret { transform: rotate(180deg); }

.langswitch__menu {
  position: absolute; top: calc(100% + .55rem); right: 0; z-index: 120;
  min-width: 9.5rem; list-style: none; padding: var(--s2);
  background: var(--paper); border: 3px solid var(--navy);
  border-radius: var(--radius); box-shadow: 0 10px 26px rgb(0 0 0 / .22);
}
.langswitch__opt {
  display: block; padding: var(--s2) var(--s3); min-height: 40px;
  font-weight: 600; font-size: var(--fs-small); text-decoration: none;
  color: var(--ink); border-radius: 10px;
  transition: background .15s ease, color .15s ease;
}
.langswitch__opt:hover { background: var(--cream); color: var(--navy); }
.langswitch__opt.is-active { color: var(--sf-red-700); font-weight: 800; }

/* 6.2c Menu category dropdowns. Hamburguesas and Bebidas open a submenu of the
   macro-categories and jump to an anchor on their page. Markup comes from
   header() in scripts/build_menus.py and is stamped onto all ten pages -- do not
   hand-edit a nav in a page.

   TWO variants ship in the markup and this file shows exactly one of them, because
   a mouse and a touchscreen want opposite things:

     .navdrop--hover   a real <a> to the page, panel revealed on :hover and on
                       :focus-within. This is what the owner asked for on 31 Aug
                       2026 and what teacherstefano.com does: clicking the word
                       itself goes to the full menu. Shown only where a fine
                       pointer that can hover actually exists.
     .navdrop--tap     the native <details> disclosure. The default, and the only
                       one a phone gets: with no hover, a tap on a link navigates
                       instead of opening anything, so there the label has to be
                       the toggle and "Toda la carta" is the way to the page.

   Both are JS-free, which is the point: same reasoning as the language switcher
   above, nothing on the page can break either one. The hidden variant is
   display:none, so it is out of the tab order and out of the a11y tree.
   `name="sfnav"` makes the browser close one <details> when the other opens. */
.navdrop { position: relative; }
.navdrop--hover { display: none; }
@media (hover: hover) and (pointer: fine) {
  .navdrop--hover { display: block; }
  .navdrop--tap { display: none; }
  /* Closed by default and revealed by the parent's :hover. Kept in the layout
     with visibility rather than display so it can transition, and so the panel
     is measurable for the bridge below. */
  .navdrop--hover .navdrop__menu {
    opacity: 0; visibility: hidden;
    transform: translateY(-6px);
    transition: opacity .15s ease, transform .15s ease, visibility .15s;
  }
  .navdrop--hover:hover .navdrop__menu,
  .navdrop--hover:focus-within .navdrop__menu {
    opacity: 1; visibility: visible; transform: translateY(0);
  }
  .navdrop--hover:hover .navdrop__caret,
  .navdrop--hover:focus-within .navdrop__caret { transform: rotate(180deg); }
  /* The bridge: the panel sits .55rem below the label, and without something
     filling that gap the menu closes the moment the pointer crosses it. Same
     trick as the ::before on teacherstefano.com's own dropdown. */
  .navdrop--hover .navdrop__menu::before {
    content: ""; position: absolute; left: 0; right: 0; top: -.6rem; height: .6rem;
  }
}
.navdrop__link {
  display: inline-flex; align-items: center; gap: .3em;
  min-height: 44px; text-decoration: none;
  font-weight: 600; color: var(--paper);
  transition: color .15s ease;
}
.navdrop__link:hover,
.navdrop__link[aria-current="page"] { color: var(--sf-lemon-700); }
.navdrop__toggle {
  display: inline-flex; align-items: center; gap: .3em;
  min-height: 44px; cursor: pointer;
  font-weight: 600; color: var(--paper); list-style: none;
  transition: color .15s ease;
}
.navdrop__toggle::-webkit-details-marker { display: none; }  /* Safari's triangle */
.navdrop__toggle:hover,
.navdrop__toggle[aria-current="page"] { color: var(--sf-lemon-700); }
.navdrop__caret { width: 11px; height: 11px; flex: 0 0 auto; transition: transform .2s ease; }
.navdrop[open] .navdrop__caret { transform: rotate(180deg); }
.navdrop__menu {
  position: absolute; top: calc(100% + .55rem); left: 0; z-index: 120;
  min-width: 13rem; list-style: none; padding: var(--s2);
  background: var(--paper); border: 3px solid var(--navy);
  border-radius: var(--radius); box-shadow: 0 10px 26px rgb(0 0 0 / .22);
}
.navdrop__opt {
  display: block; padding: var(--s2) var(--s3); min-height: 40px;
  font-weight: 600; font-size: var(--fs-small); text-decoration: none;
  color: var(--ink); border-radius: 10px; white-space: nowrap;
  transition: background .15s ease, color .15s ease;
}
/* Hover must never make a label LIGHTER than it was at rest. It did: the resting
   ink is #221F2E (14:1 on cream) and this rule used to swap in --navy #272E59
   (10.7:1), so moving the pointer onto a row faded it slightly just as the cream
   ground came up under it. Keep the ink. */
.navdrop__opt:hover { background: var(--cream); color: var(--ink); }
/* The page itself, set apart from the categories under it: a <summary> is a
   disclosure control and not a link, so this row is the only way into the page
   from the bar it is named in. Red carries that distinction, but in the text-safe
   800 rather than the CTA fill -- see the token. */
.navdrop__opt--all,
.navdrop__opt--all:hover { font-weight: 800; color: var(--sf-red-800); }
.navdrop__menu li:first-child { border-bottom: 2px solid var(--cream); padding-bottom: var(--s1);
  margin-bottom: var(--s1); }

/* Phones: the bar is already tight, so the panel stops hanging off one entry and
   becomes a sheet the width of the header. `position: static` on the .navdrop
   hands the absolute panel to the header container instead. */
@media (max-width: 40rem) {
  .site-header > .container { position: relative; }
  .navdrop { position: static; }
  .navdrop__menu {
    left: 0; right: 0; top: calc(100% + var(--s3));
    min-width: 0; border-radius: 0 0 var(--radius) var(--radius);
  }
  .navdrop__opt { white-space: normal; font-size: 1rem; }
}

/* Anchored jumps land under the sticky header instead of behind it. The header is
   about 74px on a desktop and a little less on a phone; one clamp covers both. */
.section[id], .bcard[id] { scroll-margin-top: clamp(5.25rem, 4rem + 4vw, 6.25rem); }

/* The four builds in the burger page's closing block. A real list, because the
   owner wrote them as one -- our earlier version had them semicolon-separated in
   a paragraph, which is harder to scan and is not what a menu's builds are. */
.about-list { margin: 0; padding-left: 1.2em; display: grid; gap: var(--s2); }
.about-list li { line-height: var(--lh-body, 1.6); }

/* 6.3 Hero — brand-graphic, no photography */
.hero {
  position: relative; overflow: hidden;
  background: var(--sf-sky);
  padding-block: clamp(2rem, 6vw, 4.5rem);
  text-align: center;
}
/* Retro sun-ray burst, drawn in CSS — echoes the wavy stripes in their own artwork.
   Kept faint: it is a texture behind the logo, not a pattern competing with it. */
.hero::before {
  content: ""; position: absolute; inset: -40% -10% auto; height: 180%;
  background: repeating-conic-gradient(from 0deg at 50% 24%,
    color-mix(in srgb, var(--sf-ocean) 9%, transparent) 0deg 6deg,
    transparent 6deg 13deg);
  -webkit-mask-image: radial-gradient(ellipse 62% 48% at 50% 26%, #000 0%, transparent 78%);
          mask-image: radial-gradient(ellipse 62% 48% at 50% 26%, #000 0%, transparent 78%);
  pointer-events: none;
}
.hero > .container { position: relative; }
/* Two badges, side by side: one venue, two brands, which is the premise of the
   whole site. Both source files are 400x400 squares with their own background
   baked in, so both are clipped to a circle. They wrap rather than shrink below
   the point where the lettering stops being readable. */
.hero__logos {
  display: flex; justify-content: center; align-items: center;
  gap: clamp(var(--s3), 3vw, var(--s5)); flex-wrap: wrap;
}
.hero__logo {
  width: clamp(96px, 13vw, 148px); height: auto; aspect-ratio: 1;
  border-radius: 50%;
}
.hero h1 { color: var(--navy); margin-top: var(--s4); }
.hero__lede {
  margin: var(--s4) auto 0; color: var(--navy);
  font-size: clamp(1.05rem, 2.2vw, 1.3rem); font-weight: 600;
}
.hero__actions {
  display: flex; flex-wrap: wrap; gap: var(--s3);
  justify-content: center; margin-top: var(--s6);
}
.hero__note { margin: var(--s5) auto 0; color: var(--navy); }

/* 6.3b Twinny's photographic hero, carried over from the v1 pitch site. Only the
   bar side gets a photo: Single Fin leads on its logo and palette instead. The
   scrim is what keeps butter-on-photo text legible over a bright sunset. */
.tw-hero {
  position: relative; isolation: isolate; overflow: hidden;
  padding-block: clamp(2.5rem, 8vw, 5rem);
}
.tw-hero__media { position: absolute; inset: 0; z-index: -2; }
.tw-hero__media picture { display: block; width: 100%; height: 100%; }
.tw-hero__media img { width: 100%; height: 100%; object-fit: cover; }
/* Weighted to the bottom, where the text sits, so the sky stays visible up top.
   On a phone the horizontal pass is dropped: at 390px it covers the whole frame
   and buries the photo entirely.
   The 12/62/92 stops are measured, not guessed. Sampling the actual JPEG pixels
   behind each text band and blending the scrim over them gives, worst case on a
   390px phone: eyebrow 7.6:1, h1 7.7:1, body copy 5.5:1. An earlier 12/55/90
   curve put body copy at 4.53:1, which cleared AA by 0.03 and would have failed
   on any brighter crop. */
.tw-hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--tw-indigo) 12%, transparent) 0%,
                            color-mix(in srgb, var(--tw-indigo) 62%, transparent) 45%,
                            color-mix(in srgb, var(--tw-indigo) 92%, transparent) 100%);
}
@media (min-width: 45rem) {
  .tw-hero::after {
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--tw-indigo) 10%, transparent) 0%,
                              color-mix(in srgb, var(--tw-indigo) 58%, transparent) 45%,
                              color-mix(in srgb, var(--tw-indigo) 90%, transparent) 100%),
      linear-gradient(90deg, color-mix(in srgb, var(--tw-indigo) 55%, transparent) 0%,
                             transparent 60%);
  }
}
.tw-hero > .container {
  position: relative;
  min-height: clamp(20rem, 46vh, 28rem);
  display: flex; flex-direction: column; justify-content: flex-end;
}
.tw-hero h1 { font-size: var(--fs-hero); line-height: var(--lh-display); text-wrap: balance; }

/* 6.4 The two doors */
.doors { display: grid; gap: var(--s5); grid-template-columns: 1fr; }
@media (min-width: 900px) {
  /* Single Fin leads: it takes the wider column. */
  .doors { grid-template-columns: 1.35fr 1fr; align-items: stretch; }
}
.door {
  display: flex; flex-direction: column; justify-content: flex-end;
  min-height: clamp(300px, 42vw, 420px);
  padding: var(--s6); border-radius: var(--radius);
  text-decoration: none; position: relative; overflow: hidden;
  transition: transform .15s ease, box-shadow .15s ease;
}
.door:hover { transform: translateY(-4px); box-shadow: 0 14px 34px rgb(0 0 0 / .22); }
.door__eyebrow {
  font-size: var(--fs-eyebrow); font-weight: 800; text-transform: uppercase;
  letter-spacing: var(--ls-caps);
}
.door h2 { font-size: clamp(2rem, 5vw, 3.2rem); margin-top: var(--s2); }
.door p { margin-top: var(--s3); max-width: 34ch; }
.door__go { margin-top: var(--s5); font-weight: 800; display: inline-flex; gap: var(--s2); }
.door__go::after { content: "\2192"; transition: transform .15s ease; }
.door:hover .door__go::after { transform: translateX(4px); }

.door--sf { background: var(--sf-ocean); color: var(--navy); }
.door--sf h2 { color: var(--navy); }
.door--sf .door__eyebrow { color: var(--navy); }
.door--tw { background: var(--tw-indigo); color: var(--paper); }
.door--tw h2 { color: var(--tw-butter); }
.door--tw .door__eyebrow { color: var(--tw-butter); }

/* A photograph behind each door. The brand colour stays as the base and the
   scrim is that SAME colour, so the doors still read as the palette rather than
   turning into photo cards: the picture is place and texture, not the subject.
   The base colour also covers the gap while the image loads.

   The stops are measured, not guessed. Sampling the worst-case pixel behind each
   text band (brightest for the navy text, darkest for the light text) and
   blending the scrim over it gives: Single Fin 5.34 / 5.22 / 5.09 / 4.97 and
   Twinny 11.31 / 11.22 / 17.24 / 17.00, against 4.5 for normal text and 3.0 for
   the large h2. Worth knowing that the Single Fin door is the tight one BY
   DESIGN, not because of the photo: navy on flat ocean is 4.56, which clears AA
   by 0.06, so the photo and scrim actually improve it. Re-measure if either
   photo is swapped. */
.door { isolation: isolate; }
.door__media { position: absolute; inset: 0; z-index: -2; }
.door__media img { width: 100%; height: 100%; object-fit: cover; }
.door::after { content: ""; position: absolute; inset: 0; z-index: -1; }
/* The Single Fin scrim is ocean lightened with 35% white, not ocean alone, and
   that is the whole trick. Navy on FLAT ocean is 4.56:1, so piling on more ocean
   cannot make the text stand out more: it converges on 4.56 and actually gets
   slightly worse the more you add. Mixing white in raises the ceiling instead.
   Measured over the burger photo: 7.14 / 7.07 / 7.09 / 6.90, up from 5.24 /
   5.14 / 5.09 / 4.88 with pure ocean. 50% white would reach 8:1 but the door
   stops reading as the brand blue, which is why it stops here. */
.door--sf::after {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--sf-ocean-wash) 55%, transparent) 0%,
    color-mix(in srgb, var(--sf-ocean-wash) 80%, transparent) 45%,
    color-mix(in srgb, var(--sf-ocean-wash) 92%, transparent) 100%);
}
.door--tw::after {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--tw-indigo) 45%, transparent) 0%,
    color-mix(in srgb, var(--tw-indigo) 72%, transparent) 45%,
    color-mix(in srgb, var(--tw-indigo) 88%, transparent) 100%);
}

/* Bubble motif lifted from the logo's border: outlined, not filled blobs */
.door::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    radial-gradient(circle at 86% 16%, transparent 0 30px, rgb(255 255 255 / .20) 30px 33px, transparent 34px),
    radial-gradient(circle at 95% 34%, transparent 0 13px, rgb(255 255 255 / .16) 13px 15px, transparent 16px),
    radial-gradient(circle at 76% 8%,  transparent 0 7px,  rgb(255 255 255 / .14) 7px 9px,  transparent 10px),
    radial-gradient(circle at 92% 60%, transparent 0 5px,  rgb(255 255 255 / .12) 5px 7px,  transparent 8px);
}

.tag {
  display: inline-block; font-size: .7rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: var(--ls-caps); padding: var(--s1) var(--s3);
  border-radius: var(--radius-btn); background: var(--navy); color: var(--sf-lemon);
}
.tag--hot { background: var(--sf-red-700); color: var(--paper); }

/* 6.6b Menu pages — one item per row, prices in their own colour-coded grid,
   keyed to the build tokens in §2. */
.menu-list { display: grid; gap: var(--s5); }
.menu-item {
  border: 3px solid var(--brand-dark); border-radius: var(--radius);
  padding: var(--s5); background: var(--paper);
}
.menu-item h3 {
  font-family: var(--font-display); font-size: clamp(1.4rem, 3.4vw, 1.9rem);
  letter-spacing: 0; color: var(--brand-dark);
}
.menu-item__desc { margin-top: var(--s3); color: var(--ink-soft); }
.menu-item__meta { margin-top: var(--s2); color: var(--ink-soft); font-style: italic; }
.menu-item__option { margin-top: var(--s3); color: var(--ink-soft); }
.menu-item__option-label {
  font-weight: 800; text-transform: uppercase; letter-spacing: var(--ls-caps);
  font-size: .7rem; color: var(--brand-dark); margin-right: var(--s2);
}

/* Two blocks to a row at every width, phones included, exactly as the printed
   card sets them: the type shrinks rather than the grid unstacking, so four
   builds stay one compact block instead of a four-deep column. */
.price-grid {
  margin-top: var(--s5); display: grid; gap: var(--s2);
  grid-template-columns: repeat(2, 1fr);
}
/* The veggie cards carry three bases, and print centres the odd one. */
.price-grid--three > :last-child {
  grid-column: 1 / -1; width: min(100%, 62%); margin-inline: auto;
}
.price-cell {
  display: block; text-align: center;
  padding: var(--s2) var(--s3); border-radius: 8px;
  font-family: "Bitter", Georgia, serif; font-weight: 700; color: var(--paper);
}
.price-cell__name {
  display: block; font-size: clamp(.68rem, 2.5vw, .84rem);
  letter-spacing: .04em; text-transform: uppercase; text-wrap: balance;
}
.price-cell__val {
  display: block; font-size: clamp(.8rem, 2.9vw, .98rem); white-space: nowrap;
}

.price-cell--third-pounder { background: var(--build-third-pounder); }
.price-cell--doble-smash   { background: var(--build-doble-smash); }
.price-cell--pollo         { background: var(--build-pollo); color: var(--navy); }
.price-cell--gourmet       { background: var(--build-gourmet); }
.price-cell--espinacas     { background: var(--build-espinacas); }
.price-cell--tempeh        { background: var(--build-tempeh); }
.price-cell--alcachofas    { background: var(--build-alcachofas); }

/* Drinks carry a single price, so they get a plain line instead of the grid. */
.menu-item--drink { border-color: var(--tw-indigo); }
.menu-item__price {
  margin-top: var(--s4); padding-top: var(--s3);
  border-top: 2px dashed var(--line);
  font-weight: 800; color: var(--tw-indigo);
}

.ground-indigo { background: var(--tw-indigo); }

/* 6.6c Rating block. Static and self-hosted: no third-party widget, so the site
   keeps zero external loads and needs no cookie banner. Figures are a dated
   snapshot, refreshed by hand. See TO-CONFIRM. */
.rating { display: grid; gap: var(--s5); justify-items: center; text-align: center; }
.rating__stars { display: flex; gap: var(--s1); }
.rating__star { width: clamp(28px, 6vw, 40px); height: clamp(28px, 6vw, 40px); }
.rating__star-bg { fill: color-mix(in srgb, var(--brand-dark) 18%, transparent); }
.rating__star-fg { fill: var(--brand-accent); }
.rating__score {
  font-family: var(--font-display); font-size: clamp(2.6rem, 8vw, 4rem);
  line-height: 1; color: var(--brand-dark);
}
.rating__score span { font-family: var(--font-ui); font-size: 1.25rem; font-weight: 800; color: var(--ink-soft); }
.rating__count { font-weight: 600; }
.rating__source { font-weight: 800; letter-spacing: var(--ls-caps); text-transform: uppercase; font-size: var(--fs-eyebrow); color: var(--ink-soft); }

/* 6.6d Third-party embeds. One left: the Google Maps iframe on the visit page.
   The Tripadvisor widget and its rules were removed on 20 Aug 2026 (DECISIONS),
   which is why the homepage now loads no third-party script at all.

   That one iframe is GATED: the page ships the placeholder below instead of the
   frame, and assets/js/consent.js swaps the frame in only once the visitor has
   consented. So .map-embed--gated is what a first-time visitor actually sees,
   and .map-embed iframe only ever exists after a yes. */
.map-embed {
  border: 3px solid var(--brand-dark); border-radius: var(--radius);
  overflow: hidden; line-height: 0;
}
.map-embed iframe { width: 100%; height: clamp(260px, 42vw, 420px); border: 0; display: block; }

/* The placeholder sits in exactly the box the map will fill, so consenting does
   not make the page jump. line-height is restored because the parent zeroes it
   for the frame. White fill on the lemon section: the 3px navy border is what
   draws the box (11.25:1 against the ground), not the fill. */
.map-embed--gated { line-height: var(--lh-body); background: var(--paper); }
.map-gate {
  display: flex; flex-direction: column; align-items: flex-start;
  justify-content: center; gap: var(--s4);
  padding: var(--s5); min-height: clamp(260px, 42vw, 420px);
}
.map-gate p { max-width: 54ch; }
.map-gate__lead { font-weight: 800; color: var(--ink); }            /* 16.11:1 on white */
.map-gate__alt  { font-size: var(--fs-small); color: var(--ink-soft); }  /* 7.66:1 on white */
.map-gate .btn:focus-visible { outline-color: var(--brand-accent); }     /* 4.11:1 on white */
/* Where the frame lands, focus is parked on the box so a keyboard visitor is not
   dropped back at the top of the document. */
.map-embed:focus { outline: 3px solid var(--brand-accent); outline-offset: 2px; }

/* 6.6e Reviews marquee. Same interaction model as the teacherstefano.com homepage
   (native horizontal scroller, JS-cloned track, rAF drift, pauses on interaction),
   restyled onto this design system. Populated only from quotes we are allowed to
   publish: see TO-CONFIRM on why Google/Tripadvisor text cannot be copied here. */
/* The heading above sat straight on top of the cards. Both rails get the same
   gap so the two sections read as one pattern. */
.marquee, .slideshow { margin-top: var(--s6); }
.marquee { position: relative; width: 100%; }
.marquee::before, .marquee::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: clamp(24px, 8vw, 90px);
  z-index: 2; pointer-events: none;
}
.marquee::before { left: 0;  background: linear-gradient(90deg, var(--cream), transparent); }
.marquee::after  { right: 0; background: linear-gradient(270deg, var(--cream), transparent); }
.marquee-viewport {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; cursor: grab;
}
.marquee-viewport::-webkit-scrollbar { display: none; }
.marquee-viewport.dragging { cursor: grabbing; }
.marquee-track { display: flex; gap: var(--s5); width: max-content; padding-block: var(--s2); }

.review-card {
  flex: 0 0 min(84vw, 22rem);
  display: flex; flex-direction: column; gap: var(--s3);
  background: var(--paper); border: 3px solid var(--brand-dark);
  border-radius: var(--radius); padding: var(--s5);
}
.review-card__stars { display: flex; gap: 2px; }
.review-card__stars svg { width: 18px; height: 18px; fill: var(--brand-accent); }
.review-card__text { color: var(--ink); font-size: var(--fs-small); line-height: 1.65; max-width: none; }
.review-card__who { margin-top: auto; padding-top: var(--s3); border-top: 2px dashed var(--line); }
.review-card__name { font-weight: 800; color: var(--brand-dark); }
.review-card__meta { font-size: var(--fs-small); color: var(--ink-soft); }

@media (prefers-reduced-motion: reduce) { .marquee-viewport { cursor: auto; } }
@media (max-width: 40rem) {
  .marquee::before, .marquee::after { display: none; }
  .marquee-viewport { cursor: auto; }
}

/* 6.7 Media */
.media img { border-radius: var(--radius); width: 100%; height: auto; }

/* 6.8 Extras strip */
.extras { display: grid; gap: var(--s5); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.extras h3 { color: var(--navy); }
.extras p { margin-top: var(--s2); font-size: var(--fs-small); }

/* 6.9 Hours — two shops, two sets */
.hours { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.hours-card {
  padding: var(--s5); border-radius: var(--radius);
  border: 2px solid var(--line-on-dark);
}
.hours-card h3 { color: var(--brand-warm); }
.hours-card dl { margin-top: var(--s3); display: grid; gap: var(--s1); }
.hours-card dt { font-weight: 800; font-size: var(--fs-small); }
.hours-card dd { margin: 0; font-size: var(--fs-small); }
.hours-card--tw { border-color: color-mix(in srgb, var(--tw-butter) 40%, transparent); }
.hours-card--tw h3 { color: var(--tw-butter); }

/* 6.9b Form (Work with us).
   Deliberately inert on the pitch build: the submit is type="button" and the
   form has no action, so nothing is ever sent or captured. See TO-CONFIRM. */
.form { display: grid; gap: var(--s5); max-width: 40rem; }
.field { display: grid; gap: var(--s2); }
.field label { font-weight: 800; font-size: var(--fs-small); color: var(--brand-dark); }
.field input,
.field textarea {
  font-family: var(--font-ui); font-size: var(--fs-body); color: var(--ink);
  padding: var(--s3) var(--s4); background: var(--paper);
  border: 3px solid var(--brand-dark); border-radius: var(--radius);
  width: 100%;
}
.field textarea { min-height: 9rem; resize: vertical; }
.field input::placeholder,
.field textarea::placeholder { color: var(--ink-soft); opacity: .7; }
.field input:focus-visible,
.field textarea:focus-visible { outline: 3px solid var(--brand-accent); outline-offset: 2px; }
.form__actions { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; }
.form__note { color: var(--ink-soft); }

/* 6.10 Footer */
.site-footer {
  background: var(--navy-deep); color: var(--paper);
  padding-block: var(--s7);
}
.site-footer > .container {
  display: grid; gap: var(--s6);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.site-footer h3 { color: var(--sf-lemon); font-size: var(--fs-small); text-transform: uppercase; }
.site-footer p { margin-top: var(--s3); }
.site-footer a { text-decoration-thickness: 2px; text-underline-offset: 3px; }

/* 6.11 Cocktail cards — the printed menu, rebuilt as text.
   Twinny's menu is 37 A5 posters: a swirl ground, a hand-drawn glass, a butter
   panel carrying strength / calories / three words, and an ingredient band.
   Rebuilt in HTML so the words stay selectable, searchable and translatable.
   The only image is the illustration, exported with its own alpha by
   scripts/extract_drink_art.py. Section colours are sampled from the PDF.
   Three departures from print: the price disc is not shown at all (see
   DECISIONS 2026-08-20), the coral is deepened #F16045 -> #E0492E, which now
   carries the calorie figure rather than the disc, and the three descriptor
   words are stacked rather than joined by middots, which the house rules
   forbid. */
.drinks-grid {
  display: grid; gap: var(--s5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
}

.dcard {
  --tint:  #F8BAA7;
  --deep:  #E0492E;
  --panel: #FFDFB6;
  --band:  #FDE7DD;
  --swirl: url("/assets/textures/swirl-top-seller.webp");

  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column;
  border-radius: var(--radius);
  background-color: var(--tint);
}
/* The swirl is the printed background, held back so the linework stays readable
   over it. Decorative: it never carries meaning, so it is a background, not an img. */
.dcard::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--swirl) center / cover;
  opacity: .5;
}
.dcard--muddled       { --tint:#A9BCDF; --deep:#00386E; --band:#DFE3F2; --swirl:url("/assets/textures/swirl-muddled.webp"); }
.dcard--vintage       { --tint:#EDC189; --deep:#A06706; --band:#FEE8CC; --swirl:url("/assets/textures/swirl-vintage.webp"); }
.dcard--non-alcoholic { --tint:#6EC0D9; --deep:#00707F; --band:#C0E5F8; --swirl:url("/assets/textures/swirl-non-alcoholic.webp"); }

/* The name is a direct child of the card now: the head wrapper only existed to
   pair it with the price disc, which the site no longer shows. Centred with it
   gone, so the card keeps the one axis the art, panel and band already read on. */
.dcard__name {
  padding: var(--s5) var(--s5) 0; text-align: center;
  font-family: var(--font-menu-display); font-weight: 400;
  font-size: clamp(1.6rem, 3.4vw, 2.1rem); line-height: 1.05;
  letter-spacing: 0; color: var(--ink); text-transform: uppercase;
}
.dcard__art {
  width: auto; height: clamp(200px, 26vw, 250px);
  margin: var(--s4) auto 0; object-fit: contain;
}

/* The auto top margin takes up whatever slack the grid row gives this card, so
   the panel and the ingredient band always sit together at the foot and the
   spare swirl opens up around the glass, as it does in print. */
.dcard__panel {
  margin: auto var(--s5) 0; padding: var(--s3) var(--s4);
  background: var(--panel); border-radius: 10px;
  display: grid; gap: var(--s2); justify-items: center; text-align: center;
}
.dcard__stat-label {
  font-family: var(--font-menu-ui);
  font-size: .75rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--ls-caps); color: var(--ink-soft);
}
.dcard__glasses { display: flex; gap: 5px; }
.dcard__glasses svg { width: 20px; height: 20px; fill: var(--deep); }
.dcard__glasses svg.is-empty { fill: color-mix(in srgb, var(--deep) 26%, transparent); }
.dcard__cal { font-family: var(--font-menu-display); font-size: 1.6rem; line-height: 1; color: var(--deep); }
.dcard__words {
  border-top: 2px solid color-mix(in srgb, var(--ink) 20%, transparent);
  padding-top: var(--s2); width: 100%;
  display: grid; gap: 2px;
  font-family: var(--font-menu-display); font-size: 1.15rem; line-height: 1.2;
  text-transform: uppercase; color: var(--ink);
}

/* Centred, so the whole card reads down one axis: name over glass over panel
   over ingredients. */
.dcard__foot { margin-top: var(--s5); padding: var(--s4) var(--s5); background: var(--band); text-align: center; }
.dcard__ing {
  max-width: none; font-family: var(--font-menu-ui); font-size: var(--fs-small);
  text-transform: uppercase; letter-spacing: .03em; color: var(--ink);
}
.dcard__option {
  max-width: none; margin-top: var(--s2);
  font-family: var(--font-menu-ui); font-size: .8rem; color: var(--ink-soft);
}
.dcard__option-label {
  font-weight: 700; text-transform: uppercase; letter-spacing: var(--ls-caps);
  font-size: .7rem; color: var(--deep); margin-right: var(--s2);
}

/* Section divider: the printed section page, full width. The title is set in
   paper with a hand-drawn-looking outline in the section colour, as in print. */
.drinks-head {
  --tint: #F8BAA7; --deep: #E0492E;
  --swirl: url("/assets/textures/swirl-top-seller.webp");
  border-radius: var(--radius); overflow: hidden;
  padding: clamp(1.75rem, 5vw, 3rem) var(--s5); text-align: center;
  background: var(--tint) var(--swirl) center / cover;
}
.drinks-head--muddled       { --tint:#839FD2; --deep:#00386E; --swirl:url("/assets/textures/swirl-muddled.webp"); }
.drinks-head--vintage       { --tint:#E5A857; --deep:#A06706; --swirl:url("/assets/textures/swirl-vintage.webp"); }
.drinks-head--non-alcoholic { --tint:#2EA6C9; --deep:#00707F; --swirl:url("/assets/textures/swirl-non-alcoholic.webp"); }
/* Print sets these titles in Barrio too, drawn twice with an offset to fake an
   outline. Same idea here, done with text-shadow. */
.drinks-head h2 {
  font-family: var(--font-menu-display);
  color: var(--paper); font-size: clamp(2.1rem, 6.5vw, 3.5rem); text-transform: uppercase;
  text-shadow:
     3px 0 0 var(--deep), -3px 0 0 var(--deep),  0 3px 0 var(--deep),  0 -3px 0 var(--deep),
     2px 2px 0 var(--deep), -2px 2px 0 var(--deep), 2px -2px 0 var(--deep), -2px -2px 0 var(--deep);
}

/* 6.12 Burger cards.
   A psychedelic page ground in the Single Fin stripe palette, a cream panel
   ruled in navy, the dish name across the card in its own display face, the
   description in Spanish then English, and the build prices in their colour
   blocks. The card is now text all the way through: the ten cut-out doodles came
   off on 20 Aug so each title could take the card's full width, which is what
   made the cards shorter. The artwork is still in site/assets/burgers/.

   The display faces are stand-ins until a vector export names the real ones (see
   the @font-face note in §1), and the swirl ground is a band mirror-tiled out of
   a 1080x1920 PNG rather than a vector.

   The price blocks reuse §6.6b's `.price-cell` colours, lifted from these same
   cards and darkened where a white label needed to clear AA. */
.burger-grid {
  display: grid; gap: var(--s5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
}
.ground-swirl {
  background: #D9BE8A url("/assets/textures/swirl-singlefin.webp") center / cover;
}

.bcard {
  --card: #FBE3BA;
  --title: var(--navy);
  --title-edge: none;
  --face: var(--font-display);
  position: relative; overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--card);
  border: 3px solid var(--navy); border-radius: var(--radius);
  padding: var(--s5);
}
.bcard--veg { --card: #B2C83F; }

/* The article ("El", "LA") now runs on the same line as the name, sized off the
   title so it keeps print's proportion at every width. It is inside the <h2>,
   so it needs no block of its own. */
.bcard__article {
  font-size: .5em; color: var(--navy);
  /* Inside the heading now, so it would otherwise inherit that title's contour.
     At half the title's size a 3px navy stroke on navy fill closes the counters
     and "EL" reads as a solid block, so the article takes no stroke at all. */
  -webkit-text-stroke: 0;
}
.bcard__name {
  font-family: var(--face); font-weight: var(--face-weight, 400);
  font-size: clamp(1.9rem, 5.2vw, 2.9rem); line-height: 1.02;
  letter-spacing: 0; color: var(--title);
  /* Several titles are drawn as a fill inside a heavy contour. paint-order
     keeps the stroke behind the fill so the letterform stays its own weight. */
  -webkit-text-stroke: var(--title-edge); paint-order: stroke fill;
}
/* Second line of El Clásico Premium, which prints in a face of its own. */
.bcard__sub {
  display: block; margin-top: .1em;
  font-family: "Bevan", var(--font-display); font-size: .82em; line-height: 1;
  color: #F0C264; -webkit-text-stroke: 2px var(--navy); paint-order: stroke fill;
}

/* One face and one title colour per burger, both read off the layout PNGs.
   The faces are stand-ins; the colours are sampled and exact. */
.bcard--clasico   { --face: "Abril Fatface", Georgia, serif;      --title: #C25A08; }
.bcard--increible { --face: "Titan One", var(--font-display);     --title: #F7C707; --title-edge: 3px var(--navy); }
.bcard--capitan   { --face: "Pirata One", Georgia, serif;         --title: #16161A; }
.bcard--texano    { --face: "Rye", Georgia, serif;                --title: #DC4632; }
.bcard--royale    { --face: "Playfair Display", Georgia, serif;   --title: #23306B; --face-weight: 700; }
.bcard--sunny     { --face: "Rock Salt", var(--font-ui);          --title: #B0600C; }
.bcard--numberone { --face: "Archivo Black", var(--font-ui);      --title: #AEB4C2; --title-edge: 3px var(--navy); }
.bcard--bigfin    { --face: "Anton", var(--font-ui);              --title: #1F72B8; }
.bcard--emmy      { --face: "Permanent Marker", var(--font-ui);   --title: #7A67B4; }
.bcard--imperial  { --face: "Cinzel", Georgia, serif;             --title: #23306B; --face-weight: 700; }
.bcard--capres    { --face: "Fredoka", var(--font-ui);            --title: #3F6620; --face-weight: 600; }
/* Capres is lettered in the Italian flag, two letters to a colour. On the lime
   ground the green and the red are deepened until each clears 3:1 for large
   text; the white pair cannot, so it takes a navy contour to carry the shape.
   Logged in TO-CONFIRM: it is the owner's flag, not ours to recolour. */
.bcard--capres .t1 { color: #3F6620; }
.bcard--capres .t2 { color: var(--cream); -webkit-text-stroke: 3px var(--navy); paint-order: stroke fill; }
.bcard--capres .t3 { color: #B33322; }
.bcard--popeye    { --face: "Luckiest Guy", var(--font-display);  --title: #CBDD5E; --title-edge: 3px var(--navy); }
/* Print sets Budista pale-on-lime, which is barely legible on a screen. Same
   two colours, swapped: the dark green now carries the letter (5.09:1). */
.bcard--budista   { --face: "Poiret One", var(--font-ui);         --title: #2C4F0B; --title-edge: 2px #EAF4A8; }

.bcard__es, .bcard__en {
  max-width: none; font-family: "Bitter", Georgia, serif; font-weight: 700;
  color: var(--ink); line-height: 1.35;
}
.bcard__es { margin-top: var(--s4); }
.bcard__en { font-style: italic; opacity: .82; }
.bcard__tag { align-self: flex-start; margin-top: var(--s3); }
.bcard .price-grid { margin-top: auto; padding-top: var(--s4); }

/* 6.13 Cookie consent. The bar, and the footer control that reopens it.

   Scope check before reading the rules: this site sets no cookies of its own and
   loads exactly one third party, the Google Maps iframe on the visit page. So
   there is ONE decision here and no category toggles: there is nothing else to
   toggle. The decision is kept in localStorage, which needs no consent of its
   own, so the site stays genuinely cookie-free until the map is loaded.

   It sits IN THE FLOW at the top of the document, above the sticky header, and
   is deliberately NOT fixed to the bottom of the screen. Measured, not assumed:
   fixed to the bottom of a 390x844 phone the bar ran from y=675 while the hero's
   WhatsApp button ended at y=600, a seven-pixel clearance that disappears on any
   shorter viewport, and Safari's own chrome takes about that much. In the flow it
   cannot cover a call to action on any screen, and it needs no z-index against
   the sticky header, no measured body padding and no resize listener.

   It is not a dialog: no focus trap, no scrim, no close button. A close button
   would be a third answer that means neither yes nor no, and silence must not
   read as consent, so the only ways out are Accept and Reject. */
.consent {
  background: var(--navy-deep); color: var(--paper);   /* 15.74:1 */
  border-bottom: 3px solid var(--sf-lemon);
  padding-block: var(--s4);
}
.consent__inner {
  width: min(100% - (var(--gutter) * 2), var(--container));
  margin-inline: auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4) var(--s5);
}
.consent__text { font-size: var(--fs-small); flex: 1 1 20rem; max-width: 68ch; }
.consent__actions { display: flex; gap: var(--s3); flex: 1 1 auto; }

/* Accept and Reject are ONE control drawn twice. Same class, same fill, same
   label weight, same padding, same width. Nothing here nudges towards yes, which
   is the whole point: a solid Accept next to a faint text Reject is the usual way
   these bars fail. */
.consent__btn {
  background: var(--sf-lemon); color: var(--navy);   /* 11.25:1 */
  min-height: 44px;
  /* Equal by construction, not by luck: two flex items sharing a zero basis come
     out the same width whatever their labels say, and at the wide breakpoint they
     take the same fixed 11rem. "Rechazar" is the longer word, so a size that came
     from the text would have made Reject the bigger button. */
  flex: 1 1 0; min-width: 0;
}
.consent__btn:hover { background: var(--paper); color: var(--navy); }
/* White ring, not the house red: these buttons are lemon, and the 2px offset
   puts the ring on the navy-deep bar, where white reads at 15.74:1. */
.consent__btn:focus-visible { outline-color: var(--paper); }

@media (min-width: 46rem) {
  .consent__actions { flex: 0 0 auto; }
  .consent__btn { flex: 0 0 11rem; }
}

/* The footer control that reopens the bar. A button, not a link: there is no
   page to go to. Styled as the footer's other links so it reads as one of them.
   Its wrapper ships hidden and is unhidden by consent.js only once a choice
   exists, so with JS off there is no dead control. */
.footer-consent {
  font: inherit; color: inherit; background: none; border: 0; padding: 0;
  text-align: left; cursor: pointer;
  text-decoration: underline; text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
.footer-consent:hover { color: var(--sf-lemon); }
.footer-consent:focus-visible { outline: 3px solid var(--sf-lemon); outline-offset: 2px; }

/* 6.14 Photo strip ("El local" / "The place"). A continuous drift, matching the
   reviews marquee below it rather than a stepped carousel: the rail is a native
   horizontal scroller and assets/js/slideshow.js clones the track and nudges
   scrollLeft a fraction of a pixel per frame. Dragging, swiping and the keyboard
   all work with no JS at all.

   What went, and why: arrows, dots and a counter only exist to express "which
   slide am I on", which a drift does not have. The scroll-snap container went
   with them, because a scripted smooth scroll inside `scroll-snap-type:
   mandatory` is unreliable on touch engines and was what stopped the old
   carousel dead on phones. A drift does no scripted scrolling at all. */
.slideshow { position: relative; width: 100%; padding-inline: var(--gutter); }
@media (min-width: 48rem) { .slideshow { padding-inline: 0; } }

/* Fades at both ends so photos enter and leave instead of being sliced off.
   Keyed to --cream because that is this section's ground: change both together. */
.slideshow::before, .slideshow::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: clamp(24px, 8vw, 90px);
  z-index: 2; pointer-events: none;
}
.slideshow::before { left: 0;  background: linear-gradient(90deg, var(--cream), transparent); }
.slideshow::after  { right: 0; background: linear-gradient(270deg, var(--cream), transparent); }

.slideshow__rail {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; cursor: grab;
}
.slideshow__rail::-webkit-scrollbar { display: none; }
.slideshow__rail.dragging { cursor: grabbing; }

.slideshow__track { display: flex; gap: var(--s4); width: max-content; }

/* One width rule for every screen. A drift has no notion of "one slide per
   view", so the old 100%-then-50% breakpoint split is gone: the photos are a
   comfortable size and as many fit as fit. */
.slide {
  flex: 0 0 min(78vw, 26rem);
  margin: 0; border: 3px solid var(--brand-dark);
  border-radius: var(--radius); overflow: hidden; background: var(--brand-dark);
}
.slide img { width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; }

@media (prefers-reduced-motion: reduce) { .slideshow__rail { cursor: auto; } }
@media (max-width: 40rem) {
  .slideshow::before, .slideshow::after { display: none; }
  .slideshow__rail { cursor: auto; }
}

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