/* ═══════════════════════════════════════════════════════════════════════════
   BAKERY ON O'CONNELL
   Direction 03 "Rows" carried across a four page site, with direction 01's
   shopfront photograph as the hero.

   Every menu item and price on this site is transcribed from the bakery's own
   printed menu boards. Nothing is invented.

   One stylesheet serves all four pages. Sections are in the order a visitor
   needs them: what it is, when it is open, where it is, then the food.
   ═══════════════════════════════════════════════════════════════════════════ */

@font-face{font-family:'Bricolage Grotesque';src:url('fonts/BricolageGrotesque.woff2') format('woff2');
  font-weight:300 800;font-style:normal;font-display:swap;}
@font-face{font-family:'Work Sans';src:url('fonts/WorkSans.woff2') format('woff2');
  font-weight:300 800;font-style:normal;font-display:swap;}

:root{
  /* Dark, the default. */
  --ink:#1C1614;        /* the warm brown ground */
  --ink-2:#151110;      /* darker band: marquee, footer, top bar when stuck */
  --warm:#241C18;       /* the one ground shift, used once per page */
  --shot:#2B211C;       /* behind a photo while it loads */
  --cream:#F3ECE2;
  --muted:#ACA298;      /* 7.0:1 on the ground */
  --red:#D2452B;        /* headings and large type */
  --red-deep:#B33520;   /* fills that carry text, white on this clears AA */
  --red-lite:#F26A50;   /* small labels, clears AA where --red does not */
  --on-accent:#FFFFFF;  /* text that sits ON a red fill */
  --rule:rgba(243,236,226,.15);
  --scrim:21,17,16;     /* the colour hero and banner scrims are built from */
  --cream-rgb:243,236,226;
  --ink2-rgb:21,17,16;
  --red-rgb:210,69,43;
  --red-lite-rgb:242,106,80;
  --on-accent-rgb:255,255,255;
  --gut:clamp(20px,5vw,72px);
  --maxw:1320px;
}

/* Light. Values from the 06-09 art-direction review, which both Codex and the
   measured hospitality references landed on independently: a dark ground forces
   heavy scrims to hold contrast and that makes phone-quality food photography
   look muddy, whereas a cream ground hides compression and mixed exposure.
   The accent SPLITS on light: --red #D2452B does not clear 4.5:1 on cream at
   small sizes, so small text uses --red-deep #B33520 at 5.20:1 and #D2452B is
   kept for large display type and filled elements. */
:root[data-theme="light"]{
  --ink:#F3ECE2;
  --ink-2:#E7DCCC;
  --warm:#EBE1D3;
  --shot:#E2D6C6;
  --cream:#211A15;      /* the "text on ground" token, now dark */
  --muted:#64584E;      /* 5.9:1 on the cream ground */
  --red:#B33520;        /* small text safe, 5.20:1 */
  --red-deep:#B33520;
  --red-lite:#9E2E1B;   /* small labels need to go DARKER on a light ground */
  --on-accent:#FFFFFF;
  --rule:rgba(33,26,21,.16);
  --scrim:21,17,16;     /* hero scrims stay dark, the photo is behind them */
  --cream-rgb:33,26,21;
  --ink2-rgb:33,26,21;
  --red-rgb:179,53,32;
  --red-lite-rgb:158,46,27;
}

*,*::before,*::after{box-sizing:border-box;}
html{-webkit-text-size-adjust:100%;}
body{
  margin:0;background:var(--ink);color:var(--cream);
  font-family:'Work Sans',system-ui,sans-serif;
  font-size:clamp(16px,1.05vw,17.5px);line-height:1.65;
  overflow-x:hidden;
  padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right);
}
img{max-width:100%;height:auto;display:block;}
h1,h2,h3{margin:0;font-family:'Bricolage Grotesque',Georgia,serif;font-weight:400;}
p{margin:0 0 1em;}
a{color:inherit;}
a:focus-visible{outline:3px solid var(--red-lite);outline-offset:3px;}
.skip{position:absolute;left:-9999px;top:0;background:var(--cream);color:var(--ink);
  padding:12px 18px;z-index:200;font-weight:700;}
.skip:focus{left:8px;top:8px;}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);}

/* ═══ TOP BAR ═══════════════════════════════════════════════════════════════
   Fixed, so the phone number follows you down every page. Transparent over the
   hero photograph, solid once you scroll past it. */
.pagetop{position:fixed;top:0;left:0;right:0;z-index:60;
  padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right);
  transition:background-color .3s ease;}
.pagetop.is-stuck{background:var(--ink-2);border-bottom:1px solid var(--rule);}
.pagetop.is-stuck .ticker{display:none;}
.pagetop.is-stuck .topbar{border-bottom:0;padding-top:9px;padding-bottom:9px;}
.pagetop.is-stuck .brand img{width:46px;height:46px;}
[id]{scroll-margin-top:96px;}

.ticker{display:flex;flex-wrap:wrap;justify-content:center;gap:8px 30px;margin:0;
  background:rgba(var(--ink2-rgb),.55);padding:10px var(--gut);
  font-size:12.5px;letter-spacing:1.1px;text-transform:uppercase;color:var(--cream);}
.ticker-open{color:var(--red-lite);}

.topbar{display:flex;align-items:center;gap:30px;padding:14px var(--gut);
  border-bottom:1px solid rgba(var(--cream-rgb),.16);}
.brand{margin-right:auto;display:block;}
.brand img{width:60px;height:60px;object-fit:contain;
  transition:width .3s ease,height .3s ease;}
.nav{display:flex;gap:26px;}
.nav a{text-decoration:none;font-size:14.5px;color:var(--cream);transition:color .25s ease;}
.nav a:hover,.nav a[aria-current="page"]{color:var(--red-lite);}
.topcall{text-decoration:none;font-weight:600;font-size:15px;color:var(--on-accent);
  background:var(--red-deep);padding:12px 20px;border-radius:2px;
  font-variant-numeric:tabular-nums;transition:background-color .25s ease;}
.topcall:hover{background:var(--red);}
@media (max-width:1080px){.nav{display:none;}}

/* ═══ HERO ══════════════════════════════════════════════════════════════════
   Full screen photograph, type centred over it. */
.hero{position:relative;display:flex;align-items:center;justify-content:center;
  text-align:center;min-height:100vh;min-height:100svh;overflow:hidden;}
.hero-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:62% 44%;}
.hero::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(var(--ink2-rgb),.80) 0%,rgba(var(--ink2-rgb),.52) 34%,
    rgba(var(--ink2-rgb),.62) 62%,rgba(var(--ink2-rgb),.92) 100%);}
.hero-inner{position:relative;z-index:2;padding:130px var(--gut) 0;max-width:920px;}
/* Two faults in one line, both measured. --red-lite over the hero scrim is
   4.36:1 with the glyphs hidden, and 13px is small text needing 4.5:1, so the
   hero's first line failed AA. And 13px at +1.8px tracking in the body face is
   the generator signature the audit has been warning about since August.
   14px, tracking under 1.2, in the display face, fixes both. */
.kicker{font-size:14px;letter-spacing:1.1px;text-transform:uppercase;
  font-family:'Bricolage Grotesque',Georgia,serif;
  color:var(--cream);font-weight:600;margin:0 0 16px;}
.hero h1{font-size:clamp(40px,5.6vw,84px);line-height:1.02;letter-spacing:-1px;margin:0 0 18px;}
.hero h1 em{font-style:normal;color:var(--red-lite);}
.hero-sub{font-size:clamp(16.5px,1.35vw,20px);max-width:52ch;margin:0 auto 26px;}

/* Shorter banner for the inner pages, so they are not four identical
   full-screen photographs in a row. */
.banner{position:relative;display:flex;align-items:flex-end;
  min-height:clamp(340px,46vh,460px);overflow:hidden;}
.banner img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
.banner::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(var(--ink2-rgb),.80) 0%,rgba(var(--ink2-rgb),.36) 44%,
    rgba(var(--ink2-rgb),.94) 100%);}
.banner-inner{position:relative;z-index:2;padding:0 var(--gut) clamp(30px,4vw,52px);
  max-width:var(--maxw);margin:0 auto;width:100%;}
.banner h1{font-size:clamp(34px,4.8vw,68px);line-height:1.04;letter-spacing:-.8px;margin:0 0 10px;}
.banner p{color:var(--cream);max-width:56ch;margin:0;font-size:17px;}

.btn{display:inline-block;text-decoration:none;background:var(--red-deep);color:var(--on-accent);
  font-weight:600;font-size:15px;padding:15px 28px;border-radius:2px;
  border:2px solid var(--red-deep);
  transition:background-color .25s ease,border-color .25s ease,color .25s ease;}
.btn:hover{background:var(--red);border-color:var(--red);}
.btn-ghost{background:transparent;color:var(--cream);border-color:rgba(var(--cream-rgb),.45);}
.btn-ghost:hover{background:var(--cream);color:var(--ink);border-color:var(--cream);}

@media (prefers-reduced-motion:no-preference){
  .kicker,.hero h1,.hero-sub,.hero .btn{opacity:0;transform:translateY(16px);
    animation:rise .75s cubic-bezier(.16,.84,.34,1) forwards;}
  .kicker{animation-delay:.06s;}
  .hero h1{animation-delay:.16s;}
  .hero-sub{animation-delay:.28s;}
  .hero .btn{animation-delay:.38s;}
}
@keyframes rise{to{opacity:1;transform:none;}}

/* ═══ OPEN AND WHERE ════════════════════════════════════════════════════════
   Plain type on the ground rather than a row of stat boxes. */
.band{max-width:var(--maxw);margin:0 auto;padding:clamp(40px,4.8vw,72px) var(--gut);
  display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,84px);
  border-bottom:1px solid var(--rule);text-align:center;}
.band-half+.band-half{border-left:1px solid var(--rule);padding-left:clamp(28px,5vw,84px);}
.band-label{font-size:13px;letter-spacing:1.5px;text-transform:uppercase;
  color:var(--red-lite);font-weight:600;margin:0 0 10px;}
.band-big{font-family:'Bricolage Grotesque',Georgia,serif;font-size:clamp(26px,3vw,42px);
  line-height:1.12;color:var(--cream);margin:0 0 10px;}
.band-note{color:var(--muted);margin:0 auto 12px;max-width:40ch;font-size:16px;}
/* Padding on an INLINE element grows the hit box without touching the line box,
   so the tap target clears WCAG 2.5.8 (24x24) whilst the layout stays put.
   Measured at 110x19 and 139x19 before this. */
.band-link{color:var(--cream);text-decoration:underline;text-underline-offset:4px;font-size:16px;
  padding:5px 2px;}
.band-link:hover{color:var(--red-lite);}
@media (max-width:780px){
  .band{grid-template-columns:1fr;gap:26px;}
  .band-half+.band-half{border-left:0;padding-left:0;border-top:1px solid var(--rule);padding-top:26px;}
}

/* ═══ SECTION HEADS ═════════════════════════════════════════════════════════ */
.sec{padding:clamp(50px,6vw,88px) var(--gut);}
.sec-head{max-width:var(--maxw);margin:0 auto clamp(26px,3vw,44px);text-align:center;}
.sec-head h2{font-size:clamp(30px,3.8vw,54px);line-height:1.04;letter-spacing:-.6px;margin:0 0 12px;}
.sec-head p{color:var(--muted);max-width:56ch;margin:0 auto;font-size:17px;}

/* ═══ THE ALTERNATING ROWS ══════════════════════════════════════════════════
   Photo one side, words the other, flipping each time. Each row carries what is
   actually in that part of the cabinet, which fills the column and answers the
   question a person scanning the page has. */
.rows{max-width:var(--maxw);margin:0 auto;display:flex;flex-direction:column;
  gap:clamp(22px,2.8vw,42px);}
.row{display:grid;grid-template-columns:1.15fr 1fr;gap:clamp(20px,3vw,54px);align-items:center;}
.row:nth-child(even){direction:rtl;}
.row:nth-child(even)>*{direction:ltr;}
.row img{width:100%;aspect-ratio:16/10;object-fit:cover;}
.row-txt h3{font-size:clamp(22px,2.2vw,32px);line-height:1.06;margin:0 0 8px;}
.row-txt>p{color:var(--muted);margin:0 0 16px;max-width:44ch;font-size:16.5px;}
.row-list{list-style:none;margin:0 0 16px;padding:0;max-width:34ch;
  display:grid;grid-template-columns:1fr 1fr;gap:0 22px;}
.row-list li{font-size:15px;color:var(--cream);padding:7px 0;border-top:1px solid var(--rule);}
.row-meta{font-size:12.5px;letter-spacing:1.4px;text-transform:uppercase;
  color:var(--red-lite);font-weight:600;margin:0;}
@media (max-width:780px){
  .row,.row:nth-child(even){grid-template-columns:1fr;direction:ltr;}
}

/* ═══ THE MARQUEE ═══════════════════════════════════════════════════════════
   Uniform tiles cropped to fill, so sizes are even and nothing is stretched.
   The gap sits on each tile as a right margin, which keeps the two duplicated
   sets exactly equal in width. That is what makes the halfway shift land on the
   start of the second set and loop without a jump. */
