/* ============================================================
   TASTE NAI — production stylesheet
   Ported from the T10b design comp (docs/design/taste-nai-landing.html).
   Mobile-first: base styles target 360–390px. min-width queries only.
   ============================================================ */

/* ---------- brand fonts ----------
   Self-hosted, licensed webfont use confirmed by the operator (no CDN request — closes
   the last external dependency the design comp still had). Single static weight each. */
/* woff2 first — the .otf/.ttf originals are 2-3x larger and were measurably slowing
   first paint. Legacy formats retained only as a fallback for very old browsers. */
@font-face{
  font-family:"Adoha";
  src:url("../fonts/Adoha.woff2") format("woff2"),
      url("../fonts/Adoha.otf") format("opentype");
  font-weight:400;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Baskerville Old Face";
  src:url("../fonts/BaskervilleOldFace.woff2") format("woff2"),
      url("../fonts/BaskervilleOldFace.ttf") format("truetype");
  font-weight:400;font-style:normal;font-display:swap;
}
/* Instrument Sans (OFL) — vendored locally rather than loaded from Google's CDN.
   Removes the last external request: no visitor IP/UA disclosed to a third party,
   and the CSP can stay at 'self'. Variable weight 400-700, latin subset. */
@font-face{
  font-family:"Instrument Sans";
  src:url("../fonts/InstrumentSans-normal.woff2") format("woff2");
  font-weight:400 700;font-style:normal;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:"Instrument Sans";
  src:url("../fonts/InstrumentSans-italic.woff2") format("woff2");
  font-weight:400 600;font-style:italic;font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* ---------- tokens ----------
   The brand is maroon, black and white — read straight from the authored vectors
   (taste-nai-restaurant-logo.svg fills #9e1b1f and #1e1e1e; the -white variant #fff).

   An earlier build made #F5E9D3 the dominant surface. That value came from
   "Main background - FC.svg", which is a background swatch, not a brand colour —
   promoting it to the primary surface gave the whole site a warm-cream cast that
   belongs to no part of this identity. White is the page. */
:root{
  /* brand — as authored in the supplied SVGs, do not alter */
  --maroon:#9E1B1F;
  --black:#1E1E1E;
  --white:#FFFFFF;

  /* derived */
  --maroon-deep:#7E1519;
  /* Maroon on black measures 2.09:1 — unusable. This is the same hue lifted until it
     clears AA on the dark bands, so the accent survives there instead of defaulting
     to white everywhere. */
  --maroon-light:#E8827F;
  --surface:#FAFAFA;        /* raised card on a white page */
  --surface-alt:#F4F4F4;    /* one recessed band, to break a run of white */
  /* .72 keeps secondary text clear of WCAG AA on white; .62 measured below 4.5:1. */
  --black-soft:rgba(30,30,30,.72);
  --black-faint:rgba(30,30,30,.13);
  --white-soft:rgba(255,255,255,.72);
  --white-faint:rgba(255,255,255,.18);

  /* Weight scale. Small uppercase labels get their identity from letterspacing and
     colour, not mass — at 600/700 they read chunky, and light-on-dark blooms heavier
     still. 500 keeps them legible without shouting. Only buttons hold 600, where the
     weight is doing real work as a call to action. */
  --fw-label:500;
  --fw-strong:500;
  --fw-cta:600;

  /* type — Adoha & Baskerville Old Face are the confirmed-licensed brand fonts;
     Instrument Sans (OFL) stays as body copy for tested small-size legibility (S5). */
  --display:"Adoha",Georgia,"Times New Roman",serif;
  --display-alt:"Baskerville Old Face",Georgia,"Times New Roman",serif;
  --body:"Instrument Sans","Segoe UI",system-ui,sans-serif;

  /* fluid scale — every clamp mixes rem + vw so 200% zoom still scales (WCAG 1.4.4) */
  /* Floor raised from .625rem (10px) to .75rem (12px). Letterspaced uppercase at 10px
     is an editorial habit that reads as fine print on a phone; 12px is the usual
     comfortable minimum and costs nothing here. */
  --fs-eyebrow:clamp(.75rem,.7rem + .18vw,.8125rem);
  --fs-body:clamp(.9375rem,.88rem + .22vw,1.0625rem);
  --fs-lead:clamp(1.0625rem,.95rem + .5vw,1.3125rem);
  --fs-h3:clamp(1.125rem,1rem + .55vw,1.5rem);
  --fs-h2:clamp(1.75rem,1.25rem + 2.4vw,3.25rem);
  --fs-h1:clamp(2.375rem,1.55rem + 4.1vw,5.75rem);

  /* Icon scale. Sizes were previously set ad-hoc per component (26px here, 24px
     there, 18px inline), so nothing related to anything else and the feature icons
     read undersized next to display type. Three fluid steps, tied to the same rhythm
     as the type scale. */
  --icon-sm:1.05em;                              /* inline, follows its label */
  --icon-md:clamp(28px,1.6rem + .9vw,38px);      /* card features */
  --icon-lg:clamp(38px,2.2rem + 1.8vw,56px);     /* section features */

  /* rhythm */
  --nav-h:56px;
  --pad-x:clamp(1.25rem,.9rem + 2vw,4.5rem);
  --section-y:clamp(4.5rem,3rem + 7vw,9.5rem);
  --measure:34rem;
}
@media (min-width:900px){ :root{ --nav-h:72px; } }

*,*::before,*::after{box-sizing:border-box}
*{margin:0}
html{-webkit-text-size-adjust:100%;color-scheme:light}
body{
  font-family:var(--body);
  font-size:var(--fs-body);
  line-height:1.65;
  color:var(--black);
  background:var(--white);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{max-width:100%;display:block;height:auto}
/* The UA stylesheet's padding-inline-start (~40px) survives `list-style:none`.
   Left in place it indented every list and, on .care (whose 1px grid gaps reveal
   the container background as hairlines), painted that padding strip as a grey slab. */
ul,ol{list-style:none;padding:0}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
a{color:inherit;text-decoration:none}
:focus-visible{outline:2px solid var(--maroon);outline-offset:3px;border-radius:2px}
.on-dark :focus-visible{outline-color:var(--white)}

/* paper grain — the "printed brand book" texture */
body::after{
  content:"";position:fixed;inset:0;z-index:60;pointer-events:none;opacity:.035;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- surface texture ----------
   The ruled/woven "paper" gradients are gone. They existed to sell a cream printed
   page; on white they add nothing but a grid that reads as decoration. White is left
   as white — the black bands carry the rhythm instead.

   One radial lift survives, on the dark bands only, so a full-width black block does
   not read as a flat slab. */
:root{
  --dark-lift:radial-gradient(130% 80% at 50% 0%,rgba(255,255,255,.06),transparent 64%);
}

/* ---------- primitives ---------- */
.shell{width:100%;max-width:78rem;margin-inline:auto;padding-inline:var(--pad-x)}
.section{padding-block:var(--section-y);scroll-margin-top:var(--nav-h);position:relative}
.on-light{background:var(--white);color:var(--black)}
.on-dark{background:var(--black);color:var(--white)}
.on-dark::before{
  content:"";position:absolute;inset:0;pointer-events:none;background:var(--dark-lift);
}
.on-dark a{color:var(--white)}
/* keep content above the lift layer */
.section > .shell{position:relative;z-index:1}

/* Hairline between consecutive light bands so they don't merge into one field. */
.on-light + .on-light{box-shadow:inset 0 1px 0 var(--black-faint)}

/* ---------- icons ----------
   Gravity UI (MIT), inlined so they inherit currentColor. Sized in em so they scale
   with their label rather than needing a size per context. */
.icon{
  display:inline-block;vertical-align:-.18em;flex:none;
  width:var(--icon-sm);height:var(--icon-sm);
}
/* Labels are letterspaced small-caps; the icon needs its own gap and a little
   optical lift to sit on the cap-height rather than the baseline. */
/* Maroon is the accent on light ground only. On the dark bands it measures 2.09:1
   against black — below even the 3:1 minimum for non-text (WCAG 1.4.11), so icons
   there take white. This was already wrong before the palette correction: the old
   crimson-on-ink pairing was ~1.4:1. */
.visit__block dt .icon{width:.95em;height:.95em;margin-right:.5rem;color:var(--maroon);vertical-align:-.08em}
.contact__item .icon{color:var(--maroon)}
.care li .icon{color:var(--maroon);margin-top:.28em}
.on-dark .icon,
.on-dark .visit__block dt .icon,
.on-dark .contact__item .icon,
.on-dark .care li .icon{color:var(--maroon-light)}
.on-dark .story__note dt .icon{color:var(--maroon-light)}
.btn .icon{width:1.05em;height:1.05em}
.nav__burger .icon,.drawer__close .icon{width:22px;height:22px}
.visit__map .icon{margin-left:.35rem;transition:transform .3s ease}
@media (hover:hover) and (pointer:fine){
  .visit__map:hover .icon{transform:translateX(3px)}
}
/* Icon leads the card: beside the copy when the cards are wide, above it once they
   stack four across. */
.promise__icon{width:var(--icon-md);height:var(--icon-md);flex:none;color:var(--maroon)}
.tier__icon{display:block;width:var(--icon-md);height:var(--icon-md);color:var(--maroon);margin-bottom:1rem}
.tier--hero .tier__icon{color:var(--white)}
.story__note dt .icon{color:var(--white);margin-right:.45rem}
/* Sits beside a large display heading, so it takes the large step — at the old 26px
   it read as a stray mark next to 44px type. */
.soon__icon{width:var(--icon-lg);height:var(--icon-lg);color:var(--maroon);margin-bottom:1.4rem}

/* ---------- icons draw themselves ----------
   The set is stroke-based, so each shape can be drawn rather than faded in — the
   feeling of a chalkboard menu being written up. pathLength="1" on every shape (set
   in the icon component) means one dashoffset value draws any icon at the same rate,
   whatever its real geometry.

   Only the feature icons draw. The small inline ones beside contact rows and nav
   items would read as fussy, and they appear above the fold where nothing should be
   animating on arrival. */
@keyframes icon-draw{ from{ stroke-dashoffset:1 } to{ stroke-dashoffset:0 } }

@media (prefers-reduced-motion: no-preference){
  .has-anim .icon--draw :is(path,circle,line,polyline,polygon,rect,ellipse){
    stroke-dasharray:1;stroke-dashoffset:1;
  }
  .icon--draw.is-drawn :is(path,circle,line,polyline,polygon,rect,ellipse){
    animation:icon-draw .75s cubic-bezier(.4,.1,.2,1) forwards;
  }
  /* Each stroke follows the last, so the icon assembles rather than appearing whole. */
  .icon--draw.is-drawn :nth-child(2){ animation-delay:.13s }
  .icon--draw.is-drawn :nth-child(3){ animation-delay:.26s }
  .icon--draw.is-drawn :nth-child(4){ animation-delay:.39s }
  .icon--draw.is-drawn :nth-child(5){ animation-delay:.52s }
  .icon--draw.is-drawn :nth-child(n+6){ animation-delay:.62s }

  /* A little character on hover, per icon's own idea. */
  @media (hover:hover) and (pointer:fine){
    .promise:hover .promise__icon{ animation:bell-tip .6s ease }
    .tier:hover .tier__icon{ animation:lift-nudge .5s ease }
    .btn:hover .icon{ transform:translateY(-1px) }
  }
}
.btn .icon{transition:transform .25s ease}

/* The concierge bell tips as if struck; everything else borrows the same easing. */
@keyframes bell-tip{
  0%,100%{ transform:rotate(0) }
  25%{ transform:rotate(-11deg) }
  55%{ transform:rotate(7deg) }
  80%{ transform:rotate(-3deg) }
}
@keyframes lift-nudge{
  0%,100%{ transform:translateY(0) }
  45%{ transform:translateY(-4px) }
}

/* ---------- the tower motif ----------
   The KICC's stacked rings, abstracted: tapered bars that read as the tower in
   silhouette. Used as a section accent so the brand idea recurs beyond the logo. */
.tower{
  width:26px;height:18px;flex:none;
  background:repeating-linear-gradient(180deg,var(--maroon) 0 2px,transparent 2px 5px);
  clip-path:polygon(50% 0,100% 100%,0 100%);
}
.tower--light{background:repeating-linear-gradient(180deg,var(--white) 0 2px,transparent 2px 5px)}

/* ---------- typography ---------- */
h1,h2,h3{font-family:var(--display);font-weight:400;line-height:1.06;letter-spacing:-.015em}
h2{font-size:var(--fs-h2);max-width:22ch}
h3{font-size:var(--fs-h3);line-height:1.2;letter-spacing:-.01em}
/* .lead always follows a heading, so the top margin belongs here rather than as an
   inline style — CSP style-src 'self' blocks inline style attributes. */
.lead{font-size:var(--fs-lead);line-height:1.6;max-width:var(--measure);color:var(--black-soft);margin-top:1.5rem}
.on-dark .lead{color:var(--white-soft)}
.rule{height:1px;background:var(--black-faint);border:0}
.on-dark .rule{background:var(--white-faint)}

/* buttons — primary 48px min (Material, Android-dominant audience); secondary 44px (HIG/WCAG AAA) */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  min-height:48px;padding:.75rem 1.5rem;
  font-size:.875rem;font-weight:var(--fw-cta);letter-spacing:.06em;text-transform:uppercase;
  border:1px solid var(--maroon);background:var(--maroon);color:var(--white);
  border-radius:2px;transition:background .25s ease,color .25s ease,transform .25s ease;
  -webkit-tap-highlight-color:transparent;
}
.btn--ghost{background:transparent;color:var(--maroon)}
/* On dark grounds (ink sections and the hero scrim) the crimson-on-dark ghost button
   would sit near 1.4:1 — border and label both go cream. */
.on-dark .btn--ghost,.hero .btn--ghost{border-color:var(--white-soft);color:var(--white)}
.btn--sm{min-height:44px;padding:.55rem 1.1rem;font-size:.75rem}
.btn:active{transform:translateY(1px)}
@media (hover:hover) and (pointer:fine){
  .btn:hover{background:var(--maroon-deep);border-color:var(--maroon-deep);color:var(--white)}
  .btn--ghost:hover{background:var(--maroon);color:var(--white);border-color:var(--maroon)}
  .on-dark .btn--ghost:hover,.hero .btn--ghost:hover{background:var(--white);color:var(--black);border-color:var(--white)}
}

.skip{
  position:absolute;left:.75rem;top:-4rem;z-index:70;
  background:var(--black);color:var(--white);padding:.75rem 1.25rem;border-radius:2px;
  font-size:.8125rem;font-weight:var(--fw-label);transition:top .2s ease;
}
.skip:focus{top:.75rem}

/* ---------- nav ---------- */
/* Transparent over the hero, solid once the page moves. Content colour flips with it:
   cream on the photograph, ink/crimson on the cream bar. */
.nav{
  position:sticky;top:0;z-index:50;height:var(--nav-h);
  background:transparent;
  border-bottom:1px solid transparent;
  color:var(--white);
  transition:background .35s ease,border-color .35s ease,box-shadow .35s ease,color .35s ease;
}
/* Legibility scrim behind the transparent bar. Confined to the nav's own box it read
   as a grey band with a hard edge — the opposite of transparent. It now extends well
   past the bar and fades to nothing, so it dissolves into the photograph rather than
   bounding it. Needed because the hero's own gradient is lightest exactly here. */
.nav::after{
  content:"";position:absolute;left:0;right:0;top:0;height:260%;
  z-index:-1;pointer-events:none;
  background:linear-gradient(180deg,
    rgba(30,30,30,.50) 0%,
    rgba(30,30,30,.34) 38%,
    rgba(30,30,30,.14) 68%,
    transparent 100%);
  transition:opacity .35s ease;
}
.nav.is-scrolled{
  background:rgba(255,255,255,.94);backdrop-filter:blur(12px);
  border-bottom-color:var(--black-faint);
  box-shadow:0 1px 24px rgba(30,30,30,.10);
  color:var(--black);
}
.nav.is-scrolled::after{opacity:0}

/* Mark takes the bar's colour via mask — cream over the hero, crimson when solid. */
.nav__mark{
  display:block;width:34px;height:34px;background:currentColor;
  -webkit-mask:url("../img/favicon.svg") center/contain no-repeat;
  mask:url("../img/favicon.svg") center/contain no-repeat;
  transition:background .35s ease;
}
.nav.is-scrolled .nav__mark{background:var(--maroon)}
@media (min-width:900px){ .nav__mark{width:40px;height:40px} }
.nav__in{height:100%;display:flex;align-items:center;gap:1rem}
.nav__brand{display:flex;align-items:center;margin-right:auto;min-height:44px}
.nav__links{display:none}
.nav__burger{
  display:grid;place-items:center;width:48px;height:48px;
  -webkit-tap-highlight-color:transparent;
}
/* The hand-drawn CSS bars are replaced by the icon, which inherits currentColor and
   so still flips with the bar — and matches the stroke weight of the rest of the set. */
.nav .btn{display:none}
@media (min-width:900px){
  .nav__burger{display:none}
  .nav__links{display:flex;gap:2rem;margin-right:2rem}
  .nav__links a{
    font-size:.8125rem;font-weight:500;letter-spacing:.11em;text-transform:uppercase;
    min-height:44px;display:inline-flex;align-items:center;position:relative;
  }
  .nav__links a::after{
    content:"";position:absolute;left:0;right:100%;bottom:14px;height:1px;
    background:var(--maroon);transition:right .3s ease;
  }
  @media (hover:hover) and (pointer:fine){ .nav__links a:hover::after{right:0} }
  .nav .btn{display:inline-flex}
}

/* drawer */
.drawer{
  position:fixed;inset:0;z-index:55;display:flex;flex-direction:column;
  background:var(--black);color:var(--white);
  padding:1.25rem var(--pad-x) calc(2rem + env(safe-area-inset-bottom));
  overscroll-behavior:contain;overflow-y:auto;
  transform:translateY(-100%);transition:transform .42s cubic-bezier(.7,0,.2,1);
  visibility:hidden;
}
.drawer[data-open="true"]{transform:translateY(0);visibility:visible}
.drawer__top{display:flex;align-items:center;justify-content:space-between;margin-bottom:2.5rem}
.drawer__top img{width:96px;height:auto}
/* No negative margin: it pushed the close button past the drawer's padding edge and
   out beyond the viewport (measured 777px against a 768px tablet viewport). */
.drawer__close{width:48px;height:48px;display:grid;place-items:center;font-size:1.75rem;line-height:1}
.drawer__links{display:flex;flex-direction:column}
.drawer__links a{
  font-family:var(--display);font-size:2rem;padding:.7rem 0;
  border-bottom:1px solid var(--white-faint);min-height:48px;display:flex;align-items:center;
}
.drawer .btn{margin-top:2.5rem;width:100%}

/* ---------- hero ---------- */
.hero{
  /* Pulled up under the sticky nav. Sticky elements occupy normal flow, so without
     this the hero began *below* the bar and a "transparent" nav simply showed the
     cream page background — a flat band, not the photograph. The negative margin
     puts the image behind the bar; the padding keeps the content clear of it. */
  min-height:100svh;
  margin-top:calc(var(--nav-h) * -1);
  padding-top:calc(var(--nav-h) + clamp(1.75rem,1rem + 3vw,5rem));
  padding-bottom:clamp(1.75rem,1rem + 3vw,5rem);
  display:grid;place-items:center;text-align:center;
  position:relative;isolation:isolate;
  background:var(--black);color:var(--white);
  /* The background is scaled 1.08 to give the parallax headroom; without clipping
     here that overhang widens the document and produces horizontal scroll at every
     breakpoint (measured: 1331px against a 1280px viewport). */
  overflow:hidden;
}
.hero__bg{
  position:absolute;inset:0;z-index:-2;
  width:100%;height:100%;object-fit:cover;
  transform:scale(1.08);          /* headroom so the parallax drift never shows an edge */
  will-change:transform;
}
/* Scrim. Worst case (a blown-out white area behind the text) the .78 band still puts
   cream on ~#585352 — comfortably past AA for the sub copy. Slightly lighter at the
   top so the photograph still reads under the nav. */
.hero::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(180deg,rgba(30,30,30,.60) 0%,rgba(30,30,30,.82) 50%,rgba(30,30,30,.90) 100%);
}
.hero__in{display:flex;flex-direction:column;align-items:center;width:100%}
/* Crimson on the dark scrim would fall well below AA — the eyebrow goes cream. */
.hero__cat{
  font-size:var(--fs-eyebrow);font-weight:var(--fw-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--white);margin-block:0 .9rem;
}
.hero h1{font-size:var(--fs-h1);max-width:16ch;color:var(--white)}
.hero h1 em{font-family:var(--display-alt);font-style:italic;font-weight:400}
/* Solid white, not the softened variant: over a photo the softened value measured 3.19:1,
   under AA for normal text. Solid cream on the same scrim is 6.4:1+. */
.hero__sub{
  font-size:var(--fs-body);color:var(--white);max-width:34ch;
  margin-block:1.1rem 1.9rem;line-height:1.55;
}
/* Equal-width buttons: each takes the same share of the row regardless of how long
   its label is, so the group reads as one control rather than three odd shapes. */
.hero__cta{
  display:grid;gap:.7rem;width:100%;max-width:22rem;
  grid-template-columns:1fr;
}
.hero__cta .btn{width:100%;padding-inline:1rem}
.hero__scroll{
  position:absolute;bottom:1rem;left:50%;translate:-50% 0;
  font-size:.75rem;font-weight:var(--fw-label);letter-spacing:.28em;text-transform:uppercase;
  color:var(--white-soft);display:none;
  transition:opacity .3s ease,transform .3s ease;
}
/* Dismissed on the first scroll — it has served its purpose and would otherwise sit
   over the section below. pointer-events go too, so it can't intercept a click. */
.hero__scroll.is-gone{opacity:0;transform:translateY(8px);pointer-events:none}
@media (min-width:640px){
  /* Three equal columns — 1fr each, so the widest label sets the height and every
     button matches it. */
  .hero__cta{grid-template-columns:repeat(3,1fr);max-width:44rem;gap:.85rem}
}
@media (min-width:900px){
  .hero__cta{gap:1rem}
  .hero__scroll{display:block}
}

/* ---------- story / the mark ---------- */
.story__grid{display:grid;gap:clamp(2.5rem,1.5rem + 4vw,5rem)}
.story__shot{margin:0}
.story__img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:2px}
.story__shot figcaption,.wide figcaption,.soon__shot figcaption{
  font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--white-soft);margin-top:.9rem;
}
.soon__shot figcaption{color:var(--black-soft)}

