/* ==========================================================================
   Parker's Ice Creams — concept redesign
   Aesthetic: warm Texas Hill Country creamery. Cream paper, walnut, chalkboard.
   Brand colors sampled from the real Parker's logo.
   ========================================================================== */

:root {
  /* Brand, sampled from parkerslogo.png */
  --plum:       #663C60;
  --plum-deep:  #4A2A46;
  --magenta:    #930386;
  --lilac:      #9D6C99;

  /* Warm ground */
  --cream:      #FBF5EA;
  --cream-deep: #F3E7D4;
  --paper:      #FFFDF8;
  --wood:       #4A3327;
  --caramel:    #C0762C;

  /* Chalkboard */
  --board:      #232B2C;
  --board-soft: #2E3839;
  --chalk:      #EFEAE0;
  --chalk-dim:  #A9AFA6;

  /* Ink */
  --ink:        #2B211D;
  --ink-soft:   #6C5C52;
  --rule:       #E0D2BC;

  --display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --body:    "Karla", "Avenir Next", "Segoe UI", sans-serif;
  --hand:    "Caveat", "Bradley Hand", cursive;

  --shell: 1180px;
  --gut: clamp(1.15rem, 4vw, 2.75rem);

  --shadow-sm: 0 1px 2px rgba(43, 33, 29, .07), 0 2px 8px rgba(43, 33, 29, .05);
  --shadow-md: 0 4px 10px rgba(43, 33, 29, .09), 0 14px 34px rgba(43, 33, 29, .09);
  --shadow-lg: 0 10px 24px rgba(43, 33, 29, .13), 0 30px 70px rgba(43, 33, 29, .14);

  --ease: cubic-bezier(.22, 1, .36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--body);
  font-size: clamp(1.02rem, .35vw + .95rem, 1.14rem);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Paper grain over the whole page. Cheap, one inline SVG, no image request. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9998;
  pointer-events: none;
  opacity: .5;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%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='180' height='180' filter='url(%23n)' opacity='.32'/%3E%3C/svg%3E");
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--plum); text-decoration-thickness: 1px; text-underline-offset: .18em; }
a:hover { color: var(--magenta); }

:focus-visible {
  outline: 2.5px solid var(--magenta);
  outline-offset: 3px;
  border-radius: 3px;
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  font-variation-settings: "SOFT" 40, "WONK" 1;
  line-height: 1.04;
  letter-spacing: -.021em;
  margin: 0;
  text-wrap: balance;
}

.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: var(--gut); }

/* --------------------------------------------------------------- skip link */
.skip {
  position: absolute; left: -9999px; top: 0;
  background: var(--plum); color: #fff;
  padding: .7rem 1.1rem; z-index: 10001;
  font-weight: 700; border-radius: 0 0 8px 0;
}
.skip:focus { left: 0; color: #fff; }

/* ============================================================ concept flag */
.flag {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 10000;
  background: var(--plum-deep);
  color: #F6ECEF;
  font-size: .745rem;
  letter-spacing: .085em;
  text-transform: uppercase;
  font-weight: 700;
  text-align: center;
  padding: .6rem var(--gut);
  border-top: 2px solid var(--magenta);
  line-height: 1.45;
}
.flag b { color: #fff; }
.flag span { color: #C7A9C2; font-weight: 600; letter-spacing: .06em; }
/* Wrapping to two lines on small screens is fine: --flagh is measured, so the
   dock and footer adapt. Never hide the "not the live site" wording. */
@media (max-width: 700px) {
  .flag { font-size: .655rem; padding: .55rem .9rem; letter-spacing: .06em; }
}

/* ================================================================= masthead */
.masthead {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--cream) 88%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid var(--rule);
}
.masthead-in {
  display: flex; align-items: center; gap: 1.4rem;
  min-height: 74px;
  max-width: var(--shell); margin-inline: auto;
  padding-inline: var(--gut);
}
.brand { display: flex; align-items: center; gap: .65rem; text-decoration: none; margin-right: auto; }
.brand img { width: 40px; height: 40px; object-fit: contain; }
.brand-txt { display: flex; flex-direction: column; line-height: 1; }
.brand-txt b {
  font-family: var(--display); font-size: 1.16rem; font-weight: 700;
  color: var(--plum-deep); letter-spacing: -.02em;
}
.brand-txt em {
  font-style: normal; font-size: .625rem; letter-spacing: .28em;
  text-transform: uppercase; color: var(--ink-soft); margin-top: .28rem;
}

.nav { display: flex; align-items: center; gap: 1.75rem; }
.nav a {
  font-size: .795rem; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--ink); text-decoration: none;
  position: relative; padding: .35rem 0;
}
.nav a::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: 0;
  height: 2px; background: var(--magenta);
  transition: right .38s var(--ease);
}
.nav a:hover::after, .nav a[aria-current="page"]::after { right: 0; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  font-family: var(--body); font-weight: 700;
  font-size: .875rem; letter-spacing: .045em;
  padding: .82rem 1.5rem;
  border-radius: 999px; border: 2px solid var(--plum-deep);
  background: var(--plum-deep); color: #fff; text-decoration: none;
  cursor: pointer; white-space: nowrap;
  transition: transform .28s var(--ease), background .28s var(--ease), box-shadow .28s var(--ease), color .28s var(--ease);
}
.btn:hover {
  background: var(--magenta); border-color: var(--magenta); color: #fff;
  transform: translateY(-2px); box-shadow: var(--shadow-md);
}
.btn--ghost { background: transparent; color: var(--plum-deep); }
.btn--ghost:hover { background: var(--plum-deep); border-color: var(--plum-deep); color: #fff; }
.btn--lg { padding: 1.02rem 1.9rem; font-size: .945rem; }
.btn--chalk { background: transparent; border-color: var(--chalk); color: var(--chalk); }
.btn--chalk:hover { background: var(--chalk); border-color: var(--chalk); color: var(--board); }

.masthead .btn { padding: .66rem 1.2rem; font-size: .81rem; }

@media (max-width: 900px) {
  .nav { display: none; }
  .masthead .btn { padding: .6rem 1.05rem; }
}

/* =================================================================== eyebrow */
.eyebrow {
  font-family: var(--body); font-size: .715rem; font-weight: 800;
  letter-spacing: .3em; text-transform: uppercase;
  color: var(--magenta); margin: 0 0 1.15rem;
  display: flex; align-items: center; gap: .8rem;
}
.eyebrow::after { content: ""; height: 1px; flex: 1; background: var(--rule); max-width: 120px; }
.eyebrow--center { justify-content: center; }
.eyebrow--center::before { content: ""; height: 1px; flex: 1; background: var(--rule); max-width: 120px; }
.eyebrow--chalk { color: var(--lilac); }
.eyebrow--chalk::after, .eyebrow--chalk::before { background: rgba(239,234,224,.22); }

/* ====================================================================== hero */
.hero { position: relative; padding: clamp(2.9rem, 7vw, 6rem) 0 clamp(3rem, 7vw, 5.5rem); overflow: hidden; }
.hero::after {
  content: ""; position: absolute; z-index: 0;
  width: 78vw; height: 78vw; max-width: 900px; max-height: 900px;
  right: -26vw; top: -34vw;
  background: radial-gradient(circle, rgba(147,3,134,.13), rgba(102,60,96,.05) 46%, transparent 68%);
  pointer-events: none;
}
.hero-grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 1.06fr .94fr;
  gap: clamp(2rem, 5vw, 4.4rem); align-items: center;
}
.hero h1 {
  font-size: clamp(2.75rem, 7.1vw, 5.05rem);
  font-variation-settings: "SOFT" 60, "WONK" 1;
  margin-bottom: 1.4rem;
}
.hero h1 i {
  font-style: italic; color: var(--magenta);
  font-variation-settings: "SOFT" 90, "WONK" 1;
}
.hero-lede {
  font-size: clamp(1.09rem, .5vw + 1rem, 1.28rem);
  color: var(--ink-soft); max-width: 33rem; margin: 0 0 2rem;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: .85rem; align-items: center; }