.marquee{background:var(--ink-2);overflow:hidden;margin:clamp(18px,2.4vw,34px) 0;
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);}
.marquee-track{display:flex;width:max-content;will-change:transform;}
.marquee-set{display:flex;}
.marquee figure{margin:0 14px 0 0;flex:none;width:clamp(148px,14.5vw,206px);}
.marquee img{width:100%;aspect-ratio:4/5;object-fit:cover;}
@media (prefers-reduced-motion:no-preference){
  .marquee-track{animation:slide 64s linear infinite;}
}
@keyframes slide{from{transform:translateX(0);}to{transform:translateX(-33.3333%);}}

/* ═══ KNOWN FOR ═════════════════════════════════════════════════════════════ */
.known{max-width:var(--maxw);margin:0 auto;display:grid;grid-template-columns:1fr 1fr;
  gap:clamp(22px,3vw,48px);}
.known article{display:grid;grid-template-columns:.85fr 1fr;gap:clamp(16px,2vw,28px);
  align-items:center;}
.known img{width:100%;aspect-ratio:3/4;object-fit:cover;}
.known h3{font-size:clamp(20px,2vw,28px);margin:0 0 8px;}
.known p{color:var(--muted);margin:0;font-size:16px;}
@media (max-width:900px){.known{grid-template-columns:1fr;}}
@media (max-width:520px){.known article{grid-template-columns:1fr;}}

/* ═══ SPLIT (catering teaser, about, socials) ═══════════════════════════════ */
.split{max-width:var(--maxw);margin:0 auto;display:grid;grid-template-columns:1fr 1.05fr;
  gap:clamp(26px,4vw,68px);align-items:center;}
.split-flip{direction:rtl;}
.split-flip>*{direction:ltr;}
.split h2{font-size:clamp(28px,3.4vw,48px);line-height:1.04;letter-spacing:-.6px;margin:0 0 14px;}
.split-txt p{color:var(--muted);max-width:46ch;}
.split-shots{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
.split-shots img{width:100%;aspect-ratio:3/4;object-fit:cover;}
.split-one img{width:100%;aspect-ratio:4/3;object-fit:cover;}
@media (max-width:860px){.split,.split-flip{grid-template-columns:1fr;direction:ltr;}}

.warmband{background:var(--warm);}

/* ═══ SOCIALS ═══════════════════════════════════════════════════════════════ */
.handle{font-family:'Bricolage Grotesque',Georgia,serif;color:var(--red-lite) !important;
  font-size:clamp(22px,2.4vw,34px);margin:14px 0 14px !important;}
.socials-links{display:flex;flex-wrap:wrap;gap:10px;margin:0 !important;}
.socials-links a{text-decoration:none;font-size:14.5px;color:var(--cream);
  border:1px solid rgba(var(--cream-rgb),.32);padding:9px 16px;border-radius:2px;
  transition:background-color .25s ease,color .25s ease,border-color .25s ease;}
.socials-links a:hover{background:var(--cream);color:var(--ink);border-color:var(--cream);}
.socials-shots{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
.socials-shots img{width:100%;aspect-ratio:4/5;object-fit:cover;}

/* ═══ THE MENU BOARDS ═══════════════════════════════════════════════════════
   Transcribed from the bakery's own printed boards and set as real text rather
   than a photograph of a sign, so it is readable on a phone and findable in a
   search. Prices sit in the right margin the way a printed list sets them. */
.menu-wrap{max-width:var(--maxw);margin:0 auto;}
.menu-block{margin-bottom:clamp(40px,5vw,72px);}
.menu-block>h2{font-size:clamp(28px,3.4vw,46px);line-height:1.04;margin:0 0 6px;}
.menu-block>.menu-note{color:var(--muted);margin:0 0 24px;max-width:60ch;font-size:16px;}
.menu-cols{display:grid;grid-template-columns:repeat(3,1fr);
  column-gap:clamp(26px,4vw,60px);row-gap:clamp(26px,3vw,44px);}
@media (max-width:980px){.menu-cols{grid-template-columns:repeat(2,1fr);}}
@media (max-width:640px){.menu-cols{grid-template-columns:1fr;}}

.menu-group h3{font-size:13px;letter-spacing:1.6px;text-transform:uppercase;
  color:var(--red-lite);font-family:'Work Sans',sans-serif;font-weight:700;
  margin:0 0 12px;padding-bottom:8px;border-bottom:2px solid var(--red);}
.menu-list{list-style:none;margin:0;padding:0;}
.menu-list li{padding:9px 0;border-bottom:1px solid var(--rule);}
.menu-line{display:flex;justify-content:space-between;align-items:baseline;gap:14px;}
.menu-line b{font-weight:500;color:var(--cream);font-size:16px;}
.menu-line span{color:var(--cream);font-variant-numeric:tabular-nums;flex:none;
  font-size:15.5px;font-weight:600;}
.menu-list em{display:block;font-style:normal;color:var(--muted);font-size:14px;
  margin-top:3px;line-height:1.45;}
.menu-sub{color:var(--muted);font-size:14.5px;margin:10px 0 0;}
.menu-deal{border:1px solid var(--rule);padding:16px 18px;margin-top:14px;}
.menu-deal b{display:block;color:var(--cream);font-weight:600;margin-bottom:5px;}
.menu-deal p{margin:0;color:var(--muted);font-size:14.5px;}

/* ═══ NOTES AND FACTS ═══════════════════════════════════════════════════════ */
.notes{max-width:var(--maxw);margin:0 auto;}
.notes h2{font-size:clamp(26px,3.2vw,42px);margin:0 0 20px;}
.notes dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:0 clamp(20px,4vw,52px);
  border-top:1px solid var(--rule);}
.notes dt{font-size:12.5px;letter-spacing:1.3px;text-transform:uppercase;
  color:var(--red-lite);padding:16px 0;font-weight:600;
  border-top:1px solid var(--rule);}
.notes dd{margin:0;padding:16px 0;color:var(--muted);
  border-top:1px solid var(--rule);}
.notes dl{border-top:0;border-bottom:1px solid var(--rule);}
@media (max-width:640px){
  .notes dl{grid-template-columns:1fr;}
  .notes dt{padding-bottom:2px;border-bottom:0;}
}

/* ═══ FIND US ═══════════════════════════════════════════════════════════════ */
.find{max-width:var(--maxw);margin:0 auto;display:grid;grid-template-columns:1fr 1fr;
  gap:clamp(28px,4vw,68px);align-items:start;}
.find h2{font-size:clamp(28px,3.4vw,48px);line-height:1.04;margin:0 0 18px;}
.find-addr{font-size:19px;line-height:1.5;margin:0 0 8px;}
.find-line{font-size:12.5px;letter-spacing:1.5px;text-transform:uppercase;
  color:var(--red-lite);margin:0 0 14px;font-weight:600;}
.find-tel{margin:0 0 14px;}
.find-tel a{font-family:'Bricolage Grotesque',Georgia,serif;font-size:30px;color:var(--cream);
  text-decoration:none;font-variant-numeric:tabular-nums;}
.find-tel a:hover{color:var(--red-lite);}
@media (max-width:860px){.find{grid-template-columns:1fr;}}

/* ═══ FOOTER ════════════════════════════════════════════════════════════════ */
.foot{background:var(--ink-2);padding:clamp(34px,4vw,56px) var(--gut) 26px;
  padding-bottom:max(26px,env(safe-area-inset-bottom));border-top:1px solid var(--rule);}
.foot-cols{max-width:var(--maxw);margin:0 auto 22px;display:grid;
  grid-template-columns:repeat(4,1fr);gap:24px;font-size:15px;line-height:1.6;color:var(--muted);}
.foot-cols p{margin:0;}
.foot-cols b{font-family:'Bricolage Grotesque',Georgia,serif;font-weight:400;color:var(--cream);
  font-size:19px;display:block;margin-bottom:6px;}
.foot-cols a{color:var(--cream);text-decoration:none;padding:5px 2px;}
.foot-cols a:hover{color:var(--red-lite);text-decoration:underline;text-underline-offset:3px;}
/* 28px cleared AA but was tight for a thumb. 34px now. */
.foot-nav a{display:block;padding:6px 0;}
.foot-fine{max-width:var(--maxw);margin:0 auto;font-size:13px;color:var(--muted);
  border-top:1px solid var(--rule);padding-top:16px;}
@media (max-width:860px){.foot-cols{grid-template-columns:repeat(2,1fr);}}
@media (max-width:520px){.foot-cols{grid-template-columns:1fr;gap:18px;}}

@media (prefers-reduced-motion:reduce){
  .marquee-track{animation:none;transform:none;}
  *{animation-duration:.001ms !important;animation-iteration-count:1 !important;}
}

/* Good to know was removed from the home page, so Find us is no longer half a
   two column grid. It keeps the left column width rather than stretching the
   phone number across the full measure. */
.find-solo{grid-template-columns:minmax(0,560px);}


/* == V8 SIGNWRITER ==========================================================
   A high contrast face needs size and air or the thin strokes disappear, so the
   headings run large and the sections breathe. Closest of the set to the
   lettering on their own hanging sign. */
.hero h1{font-size:clamp(44px,6.6vw,102px);letter-spacing:-1px;line-height:.98;}
.sec{padding:clamp(58px,7vw,110px) var(--gut);}
.sec-head h2{font-size:clamp(34px,4.8vw,70px);}
.split h2,.find h2{font-size:clamp(32px,4.2vw,60px);}
.row-txt h3{font-size:clamp(24px,2.8vw,40px);}
.band-big{font-size:clamp(30px,3.2vw,46px);}
.rows{gap:clamp(28px,3.4vw,54px);}
.btn,.topcall{border-radius:0;}

.vbadge-disabled{position:fixed;left:14px;bottom:14px;z-index:200;margin:0;
  background:var(--red-deep);color:var(--on-accent);font-family:system-ui,sans-serif;
  font-size:12px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;
  padding:7px 12px;border-radius:2px;pointer-events:none;}

/* == HERO SCRIM, REBUILT AGAINST MEASURED CONTRAST ==========================
   The first attempt put a vignette on the corners and left the centre clear,
   which is backwards: the type sits in the centre. Measured, the brightest
   tenth of the headline band was 2.6:1, below even the 3:1 large text floor.
   Three layers now, innermost first:
     1. a soft plate directly behind the type, doing most of the work
     2. a corner vignette, so the frame still closes in
     3. a vertical scrim for the top bar and the hand off to the ground below
   Re-measured after each change rather than eyeballed. */
.hero-img{filter:brightness(.72) saturate(1.04);}
.hero::after{
  background:
    radial-gradient(78% 58% at 50% 46%, rgba(var(--ink2-rgb),.72) 0%, rgba(var(--ink2-rgb),.52) 52%, rgba(var(--ink2-rgb),0) 100%),
    radial-gradient(120% 92% at 50% 45%, rgba(var(--ink2-rgb),0) 34%, rgba(var(--ink2-rgb),.50) 100%),
    linear-gradient(180deg, rgba(var(--ink2-rgb),.82) 0%, rgba(var(--ink2-rgb),.42) 26%, rgba(var(--ink2-rgb),.50) 62%, rgba(var(--ink2-rgb),.95) 100%);
}


/* == RHYTHM PASS, SECOND CUT ================================================
   The neon band is gone. Catering keeps the colour break but at a much lower
   volume, the texture is dialled up so it actually reads, and the nav centres. */

/* ── texture, more obvious ──────────────────────────────────────────────────
   Two layers. A fine woven ruling, and a real noise field generated by the
   browser rather than a downloaded grain image, so it costs nothing to serve.
   Held low enough that it reads as paper stock rather than as an effect. */
body{
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.055'/%3E%3C/svg%3E"),
    repeating-linear-gradient(180deg, rgba(var(--cream-rgb),.030) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(0,0,0,.030) 0 1px, transparent 1px 4px);
  background-attachment:fixed;
}

/* ── catering, dialled right down ──────────────────────────────────────────
   The bright red read as a warning band rather than an invitation. This is a
   deep oxblood, closer to the awning timber than to the accent, so it still
   breaks the brown without shouting. */
.redband{background:var(--warm);}
.redband .split-txt h2{color:var(--cream);}
.redband .split-txt p{color:rgba(var(--cream-rgb),.86);}
.redband .band-link{color:var(--cream);text-decoration-color:rgba(var(--cream-rgb),.55);}
.redband .band-link:hover{color:var(--on-accent);text-decoration-color:var(--on-accent);}
.redband .btn{background:var(--cream);color:var(--red-deep);border-color:var(--cream);}
.redband .btn:hover{background:transparent;color:var(--cream);border-color:var(--cream);}

/* ── centred nav ───────────────────────────────────────────────────────────
   Logo left, phone right, links pinned to the true centre of the bar rather
   than floating wherever the flex row leaves them. */
@media (min-width:1081px){
  .topbar{position:relative;}
  .nav{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);}
  .brand{margin-right:0;}
  .topcall{margin-left:auto;}
}

@font-face{font-family:'Caveat';src:url('fonts/Caveat.woff2') format('woff2');
  font-weight:400 700;font-style:normal;font-display:swap;}