/* Caption wrapper emitted by partials/figure.blade.php for images whose section does not
   already supply its own <figure>. margin:0 matters: the UA default on figure is
   1em 40px, which would inset every captioned image inside its grid cell. Colour follows
   the section's own on-dark/on-light convention rather than being set per section. */
.fig{margin:0}
.fig figcaption{
  font-size:.75rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--black-soft);margin-top:.9rem;
}
.on-dark .fig figcaption{color:var(--white-soft)}
/* Monogram watermark — the SVG used as a mask, so it takes any colour without a
   recoloured copy of the brand asset existing. Sits behind the notes as texture. */
.story__watermark{
  position:absolute;right:-2%;top:8%;
  width:clamp(180px,34vw,340px);aspect-ratio:1;
  background:var(--white);opacity:.05;pointer-events:none;z-index:0;
  -webkit-mask:url("../img/favicon.svg") center/contain no-repeat;
  mask:url("../img/favicon.svg") center/contain no-repeat;
}
.story__notes{display:grid;gap:1.75rem;margin-top:2.5rem;position:relative;z-index:1}
.story__note{padding-top:1.1rem;border-top:1px solid var(--white-faint)}
.story__note dt{
  font-size:var(--fs-eyebrow);font-weight:var(--fw-label);letter-spacing:.2em;text-transform:uppercase;
  margin-bottom:.5rem;
}
.story__note dd{color:var(--white-soft);font-size:.9375rem;line-height:1.6}
.story__karibu{
  font-family:var(--display-alt);font-style:italic;font-size:var(--fs-lead);
  color:var(--white);margin-top:2rem;
}
@media (min-width:900px){
  .story__grid{grid-template-columns:1fr 1fr;align-items:center}
  .story__notes{grid-template-columns:1fr 1fr;gap:2rem}
}