/* live open/closed pill */
.status {
  display: inline-flex; align-items: center; gap: .58rem;
  font-size: .755rem; font-weight: 800; letter-spacing: .15em; text-transform: uppercase;
  background: var(--paper); border: 1px solid var(--rule);
  padding: .48rem .95rem; border-radius: 999px;
  margin-bottom: 1.5rem; box-shadow: var(--shadow-sm);
}
.status i {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--chalk-dim); flex: none;
}
.status.is-open i { background: #2E9E5B; box-shadow: 0 0 0 4px rgba(46,158,91,.18); }
.status.is-open i { animation: pulse 2.6s ease-in-out infinite; }
@keyframes pulse { 50% { box-shadow: 0 0 0 8px rgba(46,158,91,0); } }
@media (prefers-reduced-motion: reduce) { .status.is-open i { animation: none; } }
.status b { color: var(--ink); }
.status span { color: var(--ink-soft); font-weight: 600; letter-spacing: .06em; text-transform: none; font-size: .84rem; }

/* hero art, tilted photo on a warm mat */
.hero-art { position: relative; }
.hero-art img {
  width: 100%; border-radius: 5px;
  box-shadow: var(--shadow-lg);
  transform: rotate(1.6deg);
  border: 9px solid var(--paper);
}
.hero-tag {
  position: absolute; left: -14px; bottom: -22px;
  background: var(--magenta); color: #fff;
  font-family: var(--hand); font-size: 1.5rem; line-height: 1;
  padding: .75rem 1.15rem .6rem;
  border-radius: 4px; transform: rotate(-3.6deg);
  box-shadow: var(--shadow-md);
}

@media (max-width: 860px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-art { order: -1; max-width: 440px; }
  .hero-art img { transform: none; }
}

/* ================================================================ trust rail */
.trust {
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  background: var(--cream-deep);
}
.trust ul {
  list-style: none; margin: 0 auto; padding: 1.3rem var(--gut);
  max-width: var(--shell);
  display: flex; flex-wrap: wrap; gap: .7rem 2.3rem; justify-content: center;
}
.trust li {
  font-size: .765rem; font-weight: 800; letter-spacing: .16em;
  text-transform: uppercase; color: var(--wood);
  display: flex; align-items: center; gap: .6rem;
}
.trust li::before {
  content: ""; width: 6px; height: 6px; flex: none;
  background: var(--magenta); border-radius: 50%;
}

/* ================================================================== sections */
.band { padding: clamp(3.4rem, 8vw, 6.4rem) 0; }
.band--paper { background: var(--paper); border-block: 1px solid var(--rule); }
.band--deep { background: var(--cream-deep); }

.band-head { max-width: 40rem; margin-bottom: clamp(2rem, 4vw, 3.1rem); }
.band-head--center { margin-inline: auto; text-align: center; }
.band-head h2 { font-size: clamp(1.95rem, 4.3vw, 3.05rem); margin-bottom: 1rem; }
.band-head p { color: var(--ink-soft); margin: 0; font-size: 1.07rem; }

/* ================================================================== events */
/* Bookings band, first section under the hero. It borrows .split from the 21+
   band and .partners from the community band rather than growing its own
   copies. Two columns rather than the community band's three, because this list
   sits in half the page width, where a third column wraps the longer names.

   Two at every width, including below 560px where the community list drops to
   one. That rule is deliberately outranked here: these are short names, not the
   long organisation names that list carries, and the longest of them still fits
   a narrow column at 430px.

   The top margin is here because .partners follows a heading in the community
   band and a paragraph in this one. */
#events .partners { columns: 2; margin-top: 1.75rem; }

/* The two photo bands lower on the page leave their h2 at the browser default,
   which is fine for a section you meet after scrolling. This one is the first
   thing under the hero, so it takes the same size the .band-head sections use. */
.lead-h2 { font-size: clamp(1.95rem, 4.3vw, 3.05rem); margin-bottom: 1rem; }

.cta-row { display: flex; flex-wrap: wrap; gap: .85rem; align-items: center; margin-top: 2rem; }
.cta-note { margin: 1.05rem 0 0; font-size: .95rem; color: var(--ink-soft); max-width: 34rem; }

/* Placeholder standing in for the event photo Parker's has not sent yet. Square,
   like the photo in the 21+ band, so it sits level with the column of copy
   beside it and a photo dropped in its place changes nothing else. Delete this
   rule with the markup when the photo arrives. */
.slot {
  display: grid; align-content: center; justify-items: center; gap: .55rem;
  aspect-ratio: 1 / 1;
  padding: 1.6rem;
  text-align: center;
  background: var(--cream-deep);
  border: 2px dashed var(--rule);
  border-radius: 5px;
}
.slot b { font-family: var(--display); font-size: 1.22rem; color: var(--plum-deep); }
.slot span { font-size: .92rem; line-height: 1.5; color: var(--ink-soft); max-width: 21rem; }

/* ============================================================== flavor case */
/* The band stays dark and the cabinet is the bright thing standing in it, which
   is how a lit dipping case actually looks after sunset, and it keeps the page's
   cream / paper / dark rhythm intact. A white case on a white band would collide
   with the cream sections either side of this one. */
.board {
  position: relative;
  background:
    radial-gradient(ellipse 120% 80% at 22% 10%, rgba(255,255,255,.045), transparent 60%),
    radial-gradient(ellipse 90% 70% at 82% 88%, rgba(255,255,255,.035), transparent 62%),
    var(--board);
  color: var(--chalk);
  padding: clamp(3.4rem, 8vw, 6.4rem) 0;
  overflow: hidden;
}
.board::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='4'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23c)' opacity='.35'/%3E%3C/svg%3E");
}
.board .shell { position: relative; z-index: 1; }
.board h2 { color: var(--chalk); }
.board .band-head p { color: var(--chalk-dim); }