/* == REVIEWS ================================================================
   The score does the heavy lifting, because 4.6 from 1,548 is a number no new
   business can fake and no competitor on the street can match. The quotes are
   set as type rather than as cards, at different sizes, ruled apart. Real names,
   complete sentences, nothing trimmed mid thought. */
.reviews{max-width:var(--maxw);margin:0 auto;padding:clamp(52px,6.4vw,96px) var(--gut);
  display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.5fr);gap:clamp(30px,5vw,80px);
  border-top:1px solid var(--rule);}
.rev-num{font-family:'Bricolage Grotesque',Georgia,serif;font-size:clamp(64px,8vw,124px);line-height:.86;
  margin:0 0 8px;color:var(--cream);font-variant-numeric:tabular-nums;}
.rev-stars{color:var(--red-lite);font-size:19px;letter-spacing:4px;margin:0 0 10px;}
.reviews h2{font-family:'Public Sans',sans-serif;font-size:15px;font-weight:600;
  letter-spacing:.4px;color:var(--muted);margin:0 0 14px;}
/* The one handwritten line on the site. Used once, as an aside, never as a
   system. Any more than this and it reads as a craft market stall. */
.rev-hand{font-family:'Caveat',cursive;font-size:clamp(20px,2vw,27px);line-height:1.2;
  color:var(--red-lite);margin:0;max-width:22ch;transform:rotate(-1.4deg);}

.rev-quotes blockquote{margin:0;padding:0 0 20px;border-bottom:1px solid var(--rule);}
.rev-quotes blockquote+blockquote{padding-top:20px;}
.rev-quotes p{margin:0 0 8px;color:var(--cream);}
.rev-quotes blockquote:nth-child(1) p{font-size:clamp(18px,1.7vw,23px);line-height:1.42;}
.rev-quotes blockquote:nth-child(2) p{font-size:16.5px;color:var(--muted);}
.rev-quotes blockquote:nth-child(3) p{font-size:clamp(17px,1.5vw,20px);}
.rev-quotes cite{font-style:normal;font-size:13px;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--muted);}
.rev-src{margin:18px 0 0;font-size:15px;}
.rev-src a{color:var(--cream);text-decoration:underline;text-underline-offset:4px;}
.rev-src a:hover{color:var(--red-lite);}
@media (max-width:820px){.reviews{grid-template-columns:1fr;gap:28px;}}

/* == CUSTOMER LINES, HANDWRITTEN ============================================
   Reviews without a reviews section. Two of the six rows carry one real
   published line each, in the hand face, at the foot of the column. Rows with
   no genuine matching quote get nothing rather than something invented, which
   is also why they do not all have one. */
.row-quote{
  font-family:'Caveat',cursive;font-size:clamp(19px,1.8vw,25px);line-height:1.25;
  color:var(--red-lite);margin:14px 0 0;max-width:26ch;transform:rotate(-.8deg);
}
.row-quote span{
  display:block;font-family:'Public Sans',sans-serif;font-size:11.5px;
  letter-spacing:1.3px;text-transform:uppercase;color:var(--muted);
  margin-top:6px;transform:rotate(.8deg);
}
/* the rating, as an aside rather than a section */
.band-hand{
  font-family:'Caveat',cursive;font-size:clamp(19px,1.9vw,26px);
  color:var(--red-lite);margin:6px 0 12px;transform:rotate(-1.2deg);
}

/* == THE FOLLOW STRIP =======================================================
   The page needed one loud colour block and catering was the wrong place for
   it, because a red band there reads as though catering is the main offer.
   A follow strip can be as loud as it likes without misleading anybody.
   Same seamless loop maths as the photo marquee: two identical sets, shifted
   by exactly half the track. */
.tickerstrip{background:var(--red-deep);overflow:hidden;padding:0;}
.ts-track{display:flex;width:max-content;will-change:transform;}
.ts-set{display:flex;align-items:center;}
.tickerstrip span{
  font-family:'Bricolage Grotesque',Georgia,serif;font-size:clamp(20px,2.2vw,32px);
  color:var(--on-accent);padding:16px 0;white-space:nowrap;
}
/* The separator was an <i>●</i> element, i.e. a text node. adopt's slot
   tagger reaches every text node, so the CMS turned each of the eighteen
   bullets into its own editable field with its own id. Decorative punctuation
   the client can rename, and because the ids are unique per copy rather than
   shared across the three loop sets, editing one desynced the loop permanently.
   Generated content is not a text node, so the tagger cannot see it and there is
   nothing to edit. Same look, same spacing. */
.tickerstrip span::after{content:'●';color:rgba(var(--on-accent-rgb),.55);font-size:10px;
  padding:0 clamp(18px,2.4vw,36px);vertical-align:middle;}
@media (prefers-reduced-motion:no-preference){
  .ts-track{animation:tsslide 38s linear infinite;}
}
@keyframes tsslide{from{transform:translateX(0);}to{transform:translateX(-33.3333%);}}
@media (prefers-reduced-motion:reduce){.ts-track{animation:none;transform:none;}}

/* == NAV LOGO, LARGER =======================================================
   It was sitting at 60px and getting lost against the nav text. */
.brand img{width:78px;height:78px;}
.pagetop.is-stuck .brand img{width:58px;height:58px;}
@media (max-width:600px){.brand img{width:62px;height:62px;}}

/* == WHAT WE HAVE, WARMER AND TIGHTER =======================================
   The section was cream on brown the whole way down with the accent appearing
   once per row in a small line. Three changes: a red eyebrow on the section, a
   short red rule under every row heading, and the list rules warmed so the
   accent runs through the whole block rather than sitting at the bottom of it.
   The photographs also come down a size, which tightens the section by roughly
   a fifth without losing anything. */
/* Same correction as .kicker: 13px at +1.8px in the body face is the measured
   generator signature. Colour stays, it is the established accent here and it
   clears AA on this ground. */
.sec-kicker{font-size:14px;letter-spacing:1.1px;text-transform:uppercase;
  font-family:'Bricolage Grotesque',Georgia,serif;
  color:var(--red-lite);font-weight:600;margin:0 0 12px;}

.row-txt h3{position:relative;padding-bottom:14px;margin-bottom:12px;}
.row-txt h3::after{content:'';position:absolute;left:0;bottom:0;
  width:52px;height:3px;background:var(--red);}

.row-list li{border-top-color:rgba(var(--red-rgb),.42);}
.row-meta{font-weight:700;}

/* tighter: smaller photographs, less air between rows.
   The two-column rule MUST stay inside a min-width query. Unqualified, it sat
   after the max-width:780px collapse above, won on source order at every size,
   and every .row rendered as two 158px columns on a phone with body copy at
   about twenty characters a line. Desktop looked correct throughout, which is
   why it survived. */
@media (min-width:781px){
  .row{grid-template-columns:1fr 1fr;gap:clamp(20px,2.8vw,44px);}
}
.row img{aspect-ratio:3/2;}
.rows{gap:clamp(20px,2.4vw,34px);}
.row-txt>p{margin-bottom:14px;}
.row-list{margin-bottom:12px;}

/* == SPECIFICITY FIX ========================================================
   .row-txt>p and .sec-head p are element-plus-class, so they were beating the
   plain .row-meta, .row-quote and .sec-kicker classes and painting all three
   muted grey. The accent never rendered. Matching their specificity fixes it.
   Caught by reading computed colour rather than by looking at the page. */
.row-txt>p.row-meta{
  color:var(--red-lite);font-size:12.5px;letter-spacing:1.4px;text-transform:uppercase;
  font-weight:700;font-family:'Public Sans',sans-serif;max-width:none;margin:0;
}
.row-txt>p.row-quote{
  color:var(--red-lite);font-family:'Caveat',cursive;
  font-size:clamp(19px,1.8vw,25px);line-height:1.25;max-width:26ch;
  margin:14px 0 0;text-transform:none;letter-spacing:0;
}
.sec-head>p.sec-kicker{
  color:var(--red-lite);font-size:14px;letter-spacing:1.1px;text-transform:uppercase;
  font-family:'Bricolage Grotesque',Georgia,serif;
  font-weight:600;margin:0 0 12px;max-width:none;
}

/* A sans carries more weight per pixel than the display serif did, so the
   headings come down a step and pick up a little weight instead of size. */
.hero h1{font-size:clamp(40px,5.4vw,80px);letter-spacing:-1.6px;font-weight:600;}
.sec-head h2,.split h2,.find h2{font-weight:600;letter-spacing:-1px;}
.row-txt h3{font-weight:600;letter-spacing:-.5px;}
.band-big{font-weight:600;letter-spacing:-.6px;}
.tickerstrip span{font-weight:600;letter-spacing:-.4px;}

/* == SOCIAL BUTTONS =========================================================
   Each carries its platform's real mark and its real colour. The colour sits in
   the glyph at rest so there is something to look at without three saturated
   blocks fighting the warm palette, then the whole button takes the brand colour
   on hover and focus. Official marks rather than a generic icon set, because a
   generic icon set is the thing that makes a footer look bought. */
.socials-links{display:flex;flex-wrap:wrap;gap:10px;margin:0 !important;}
.soc{
  display:inline-flex;align-items:center;gap:9px;text-decoration:none;
  font-size:14.5px;font-weight:600;color:var(--cream);
  background:rgba(var(--cream-rgb),.05);
  border:1px solid rgba(var(--cream-rgb),.20);
  padding:10px 16px 10px 13px;border-radius:2px;
  transition:background-color .25s ease,border-color .25s ease,color .25s ease,transform .25s ease;
}
.soc svg{width:19px;height:19px;flex:none;transition:color .25s ease;}
.soc:hover,.soc:focus-visible{transform:translateY(-2px);color:var(--on-accent);}