/* ---------- promises ---------- */
.promises__list{display:grid;gap:1px;background:var(--black-faint);margin-top:3rem}
.promise{
  background:var(--white);padding:2rem 1.5rem;display:flex;gap:1.25rem;align-items:flex-start;
  position:relative;transition:background .4s ease;
}
/* Border that draws itself on hover: ::before traces top-then-left from the corner,
   ::after traces bottom-then-right, and the two meet. Each edge animates width first,
   then height on a delay, so it reads as a single continuous stroke rather than four
   lines fading in at once. */
.promise::before,
.promise::after{
  content:"";position:absolute;width:0;height:0;pointer-events:none;
  border-color:var(--maroon);border-style:solid;border-width:0;
}
.promise::before{top:0;left:0;border-top-width:1px;border-left-width:1px}
.promise::after{bottom:0;right:0;border-bottom-width:1px;border-right-width:1px}
@media (hover:hover) and (pointer:fine){
  .promise::before,
  .promise::after{
    transition:width .3s cubic-bezier(.4,0,.2,1),height .3s cubic-bezier(.4,0,.2,1) .3s;
  }
  .promise:hover{background:var(--surface)}
  .promise:hover::before,
  .promise:hover::after{
    width:100%;height:100%;
    transition:width .3s cubic-bezier(.4,0,.2,1),height .3s cubic-bezier(.4,0,.2,1) .3s;
  }
}
/* The 01-04 numerals are gone: they were the cards' only visual anchor before the
   icons existed, and kept afterwards they put two markers in one slot and implied a
   sequence these four parallel commitments do not have. The count is already stated
   in the lead copy. */