/* One rail is one case: its label, then the cabinet under it. */
.rail { margin-bottom: clamp(2.4rem, 5vw, 3.6rem); }
.rail:last-of-type { margin-bottom: 0; }

.rail-label {
  display: flex; align-items: baseline; gap: .9rem;
  font-size: .715rem; font-weight: 800; letter-spacing: .3em;
  text-transform: uppercase; color: var(--lilac);
  margin-bottom: 1.1rem;
}
.rail-label b { color: var(--chalk); }
.rail-label s { text-decoration: none; color: var(--chalk-dim); font-weight: 600; letter-spacing: .06em; text-transform: none; font-size: .9rem; }

/* One <ul> is one case. The enclosure lives on the <ul> itself rather than in a
   wrapper element, because the <ul> is the only part of this section the editor
   writes into; anything wrapped around it is hand-written markup that would then
   have to be kept in step with the generated contents by hand.

   That leaves exactly two pseudo-elements for the glass and the light, which is
   enough. Neither may ever intercept a tap. */
.chips {
  list-style: none;
  margin: 0;
  /* padding-inline must be set explicitly: a <ul> defaults to 40px of list
     indent, which shoves the whole grid right and knocks it off centre. */
  padding: clamp(1.1rem, 2.4vw, 1.9rem);
  position: relative;

  display: grid;
  gap: clamp(1.15rem, 2.2vw, 1.75rem);
  /* One value covers every width, with no breakpoint of its own to maintain: six
     tubs across the shell at desktop, two at 430px.

     150px was the first try and the pans came out at 184px, which Micah found too
     big. 128px lands them at 148px and happens to divide the eighteen regulars
     into three full rows instead of three rows and three orphans. Do not go below
     107px: that is where a 430px phone flips to three columns, and the cards get
     too narrow for a name like Blueberry Cheesecake. */
  grid-template-columns: repeat(auto-fill, minmax(128px, 1fr));

  border-radius: 12px;
  background:
    /* the strip lamp under the front rail */
    linear-gradient(180deg, rgba(208,233,243,.4) 0%, transparent 20%),
    /* cold air pooling in the bottom of the well */
    radial-gradient(ellipse 130% 42% at 50% 110%, rgba(116,158,178,.3), transparent 68%),
    /* the well itself. Deep enough in value that a cream or vanilla tub still
       reads against it: the first pass was near-white and every pale flavor
       vanished into the floor. */
    linear-gradient(180deg, #DBE2E4 0%, #C3CCCF 36%, #AEB8BC 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.95),      /* rail highlight */
    inset 0 16px 28px rgba(35,43,44,.26),     /* recess, deepest under the glass */
    inset 0 -10px 20px rgba(35,43,44,.16),
    /* the stainless frame the glass is bolted to, as rings rather than a border,
       so the corners stay a single clean radius */
    0 0 0 1px #8E9B9E,
    0 0 0 5px #DBE2E4,
    0 0 0 6px #7F8C90,
    0 0 70px rgba(190,226,245,.14),           /* the case lighting the dark room */
    0 22px 46px rgba(0,0,0,.48);              /* and standing in it */
}

/* The glass. A bright line where the pane meets the frame, then two reflections
   rather than one: a broad sweep and a narrow trailing band. One band alone reads
   as a gradient; two read as a sheet of glass. Screen blending was tried first and
   did almost nothing over a light grey well. */
.chips::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  border-radius: 12px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.55) 0 1px, transparent 1px),
    linear-gradient(101deg,
      rgba(255,255,255,0)   13%,
      rgba(255,255,255,.24) 26%,
      rgba(255,255,255,.09) 36%,
      rgba(255,255,255,0)   48%,
      rgba(255,255,255,0)   71%,
      rgba(255,255,255,.12) 82%,
      rgba(255,255,255,0)   91%);
}

/* An empty group is worse than no group. The 21+ case is empty whenever the shop
   has no infused flavor on, and an enclosure with nothing in it reads as broken
   rather than as a case waiting for a tub. Hide the label along with it.

   The test is any <li>, not .tub: the third group is a plain list of to-go pints
   rather than a case, and keying on .tub would hide it permanently.

   :empty cannot be used here either. The generated region leaves indentation
   inside the <ul>, and one whitespace text node defeats it. */
.rail:not(:has(li)) { display: none; }