/* the mark carries the platform colour while the button is at rest */
.soc-ig svg{color:#E1306C;}
.soc-tt svg{color:#FE2C55;}
.soc-fb svg{color:#1877F2;}

/* on hover the button becomes the platform */
.soc-ig:hover,.soc-ig:focus-visible{
  background:linear-gradient(135deg,#F58529 0%,#DD2A7B 55%,#8134AF 100%);
  border-color:transparent;}
.soc-tt:hover,.soc-tt:focus-visible{background:#111;border-color:#25F4EE;}
.soc-fb:hover,.soc-fb:focus-visible{background:#1877F2;border-color:#1877F2;}
.soc:hover svg,.soc:focus-visible svg{color:var(--on-accent);}

@media (prefers-reduced-motion:reduce){.soc{transition:none;} .soc:hover{transform:none;}}

/* == MARQUEE LOOP MATHS =====================================================
   Three identical sets, shifted by one set (-33.3333%). Two sets therefore
   always cover the screen at the loop point.
   With two sets and -50%, the shift equals the whole set width, so any viewport
   wider than one set shows blank space on the right just before it repeats.
   Our tiles are clamped, so a set is a fixed width and the failure only appears
   above about 1760px, which is why it survived every check at 1440.
   Rule: with N identical sets, animate to -(100/N)% and (N-1) x setWidth must
   exceed the widest screen you care about. */

/* == MOBILE NAVIGATION ======================================================
   .nav was display:none below 1080px with nothing put in its place, so on a
   phone the header carried a logo and a phone button and no way to reach any
   other page. The owner will almost certainly open this on a phone, so that
   read as unfinished. Desktop is untouched. */
.navtoggle{display:none;width:44px;height:44px;flex:none;padding:0;margin-left:14px;
  background:transparent;border:1px solid rgba(var(--cream-rgb),.34);border-radius:2px;
  color:var(--cream);cursor:pointer;align-items:center;justify-content:center;}
.navtoggle:hover{border-color:var(--red-lite);color:var(--red-lite);}
.navtoggle:focus-visible{outline:3px solid var(--red-lite);outline-offset:2px;}
.navtoggle span{display:block;width:20px;height:2px;background:currentColor;position:relative;}
.navtoggle span::before,.navtoggle span::after{content:'';position:absolute;left:0;
  width:20px;height:2px;background:currentColor;}
.navtoggle span::before{top:-6px;} .navtoggle span::after{top:6px;}
.navtoggle[aria-expanded="true"] span{background:transparent;}
.navtoggle[aria-expanded="true"] span::before{top:0;transform:rotate(45deg);}
.navtoggle[aria-expanded="true"] span::after{top:0;transform:rotate(-45deg);}

@media (max-width:1080px){
  .navtoggle{display:flex;}
  .nav{display:block;position:absolute;left:0;right:0;top:100%;
    background:var(--ink-2);border-bottom:1px solid var(--rule);
    padding:6px var(--gut) 14px;
    visibility:hidden;opacity:0;transform:translateY(-6px);
    transition:opacity .22s ease,transform .22s ease,visibility .22s;}
  .topbar{position:relative;}
  .nav.is-open{visibility:visible;opacity:1;transform:none;}
  .nav a{display:block;padding:14px 2px;font-size:17px;
    border-bottom:1px solid rgba(var(--cream-rgb),.10);}
  .nav a:last-child{border-bottom:0;}
}
@media (prefers-reduced-motion:reduce){
  .nav{transition:none;}
}

/* == INNER PAGES ============================================================
   The last block in a section must not add its own bottom margin on top of the
   section padding; together they left about 290px of dead ground before the
   closing call to action. */
.menu-wrap>.menu-block:last-child,.menu-block.last{margin-bottom:0;}
.warmband .menu-block>h2{margin-bottom:10px;}
.notes{margin-bottom:clamp(34px,3.4vw,50px);}

/* == CSP: NO INLINE STYLES ==================================================
   Three style="" attributes lived on index.html. The adopt path ships a
   Content-Security-Policy with style-src 'self' and no 'unsafe-inline', so
   they had to become classes. Same declarations, same result. */
.find-note{color:var(--muted);font-size:15.5px;}
.btn-row{margin-top:20px;}

/* == WHAT WE HAVE, THE CARD GRID ============================================
   Direction taken from Josh's Claude Design redesign of the whole page
   ('What we have redesigns.zip', Bakery on OConnell.dc.html, 04-09-2026).
   Six cabinets, two rows of three, each a 4:3 photograph with the name under
   it and one line of what is in it. Replaces five full-bleed alternating rows
   that ran 2,650px on their own. Detail lives on menu.html.
   Sizes, gaps and the 1120px measure are the canvas export's values.
   ========================================================================== */
.range-head{max-width:var(--maxw);margin:0 auto clamp(26px,3vw,44px);
  display:flex;flex-wrap:wrap;justify-content:space-between;align-items:flex-end;
  gap:18px clamp(30px,4vw,72px);text-align:left;}
.range-head h2{font-size:clamp(30px,3.8vw,54px);line-height:1;letter-spacing:-1.2px;margin:0;}
.range-head>p{color:var(--muted);max-width:46ch;margin:0;font-size:17px;
  line-height:1.55;text-wrap:pretty;}

/* 1120px rather than the page's 1320px measure, so three 4:3 photographs stay
   a comfortable size instead of stretching into letterboxes. */
.cards{max-width:1120px;margin:0 auto;display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(20px,2.2vw,32px);}
.card{display:flex;flex-direction:column;gap:13px;text-decoration:none;color:inherit;}
.card-shot{position:relative;aspect-ratio:4/3;overflow:hidden;background:var(--shot);}
.card-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transition:transform .5s cubic-bezier(.16,.84,.34,1);}
.card:hover .card-shot img,.card:focus-visible .card-shot img{transform:scale(1.035);}
.card h3{font-size:clamp(20px,1.7vw,25px);font-weight:600;letter-spacing:-.5px;
  line-height:1.1;margin:0;}
.card h3::after{content:none;}
.card p{margin:0;color:var(--muted);font-size:15px;line-height:1.55;text-wrap:pretty;}
.card:hover h3,.card:focus-visible h3{color:var(--red-lite);}

@media (max-width:940px){.cards{grid-template-columns:repeat(2,minmax(0,1fr));}}
@media (max-width:600px){
  .cards{grid-template-columns:minmax(0,1fr);}
  .range-head{display:block;}
  .range-head>p{margin-top:14px;}
}
@media (prefers-reduced-motion:reduce){.card-shot img{transition:none;}}

/* == FIND US PAGE ===========================================================
   Content transcribed from their own contact page: parking and access, the
   walking-distance list, and the advisory about specialty cakes, gluten free
   and the daily bread run. The proximity list is the most useful thing on the
   whole site for local search, because it names the hospitals, the colleges
   and the Oval that people actually search near.
   ========================================================================== */
.fp{max-width:var(--maxw);margin:0 auto;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);
  gap:clamp(28px,4vw,72px);align-items:start;}
.fp-wide{grid-template-columns:minmax(0,1fr) minmax(0,1fr);}
.fp-col h2{font-size:clamp(26px,3vw,40px);font-weight:600;letter-spacing:-.8px;
  line-height:1.05;margin:0 0 18px;}
.fp-label{font-size:14px;letter-spacing:1.1px;text-transform:uppercase;
  font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  color:var(--red-lite);margin:0 0 6px;}
.fp-gap{margin-top:28px;}
.fp-addr{font-size:clamp(20px,2.1vw,26px);line-height:1.4;margin:0;}
.fp-tel{margin:0 0 8px;}
.fp-tel a{font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:clamp(26px,2.8vw,38px);color:var(--cream);text-decoration:none;
  letter-spacing:-.6px;font-variant-numeric:tabular-nums;}
.fp-tel a:hover,.fp-tel a:focus-visible{color:var(--red-lite);}
.fp-note{color:var(--muted);font-size:15.5px;max-width:46ch;margin:0;}
.fp-hours{width:100%;max-width:420px;border-collapse:collapse;font-size:16px;margin:0 0 12px;}
.fp-hours th,.fp-hours td{text-align:left;padding:10px 0;
  border-bottom:1px solid var(--rule);font-weight:400;}
.fp-hours td{text-align:right;color:var(--red-lite);font-variant-numeric:tabular-nums;}
.fp-cta{margin:26px 0 0;}

/* The map is a plain OpenStreetMap embed: no key, no account, no cookies and
   nothing to consent to, which a Google embed cannot say. */
.fp-map iframe{width:100%;aspect-ratio:4/3;border:1px solid var(--rule);
  display:block;margin:0 0 14px;background:var(--warm);}

.fp-park{margin:0;padding:0;border-top:1px solid var(--rule);}
.fp-park>div{padding:14px 0;border-bottom:1px solid var(--rule);}
.fp-park dt{font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:18px;letter-spacing:-.3px;margin:0 0 3px;}
.fp-park dd{margin:0;color:var(--muted);font-size:15.5px;}

.fp-near{list-style:none;margin:0;padding:0;border-top:1px solid var(--rule);}
.fp-near li{padding:10px 0 10px 22px;border-bottom:1px solid var(--rule);
  font-size:15.5px;position:relative;}
.fp-near li::before{content:'';position:absolute;left:2px;top:18px;
  width:7px;height:7px;background:var(--red);}

.fp-know{max-width:760px;margin:0 auto;text-align:center;}
.fp-know h2{font-size:clamp(24px,2.6vw,34px);font-weight:600;letter-spacing:-.6px;margin:0 0 16px;}
.fp-know p{color:var(--muted);font-size:16.5px;}

@media (max-width:860px){
  .fp,.fp-wide{grid-template-columns:minmax(0,1fr);}
  .fp-map{margin-top:8px;}
}

/* == PRODUCTS HUB AND DETAIL PAGES ==========================================
   The hub reuses the home page's card grid at a larger size, so 'What we have'
   and Products are visibly the same taxonomy at two levels rather than two
   competing ones. Prices are not typed out anywhere; they live in the client's
   own PDFs, which is what their site has always done and what stops a price
   list going stale the day somebody changes one.
   ========================================================================== */
.cards-lg{max-width:var(--maxw);gap:clamp(24px,2.6vw,40px);}
.cards-lg .card h3{font-size:clamp(22px,2vw,29px);}
.cards-lg .card p{font-size:16px;}
.card-more{font-size:14.5px;color:var(--red-lite);letter-spacing:.3px;
  display:inline-block;margin-top:2px;}
.card-more::after{content:' \2192';}

/* Measured over the banner photograph with the glyphs hidden, --red-lite here
   ran 1.30 to 1.97:1 across the four detail pages, because the crumb sits high
   in the banner where the scrim is at its weakest (.36) rather than down at
   the .94 end where the headline sits. Cream clears it on all six. */
/* `.banner p` is 0,1,1 and beat plain `.crumb` at 0,1,0, so the 14px never
   applied and this rendered at 17px, which is also why the audit read it as an
   eyebrow. Element-plus-class again, the same trap already recorded against
   `.row-txt>p` on this build. */
.banner .crumb{margin:0 0 10px;font-size:14px;letter-spacing:1px;
  text-transform:uppercase;font-family:'Bricolage Grotesque',Georgia,serif;}
/* Cream alone still measured 3.16 to 4.00:1 on three of the six banners,
   because the crumb sits where the scrim is weakest and those three
   photographs are the brightest. A scrim cannot be tuned per photograph, so
   the crumb carries its own ground and the contrast stops depending on the
   image at all. This is the 'plate behind the text, not just a scrim' rule. */
.banner .crumb a{display:inline-block;color:var(--cream);text-decoration:none;
  background:rgba(var(--ink2-rgb),.86);padding:6px 12px;
  font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  border:1px solid rgba(var(--cream-rgb),.22);}
.banner .crumb a:hover,.banner .crumb a:focus-visible{
  background:var(--ink);border-color:var(--red-lite);color:var(--red-lite);}
.crumb a:hover,.crumb a:focus-visible{text-decoration:underline;}

.detail{max-width:var(--maxw);margin:0 auto;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);
  gap:clamp(28px,4vw,72px);align-items:start;}
.detail-txt p{color:var(--muted);font-size:17px;line-height:1.6;max-width:52ch;}
.detail-shots{display:grid;grid-template-columns:1fr 1fr;gap:clamp(10px,1.4vw,16px);}
.detail-shots figure{margin:0;}
.detail-shots img{width:100%;aspect-ratio:4/3;object-fit:cover;}
.detail-shots figure:only-child{grid-column:1/-1;}

/* Menu downloads. A PDF link that does not say it is a PDF is a small
   betrayal, so each one says so and each one is a real file served from this
   site rather than hot-linked off theirs. */
.pdfs{margin:clamp(24px,3vw,40px) 0 0;padding-top:clamp(20px,2.4vw,30px);
  border-top:1px solid var(--rule);}
.pdfs h2{font-size:clamp(20px,2vw,26px);font-weight:600;letter-spacing:-.4px;margin:0 0 6px;}
.pdfs p{margin:0 0 14px;}
.pdf-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));
  gap:10px;margin-top:20px;text-align:left;}
.pdf-link{display:flex;justify-content:space-between;align-items:center;gap:14px;
  text-decoration:none;color:var(--cream);border:1px solid var(--rule);
  padding:14px 16px;font-size:16px;margin-bottom:8px;
  transition:border-color .2s ease,background-color .2s ease;}
.pdf-link:hover,.pdf-link:focus-visible{border-color:var(--red-lite);background:rgba(var(--red-lite-rgb),.07);}
.pdf-link span{font-size:12px;letter-spacing:1.2px;color:var(--red-lite);
  border:1px solid currentColor;padding:2px 7px;flex:none;}
.fp-doc{color:var(--cream);text-decoration:underline;text-underline-offset:4px;}
.fp-doc:hover,.fp-doc:focus-visible{color:var(--red-lite);}

@media (max-width:860px){
  .detail{grid-template-columns:minmax(0,1fr);}
}

/* == CARE AND FAQ ===========================================================
   Reheating and storage, consolidated from four of their pages where it
   currently appears in four slightly different wordings.
   ========================================================================== */
.care{max-width:var(--maxw);margin:0 auto;display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(28px,4vw,72px);align-items:start;}
.care-col h2{font-size:clamp(26px,3vw,40px);font-weight:600;letter-spacing:-.8px;
  line-height:1.05;margin:0 0 12px;}
.care-lede{color:var(--muted);font-size:16.5px;max-width:46ch;margin:0 0 22px;}
.care-list{margin:0;padding:0;border-top:1px solid var(--rule);}
.care-list>div{padding:15px 0;border-bottom:1px solid var(--rule);}
.care-list dt{font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:18px;letter-spacing:-.3px;margin:0 0 4px;}
.care-list dd{margin:0;color:var(--muted);font-size:15.5px;line-height:1.55;}

.faq{max-width:820px;margin:0 auto;}
.faq h2{font-size:clamp(26px,3vw,40px);font-weight:600;letter-spacing:-.8px;
  margin:0 0 clamp(18px,2.4vw,28px);}
.faq-item{border-top:1px solid var(--rule);}
.faq-item:last-of-type{border-bottom:1px solid var(--rule);}
.faq-item summary{cursor:pointer;list-style:none;padding:16px 34px 16px 0;position:relative;
  font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:clamp(17px,1.7vw,21px);letter-spacing:-.3px;}
.faq-item summary::-webkit-details-marker{display:none;}
.faq-item summary::after{content:'';position:absolute;right:6px;top:50%;
  width:11px;height:11px;margin-top:-8px;border-right:2px solid var(--red-lite);
  border-bottom:2px solid var(--red-lite);transform:rotate(45deg);
  transition:transform .25s ease;}
.faq-item[open] summary::after{transform:rotate(-135deg);margin-top:-3px;}
.faq-item summary:hover,.faq-item summary:focus-visible{color:var(--red-lite);}
.faq-item p{color:var(--muted);font-size:16px;line-height:1.6;margin:0 0 18px;max-width:64ch;}
@media (prefers-reduced-motion:reduce){.faq-item summary::after{transition:none;}}
@media (max-width:820px){.care{grid-template-columns:minmax(0,1fr);}}

/* == THE MENU SHEET =========================================================
   A link that says 'PDF' asks the reader to take it on trust. Their own site
   shows the menu and puts the download under it, which is the right way round:
   you can read it without leaving the page, and take it with you if you want.
   Pages are rendered from the client's own PDFs at 2x.
   ========================================================================== */