.promise p{color:var(--black-soft);font-size:.9375rem;margin-top:.5rem;max-width:40ch}
/* Padding stays symmetric at every breakpoint so the hover border sits an equal
   distance from the text on all four sides. */
@media (min-width:700px){
  .promises__list{grid-template-columns:1fr 1fr}
  .promise{padding:2.25rem 1.75rem}
}
@media (min-width:1100px){
  .promises__list{grid-template-columns:repeat(4,1fr)}
  .promise{flex-direction:column;gap:1rem;padding:2.25rem 1.5rem}
}

/* ---------- contextual imagery ----------
   No gallery: every image sits beside the copy it illustrates. Three primitives. */

/* full-bleed band — breaks the shell, establishes place */
.band{position:relative;margin:0}
.band__img{width:100vw;aspect-ratio:16/9;object-fit:cover;display:block}
.band__cap{
  position:absolute;left:0;bottom:0;
  padding:.6rem var(--pad-x);
  font-size:.75rem;font-weight:var(--fw-label);letter-spacing:.22em;text-transform:uppercase;
  color:var(--white);background:rgba(30,30,30,.72);
}
@media (min-width:900px){ .band__img{aspect-ratio:21/9} }

/* two-up pair */
.duo{display:grid;gap:1rem;margin-top:3rem}
.duo__img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:2px}
@media (min-width:900px){ .duo{grid-template-columns:1fr 1fr;gap:1.25rem} }