/* --------------------------------------------------------------------- tubs */
.tub {
  display: grid;
  justify-items: center;
  gap: .62rem;
  /* One colour per flavor. The lit and deep ends are mixed off it below, so
     adding a tone is one line rather than three. */
  --ice: #F3E6CC;
}
.tub--cream      { --ice: #F3E6CC; }
.tub--chocolate  { --ice: #58382A; }
.tub--coffee     { --ice: #8B6349; }
.tub--caramel    { --ice: #CE9455; }
.tub--strawberry { --ice: #E79FAD; }
.tub--blueberry  { --ice: #8D9AD0; }
.tub--mint       { --ice: #A6D5B2; }
.tub--cookie     { --ice: #7CA6D9; }
.tub--citrus     { --ice: #F0AA55; }
.tub--lemon      { --ice: #F0E19B; }

/* The pan of ice cream, seen from above through the glass.

   The first pass at this lit the surface like a sphere and the tubs came out
   looking like billiard balls. A pan of ice cream is close to flat: it darkens at
   the edge because the steel rim shades it, not because it curves away. So the
   base gradient stays nearly even and all the relief comes from three things on
   top of it: churn from the paddle, one crater where a scoop has been served, and
   a tight specular where the case light hits the wet surface.

   overflow: hidden so none of those layers can escape the circle. */
.scoop {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;

  --ice-lit:  color-mix(in srgb, var(--ice) 82%, #fff);
  --ice-deep: color-mix(in srgb, var(--ice) 84%, #2B211D);

  /* Nearly flat. Every bit of relief is applied on ::before, where it can be
     rotated per tub. A conic gradient was tried here for paddle ridges and had
     to go: radial spokes on a circle read as a beach parasol, not as ice cream. */
  background: radial-gradient(circle at 50% 44%,
    var(--ice-lit) 0%, var(--ice) 54%, var(--ice-deep) 100%);

  box-shadow:
    inset 0 0 12px 5px rgba(0,0,0,.13),       /* the rim shading the surface */
    0 0 0 3px #EEF2F3,                        /* the steel pan rim, lit edge */
    0 0 0 4px #97A2A5,                        /* and its shadowed edge */
    0 0 0 9px rgba(35,43,44,.09),             /* the socket the pan drops into */
    0 8px 15px rgba(35,43,44,.4);
  transition: transform .35s var(--ease);
}

/* All of the relief, in one rotatable layer: the hollow somebody has served from,
   and the softer scoop marks left in the surface around it. Rotating them together
   is what actually happens, since they are all the result of digging in from one
   side of the pan.

   Heavily blurred on purpose. Any hard edge here reads as a decal stuck on the
   ice cream rather than as the ice cream's own surface. */
.scoop::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  transform: rotate(var(--dig, 0deg));
  background:
    /* the hollow: shadowed far wall, then the floor catching light below it */
    radial-gradient(ellipse 50% 39% at 41% 37%,
      rgba(0,0,0,.24), rgba(0,0,0,.06) 60%, transparent 78%),
    radial-gradient(ellipse 42% 29% at 45% 53%, rgba(255,255,255,.17), transparent 74%),
    /* scoop marks in the surface around it */
    radial-gradient(ellipse 29% 21% at 74% 66%, rgba(0,0,0,.11), transparent 72%),
    radial-gradient(ellipse 25% 19% at 26% 74%, rgba(255,255,255,.11), transparent 70%),
    radial-gradient(ellipse 21% 17% at 69% 24%, rgba(255,255,255,.09), transparent 70%);
  filter: blur(3px);
}

/* Wet surface. One tight specular where the case light lands, plus the rim's own
   shadow falling across the top of the ice cream. */
.scoop::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: 50%;
  background: radial-gradient(ellipse 32% 19% at 31% 19%,
    rgba(255,255,255,.32), transparent 72%);
  box-shadow:
    inset 0 7px 11px rgba(0,0,0,.15),
    inset 0 -5px 9px rgba(255,255,255,.1);
}

/* Nothing in a real case is stamped from the same die. Seven rotations on a prime
   cycle, so no two tubs anywhere in a row match and the sequence does not repeat
   inside a case this size. */
.tub:nth-child(7n+2) .scoop { --dig:  52deg; }
.tub:nth-child(7n+3) .scoop { --dig: 143deg; }
.tub:nth-child(7n+4) .scoop { --dig: 209deg; }
.tub:nth-child(7n+5) .scoop { --dig:  98deg; }
.tub:nth-child(7n+6) .scoop { --dig: 297deg; }
.tub:nth-child(7n+7) .scoop { --dig: 174deg; }

@media (hover: hover) {
  .tub:hover .scoop { transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .scoop { transition: none; }
  .tub:hover .scoop { transform: none; }
}

/* The printed card on the front rail: dark ink on pale board, so the name reads
   whatever colour the ice cream above it happens to be. The chalkboard set each
   name in its own flavor colour, which tied legibility to the colour and put
   pastel type on near-black. */
.tag {
  display: grid;
  justify-items: center;
  gap: .2rem;
  width: 100%;
  padding: .44rem .5rem .48rem;
  text-align: center;
  background: linear-gradient(#FFFDF8, #F3EDE0);
  border: 1px solid #BFC7C9;
  border-radius: 3px;
  box-shadow: 0 2px 5px rgba(35,43,44,.3);
}
.tag b {
  font-family: var(--body);
  font-size: .805rem; font-weight: 700; line-height: 1.22;
  color: var(--ink);
}
.tag small {
  font-size: .575rem; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-soft);
}
.tag .age {
  font-size: .58rem; font-weight: 800; letter-spacing: .1em;
  color: #fff; background: var(--magenta);
  padding: .09rem .3rem; border-radius: 2px;
}

/* -------------------------------------------------- flavor of the month card */
/* Sits above the in-store case. Deliberately not as bright as the case: the case
   is the substance of this section and should stay the thing your eye lands on,
   so this reads as a sign propped in front of it rather than as a second case.

   It borrows the pan from the tubs, and the tone class the renderer puts on it
   supplies --ice, which the pan inherits. No new colour plumbing. */
.fotm {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: clamp(1.3rem, 3vw, 2.2rem);
  margin-bottom: clamp(1.8rem, 4vw, 2.8rem);
  padding: clamp(1.2rem, 2.6vw, 1.8rem) clamp(1.3rem, 3vw, 2.1rem);
  border-radius: 10px;
  background:
    linear-gradient(150deg, rgba(147, 3, 134, .16), transparent 62%),
    linear-gradient(#333D3E, #262F30);
  border: 1px solid rgba(239, 234, 224, .16);
  border-left: 3px solid var(--magenta);
  box-shadow: 0 14px 30px rgba(0, 0, 0, .34);
  /* No --ice default here on purpose. One was tried and it silently broke the
     colour: .fotm and .tub--citrus have equal specificity and .fotm is further
     down the file, so the default won and every card came out vanilla. It is not
     needed anyway, because renderFotm always emits a tone class and falls back to
     cream itself when the tone is auto or unrecognised. */
}

/* Smaller than a tub in the case, so it reads as one scoop being shown off rather
   than as a pan that has escaped the cabinet. */
.fotm .scoop { width: clamp(92px, 15vw, 132px); }

/* Lilac, not --magenta. The brand magenta is #930386, which on this near-black
   card is dark-on-dark and effectively unreadable; the rest of this band already
   uses lilac for the same reason. */
.fotm-eyebrow {
  margin: 0 0 .5rem;
  font-size: .68rem; font-weight: 800;
  letter-spacing: .26em; text-transform: uppercase;
  color: var(--lilac);
}
.fotm h3 {
  margin: 0;
  font-size: clamp(1.5rem, 3.1vw, 2.15rem);
  color: var(--chalk);
}
.fotm-txt p:last-child {
  margin: .6rem 0 0;
  color: var(--chalk-dim);
  font-size: .985rem;
  max-width: 40rem;
}

/* At phone widths the pan and the copy stop fitting side by side. */
@media (max-width: 560px) {
  .fotm { grid-template-columns: 1fr; justify-items: start; gap: 1rem; }
}

/* ------------------------------------------------------------ the to-go list */
/* Reuses .flavor-cols, the plain list from the menu page, because that is exactly
   what this is meant to be: a normal list of names, pointedly not built like the
   case. Only the colours change, since that component was written for a cream
   page and this band is near-black.

   The magenta the marks use on the menu page is too dark to read here, so they
   take the lilac the rest of this band already uses. */
.rail--togo .flavor-cols li {
  border-bottom-color: rgba(239, 234, 224, .17);
  color: var(--chalk);
}
.rail--togo .flavor-cols mark { color: var(--lilac); }
/* Room for four columns rather than three, because these are names only and the
   case above sets an expectation of a wide, densely packed run.

   It is a ceiling, not a count. Multicol balances by height and then uses the
   fewest columns that reach that height, so at the nine flavors marked to go
   today this still renders three; it widens on its own once the shop marks
   enough that a fourth column would make the list shorter. */
@media (min-width: 901px) { .rail--togo .flavor-cols { columns: 4; } }

/* The case needs nothing of its own at this width. Its padding and gap are
   already clamped, and auto-fill lands on two columns at 430px without being
   told to, which is why the grid is expressed as one minmax rather than as a
   column count per breakpoint. Only the section label needs help. */
@media (max-width: 560px) {
  /* the label and its descriptor do not fit side by side at this width */
  .rail-label {
    flex-direction: column; align-items: flex-start; gap: .25rem;
    letter-spacing: .2em; margin-bottom: .8rem;
  }
  .rail-label s { font-size: .84rem; }
}

.board-note {
  margin: clamp(2.2rem, 5vw, 3.2rem) 0 0;
  padding-top: 1.6rem; border-top: 1px dashed rgba(239,234,224,.24);
  display: flex; flex-wrap: wrap; gap: 1.2rem 2rem; align-items: center;
  justify-content: space-between;
}
.board-note p { margin: 0; color: var(--chalk-dim); font-size: .98rem; max-width: 44rem; }
.board-note a { color: var(--chalk); }

/* ================================================================== 21+ band */
.split {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 4.2rem); align-items: center;
}
@media (max-width: 860px) { .split { grid-template-columns: 1fr; } }
.split-art img { border-radius: 5px; box-shadow: var(--shadow-lg); border: 9px solid var(--paper); }

.pourlist { list-style: none; margin: 1.9rem 0 0; padding: 0; }
.pourlist li {
  display: flex; align-items: baseline; gap: .9rem;
  padding: .78rem 0; border-bottom: 1px solid var(--rule);
  font-size: 1.02rem;
}
.pourlist li:first-child { border-top: 1px solid var(--rule); }
.pourlist b { font-family: var(--display); font-weight: 600; font-size: 1.09rem; min-width: 8.4rem; }
.pourlist span { color: var(--ink-soft); font-size: .95rem; }

/* =================================================================== visit */
.visit { display: grid; grid-template-columns: 1.02fr .98fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
@media (max-width: 860px) { .visit { grid-template-columns: 1fr; } }

/* live map, so "where are you" is answered without leaving the page */
.map {
  margin-top: 1.5rem;
  border: 1px solid var(--rule); border-radius: 6px;
  overflow: hidden; background: var(--cream-deep);
  aspect-ratio: 16 / 11;
}
.map iframe { width: 100%; height: 100%; border: 0; display: block; filter: saturate(.88); }

.hours { list-style: none; margin: 0; padding: 0; }
.hours li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1rem;
  padding: .82rem .95rem; border-radius: 5px;
  font-size: 1.02rem;
}
.hours li + li { border-top: 1px solid var(--rule); }
.hours li[data-today] {
  background: var(--plum-deep); color: #fff;
  border-top-color: transparent !important;
  box-shadow: var(--shadow-sm);
}
.hours li[data-today] + li { border-top-color: transparent; }
.hours dt, .hours b {
  font-weight: 700; letter-spacing: .04em;
  font-size: .81rem; text-transform: uppercase;
}
.hours span { font-variant-numeric: tabular-nums; font-weight: 600; }

/* A weekly draw, on the day it happens on: Wine Down Wednesday, Thursday's live
   music. flex-basis puts it on its own line inside the existing space-between row
   rather than making it a third thing fighting for the same line, which is why the
   row now wraps. Not uppercased like the day, so the two do not compete. */
.hours li { flex-wrap: wrap; }
.hours em {
  flex-basis: 100%;
  margin-top: .28rem;
  font-style: normal;
  font-size: .845rem; font-weight: 700;
  letter-spacing: .02em;
  color: var(--magenta);
}
/* Today's row is plum with white text, and the brand magenta disappears into it. */
.hours li[data-today] em { color: #EFC4E6; }

.card {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 8px; padding: clamp(1.5rem, 3vw, 2.15rem);
  box-shadow: var(--shadow-sm);
}
.card h3 { font-size: 1.32rem; margin-bottom: 1.1rem; }

.contact { list-style: none; margin: 0; padding: 0; }
.contact li { display: flex; gap: 1rem; padding: 1rem 0; border-bottom: 1px solid var(--rule); }
.contact li:last-child { border-bottom: 0; padding-bottom: 0; }
.contact li:first-child { padding-top: 0; }
.contact .ic { flex: none; width: 22px; color: var(--magenta); padding-top: .2rem; }
.contact .ic svg { width: 22px; height: 22px; display: block; }
.contact b { display: block; font-size: .715rem; font-weight: 800; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-soft); margin-bottom: .28rem; }
.contact a, .contact address {
  font-style: normal; font-size: 1.06rem; font-weight: 600;
  color: var(--ink); text-decoration: none; line-height: 1.45;
}
.contact a:hover { color: var(--magenta); text-decoration: underline; }
.contact em { display: block; font-style: normal; font-size: .875rem; color: var(--ink-soft); font-weight: 500; margin-top: .22rem; }

/* The Hours card used to end with two big number tiles, 18 seats inside and 40
   on the patio. They are out of the Visit section, so .seats and .seat are gone
   with them rather than left behind unused. The counts survive in one place, the
   parties band, as a sentence. */

/* =============================================================== gift cards */
.gift {
  background: linear-gradient(115deg, var(--plum-deep), var(--plum) 52%, var(--magenta));
  color: #fff; border-radius: 10px;
  padding: clamp(2rem, 5vw, 3.3rem);
  display: grid; grid-template-columns: 1fr auto;
  gap: 2rem; align-items: center;
  box-shadow: var(--shadow-md);
  position: relative; overflow: hidden;
}
.gift::before {
  content: ""; position: absolute; inset: 0; opacity: .5; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='.18'/%3E%3C/svg%3E");
}
.gift > * { position: relative; z-index: 1; }
.gift h2 { color: #fff; font-size: clamp(1.7rem, 3.6vw, 2.5rem); margin-bottom: .85rem; }
.gift p { margin: 0; color: #EBD6E7; max-width: 32rem; }
@media (max-width: 720px) { .gift { grid-template-columns: 1fr; } }

/* ================================================================ community */
.partners {
  list-style: none; margin: 0; padding: 0;
  columns: 3; column-gap: 2.6rem;
}
@media (max-width: 900px) { .partners { columns: 2; } }
@media (max-width: 560px) { .partners { columns: 1; } }
.partners li {
  break-inside: avoid;
  padding: .62rem 0 .62rem 1.5rem;
  border-bottom: 1px solid var(--rule);
  font-size: .985rem; line-height: 1.4;
  position: relative;
}
.partners li::before {
  content: ""; position: absolute; left: 0; top: 1.02em;
  width: 8px; height: 2px; background: var(--magenta);
}

/* =================================================================== quotes */
.quotes {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(1.2rem, 3vw, 2rem);
  align-items: start;
}
@media (max-width: 760px) { .quotes { grid-template-columns: 1fr; } }

.quote {
  position: relative;
  background: var(--cream-deep);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: clamp(1.6rem, 3.5vw, 2.3rem);
  box-shadow: var(--shadow-sm);
  margin: 0;
  overflow: hidden;
}
/* oversized quote mark in the corner. Keep enough of the glyph on the card
   that it reads as a quote mark rather than a smudge. */
.quote::before {
  content: "\201C";
  position: absolute; top: -.06em; right: .04em;
  font-family: var(--display); font-weight: 700;
  font-size: 7rem; line-height: 1;
  color: var(--lilac); opacity: .22;
  pointer-events: none;
}
.stars {
  display: block; color: var(--caramel);
  font-size: 1.06rem; letter-spacing: .2em;
  margin-bottom: .9rem;
}
.quote p {
  position: relative; z-index: 1;
  margin: 0 0 1.3rem;
  font-family: var(--display);
  font-size: clamp(1.06rem, .4vw + 1rem, 1.19rem);
  font-variation-settings: "SOFT" 40, "WONK" 1;
  line-height: 1.52;
  color: var(--ink);
  text-wrap: pretty;
}
.quote footer {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .3rem .7rem;
  padding-top: 1rem; border-top: 1px solid var(--rule);
}
.quote cite {
  font-style: normal; font-weight: 700;
  font-size: .93rem; color: var(--plum-deep);
}
.quote span {
  font-size: .715rem; font-weight: 800;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft);
}
.quotes-more { margin: clamp(1.6rem, 4vw, 2.4rem) 0 0; text-align: center; }

/* ================================================================== footer */
.foot {
  background: var(--wood); color: #E8DCCD;
  padding: clamp(2.8rem, 6vw, 4.4rem) 0 clamp(4.4rem, 6vw, 5rem);
}
.foot a { color: #E8DCCD; }
.foot a:hover { color: #fff; }
.foot-grid {
  display: grid; grid-template-columns: 1.35fr 1fr 1fr;
  gap: clamp(1.8rem, 4vw, 3.4rem);
}
@media (max-width: 760px) { .foot-grid { grid-template-columns: 1fr; } }
.foot h4 {
  font-family: var(--body); font-size: .715rem; font-weight: 800;
  letter-spacing: .22em; text-transform: uppercase;
  color: #C3AE96; margin-bottom: 1.05rem;
}
.foot ul { list-style: none; margin: 0; padding: 0; }
.foot li { padding: .32rem 0; font-size: .975rem; }
.foot img { width: 66px; margin-bottom: 1.1rem; }
.foot address { font-style: normal; font-size: .975rem; line-height: 1.65; }
.social { display: flex; gap: .7rem; margin-top: 1.15rem; }
.social a {
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid rgba(232,220,205,.32);
  display: grid; place-items: center;
  transition: background .26s var(--ease), border-color .26s var(--ease), transform .26s var(--ease);
}
.social a:hover { background: var(--magenta); border-color: var(--magenta); transform: translateY(-2px); }
.social svg { width: 19px; height: 19px; fill: currentColor; }
.legal {
  margin-top: clamp(2.2rem, 4vw, 3rem); padding-top: 1.5rem;
  border-top: 1px solid rgba(232,220,205,.2);
  display: flex; flex-wrap: wrap; gap: .7rem 1.6rem; justify-content: space-between;
  font-size: .855rem; color: #BCA88F;
}

/* ========================================================= mobile call dock */
/* --flagh is measured at runtime from the concept-preview bar so the dock and
   the footer always clear it, however many lines it wraps to. */
.dock {
  position: fixed; z-index: 70;
  left: max(var(--gut), 1rem); right: max(var(--gut), 1rem);
  bottom: calc(var(--flagh, 3rem) + .8rem);
  display: none; gap: .7rem;
}
.dock .btn { flex: 1; box-shadow: var(--shadow-lg); }
@media (max-width: 900px) {
  .dock { display: flex; }
  .foot { padding-bottom: calc(var(--flagh, 3rem) + 5.5rem); }
}
@media (min-width: 901px) {
  .foot { padding-bottom: calc(var(--flagh, 3rem) + 2rem); }
}

/* ==================================================== reveal on scroll (JS)
   Visible by default and only hidden once JS has confirmed it can reveal them.
   Hiding unconditionally would leave the whole page blank if the observer
   never fires. The `js` class is set by an inline script in <head>, before
   first paint, so there is no flash of shifted content. */
.rise { opacity: 1; transform: none; }
html.js .rise { opacity: 0; transform: translateY(22px); }
html.js .rise.in {
  opacity: 1; transform: none;
  transition: opacity .8s var(--ease), transform .8s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  html.js .rise { opacity: 1; transform: none; }
  html.js .rise.in { transition: none; }
}

/* ============================================================== menu page */
.menu-hero {
  background: var(--cream-deep);
  border-bottom: 1px solid var(--rule);
  padding: clamp(2.6rem, 6vw, 4.4rem) 0;
  text-align: center;
}
.menu-hero h1 { font-size: clamp(2.4rem, 6vw, 4rem); margin-bottom: 1rem; }
.menu-hero p { color: var(--ink-soft); max-width: 38rem; margin: 0 auto; }

.price-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.6rem, 4vw, 3rem); }
@media (max-width: 800px) { .price-grid { grid-template-columns: 1fr; } }

.price-card {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 8px; overflow: hidden; box-shadow: var(--shadow-sm);
}
.price-card > header {
  padding: 1.35rem clamp(1.3rem, 3vw, 1.9rem);
  border-bottom: 1px solid var(--rule);
  background: var(--cream-deep);
}
.price-card--21 > header { background: var(--plum-deep); }
.price-card--21 > header h3 { color: #fff; }
.price-card--21 > header p { color: #E6D6E3; }
.price-card header h3 { font-size: 1.4rem; margin-bottom: .35rem; }
.price-card header p { margin: 0; font-size: .855rem; color: var(--ink-soft); }
.price-card dl { margin: 0; padding: .6rem clamp(1.3rem, 3vw, 1.9rem) 1.3rem; }
.price-row {
  display: flex; align-items: baseline; gap: .8rem;
  padding: .72rem 0; border-bottom: 1px dotted var(--rule);
}
.price-row:last-child { border-bottom: 0; }
.price-row dt { font-weight: 600; font-size: 1.03rem; order: 0; }
/* leader dots as a pseudo-element: a <span> between <dt> and <dd> is not valid inside <dl> */
.price-row::after {
  content: ""; flex: 1; order: 1;
  border-bottom: 1px dotted var(--rule);
  transform: translateY(-4px);
}
.price-row dd { margin: 0; order: 2; font-family: var(--display); font-weight: 600; font-size: 1.16rem; font-variant-numeric: tabular-nums; color: var(--plum-deep); }
.price-row em { font-style: normal; display: block; font-size: .84rem; color: var(--ink-soft); font-weight: 500; margin-top: .12rem; }

.flavor-cols { columns: 3; column-gap: 2.4rem; list-style: none; margin: 0; padding: 0; }
@media (max-width: 900px) { .flavor-cols { columns: 2; } }
@media (max-width: 540px) { .flavor-cols { columns: 1; } }
.flavor-cols li {
  break-inside: avoid; padding: .58rem 0;
  border-bottom: 1px solid var(--rule); font-size: 1rem;
}
.flavor-cols mark {
  background: none; color: var(--magenta); font-weight: 800;
  font-size: .68rem; letter-spacing: .12em; margin-left: .5rem;
  vertical-align: .12em;
}

.note {
  margin-top: 2rem; padding: 1.15rem 1.35rem;
  background: var(--cream-deep); border-left: 3px solid var(--caramel);
  border-radius: 0 6px 6px 0;
  font-size: .935rem; color: var(--ink-soft);
}
.note b { color: var(--ink); }

/* ==========================================================================
   MOBILE PAGE SPLIT AND MENU

   At 430px the homepage is 11,154px tall, about twelve phone screens, and the
   desktop nav is hidden below 900px with nothing replacing it. So on a phone
   there was no navigation at all: only the fixed dock and a footer list you
   could not reach without scrolling the whole page.

   Below 900px index.html therefore behaves as seven pages. All ten sections
   stay in the one file and one page's worth is shown at a time, so no copy is
   duplicated and there is nothing to keep in sync.

   Two gates matter here:

     max-width: 900px   Desktop never evaluates any of this, so the long
                        scroll is untouched. Matches the breakpoint already
                        used for .nav and .dock.

     html.js[data-mpage]  data-mpage is only ever set by site.js. With JS off
                        neither gate passes, no rule matches, and mobile falls
                        back to exactly the scroll it has today rather than
                        showing a blank page.
   ========================================================================== */

/* Both the trigger and the panel are mobile-only, and both must be display:none
   by default. The panel especially: it is real markup sitting in the document,
   so without this it lays out as ordinary content on desktop and adds 3,231px
   to the homepage. */
.burger, .mnav { display: none; }

@media (max-width: 900px) {

  /* ----------------------------------------------------- the seven pages */
  /* Scroll anchoring would otherwise try to hold the reading position steady
     when a page swap changes what is above the viewport, fighting the jump to
     the top and leaving the heading tucked under the sticky masthead. */
  html.js[data-mpage] main { overflow-anchor: none; }

  html.js[data-mpage] main > [data-page]                     { display: none; }
  html.js[data-mpage="home"]    main > [data-page="home"]     { display: block; }
  html.js[data-mpage="events"]  main > [data-page="events"]   { display: block; }
  html.js[data-mpage="flavors"] main > [data-page="flavors"]  { display: block; }
  html.js[data-mpage="bar"]     main > [data-page="bar"]      { display: block; }
  html.js[data-mpage="visit"]   main > [data-page="visit"]    { display: block; }
  html.js[data-mpage="about"]   main > [data-page="about"]    { display: block; }

  /* --------------------------------------------------------- the trigger */
  /* The masthead's Call button duplicates the one in the fixed dock, so trade
     it for the menu control instead of crowding a 430px header with both.

     Both halves of that trade are gated on html.js. Nothing can open the panel
     without JS, so an ungated swap would leave a hamburger that does nothing
     where a working Call button used to be. Gated, a visitor without JS keeps
     the Call button and the full scroll, exactly as before. */
  html.js .masthead .btn { display: none; }

  html.js .burger {
    display: inline-flex; align-items: center; justify-content: center;
    flex-direction: column;
    width: 46px; height: 46px; padding: 0;
    background: transparent; color: var(--plum-deep);
    border: 2px solid var(--plum-deep); border-radius: 999px;
    cursor: pointer;
    transition: background .28s var(--ease), color .28s var(--ease);
  }
  .burger i {
    display: block; width: 19px; height: 2px; border-radius: 2px;
    background: currentColor;
    transition: transform .32s var(--ease), opacity .18s var(--ease);
  }
  .burger i + i { margin-top: 5px; }

  /* The same button becomes the close control, so it needs to sit above the
     panel. Raising the masthead keeps the brand visible too, which is what
     stops the open menu feeling like a different site. */
  html.is-menu-open .masthead {
    z-index: 90;
    background: var(--cream);          /* solid: plum must not bleed through */
    backdrop-filter: none;
  }
  html.is-menu-open .burger { background: var(--plum-deep); color: var(--cream); }
  html.is-menu-open .burger i:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  html.is-menu-open .burger i:nth-child(2) { opacity: 0; }
  html.is-menu-open .burger i:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  /* ----------------------------------------------------------- the panel */
  html.js .mnav {
    display: flex; flex-direction: column;
    position: fixed; inset: 0; z-index: 80;
    overflow-y: auto; overscroll-behavior: contain;
    /* clears the 74px sticky masthead at the top and the concept bar below */
    padding: calc(74px + 1.5rem) var(--gut) calc(var(--flagh, 3rem) + 1.5rem);
    background:
      radial-gradient(115% 80% at 12% 0%, rgba(157, 108, 153, .40), transparent 62%),
      var(--plum-deep);
    opacity: 0; visibility: hidden;
    /* visibility is a discrete property, so transitioning it would flip it
       halfway through the fade. That leaves the panel unfocusable for the first
       150ms, and a menu you cannot move focus into is a menu a keyboard or
       screen reader user cannot open. Snap it on immediately instead, and delay
       only the hide so the fade out is still visible. */
    transition: opacity .3s var(--ease), visibility 0s linear .3s;
  }
  html.is-menu-open .mnav {
    opacity: 1; visibility: visible;
    transition: opacity .3s var(--ease), visibility 0s linear 0s;
  }

  /* Lock the page behind the panel so a swipe scrolls the menu, not the site. */
  html.is-menu-open, html.is-menu-open body { overflow: hidden; }

  /* The dock would otherwise float its two buttons over the open panel. */
  html.is-menu-open .dock { display: none; }

  .mnav-list { list-style: none; margin: 0; padding: 0; }
  .mnav-list a {
    display: block; position: relative;
    font-family: var(--hand); font-weight: 600;
    font-size: clamp(1.95rem, 9.5vw, 2.7rem); line-height: 1.1;
    color: var(--chalk); text-decoration: none;
    /* Every item reserves the marker gutter, so the names stay aligned whether
       or not they are the current page. */
    padding: .42rem 0 .42rem 1.15rem;
    border-bottom: 1px solid rgba(239, 234, 224, .14);
  }
  .mnav-list a[aria-current="page"] { color: #fff; }
  /* A chalk dot beside the page you are on. Absolutely placed in the reserved
     gutter rather than pulled out with a negative margin, which at 430px put it
     past the panel's own padding and clipped it against the screen edge. */
  .mnav-list a[aria-current="page"]::before {
    content: ""; position: absolute; left: 0; top: 50%;
    width: .5rem; height: .5rem; border-radius: 999px;
    background: var(--lilac);
    transform: translateY(-50%);
  }

  .mnav-meta { margin-top: auto; padding-top: 1.9rem; }
  .mnav-meta .btn--chalk { width: 100%; }
  .mnav-tel {
    display: block; margin-top: 1.1rem;
    font-family: var(--display); font-size: 1.3rem; font-weight: 600;
    color: var(--chalk); text-decoration: none;
  }
  .mnav-note {
    margin: .35rem 0 0;
    font-size: .72rem; font-weight: 700; letter-spacing: .22em;
    text-transform: uppercase; color: var(--chalk-dim);
  }
  .mnav-social { display: flex; gap: .75rem; margin-top: 1.4rem; }
  .mnav-social a {
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; border-radius: 999px;
    border: 1px solid rgba(239, 234, 224, .3);
    color: var(--chalk);
  }
  .mnav-social svg { width: 19px; height: 19px; fill: currentColor; }
}

@media (prefers-reduced-motion: reduce) {
  .mnav, .burger, .burger i { transition: none; }
}