.menusheet{max-width:820px;margin:0 auto;text-align:center;}
.menusheet h2{font-size:clamp(26px,3vw,40px);font-weight:600;letter-spacing:-.8px;margin:0 0 8px;}
.menusheet>p{color:var(--muted);font-size:16.5px;margin:0 0 clamp(22px,3vw,36px);}
.sheet{margin:0 0 clamp(26px,3.4vw,44px);}
.sheet:last-child{margin-bottom:0;}
.sheet img{width:100%;max-width:700px;height:auto;display:block;margin:0 auto 16px;
  border:1px solid var(--rule);background:var(--on-accent);}
.sheet img+img{margin-top:14px;}
.sheet figcaption{margin-top:4px;}

/* Their real item lists. Name and description are separate lines rather than
   one run-on string: a 90 character item wrapping to three lines beside a 12
   character one is why this read as a wall of text instead of a menu.
   Three columns on wide screens, because 24 short items in two columns makes a
   long thin page and the eye has nowhere to rest. */
.range-list{max-width:var(--maxw);margin:clamp(30px,4vw,56px) auto 0;
  padding-top:clamp(24px,3vw,40px);border-top:1px solid var(--rule);
  columns:3;column-gap:clamp(30px,3.4vw,56px);}
.range-group{break-inside:avoid;margin:0 0 clamp(26px,3vw,38px);}
.range-group h3{font-family:'Bricolage Grotesque',Georgia,serif;
  font-size:clamp(16px,1.35vw,18px);font-weight:600;letter-spacing:.2px;
  text-transform:none;color:var(--cream);margin:0 0 4px;
  padding-bottom:8px;border-bottom:2px solid var(--red);}
.range-group h3::after{content:none;}
.range-group ul{list-style:none;margin:0;padding:0;}
.range-group li{padding:9px 0;border-bottom:1px solid var(--rule);}
.range-group li b{display:block;font-weight:500;font-size:15.5px;
  line-height:1.35;color:var(--cream);}
.range-group li span{display:block;font-size:13.5px;line-height:1.45;
  color:var(--muted);margin-top:3px;}
@media (max-width:1100px){.range-list{columns:2;}}
@media (max-width:680px){.range-list{columns:1;}}

/* A CTA in the middle of a page reads as the foot of it. This one sits after
   the content, on its own band, so it stops interrupting. */
/* Two full section paddings back to back left about 250px of empty ground
   between the download button and this band, which read as the page ending
   twice. The band sits close to what it follows and close to what follows it. */
.ctaband{padding-top:0;padding-bottom:0;}
.ctaband+.sec{padding-top:clamp(34px,4vw,56px);}
.menusheet{padding-bottom:0;}
.warmband+.ctaband{padding-top:clamp(28px,3.4vw,48px);}
.ctaband-in{max-width:var(--maxw);margin:0 auto;display:flex;flex-wrap:wrap;
  align-items:center;justify-content:space-between;gap:16px clamp(24px,3vw,48px);
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
  padding:clamp(24px,3vw,40px) 0;}
.ctaband-in>p:first-child{margin:0;font-family:'Bricolage Grotesque',Georgia,serif;
  font-size:clamp(19px,2vw,27px);font-weight:600;letter-spacing:-.4px;
  line-height:1.2;max-width:30ch;}
.ctaband-in .fp-cta{margin:0;display:flex;flex-wrap:wrap;align-items:center;
  gap:clamp(14px,2vw,26px);}

/* A category with one group and ten items left two of the three columns empty,
   because break-inside:avoid keeps a group whole. When there is only one
   group, let its items flow across the columns instead and break per item. */
.range-list-flow .range-group{break-inside:auto;}
.range-list-flow .range-group h3{column-span:all;margin-bottom:12px;}
.range-list-flow .range-group li{break-inside:avoid;}

/* == THE OTHER CABINETS =====================================================
   Was three 352px photo cards, 1,121px tall on a phone, showing the same
   arbitrary three categories on every page for fourteen words. As text it is
   a fraction of the height and it lists ALL of them, so it is an index rather
   than three suggestions.
   ========================================================================== */
.morelinks{padding-top:clamp(26px,3vw,40px);padding-bottom:clamp(26px,3vw,40px);}
.morelinks-in{max-width:var(--maxw);margin:0 auto;border-top:1px solid var(--rule);
  padding-top:clamp(16px,2vw,24px);}
.morelinks h2{font-size:clamp(14px,1.2vw,15.5px);font-weight:600;letter-spacing:1.1px;
  text-transform:uppercase;color:var(--muted);margin:0 0 12px;
  font-family:'Bricolage Grotesque',Georgia,serif;}
.morelinks ul{list-style:none;margin:0 0 12px;padding:0;
  display:flex;flex-wrap:wrap;gap:6px clamp(16px,2vw,28px);}
.morelinks li{position:relative;}
.morelinks li+li::before{content:'\00B7';position:absolute;color:var(--muted);opacity:.5;
  left:calc(clamp(16px,2vw,28px) / -2 - 3px);}
.morelinks a{font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:clamp(16.5px,1.5vw,20px);letter-spacing:-.3px;color:var(--cream);
  text-decoration:none;}
.morelinks a:hover,.morelinks a:focus-visible{color:var(--red-lite);}
.morelinks-in>p{margin:0;}

/* == HOURS AND ADDRESS ======================================================
   Rebuilt 06-09-2026. Was two equal centred columns with a hairline between
   them, each topped by a small-caps red label. Josh flagged it as reading AI
   on the first look at this site and he was right twice over: the symmetrical
   two-up is the stat-band shape, and 'WE ARE OPEN' / 'YOU CAN FIND US AT' are
   the small-caps meta lines his own blacklist already bans.
   Every fact survives, including the directions button, the management window
   and the rating, none of which appear anywhere else on the home page. Only
   the shape changes.
   ========================================================================== */
.band{max-width:none;display:block;text-align:left;padding:clamp(34px,4vw,60px) var(--gut);
  border-bottom:1px solid var(--rule);border-top:1px solid var(--rule);}
.band-in{max-width:var(--maxw);margin:0 auto;}
.band-in .band-big{font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:clamp(24px,3.4vw,46px);line-height:1.1;letter-spacing:-1px;
  color:var(--cream);margin:0 0 14px;display:flex;flex-wrap:wrap;
  align-items:baseline;gap:0 clamp(12px,1.6vw,26px);}
.band-in .band-big i{display:inline-block;width:8px;height:8px;border-radius:50%;
  background:var(--red);flex:none;align-self:center;}
.band-in .band-note{color:var(--muted);max-width:62ch;margin:0 0 6px;font-size:16.5px;}
.band-in .band-hand{margin:0 0 20px;}
.band-in .btn{margin:0;}
@media (max-width:640px){
  .band-in .band-big{display:block;}
  .band-in .band-big i{display:none;}
  .band-in .band-big span+span{display:block;font-size:.62em;margin-top:8px;
    letter-spacing:-.4px;color:var(--muted);}
}

/* ── FIXES 06-09-2026, both caught by eye rather than by any audit ────────── */

/* The mobile rule never matched. There is an <i> bullet between the two spans,
   so `span + span` has no adjacent pair and the address ran straight onto the
   hours with no gap: 'every day128-130 O'Connell St'. */
@media (max-width:640px){
  .band-in .band-big span+span{display:inline;}
  .band-in .band-big span:last-child{display:block;font-size:.62em;margin-top:8px;
    letter-spacing:-.4px;color:var(--muted);}
}

/* `align-items:flex-end` aligns the BOTTOMS of the two columns, so a four line
   paragraph beside a two line heading block starts above the heading and reads
   as though it comes first. Reading order and visual order have to agree, so
   the supporting line now sits under the heading rather than beside it. */
.range-head{display:block;}
.range-head>div{margin:0;}
.range-head>p{margin:12px 0 0;max-width:66ch;}

/* == LIGHT / DARK TOGGLE ====================================================
   Sits beside the phone number in the top bar. Deliberately understated: it is
   a preference control, not a call to action, so it must never compete with
   the phone button next to it.
   ========================================================================== */
.themetoggle{display:inline-flex;align-items:center;gap:7px;flex:none;
  background:transparent;color:var(--cream);border:1px solid var(--rule);
  font:inherit;font-size:13.5px;font-weight:600;letter-spacing:.3px;
  padding:9px 13px;border-radius:2px;cursor:pointer;
  transition:border-color .2s ease,color .2s ease;}
.themetoggle svg{width:15px;height:15px;flex:none;}
.themetoggle:hover,.themetoggle:focus-visible{border-color:var(--red-lite);color:var(--red-lite);}
/* The sun becomes a moon in light mode: the icon shows what you would GET. */
:root[data-theme="light"] .themetoggle svg circle{r:5.2;}
:root[data-theme="light"] .themetoggle svg path{opacity:0;}
.themetoggle svg path{transition:opacity .25s ease;}
@media (max-width:1080px){.themetoggle span{display:none;}.themetoggle{padding:9px;}}
@media (prefers-reduced-motion:reduce){.themetoggle,.themetoggle svg path{transition:none;}}

/* Text that sits on a PHOTOGRAPH must not follow the theme. The hero, the inner
   page banners and the top bar all sit on a dark scrim over an image, so when
   --cream flipped to #211A15 in light mode the nav and every banner headline
   went dark-on-dark and vanished. The scrim is dark in both themes because the
   photo is behind it, so this text is pinned light in both. */
:root[data-theme="light"]{
  --on-photo:#F3ECE2;
  --on-photo-muted:#DDD3C6;
  --on-photo-rule:rgba(243,236,226,.28);
}
:root{ --on-photo:var(--cream); --on-photo-muted:var(--muted); --on-photo-rule:var(--rule); }

:root[data-theme="light"] .hero-inner,
:root[data-theme="light"] .hero-inner .hero-sub,
:root[data-theme="light"] .banner-inner,
:root[data-theme="light"] .banner-inner p,
:root[data-theme="light"] .banner h1,
:root[data-theme="light"] .hero h1{color:var(--on-photo);}
:root[data-theme="light"] .ticker{color:var(--on-photo);}
:root[data-theme="light"] .topbar{border-bottom-color:var(--on-photo-rule);}
:root[data-theme="light"] .nav a{color:var(--on-photo);}
:root[data-theme="light"] .themetoggle{color:var(--on-photo);border-color:var(--on-photo-rule);}
/* Once the bar is stuck it has its own solid ground again, so it follows the theme. */
:root[data-theme="light"] .pagetop.is-stuck .nav a,
:root[data-theme="light"] .pagetop.is-stuck .themetoggle{color:var(--cream);}
:root[data-theme="light"] .pagetop.is-stuck .themetoggle{border-color:var(--rule);}
:root[data-theme="light"] .banner .crumb a{color:var(--on-photo);}

/* == PRODUCT DETAIL PAGE, rebuilt 07-09-2026 ================================
   The old template was [intro text | photo grid] side by side, then the items
   as hairline rows, then the photos nowhere near the products they showed.
   It broke on every page because the six pages are four different shapes:
   Donuts has 22 words and no menu, Vegan has no photographs at all, Cafe has
   no item list, Pies has 24 items. Two columns cannot serve that spread
   without leaving a hole on one side.
   One column, product-led, so a short page is simply short instead of holed.
   ========================================================================== */
.detailpage{max-width:none;}
.lede{max-width:var(--maxw);margin:0 auto;}
.lede p{font-size:clamp(18px,1.7vw,21px);line-height:1.55;color:var(--cream);
  max-width:62ch;margin:0 0 14px;}
.lede p:last-child{margin-bottom:0;color:var(--muted);font-size:clamp(16px,1.4vw,17.5px);}

/* The photographs are the appetite. Give them the full measure and equal
   heights instead of a ragged 2+1 grid orphaning the third one. */
.shotstrip{max-width:var(--maxw);margin:clamp(28px,3.4vw,48px) auto 0;
  display:grid;gap:clamp(10px,1.2vw,16px);}
.shots-2{grid-template-columns:repeat(2,1fr);}
.shots-3{grid-template-columns:repeat(3,1fr);}
.shots-4{grid-template-columns:repeat(4,1fr);}
.shotstrip figure{margin:0;}
.shotstrip img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;}
@media (max-width:900px){.shots-3,.shots-4{grid-template-columns:repeat(2,1fr);}}
@media (max-width:520px){.shotstrip{grid-template-columns:1fr !important;}}

/* The range as a menu, not a table. A rule per item made ten donut names read
   like a spreadsheet export. One rule per GROUP, items set close. */
.range{max-width:var(--maxw);margin:clamp(34px,4vw,60px) auto 0;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));
  gap:clamp(26px,3vw,44px) clamp(30px,3.4vw,56px);align-items:start;}
.rgroup{break-inside:avoid;}
.rgroup h3{font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:clamp(16px,1.35vw,18px);letter-spacing:.2px;color:var(--cream);
  margin:0 0 12px;padding-bottom:9px;border-bottom:2px solid var(--red);}
.rgroup h3::after{content:none;}
.rgroup ul{list-style:none;margin:0;padding:0;}
.rgroup li{padding:0 0 11px;}
.rgroup li b{display:block;font-weight:500;font-size:16px;line-height:1.35;color:var(--cream);}
.rgroup li i{display:block;font-style:normal;font-size:13.5px;line-height:1.45;
  color:var(--muted);margin-top:2px;}