/* single wide feature */
.wide{margin:3rem 0 0}
.wide__img{width:100%;object-fit:cover;border-radius:2px;aspect-ratio:21/9}
.wide figcaption{color:var(--black-soft)}

/* ---------- delivery ---------- */
.delivery__grid{display:grid;gap:clamp(2.5rem,1.5rem + 4vw,4.5rem)}
.care{display:grid;gap:1px;background:var(--white-faint);margin-top:2.5rem}
.care li{background:var(--black);padding:1rem 0;display:flex;gap:.85rem;align-items:flex-start;list-style:none}
.care li span{color:var(--white-soft);font-size:.9375rem}
.platforms{margin-top:2.5rem;padding-top:1.5rem;border-top:1px solid var(--white-faint)}
.platforms p{
  font-size:.75rem;font-weight:var(--fw-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--white-soft);margin-bottom:.9rem;
}
.platforms ul{display:flex;flex-wrap:wrap;gap:.5rem 1.75rem;list-style:none}
.platforms li{font-family:var(--display);font-size:1.125rem}
.delivery__shots{display:grid;gap:1rem}
.delivery__img{aspect-ratio:4/3;object-fit:cover;width:100%;border-radius:2px}
@media (min-width:900px){
  .delivery__grid{grid-template-columns:1fr .85fr;align-items:center}
  .delivery__shots{gap:1.25rem}
  /* Scoped to .delivery__shots, the actual parent of the images — .delivery__grid's
     first child is the copy column. See the .duo note below for why this is keyed off
     the wrapper rather than the image. */
  .delivery__shots > :first-child .delivery__img,
  .delivery__shots > .delivery__img:first-child{aspect-ratio:4/5}
}