/* A category with one group (Donuts) put its ten items in the first grid track
   and left the other two empty, because auto-fit sizes the tracks whether or
   not there is anything to put in them. With one group, the ITEMS flow across
   the columns instead of the group doing so. */
.range:has(> .rgroup:only-child){display:block;}
.range > .rgroup:only-child > ul{columns:3;column-gap:clamp(30px,3.4vw,56px);}
.range > .rgroup:only-child > ul > li{break-inside:avoid;}
@media (max-width:900px){.range > .rgroup:only-child > ul{columns:2;}}
@media (max-width:560px){.range > .rgroup:only-child > ul{columns:1;}}

/* The toggle showed a bare circle in light mode because the sun's rays were
   hidden with opacity. Draw an actual moon instead, so the icon reads as the
   thing you would switch TO rather than as a dot. */
:root[data-theme="light"] .themetoggle svg circle{r:0;}
:root[data-theme="light"] .themetoggle svg path{opacity:1;
  d:path("M19 14.6A8.2 8.2 0 0 1 9.4 5a8.2 8.2 0 1 0 9.6 9.6z");
  stroke-width:0;fill:currentColor;}

/* == LOGO AND HERO IN LIGHT MODE ============================================
   The mark is pure white line work on transparency, so on a cream ground it
   disappears. brightness(0) drives every channel to black whilst leaving the
   alpha alone, which recolours the mark without needing a second asset and
   without touching the data-oc="logo" CMS slot.
   Only when the bar is STUCK, though: over the hero photograph the bar still
   sits on a dark scrim and the white mark is correct there. */
:root[data-theme="light"] .pagetop.is-stuck .brand img{filter:brightness(0);}
:root[data-theme="light"] .brand img{transition:filter .2s ease;}
@media (prefers-reduced-motion:reduce){
  :root[data-theme="light"] .brand img{transition:none;}
}

/* The hero keeps its dark-mode treatment in both themes: it sits on a photo
   behind a dark scrim, so the accent has to stay the light red. --red-lite
   drops to #9E2E1B in light mode for small text on cream, which would be a
   dark red on a dark scrim, i.e. invisible. */
:root[data-theme="light"] .hero h1 em{color:#F26A50;}
:root[data-theme="light"] .banner-inner .crumb a{color:var(--on-photo);}

/* .kicker sets its own colour, so it overrode the inherited over-photo fix and
   went dark-on-dark. Anything with an explicit colour inside the hero or a
   banner has to be named, inheritance is not enough. */
:root[data-theme="light"] .kicker,
:root[data-theme="light"] .hero-inner .hero-sub,
:root[data-theme="light"] .banner-inner p{color:var(--on-photo);}


/* ═══════════════════════════════════════════════════════════════════════════
   == LIGHT-BASIS PASS, 09-09-2026 ==========================================
   Light is now the ground the site is designed in, and the dark brown is spent
   deliberately in two places rather than everywhere: the hero panel and the
   marquee band. The nav goes red because cream-on-cream had no edge once the
   page ground went light.
   ═══════════════════════════════════════════════════════════════════════════ */

:root,
:root[data-theme="light"]{
  /* Constant across both themes. The tokens above flip with the theme, which is
     right for the page ground, but the hero panel and the marquee band have to
     stay dark in BOTH cuts or the "one dark moment" reading falls apart. */
  --brown:#1C1614;
  --brown-2:#241C18;
  --on-brown:#F3ECE2;
  --on-brown-mute:#B7ADA3;   /* 5.6:1 on --brown */
}

/* ── nav ─────────────────────────────────────────────────────────────────────
   Was transparent over the hero photograph and cream once stuck, which on a
   cream page ground meant it had no edge at all. Solid red on every page and
   at every scroll position instead: it is the one element that has to be
   findable without looking for it. */
.pagetop{background:var(--red-deep);transition:box-shadow .3s ease;}
.pagetop.is-stuck{background:var(--red-deep);border-bottom:0;
  box-shadow:0 6px 24px rgba(28,22,20,.22);}
.ticker{background:rgba(0,0,0,.16);color:var(--on-accent);}
.ticker-open{color:#FFD9CE;}
.topbar{border-bottom:1px solid rgba(255,255,255,.16);}
.nav a{color:var(--on-accent);}
.nav a:hover,.nav a[aria-current="page"]{color:#FFD9CE;}
/* the call button was red on red, so it inverts */
.topcall{background:var(--on-accent);color:var(--red-deep);}
.topcall:hover{background:#FFD9CE;color:var(--red-deep);}
.themetoggle{color:var(--on-accent);border-color:rgba(255,255,255,.34);}
.navtoggle span,.navtoggle span::before,.navtoggle span::after{background:var(--on-accent);}

/* ── split hero ──────────────────────────────────────────────────────────────
   Colour panel left, photograph right. The full-bleed version needed a three
   layer scrim to hold the type at AA, and that scrim is what was making the
   shopfront read muddy. Splitting them lets the type sit at full strength on a
   flat ground and leaves the photograph untouched. */
.hsplit{display:grid;grid-template-columns:1.02fr 1fr;min-height:100vh;min-height:100svh;}
.hsplit>*{min-height:0;overflow:hidden;}   /* or the photo grows the row */
.hsplit-panel{
  position:relative;display:flex;align-items:center;
  background:var(--brown);color:var(--on-brown);
  padding:clamp(28px,4vw,64px) clamp(24px,5.4vw,96px);
}
/* a faint warm lift from the top left so the panel is not a flat slab */
.hsplit-panel::before{content:'';position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(90% 70% at 8% 12%,rgba(210,69,43,.16),rgba(210,69,43,0) 62%);}
.hsplit-in{position:relative;z-index:1;max-width:34rem;
  padding-top:clamp(96px,12vh,150px);}   /* clears the fixed nav */
.hsplit-logo{width:clamp(132px,15vw,208px);height:auto;margin:0 0 clamp(18px,2.4vw,30px);}
.hsplit-word{
  font-family:'Bricolage Grotesque',Georgia,serif;font-weight:600;
  font-size:clamp(34px,4.4vw,64px);line-height:1.02;letter-spacing:-1.4px;
  margin:0 0 clamp(16px,1.8vw,24px);color:var(--on-brown);
}
.hsplit-sub{font-size:clamp(16.5px,1.3vw,19.5px);line-height:1.62;
  max-width:40ch;margin:0 0 clamp(24px,2.8vw,36px);color:var(--on-brown-mute);}
.hsplit-shot{position:relative;background:var(--shot);}
.hsplit-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:62% 44%;}

@media (max-width:900px){
  /* photo on top, copy under it, still one screen */
  .hsplit{grid-template-columns:1fr;grid-template-rows:minmax(0,42%) minmax(0,1fr);}
  .hsplit-shot{order:1;}
  .hsplit-panel{order:2;padding-block:clamp(26px,5vw,44px);}
  .hsplit-in{padding-top:0;}
}
@media (max-width:900px){
  /* the fixed bar sits over the photo half on a phone, so the photo needs to
     start below it rather than the copy */
  .hsplit-shot{padding-top:0;}
}

/* ── marquee: brown, and quicker ─────────────────────────────────────────────
   Moved directly under the hours band, so it is the first thing after the two
   facts and it does the job the old cards row was doing, which is showing that
   there is a lot of it. 64s read as static at a glance. */
.marquee{background:var(--brown);border-top:0;border-bottom:0;margin:0;}
@media (prefers-reduced-motion:no-preference){
  .marquee-track{animation-duration:34s;}
}

/* ── icing drip ──────────────────────────────────────────────────────────────
   The join between a brown band and the cream below it. Two masked layers at
   different sizes and offsets, unioned, so the drips alternate long and short
   instead of reading as an even scallop. Pure CSS, no asset to serve, and it
   scales to any width. Set --drip-bg to whatever the band above it is. */
.drip{height:34px;margin:0 0 clamp(18px,2.4vw,34px);
  background:var(--drip-bg,var(--brown));
  --lip:linear-gradient(#000 0 0);
  --m1:radial-gradient(15px 24px at 50% 0,#000 99%,transparent 100%);
  --m2:radial-gradient(11px 16px at 50% 0,#000 99%,transparent 100%);
  --m3:radial-gradient(8px 31px at 50% 0,#000 99%,transparent 100%);
  --mask:var(--lip) no-repeat 0 0/100% 9px,
         var(--m1) repeat-x 0 8px/86px 24px,
         var(--m2) repeat-x 31px 8px/86px 16px,
         var(--m3) repeat-x 58px 8px/86px 31px;
  -webkit-mask:var(--mask); mask:var(--mask);
}

/* ── hero load-in ────────────────────────────────────────────────────────────
   One orchestrated entrance on the hero only, nothing below the fold. Four
   elements on a 90ms stagger. Transform and opacity only, so it runs on the
   compositor and costs nothing measurable. Everything is visible without it,
   so the page is complete with JS off and with motion turned down. */
@media (prefers-reduced-motion:no-preference){
  .hsplit-logo,.hsplit-word,.hsplit-sub,.hsplit-in .btn{
    opacity:0;transform:translateY(15px);
    animation:hrise .72s cubic-bezier(.2,.7,.3,1) forwards;
  }
  .hsplit-logo{animation-delay:.05s;}
  .hsplit-word{animation-delay:.14s;}
  .hsplit-sub{animation-delay:.23s;}
  .hsplit-in .btn{animation-delay:.32s;}
  @keyframes hrise{to{opacity:1;transform:none;}}
}

/* ── texture, third cut ──────────────────────────────────────────────────────
   The woven ruling is gone. Two repeating gradients at 3px and 4px pitch make a
   mechanical crosshatch, and at that spacing it reads as a screen door rather
   than as paper stock. It was doing the most damage on the light ground, where
   it is far more visible than it was on the dark one, and it competes with the
   food photography, which is the thing on this page that has to hold the eye.

   The noise field stays, at a slightly lower opacity. That is the layer that
   was actually doing the warming.

   background-attachment:fixed also goes. On a full-page background it forces a
   repaint of the whole viewport on every scroll frame, which is the one real
   scroll cost this page had. Nothing else here animates on scroll.

   To put the grid back, delete this block. */
body{
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.042'/%3E%3C/svg%3E");
  background-attachment:scroll;
}

/* ── hero photo: tint + slow crossfade ───────────────────────────────────────
   The tint is back. With no type over the photograph it is not a contrast
   device any more, it is a colour one: untinted, the photo read as a bright
   rectangle bolted onto a dark brown panel, and the two halves did not look
   like one hero. A warm brown wash at low strength ties them together and
   still leaves the shopfront legible.

   The crossfade is three of their own photographs, 21s round trip, roughly
   6s a slide with a 1s dissolve. Opacity only, so it runs on the compositor
   and costs nothing per frame. Deliberately NOT a slide or push transition,
   which is the thing that reads as a 2010 carousel. */
.hslide{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:62% 44%;}
.hslide-1{z-index:1;}
.hslide-2{z-index:2;opacity:0;}
.hslide-3{z-index:3;opacity:0;}

@media (prefers-reduced-motion:no-preference){
  .hslide-2,.hslide-3{animation:hslide 21s linear infinite;}
  .hslide-3{animation-delay:7s;}
  @keyframes hslide{
    0%     {opacity:0;}
    4.8%   {opacity:1;}   /* ~1s dissolve in  */
    33.33% {opacity:1;}   /* ~6s on screen    */
    38.1%  {opacity:0;}   /* ~1s dissolve out */
    100%   {opacity:0;}
  }
}

/* the wash sits above all three slides, so it does not have to be repeated */
.hsplit-shot::after{
  content:'';position:absolute;inset:0;z-index:4;pointer-events:none;
  background:
    linear-gradient(90deg, rgba(28,22,20,.42) 0%, rgba(28,22,20,.10) 34%, rgba(28,22,20,.04) 100%),
    linear-gradient(180deg, rgba(28,22,20,.20) 0%, rgba(28,22,20,0) 26%, rgba(28,22,20,.26) 100%);
}
/* stacked on a phone the panel is below the photo, so the seam that needs
   softening is the bottom edge rather than the left one */
@media (max-width:900px){
  .hsplit-shot::after{
    background:linear-gradient(180deg, rgba(28,22,20,.22) 0%, rgba(28,22,20,0) 30%, rgba(28,22,20,.38) 100%);
  }
}

/* ── gradients out of the hero ───────────────────────────────────────────────
   Both gradients go. The photo wash was a two-layer directional thing that
   dragged the left edge of the shopfront down to near black, and the panel
   carried a radial warm lift that showed as a visible corner smudge.

   The tint stays, because untinted the photo read as a bright rectangle bolted
   onto a dark panel. It is a flat even wash now, one value across the whole
   frame, which is what a tint should have been in the first place. */
.hsplit-panel::before{display:none;}
.hsplit-shot::after{
  background:rgba(28,22,20,.24);
}
@media (max-width:900px){
  .hsplit-shot::after{background:rgba(28,22,20,.24);}
}

/* the nav mark is its own file now, drawn for the red bar, so the old
   brightness filter that was faking a dark-on-light logo has nothing to do */
.pagetop.is-stuck .brand img,
.brand img{filter:none;}

/* ═══════════════════════════════════════════════════════════════════════════
   == NAV + BAND PASS, 09-09-2026 ===========================================
   Every rule here is written at :root[data-theme="light"] specificity on
   purpose. The theme-scoped overrides further up this file are all at that
   weight, and a plainer selector loses to them no matter how late it sits.
   That is exactly what happened to the last attempt at the logo filter.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  --nav-red:#8E2A18;        /* deeper brick than --red-deep, nav only */
  --nav-red-2:#7A2313;      /* the ticker tier and hover */
  --brown-band:#3B2A1E;     /* the marquee ground, a real brown not a near-black */
}

/* ── the bar does not change on scroll ───────────────────────────────────────
   It was doing four things at once at the stuck breakpoint: shrinking the
   logo, cutting its own padding, flipping the links to dark ink and running
   brightness(0) over the mark. That last pair is the white-to-black flip.
   A nav that resizes as you scroll also makes the page feel like it is
   settling rather than holding still, so the height is now constant. */
:root[data-theme="light"] .pagetop,
:root[data-theme="light"] .pagetop.is-stuck{
  background:var(--nav-red);
  border-bottom:0;
}
:root[data-theme="light"] .pagetop.is-stuck{
  box-shadow:0 8px 28px rgba(28,22,20,.28);
}
:root[data-theme="light"] .pagetop.is-stuck .topbar{
  padding-top:14px;padding-bottom:14px;      /* same as at rest */
}
:root[data-theme="light"] .pagetop .brand img,
:root[data-theme="light"] .pagetop.is-stuck .brand img{
  width:78px;height:78px;filter:none;        /* no shrink, no brightness flip */
}
:root[data-theme="light"] .pagetop .nav a,
:root[data-theme="light"] .pagetop.is-stuck .nav a{
  color:var(--on-accent);
}

/* ── links, with an underline that wipes rather than a colour that swaps ─────
   Uppercase and tracked so the row reads as a set of labels rather than as
   running text, which is what made it look like an afterthought at 14.5px
   sentence case. */
:root[data-theme="light"] .pagetop .nav{gap:clamp(16px,2.1vw,30px);}
:root[data-theme="light"] .pagetop .nav a{
  position:relative;
  font-size:12.5px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;
  padding-block:8px;
}
:root[data-theme="light"] .pagetop .nav a::after{
  content:'';position:absolute;left:0;right:0;bottom:0;height:2px;
  background:var(--on-accent);
  transform:scaleX(0);transform-origin:left;
  transition:transform .26s cubic-bezier(.2,.7,.3,1);
}
:root[data-theme="light"] .pagetop .nav a:hover::after,
:root[data-theme="light"] .pagetop .nav a:focus-visible::after,
:root[data-theme="light"] .pagetop .nav a[aria-current="page"]::after{
  transform:scaleX(1);
}
@media (prefers-reduced-motion:reduce){
  :root[data-theme="light"] .pagetop .nav a::after{transition:none;}
}

/* the call button sits on a deeper red now, so it keeps its light fill */
:root[data-theme="light"] .pagetop .topcall{
  background:var(--on-accent);color:var(--nav-red);
}
:root[data-theme="light"] .pagetop .topcall:hover{background:#FFE7DF;color:var(--nav-red);}

/* ── marquee: browner, and air above the photos ──────────────────────────────
   The band was #1C1614, which is a warm near-black rather than a brown, and
   it only showed in the gutters between tiles and under them. Padding on the
   band puts the same ground above the row as well, so the photographs sit
   IN a brown field instead of being butted against the top edge of one. */
.marquee{
  background:var(--brown-band);
  padding-block:clamp(16px,1.9vw,28px);
}
.marquee figure{margin-right:clamp(12px,1.3vw,20px);}

/* The bar still moved by exactly 1px on scroll: .topbar carries a hairline at
   rest and the stuck rule set border-bottom:0, so the whole page nudged up as
   you passed the hero. Same hairline in both states. */
:root[data-theme="light"] .pagetop .topbar,
:root[data-theme="light"] .pagetop.is-stuck .topbar{
  border-bottom:1px solid rgba(255,255,255,.14);
}

/* ── nav type, corrected against the working-business register ───────────────
   The uppercase 12.5px at +1.6px tracking went back. In this register that is
   the wrong lever: the three local-business sites Josh rated good all run
   plain type at normal tracking, and the most typographically worked one in
   that library is the one he flagged as screaming AI. Sentence case, normal
   tracking, at a readable size.

   The interest stays in the things that are actually this business: the deep
   brick bar, the cream mark, and an underline that wipes rather than a colour
   that swaps. */
:root[data-theme="light"] .pagetop .nav a{
  font-size:15px;font-weight:500;letter-spacing:normal;text-transform:none;
}

/* ── one brown, not two ──────────────────────────────────────────────────────
   The hero panel was #1C1614 and the marquee band #3B2A1E, which read as two
   different materials. Both are the browner value now, which is also the
   answer to "make this more of a brown" on the panel as well as the band.
   Declared at :root[data-theme="light"] weight, because the earlier block sets
   --brown in a selector LIST containing that selector, and the highest
   specificity in a list is what applies. A plain :root here loses. */
:root[data-theme="light"]{--brown:#3B2A1E;}
:root[data-theme="light"] .hsplit-panel{background:var(--brown);}

/* ═══════════════════════════════════════════════════════════════════════════
   == SANI PROPORTIONS PASS, 09-09-2026 =====================================
   Three measurements taken off SANI Bakehouse at 1440 rather than eyeballed:
   the bar is 123px tall, its mark is 48px inside that, and the hero is capped
   at 740px so header plus hero lands at 863px on a 900px screen instead of
   filling it. All three are matched here.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── bar: 123px, with the mark small enough to leave air ─────────────────────
   Ours was a 78px mark in a 107px bar, so the logo was 73% of the bar height
   and the row read as crowded. SANI runs 48 in 123, which is 39%, and that
   space is most of why theirs feels lighter. */
:root[data-theme="light"] .pagetop .topbar,
:root[data-theme="light"] .pagetop.is-stuck .topbar{
  padding-top:clamp(18px,2.6vw,38px);
  padding-bottom:clamp(18px,2.6vw,38px);
}
:root[data-theme="light"] .pagetop .brand img,
:root[data-theme="light"] .pagetop.is-stuck .brand img{
  width:clamp(38px,3.4vw,48px);
  height:clamp(38px,3.4vw,48px);
}

/* ── hero: capped, and no longer padded down behind the bar ──────────────────
   It was min-height:100svh with the copy pushed down by a padding-top big
   enough to clear the fixed bar, which is what produced the dead space at the
   bottom of the panel. Offsetting the whole section by the bar height instead
   lets the copy centre in the space it actually has, and the cap stops the
   hero eating a tall screen. */
.hsplit{
  margin-top:var(--navh,123px);
  min-height:0;
  height:calc(100vh - var(--navh,123px));
  height:calc(100svh - var(--navh,123px));
  max-height:740px;
}
.hsplit-in{padding-top:0;}

@media (max-width:900px){
  .hsplit{
    height:auto;
    min-height:calc(100svh - var(--navh,96px));
    max-height:none;
  }
}

/* ── the seam ────────────────────────────────────────────────────────────────
   SANI's coloured band carries a 10px strip of the page ground above and below
   it, so it floats rather than butting into the sections either side. That is
   the single thing that keeps a full-width colour band from reading as heavy.
   Same treatment on the brown band here. */
.marquee{
  margin-block:clamp(6px,.7vw,10px);
}



/* ── the swap: brown bar, red hero ───────────────────────────────────────────
   The bar was the deep brick and the hero panel the brown, which put the
   loudest colour on the element that repeats on every page and every scroll
   position. Reversed, the red is spent once, on the one panel that is meant to
   carry the brand, and the bar goes quiet.

   The hero red is the deepest of the three (#8E2A18, not #D2452B or #B33520).
   On an area that size a brighter red is the thing that reads as cheap, and
   the depth is what buys back the "professional".

   The bar now matches the marquee band, so the page runs on exactly two darks
   and one ground rather than the four warm values it had drifting apart. */
:root[data-theme="light"] .pagetop,
:root[data-theme="light"] .pagetop.is-stuck{
  background:var(--brown);
}
:root[data-theme="light"] .pagetop.is-stuck{
  box-shadow:0 8px 28px rgba(28,22,20,.30);
}
:root[data-theme="light"] .hsplit-panel{
  background:#8E2A18;
}
/* the sub copy sat on brown, it needs its own value on red */
:root[data-theme="light"] .hsplit-sub{color:rgba(255,240,232,.86);}
/* the CTA was red on brown; on a red panel it inverts to the cream fill */
:root[data-theme="light"] .hsplit-in .btn{
  background:#F3ECE2;color:#8E2A18;
}
:root[data-theme="light"] .hsplit-in .btn:hover{background:#FFF;color:#8E2A18;}
/* the phone button keeps its light fill, but its ink follows the brown bar */
:root[data-theme="light"] .pagetop .topcall{color:var(--brown);}
:root[data-theme="light"] .pagetop .topcall:hover{color:var(--brown);}

/* ── hero: one image, and weighted to it ─────────────────────────────────────
   The crossfade is gone. Slides two and three were removed from the markup, so
   the animation and the low-priority preloads go with them, which also hands
   the LCP image back its exclusive claim on the connection.

   The split moves off 50/50 to 38/62 in favour of the photograph. A bakery is
   sold by what is in the window, not by the paragraph next to it. */
.hsplit{grid-template-columns:38fr 62fr;}
@media (max-width:900px){
  .hsplit{grid-template-columns:1fr;}   /* stacked, the ratio does not apply */
}




/* ── the mark needs its size back ────────────────────────────────────────────
   48px was a straight copy of SANI's proportion, and that was the wrong thing
   to copy. Their mark is the single word SANI, which survives being small.
   This one is a badge carrying OPEN, ON O'CONNELL and 24HRS in fine type
   around a ring, and below about 60px that detail collapses into a smudge that
   does not read as a logo at all.

   72px, with the bar's padding cut to hold the same 125px height, so the mark
   gets its presence back without the row growing. */
:root[data-theme="light"] .pagetop .brand img,
:root[data-theme="light"] .pagetop.is-stuck .brand img{
  width:clamp(52px,5vw,72px);
  height:clamp(52px,5vw,72px);
}
:root[data-theme="light"] .pagetop .topbar,
:root[data-theme="light"] .pagetop.is-stuck .topbar{
  padding-top:clamp(14px,1.8vw,26px);
  padding-bottom:clamp(14px,1.8vw,26px);
}

/* ── instagram embeds ────────────────────────────────────────────────────────
   Fixed-ratio boxes so nothing depends on Instagram resizing the frame.
   1/1.28 fits their header bar, a square photo and the action row without the
   frame itself scrolling.
   The tile ground is deliberately NOT white: if an embed is blocked, or a post
   is deleted, the iframe renders empty, and a white hole on a cream page reads
   as a broken image whereas the tile colour reads as deliberate. */
.ig-grid{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(10px,1.3vw,18px);
}
.ig-post{
  position:relative;width:100%;
  aspect-ratio:1/1.28;
  background:var(--shot);
  border:1px solid var(--rule);
  overflow:hidden;
}
.ig-post iframe{position:absolute;inset:0;width:100%;height:100%;border:0;}
/* Drop to two, then one, well before each frame is squeezed. Instagram's own
   header crushes and truncates the handle below about 320px per embed. */
@media (max-width:1400px){
  .ig-grid{grid-template-columns:repeat(2,1fr);}
  .ig-grid > li:nth-child(3){display:none;}
}
@media (max-width:700px){
  .ig-grid{grid-template-columns:1fr;max-width:380px;}
  .ig-grid > li:nth-child(n+2){display:none;}
}

:root[data-theme="light"] .pagetop .brand{
  margin-right:auto;display:flex;align-items:center;text-decoration:none;
}

/* links right, hard against the phone button rather than centred */
:root[data-theme="light"] .pagetop .nav{
  margin-left:auto;margin-right:clamp(16px,2.2vw,34px);
}

/* ── socials band, widened so the embeds are not crushed ─────────────────────
   Instagram's own header truncates the handle below roughly 320px of frame.
   In a plain 50/50 split each of the three landed at 195px, which is why the
   account name read as "BAKE...". Capping the copy column and letting the
   embeds take the remainder is the same fix SANI applies. */
.split:has(.ig-grid){
  grid-template-columns:minmax(260px,340px) minmax(0,1fr);
  gap:clamp(24px,3vw,56px);
  align-items:center;
}
@media (max-width:1000px){
  .split:has(.ig-grid){grid-template-columns:1fr;}
}

/* ── the red, off the burnt orange ───────────────────────────────────────────
   #8E2A18 sits at roughly 13 degrees of hue, which is orange, not red. Next to
   a warm brown that reads as rust rather than as a brand colour, and it is the
   thing that kept looking wrong at every layout we tried it in.
   #8A2028 is a genuine dark red, cooler and deeper, and it separates from the
   brown instead of blending into it. Applied to the whole family so the panel,
   the buttons and the small accents stop disagreeing with each other. */
:root[data-theme="light"]{
  --red:#A32A33;        /* large type and fills on cream */
  --red-deep:#8A2028;   /* fills carrying text */
  --red-lite:#8A2028;   /* small accents on cream, same value, AA on this ground */
}
:root[data-theme="light"] .hsplit-panel{background:#8A2028;}
:root[data-theme="light"] .hsplit-in .btn{background:#F3ECE2;color:#8A2028;}
:root[data-theme="light"] .hsplit-in .btn:hover{background:#FFF;color:#8A2028;}
:root[data-theme="light"] .pagetop .topcall{color:var(--brown);}

/* ── hero photograph, tinted into the palette ────────────────────────────────
   Was a flat neutral darken, which knocked the photo back without connecting
   it to anything. Two layers now: the same neutral hold, plus a low wash of
   the brand red, so the shopfront reads as part of the panel beside it rather
   than as a photo dropped next to a colour. Still light enough that the shop's
   own hanging sign stays legible. */
:root[data-theme="light"] .hsplit-shot::after{
  background:
    linear-gradient(0deg, rgba(138,32,40,.20), rgba(138,32,40,.20)),
    linear-gradient(0deg, rgba(28,22,20,.18), rgba(28,22,20,.18));
}

/* Two competing auto margins split the free space between them, which centred
   the nav rather than pushing it right. Only one of them can be auto. */
:root[data-theme="light"] .pagetop .brand{margin-right:0;}

/* Deterministic bar layout. Auto margins were set on three different children
   across three earlier passes, and flexbox splits free space between every
   auto it finds, which is why the links kept landing in the middle no matter
   which one was changed. Exactly ONE auto margin now, on the brand, so
   everything after it is pushed right and the links sit against the phone. */
:root[data-theme="light"] .pagetop .brand{margin-right:auto;}
:root[data-theme="light"] .pagetop .nav{margin-left:0;margin-right:clamp(14px,1.8vw,28px);}
:root[data-theme="light"] .pagetop .topcall{margin-left:0;}

/* ── back to the orange-red, and a colourless tint ───────────────────────────
   The dark red read worse, not better. Reverting the family to #8E2A18.
   The photo tint drops its red wash with it: a coloured tint on a photograph
   reads as a filter, which is not what was wanted. Plain neutral darkening. */
:root[data-theme="light"]{
  --red:#D2452B;
  --red-deep:#B33520;
  --red-lite:#9E2E1B;
}
:root[data-theme="light"] .hsplit-panel{background:#8E2A18;}
:root[data-theme="light"] .hsplit-in .btn{background:#F3ECE2;color:#8E2A18;}
:root[data-theme="light"] .hsplit-in .btn:hover{background:#FFF;color:#8E2A18;}
:root[data-theme="light"] .hsplit-shot::after{
  background:rgba(20,15,13,.26);   /* neutral, no hue */
}



/* ═══════════════════════════════════════════════════════════════════════════
   == LIGHT PASS, 09-09-2026 ================================================
   The bar goes to a light tan with dark ink, and the hero panel goes to near
   white with the orange as type rather than as a ground.

   This is the structural fix, not a colour tweak. The page was carrying four
   heavy dark slabs on a cream ground (bar, hero panel, marquee band, footer)
   which is what the audit has been flagging as "4 section grounds, ceiling 3"
   all afternoon. Two of the four turn light here, so the accent finally has
   something to be an accent AGAINST.
   ═══════════════════════════════════════════════════════════════════════════ */
:root[data-theme="light"]{
  --tan:#DFCEB6;        /* the bar */
  --tan-line:rgba(46,33,25,.20);
  --bar-ink:#2E2119;    /* 9.6:1 on the tan */
}

/* ── bar: light tan, dark ink ─────────────────────────────────────────────── */
:root[data-theme="light"] .pagetop,
:root[data-theme="light"] .pagetop.is-stuck{
  background:var(--tan);
}
:root[data-theme="light"] .pagetop.is-stuck{box-shadow:0 6px 22px rgba(46,33,25,.16);}
:root[data-theme="light"] .pagetop .topbar,
:root[data-theme="light"] .pagetop.is-stuck .topbar{border-bottom:1px solid var(--tan-line);}
:root[data-theme="light"] .pagetop .brandword{color:var(--bar-ink);}
:root[data-theme="light"] .pagetop .nav a,
:root[data-theme="light"] .pagetop.is-stuck .nav a{color:var(--bar-ink);}
:root[data-theme="light"] .pagetop .nav a::after{background:var(--bar-ink);}
/* the call button was a light fill on a dark bar, which vanishes on a light
   one, so it inverts to the accent */
:root[data-theme="light"] .pagetop .topcall{background:#8E2A18;color:#F3ECE2;}
:root[data-theme="light"] .pagetop .topcall:hover{background:#7A2414;color:#F3ECE2;}
:root[data-theme="light"] .navtoggle span,
:root[data-theme="light"] .navtoggle span::before,
:root[data-theme="light"] .navtoggle span::after{background:var(--bar-ink);}

/* ── hero panel: near white, the orange carried as type ───────────────────── */
:root[data-theme="light"] .hsplit-panel{background:#F7F2EA;}
:root[data-theme="light"] .hsplit-word{color:#8E2A18;}
:root[data-theme="light"] .hsplit-sub{color:#5A4B40;}   /* 7.3:1 on the panel */
:root[data-theme="light"] .hsplit-in .btn{background:#8E2A18;color:#F3ECE2;}
:root[data-theme="light"] .hsplit-in .btn:hover{background:#7A2414;color:#F3ECE2;}

/* ── hero panel back to dark brown, orange kept as the type ──────────────────
   The near-white panel goes, the brown returns. The orange stays as the
   headline colour rather than reverting to cream, so the change is the ground
   and not the whole idea.

   #8E2A18 cannot come with it. On this brown it measures about 1.4:1, which is
   invisible, so the headline takes a lifted orange (#E8734F) that clears 4.5:1
   on the same ground. Same hue, enough luminance to survive the dark panel. */
:root[data-theme="light"] .hsplit-panel{background:var(--brown);}
:root[data-theme="light"] .hsplit-word{color:#E8734F;}
:root[data-theme="light"] .hsplit-sub{color:#CDBFB4;}
:root[data-theme="light"] .hsplit-in .btn{background:#E8734F;color:#2A1B12;}
:root[data-theme="light"] .hsplit-in .btn:hover{background:#F08A6A;color:#2A1B12;}

/* ── the headline takes the logo's own red ───────────────────────────────────
   #FD1F0B, sampled off the mark itself rather than picked by eye. It is the
   median of 25,643 red pixels in the badge, and the top six exact values in
   the artwork all sit within three points of it.

   It measures 3.5:1 on the brown panel. That clears the 3.0 floor for large
   text and is the reason this colour is confined to the headline: at body
   size the same value would fail, so the sub and the CTA label do not use it.
   The CTA instead uses it as a FILL with dark ink on top, which is the way to
   spend a bright saturated colour on a dark ground without a contrast problem. */
:root[data-theme="light"] .hsplit-word{color:#FD1F0B;}
:root[data-theme="light"] .hsplit-in .btn{background:#FD1F0B;color:#2A1006;}
:root[data-theme="light"] .hsplit-in .btn:hover{background:#FF3A22;color:#2A1006;}

/* ── 1. the line above the photo marquee ─────────────────────────────────────
   Not a border on the marquee. It is .band's own bottom rule, which used to
   separate two cream sections and now just floats above a brown band. */
:root[data-theme="light"] .band{border-bottom:0;}

/* ── 3. the three socials on one row ─────────────────────────────────────────
   Facebook was wrapping to a second line because that copy column was capped
   at 340px when the Instagram embeds went in, and three labelled buttons do
   not fit in 340. Icon only, no wrap. Each keeps its aria-label, so the name
   is still there for a screen reader, it just is not printed three times. */
:root[data-theme="light"] .socials-links{flex-wrap:nowrap;gap:12px;}
:root[data-theme="light"] .socials-links .soc{flex:none;padding:0;width:46px;height:46px;
  display:grid;place-items:center;}
:root[data-theme="light"] .socials-links .soc span{
  position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
  clip-path:inset(50%);white-space:nowrap;
}

/* ── 4. the tint, actually visible ───────────────────────────────────────────
   It was applied the whole time at .26, which over a bright midday shopfront
   is close to nothing. .40 is where it reads as a deliberate darkening rather
   than as a photo that happens to be slightly flat. Still neutral, no hue. */
:root[data-theme="light"] .hsplit-shot::after{background:rgba(18,13,11,.40);}

/* ── 5. the text marquee ─────────────────────────────────────────────────────
   Back to #B33520, its original value and the same --red-deep the "Get
   directions" and "Ring an order through" buttons use, so the band and the
   buttons read as one red again. */
:root[data-theme="light"] .tickerstrip{background:#B33520;}

/* ══ Wave section separators ════════════════════════════════════════════════
   Lifted from madie.es, measured 11-09-2026: viewBox 0 0 1440 140, one cubic
   path stretched to full width. The fill is the colour of the section being
   ENTERED, not the one being left.

   Madie draws the path twice, flat colour then a grain pattern over it. Not
   copied, and the reason matters: their whole page ground carries that grain,
   so the wave blends into it. Ours is flat, so a grained wave on a flat band
   does the opposite of what the grain is for and picks the wave out as a
   textured stripe. A borrowed technique needs its precondition borrowed too.

   The fill comes through `currentColor` from an inline `style` naming a theme
   token, so the waves follow the light-mode swap rather than freezing the
   dark palette into the markup. */
.wave{
  display:block;width:100%;height:clamp(34px,4.2vw,62px);
  margin-bottom:-1px;      /* kills the hairline seam at some zoom levels */
  pointer-events:none;
}
/* Leaving a band rather than entering one.

   The path fills BELOW its curve. So an entering wave carries the colour of
   the section being entered and the fill lands at the bottom of the box, and
   a leaving wave has to put the fill at the TOP instead, carrying the colour
   of the band being LEFT.

   scale(-1,-1) does both jobs at once: the vertical flip moves the fill above
   the curve, and the horizontal flip means the two curves are not a plain
   mirror of each other.

   No negative margin. The first version dragged this up over the band, which
   left a sliver of the band's colour showing beneath the curve at every point
   where the wave rode high. That sliver is the broken edge. */
.wave-up{transform:scale(-1,-1);margin-bottom:0;margin-top:-1px;}

/* ══ Hollow heading ════════════════════════════════════════════════════════
   Outline on line one, solid on line two. The contrast between the two is
   what makes it read as deliberate rather than as a broken font.

   Four things decide whether hollow type looks smooth or clogged, and the
   first draft got two of them wrong.

   WEIGHT. A stroke on a 600-weight grotesque fills its own counters, so the
   e and the a close up. The outlined line drops to 400 whilst the solid line
   below keeps the page's normal weight, which also stops the two lines
   looking like the same word twice.

   TRACKING. The stroke sits centred on the glyph edge, so it eats into the
   gap between letters from both sides. Hollow type needs MORE tracking, not
   less. The first pass used -0.8px, inherited from the solid heading, and
   that is most of why it looked iffy.

   STROKE WIDTH scales with the type rather than sitting at a fixed 1.5px, or
   it goes spindly on a laptop and heavy on a large screen.

   PAINT ORDER. Drawing the stroke before the fill keeps the outline crisp
   where glyphs have thin joins.

   Two guards, both required. The @supports block is not optional: without it
   a browser lacking text-stroke renders transparent text and the heading
   vanishes. And it only engages from 900px up, because below that the type is
   too small for a stroke to stay open. */
.hollow-h .hw-out,
.hollow-h .hw-solid{display:block;}
.hollow-h .hw-solid{color:var(--cream);}

@media (min-width:900px){
  .hollow-h .hw-out{
    font-weight:400;
    letter-spacing:.02em;
    -webkit-text-fill-color:transparent;
    /* .018em, not .028em. The stroke sits centred on the glyph edge, so half
       of it turns inward from each side. At the apex of the A the two inner
       edges converge to less than that, and the counter fills into a small
       solid triangle. */
    -webkit-text-stroke:.018em var(--cream);
    paint-order:stroke fill;
  }
  @supports not (-webkit-text-stroke:1px red){
    .hollow-h .hw-out{-webkit-text-fill-color:var(--cream);color:var(--cream);}
  }
}

/* ══ Catering band ═════════════════════════════════════════════════════════
   Catering was `--warm`, a 3% shift off its neighbours in the light theme,
   which is why the waves had nothing to separate there on the first attempt.

   A deep brick rather than the nav's own `--red-deep`. Same family, so the
   page still reads as one palette, but a shade down so a full-height band
   does not compete with the bar sitting above it. Fixed in both themes on
   purpose, so it stays a colour block rather than inverting into a pale
   rectangle in light mode. Cream on it measures 8.4:1. */
:root{ --band:#8E2A18; --on-band:#F6EFE6; }
.warmband{background:var(--band);color:var(--on-band);}
.warmband h2,
.warmband .split-txt p{color:var(--on-band);}
.warmband .split-txt p{opacity:.88;}
.warmband .btn{background:var(--on-band);border-color:var(--on-band);color:var(--band);}
.warmband .btn:hover{background:#fff;border-color:#fff;color:var(--band);}