/* ---------- loyalty ---------- */
.tiers{display:grid;gap:1rem;margin-top:3rem}
/* On a white page a card is defined by its edge, not by a tint — a filled panel on
   white just looks like a lighter patch. Border does the work; the fill is barely
   off-white to keep the edge from floating. */
.tier{
  border:1px solid var(--black-faint);border-radius:2px;padding:2rem 1.75rem;
  background:var(--surface);display:flex;flex-direction:column;
  transition:border-color .35s ease,box-shadow .35s ease;
}
@media (hover:hover) and (pointer:fine){
  .tier:hover{border-color:var(--maroon);box-shadow:0 2px 20px rgba(30,30,30,.07)}
  .tier--hero:hover{border-color:var(--maroon-deep);box-shadow:0 4px 26px rgba(158,27,31,.22)}
}
.tier__name{font-family:var(--display);font-size:1.5rem;margin-bottom:.35rem}
.tier__q{
  font-size:.75rem;font-weight:var(--fw-label);letter-spacing:.18em;text-transform:uppercase;
  color:var(--maroon);margin-bottom:1.25rem;
}
.tier ul{list-style:none;display:grid;gap:.6rem;margin-top:auto}
.tier li{font-size:.9375rem;color:var(--black-soft);padding-left:1rem;position:relative}
.tier li::before{content:"—";position:absolute;left:0;color:var(--maroon)}
/* The hero tier is a solid maroon panel, so its own accents must invert. */
.tier--hero .tier__q{color:rgba(255,255,255,.82)}
.tier--hero{background:var(--maroon);border-color:var(--maroon);color:var(--white)}
.tier--hero .tier__q{color:rgba(255,255,255,.82)}
.tier--hero li{color:rgba(255,255,255,.9)}
.tier--hero li::before{color:var(--white)}
@media (min-width:900px){
  .tiers{grid-template-columns:repeat(3,1fr);gap:1.25rem}
  .tier--hero{translate:0 -1.5rem;padding-block:2.75rem}
}

/* ---------- menu soon ----------
   The one recessed band: a near-white grey so this section reads as a pause between
   the two black bands either side of it, without introducing a fourth colour. */
.soon{background:var(--surface-alt)}
.soon__grid{display:grid;gap:clamp(2.5rem,1.5rem + 4vw,4.5rem);align-items:center}
.soon__copy{display:flex;flex-direction:column;align-items:flex-start}
.soon h2{
  font-family:var(--display-alt);font-style:italic;font-weight:400;max-width:24ch;
  font-size:clamp(1.625rem,1.2rem + 2vw,2.75rem);
}
.soon p{color:var(--black-soft);max-width:34ch;margin-top:1.25rem}
.soon__rule{margin-bottom:2rem}
.soon__shot{margin:0}
.soon__img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:2px}
@media (min-width:900px){ .soon__grid{grid-template-columns:1fr 1fr} }

/* ---------- visit / footer ---------- */
.visit__grid{display:grid;gap:2.5rem;margin-top:3rem}
.visit__block dt{
  font-size:var(--fs-eyebrow);font-weight:var(--fw-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--white);margin-bottom:.9rem;padding-bottom:.9rem;border-bottom:1px solid var(--white-faint);
}
.visit__block dd{color:var(--white-soft);font-size:.9375rem;line-height:1.8}
.visit__block dd b{color:var(--white);font-weight:var(--fw-strong)}
.visit__map{
  display:inline-flex;align-items:center;min-height:44px;margin-top:.35rem;
  color:var(--white);text-decoration:underline;text-underline-offset:.28em;
  text-decoration-color:var(--white-faint);
}
@media (hover:hover) and (pointer:fine){
  .visit__map:hover{color:var(--white);text-decoration-color:var(--white)}
}

/* Contact details read as one line, not a stacked list — they're a single way of
   reaching us, three routes. Wraps to multiple rows on narrow screens; the middot
   separators are drawn between items and suppressed at the start of each row. */
.contact{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem 1.15rem}
.contact__item{
  display:inline-flex;align-items:center;gap:.45rem;
  min-height:44px;color:var(--white);position:relative;
  text-decoration:underline;text-underline-offset:.28em;text-decoration-color:var(--white-faint);
}
.contact__item + .contact__item::before{
  content:"·";position:absolute;left:-.65rem;
  color:var(--white-faint);text-decoration:none;
}
.contact__item--wa{text-decoration:none}
.contact__item svg{flex:none}
@media (hover:hover) and (pointer:fine){
  .contact__item:hover{color:var(--white);text-decoration-color:var(--white)}
}
/* auto-fit with 1fr tracks: blocks are omission-tolerant, so this has to look right
   with 1, 2 or 3 present — and they stretch to fill the band rather than huddling
   at the left edge leaving dead space. */
@media (min-width:700px){
  .visit__grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,13rem),1fr));gap:2.5rem 3rem}
}
/* Opening hours read as a small table: days left, time right, aligned down the column. */
.hours__row{
  display:flex;justify-content:space-between;align-items:baseline;gap:1rem;
  padding-block:.15rem;max-width:20rem;
}

/* Tighter than a full section rhythm — the footer belongs to the Visit block above
   it, and a --section-y gap left a dead band between them. */
.foot{
  border-top:1px solid var(--white-faint);
  margin-top:clamp(3rem,2rem + 3vw,5rem);padding-top:2.5rem;
}
.foot__in{display:flex;flex-direction:column;gap:1.25rem;padding-inline:0;max-width:none}
/* The footer lockup is a sign-off, not a second logo moment — the hero already
   carries the mark at full size. 132px competed with it. */
.foot__mark img{width:84px;height:auto}
.foot__meta{font-size:.75rem;line-height:1.9;color:var(--white-soft)}
@media (min-width:700px){
  .foot__in{flex-direction:row;justify-content:space-between;align-items:end}
  .foot__meta{text-align:right}
}

/* ---------- 404 ----------
   Full-height dark page: it should read as part of the brand rather than a server
   default, and give the visitor somewhere to go rather than just an apology. */
/* Fills the viewport exactly and centres on it. No negative nav offset here: unlike
   the home page, this layout renders no nav, so pulling up by --nav-h left a band of
   page background below the fold. Symmetric padding is what makes the centring true. */
.notfound{
  min-height:100svh;
  display:grid;place-items:center;text-align:center;
  background:var(--black);color:var(--white);
  padding-block:3rem;position:relative;
}

/* Overscroll past either end would otherwise flash the white page background. */
body:has(.notfound){background:var(--black)}
.notfound::before{
  content:"";position:absolute;inset:0;pointer-events:none;background:var(--dark-lift);
}
.notfound__in{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center}
.notfound__logo{width:clamp(84px,16vw,120px);height:auto;margin-bottom:2rem}
.notfound__code{
  font-size:var(--fs-eyebrow);font-weight:var(--fw-label);
  letter-spacing:.28em;text-transform:uppercase;color:var(--maroon-light);
  margin-bottom:1rem;
}
.notfound h1{font-size:var(--fs-h1);max-width:14ch}
.notfound__lead{
  font-size:var(--fs-lead);color:var(--white-soft);max-width:46ch;
  margin-block:1.25rem 2.25rem;line-height:1.6;
}
.notfound__cta{display:grid;gap:.85rem;width:100%;max-width:22rem}
@media (min-width:640px){
  .notfound__cta{grid-template-columns:1fr 1fr;max-width:34rem}
}
.notfound__cta .btn{width:100%}
.notfound__cta .btn--ghost{border-color:var(--white-soft);color:var(--white)}
@media (hover:hover) and (pointer:fine){
  .notfound__cta .btn--ghost:hover{background:var(--white);color:var(--black);border-color:var(--white)}
}
.notfound__contact{
  display:flex;flex-wrap:wrap;justify-content:center;gap:.4rem 1.5rem;
  margin-top:2.5rem;padding-top:1.75rem;border-top:1px solid var(--white-faint);
  width:100%;max-width:34rem;
}

/* ---------- back to top ----------
   Hidden until the page has actually been scrolled, so it never covers hero content.
   Sits inside the safe-area inset for notched phones. */
.totop{
  position:fixed;z-index:55;
  right:calc(1rem + env(safe-area-inset-right));
  bottom:calc(1rem + env(safe-area-inset-bottom));
  width:48px;height:48px;display:grid;place-items:center;
  background:var(--maroon);color:var(--white);border-radius:50%;
  box-shadow:0 4px 18px rgba(30,30,30,.28);
  opacity:0;visibility:hidden;transform:translateY(10px);
  transition:opacity .3s ease,transform .3s ease,visibility .3s,background .25s ease;
  -webkit-tap-highlight-color:transparent;
}
.totop.is-shown{opacity:1;visibility:visible;transform:translateY(0)}
@media (hover:hover) and (pointer:fine){
  .totop:hover{background:var(--maroon-deep)}
  .totop:hover .icon{transform:translateY(-2px)}
}
.totop .icon{transition:transform .25s ease}

/* ---------- reveals ----------
   Gated on .has-js: without JavaScript (or if the observer never runs) content
   stays visible rather than stranded at opacity:0. Progressive enhancement. */
.has-js .rv{opacity:0;translate:0 18px;transition:opacity .7s ease,translate .7s cubic-bezier(.2,0,.2,1)}
.has-js .rv.in{opacity:1;translate:0 0}

/* Staggered children — CSS only, driven by nth-child off the parent's .in state.
   No JS, no per-element inline delays. */
.has-js .promises__list .promise,
.has-js .tiers .tier,
.has-js .duo > *,
.has-js .care li{
  opacity:0;translate:0 14px;
  transition:opacity .55s ease,translate .55s cubic-bezier(.2,0,.2,1);
}
.has-js .promises__list.in .promise,
.has-js .tiers.in .tier,
.has-js .duo.in > *,
.has-js .rv.in .care li{opacity:1;translate:0 0}
.has-js .promises__list.in .promise:nth-child(2),
.has-js .tiers.in .tier:nth-child(2),
.has-js .duo.in > *:nth-child(2),
.has-js .rv.in .care li:nth-child(2){transition-delay:.07s}
.has-js .promises__list.in .promise:nth-child(3),
.has-js .tiers.in .tier:nth-child(3),
.has-js .rv.in .care li:nth-child(3){transition-delay:.14s}
.has-js .promises__list.in .promise:nth-child(4),
.has-js .rv.in .care li:nth-child(4){transition-delay:.21s}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;transition-duration:.01ms !important;
    transition-delay:0ms !important;
  }
  /* Everything visible immediately — including the staggered children, which would
     otherwise sit at opacity:0 waiting for a transition that no longer runs. */
  .has-js .rv,
  .has-js .promises__list .promise,
  .has-js .tiers .tier,
  .has-js .duo > *,
  .has-js .care li{opacity:1;translate:0 0}
  .hero__bg{transform:none !important}
  html{scroll-behavior:auto}
}
html{scroll-behavior:smooth}
body.is-locked{position:fixed;inset:0;width:100%;overflow:hidden}

/* ============================================================
   SCROLL-DRIVEN ANIMATIONS

   Native animation-timeline rather than JS scroll listeners: the work happens off
   the main thread, so it stays smooth on the mid-range Android this audience uses,
   and there is no listener to throttle or leak.

   Everything is behind BOTH gates:
     @supports (animation-timeline: view())  — ~85% support; older browsers keep the
                                               IntersectionObserver fallback
     prefers-reduced-motion: no-preference   — layered parallax is a genuine
                                               vestibular trigger, so it is opt-out
   ============================================================ */

/* Reading progress hairline. scroll() tracks the document, not an element. */
.progress{
  position:fixed;top:0;left:0;height:2px;width:100%;z-index:60;
  background:var(--maroon);transform-origin:0 50%;transform:scaleX(0);
  pointer-events:none;
}

@keyframes progress-grow{ to{ transform:scaleX(1) } }

/* Word reveal — TIME-based, not scroll-scrubbed.
   Scrubbing tied each word to scroll position: fast scrolling ripped through the whole
   animation in a few frames, stopping froze words half-risen, and scrolling back up ran
   them backwards. That flicker is what read as blinking. These play once on entry and
   stay. A fade with a small lift, no mask and no rotation — the mask made it mechanical.
   Driven by the Web Animations API from site.js, so the keyframes live there. */

/* Word wrappers. No overflow:hidden — the mask is what made the old reveal feel
   mechanical, and a fade needs nothing to clip against. Set to the pre-animation
   state only when JS will actually animate them (html.has-anim), so a failed script
   can never leave a heading invisible. */
.word{display:inline-block}
.has-anim .word{opacity:0}

/* Images wipe open rather than fade — a fade reads cheap, a wipe reads printed. */
@keyframes wipe-in{
  from{ clip-path:inset(0 0 100% 0); transform:scale(1.06) }
  to{ clip-path:inset(0 0 0 0); transform:scale(1) }
}

@keyframes rise-in{
  from{ opacity:0; transform:translateY(26px) }
  to{ opacity:1; transform:translateY(0) }
}

/* Hero background drift. Replaces the rAF listener that used to do this in JS. */
@keyframes hero-drift{
  from{ transform:translateY(0) scale(1.08) }
  to{ transform:translateY(14%) scale(1.14) }
}

/* The scrim deepens as the hero leaves, so the type stays readable while the
   photograph darkens away rather than sliding out at full brightness. */
@keyframes hero-scrim{
  from{ opacity:1 }
  to{ opacity:1.35 }
}

/* Opposing drift on paired images — the depth cue from the Mountain-parallax idea,
   applied at a scale that suits an editorial page rather than a showreel. */
@keyframes drift-up{ from{ transform:translateY(22px) } to{ transform:translateY(-22px) } }
@keyframes drift-down{ from{ transform:translateY(-16px) } to{ transform:translateY(16px) } }

/* The watermark turns slowly through the story section — the KICC silhouette
   rotating a few degrees, not a spin. */
@keyframes mark-turn{
  from{ transform:rotate(-6deg) scale(.94); opacity:.03 }
  to{ transform:rotate(4deg) scale(1.04); opacity:.08 }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    .progress{
      animation:progress-grow linear;
      animation-timeline:scroll(root block);
    }

    /* --- hero ------------------------------------------------------------- */
    .hero__bg{
      animation:hero-drift linear;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    .hero::after{
      animation:hero-scrim linear;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }

    /* --- content blocks ---------------------------------------------------- */
    .rv{
      animation:rise-in cubic-bezier(.2,.7,.2,1) backwards;
      animation-timeline:view();
      animation-range:entry 5% entry 55%;
    }

    /* --- images ------------------------------------------------------------ */
    .story__img,.soon__img,.wide__img,.delivery__img,.duo__img{
      animation:wipe-in cubic-bezier(.3,.7,.2,1) backwards;
      animation-timeline:view();
      animation-range:entry 0% entry 65%;
    }
    /* Paired images drift against each other while the section crosses. */
    /* Keyed off the grid item, not the image. Once an image is wrapped in a <figure>
       for its caption it is no longer its parent's first or last child — it is the only
       child of its own figure, so a bare .duo__img:first-child matched both of them and
       the pair drifted the same way instead of opposing. */
    .duo > :first-child .duo__img,
    .duo > .duo__img:first-child{
      animation:wipe-in cubic-bezier(.3,.7,.2,1) backwards, drift-up linear;
      animation-timeline:view(),view();
      animation-range:entry 0% entry 65%, cover 0% cover 100%;
    }
    .duo > :last-child .duo__img,
    .duo > .duo__img:last-child{
      animation:wipe-in cubic-bezier(.3,.7,.2,1) backwards, drift-down linear;
      animation-timeline:view(),view();
      animation-range:entry 0% entry 65%, cover 0% cover 100%;
    }

    /* --- promises ---------------------------------------------------------- */
    .promise{
      animation:rise-in cubic-bezier(.2,.7,.2,1) backwards;
      animation-timeline:view();
      animation-range:entry 8% entry 58%;
    }

    /* --- loyalty tiers ----------------------------------------------------- */
    .tier{
      animation:rise-in cubic-bezier(.2,.7,.2,1) backwards;
      animation-timeline:view();
      animation-range:entry 6% entry 56%;
    }
    .tier:nth-child(2){ animation-range:entry 11% entry 61% }
    .tier:nth-child(3){ animation-range:entry 16% entry 66% }

    /* --- care list --------------------------------------------------------- */
    .care li{
      animation:rise-in cubic-bezier(.2,.7,.2,1) backwards;
      animation-timeline:view();
      animation-range:entry 6% entry 50%;
    }
    .care li:nth-child(2){ animation-range:entry 10% entry 54% }
    .care li:nth-child(3){ animation-range:entry 14% entry 58% }
    .care li:nth-child(4){ animation-range:entry 18% entry 62% }

    /* --- story watermark --------------------------------------------------- */
    .story__watermark{
      animation:mark-turn linear;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }

    /* Scroll-driven mode supersedes the JS reveal entirely — neutralise its
       hidden state so the two systems can never both hold an element at opacity 0. */
    .has-js .rv{opacity:1;translate:none}
  }
}
