/* =========================================================================
   Pastry Collective
   Everything on this page is lit like a kitchen at the end of the day:
   warm paper, olive leaves, honey light pools, and a bulb that never quite sits still.
   ========================================================================= */

:root{
  /* paper and ink, both pulled warm */
  --paper:#FFFBF3;
  --paper-2:#FFF8EC;   /* the warmer paper used in gradients */
  --cream:#FCF4E6;
  --cream-2:#F7EAD3;
  --cream-3:#F0DCBA;
  --ink:#211913;
  --ink-2:#40332A;
  --ink-soft:#7A6653;

  /* the grove. Gold stays as a quiet honey, olive takes the accent that
     used to be copper and ember. */
  --gold:#B8A04A;
  --gold-lt:#D4C878;
  --amber:#7a9440;
  --ember:#8fa05a;
  --copper:#556b2f;
  --copper-dk:#3e4f22;
  --olive:#556b2f;
  --olive-lt:#8fa05a;
  --olive-dk:#3e4f22;

  /* night side */
  --night:#17110C;
  --night-2:#221811;

  --line:rgba(33,25,19,.13);
  --line-strong:rgba(33,25,19,.24);

  --glow-soft:0 0 60px rgba(85,107,47,.22);
  --shadow-sm:0 2px 12px rgba(84,52,20,.09);
  --shadow-md:0 20px 44px -24px rgba(84,45,15,.5);
  --shadow-lg:0 46px 90px -44px rgba(70,36,10,.62);
  --shadow-warm:0 18px 50px -26px rgba(85,107,47,.5);

  --radius:18px;
  --radius-lg:28px;
  --shell:1200px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --topbar-h:88px;
}

*,*::before,*::after{box-sizing:border-box;}
/* Tapping anything that behaves as a button drew the browser's own blue
   rectangle over it, square corners and all, which fights every rounded shape
   on the page. It is removed and replaced with a press of our own further
   down, so a tap still answers back. */
html{-webkit-tap-highlight-color:transparent;}
a,button,input,select,textarea,summary,label,[role="button"]{-webkit-tap-highlight-color:transparent;}

html{
  /* No scroll-behavior here. The wheel is eased by smooth-scroll.js and the
     two animating the same scroll at once is what makes it fight itself. */
  /* Only used when the script is off; the script itself aims at the section's
     first line of type instead. */
  scroll-padding-top:calc(var(--topbar-h) + 12px);
  -webkit-text-size-adjust:100%;
}

body{
  margin:0;
  color:var(--ink);
  font-family:'Work Sans',system-ui,-apple-system,'Segoe UI',sans-serif;
  font-size:17px;
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
  background:var(--cream);
}

/* The room light. Its own compositor layer, so scrolling never repaints it. */
body::before{
  content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:
    radial-gradient(70% 46% at 8% 0%,rgba(212,200,120,.22),transparent 62%),
    radial-gradient(58% 40% at 100% 22%,rgba(143,160,90,.16),transparent 60%),
    radial-gradient(80% 52% at 50% 100%,rgba(85,107,47,.12),transparent 66%);
  transform:translateZ(0);
}

img{max-width:100%;display:block;}
a{color:inherit;}
button{font:inherit;cursor:pointer;}

h1,h2,h3,h4{
  font-family:'Fraunces','Georgia',serif;
  font-weight:500;line-height:1.08;letter-spacing:-.015em;margin:0;
  font-optical-sizing:auto;
}
h1{font-size:clamp(2.6rem,7vw,5.3rem);}
h2{font-size:clamp(2rem,4.4vw,3.5rem);}
h3{font-size:clamp(1.15rem,2vw,1.5rem);}
p{margin:0 0 1.1em;}
p:last-child{margin-bottom:0;}

.shell{width:100%;max-width:var(--shell);margin:0 auto;padding:0 clamp(20px,5vw,44px);}
section{position:relative;padding:clamp(76px,10vw,138px) 0;}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
.skip{position:absolute;left:-999px;top:0;z-index:200;background:var(--ink);color:var(--cream);padding:12px 18px;border-radius:0 0 10px 0;}
.skip:focus{left:0;}
.hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0;}

/* ---------------------------------------------------------- atmosphere */

/* Fine film grain over the whole page, so flat colour never looks flat. */
.grain{
  position:fixed;inset:-120px;z-index:1;pointer-events:none;opacity:.1;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E");
  animation:grainShift 8s steps(6) infinite;will-change:transform;
}
@keyframes grainShift{
  0%{transform:translate(0,0)}
  20%{transform:translate(-14px,8px)}
  40%{transform:translate(10px,-12px)}
  60%{transform:translate(-8px,-6px)}
  80%{transform:translate(12px,10px)}
  100%{transform:translate(0,0)}
}

/* Reading progress, drawn as a burning ember line. */
.progress{position:fixed;inset:0 0 auto 0;height:2px;z-index:120;pointer-events:none;background:rgba(33,25,19,.06);}
.progress i{
  display:block;height:100%;width:100%;transform:scaleX(0);transform-origin:left center;will-change:transform;
  background:linear-gradient(90deg,var(--olive-dk),var(--olive) 55%,var(--olive-lt));
  box-shadow:0 0 12px rgba(143,160,90,.5);
  will-change:transform;
}

.eyebrow{
  font-size:.72rem;letter-spacing:.24em;text-transform:uppercase;
  font-weight:600;color:var(--copper);margin:0 0 .9rem;
  display:flex;align-items:center;gap:.7rem;
}
.eyebrow::before{
  content:"";width:26px;height:1px;background:currentColor;opacity:.6;
  transform-origin:left;animation:ruleIn 1s var(--ease-out) both;
}
@keyframes ruleIn{from{transform:scaleX(0);}to{transform:scaleX(1);}}

/* ---------------------------------------------------------------- buttons */

.btn{
  position:relative;overflow:hidden;isolation:isolate;
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  border:1.5px solid transparent;border-radius:999px;
  padding:.95rem 1.75rem;font-size:.95rem;font-weight:500;letter-spacing:.01em;
  text-decoration:none;white-space:nowrap;
  transition:transform .4s var(--ease-out),background .3s,color .3s,border-color .3s,box-shadow .4s;
}
.btn::after{
  content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(115deg,transparent 20%,rgba(255,255,255,.42) 48%,transparent 76%);
  transform:translateX(-130%);
}
.btn:hover::after{animation:shine .85s var(--ease-out);}
@keyframes shine{to{transform:translateX(130%);}}

.btn-sm{padding:.62rem 1.25rem;font-size:.86rem;}
.btn-solid{
  background:linear-gradient(135deg,var(--olive),#6a8439 60%,var(--olive-dk));
  color:#F7F4E6;box-shadow:var(--shadow-warm);
}
.btn-solid:hover{transform:translateY(-3px);box-shadow:0 22px 44px -20px rgba(85,107,47,.9),var(--glow-soft);}
.btn-ghost{background:transparent;color:var(--ink);border-color:var(--line-strong);}
.btn-ghost:hover{border-color:var(--ink);background:var(--ink);color:var(--cream);transform:translateY(-2px);}
.btn-ghost-light{background:rgba(255,246,231,.12);color:#FFF6E7;border-color:rgba(255,246,231,.55);}
.btn-ghost-light:hover{background:#FFF6E7;color:var(--ink);transform:translateY(-2px);}
.btn[disabled]{opacity:.5;pointer-events:none;}

/* ---------------------------------------------------------------- topbar */

.topbar{
  position:fixed;inset:0 0 auto 0;z-index:100;height:var(--topbar-h);
  display:flex;align-items:flex-start;
  transition:height .45s var(--ease);
}
/* The bar used to change background from nothing to a gradient, and a gradient
   cannot be faded into from transparent: CSS has nothing to interpolate, so it
   arrives in one frame however long the transition says. The filled bar is a
   layer of its own instead, sitting behind the logo and the links, and what
   fades is its opacity, which the browser can genuinely animate. */
.topbar::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;opacity:0;
  background:linear-gradient(to bottom,var(--cream),var(--cream-2));
  box-shadow:0 1px 0 rgba(139,145,84,.32),0 16px 34px -30px rgba(70,36,10,.8);
  transition:opacity .55s var(--ease);
}
.topbar.solid::before{opacity:1;}
/* The bar carries nine links, a theme toggle and a button, which is more than
   the 1200px body column can hold without wrapping. It gets its own width. */
.topbar-inner{
  /* Three tracks rather than a spaced out row. Spacing them apart put the menu
     wherever the logo and the button left room, so it slid around as items were
     added or dropped, and sat differently on the story page to the front page.
     An equal track either side of it holds the menu in the middle of the bar
     whatever it contains. */
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:clamp(16px,2vw,30px);
  width:100%;max-width:1560px;padding:0 clamp(20px,2.6vw,40px);
  height:var(--topbar-h);
}
.topbar-inner > .brand{justify-self:start;}
.topbar-inner > .nav-links{justify-self:center;}
.topbar-inner > .topbar-actions{justify-self:end;}
.nav-links a{white-space:nowrap;}
/* The fill itself lives on the layer above. Nothing here, or it would snap back
   to arriving in one frame. */

/* On landing the mark is far taller than the bar, so it hangs down over the hero
   rather than trying to fit inside it. Anchoring it to the top of the bar and
   moving it with margin, not alignment, keeps the shrink animatable: alignment
   cannot be transitioned, a margin can. */
.brand{display:flex;align-items:flex-start;align-self:flex-start;text-decoration:none;
  margin-top:6px;transition:margin-top .45s var(--ease);}
.topbar.solid .brand{margin-top:14px;}
/* The logo's lettering is black, so it disappears against the hero photo and
   against the dark theme. Show the white version wherever the bar is dark. */
.brand-mark.on-light{display:none;}
.topbar.solid .brand-mark.on-dark{display:none;}
.topbar.solid .brand-mark.on-light{display:block;}
:root[data-theme="dark"] .topbar.solid .brand-mark.on-dark{display:block;}
:root[data-theme="dark"] .topbar.solid .brand-mark.on-light{display:none;}
.brand-mark{
  height:207px;width:auto;
  transition:height .45s var(--ease);
}
/* Landing 83px, scrolled 61px: a wider gap between the two on purpose, so the
   bar visibly settles as you go down the page. */
.topbar.solid .brand-mark{height:61px;}

.nav-links{display:flex;gap:clamp(.9rem,1.5vw,1.5rem);align-items:center;flex-wrap:nowrap;}
.nav-links a{
  font-size:.92rem;text-decoration:none;color:#FFF6E7;position:relative;padding:.2rem 0;
  /* Three shadows rather than one. The tight pair sits right behind the letters
     and carries the contrast; the wide one pools underneath so the words hold up
     over the bright parts of the hero. One soft shadow was not enough on load. */
  text-shadow:0 0 3px rgba(0,0,0,.85),
              0 1px 3px rgba(0,0,0,.8),
              0 2px 12px rgba(0,0,0,.72),
              0 4px 30px rgba(0,0,0,.6);
  transition:color .3s,text-shadow .3s;
}
.nav-links a::after{
  content:"";position:absolute;left:0;right:100%;bottom:-3px;height:1.5px;
  background:linear-gradient(90deg,var(--amber),var(--olive-lt));
  box-shadow:0 0 8px rgba(143,160,90,.55);
  transition:right .4s var(--ease-out);
}
.nav-links a:hover::after{right:0;}
/* the story is a different kind of destination to the anchors beside it */
.nav-links a.nav-story{color:var(--olive-lt);}
.topbar.solid .nav-links a.nav-story{color:var(--copper);}
.nav-links a:hover{
  text-shadow:0 1px 2px rgba(0,0,0,.78),
              0 2px 9px rgba(0,0,0,.6),
              0 0 18px rgba(240,201,107,.405);
}
.topbar.solid .nav-links a{color:var(--ink-soft);text-shadow:none;}
.topbar.solid .nav-links a:hover{color:var(--ink);}

.topbar-actions{display:flex;align-items:center;gap:.8rem;}

.nav-toggle{background:none;border:0;padding:10px 6px;display:none;}
.nav-toggle span{
  display:block;width:24px;height:2px;background:#FFF6E7;margin:5px 0;border-radius:2px;
  transition:transform .4s var(--ease-out),opacity .25s,background .3s;
}
.topbar.solid .nav-toggle span{background:var(--ink);}
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg);}
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0;}
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg);}

/* The hidden attribute sets display:none from the browser's own stylesheet,
   but the rule below overrides it, which left this overlay laid across the
   whole page at all times: invisible, but swallowing every hover and click
   below the header. The [hidden] rule underneath outranks it and puts that
   right. Do not remove it. */
.mobile-nav[hidden]{display:none;}
.mobile-nav{
  position:fixed;inset:0;z-index:99;
  background:radial-gradient(80% 60% at 50% 12%,#2A1D12,#100C08 70%);
  display:flex;align-items:center;justify-content:center;
  opacity:0;transition:opacity .4s var(--ease);
}
.mobile-nav{pointer-events:none;}
.mobile-nav.open{opacity:1;pointer-events:auto;}
.mobile-nav nav{display:flex;flex-direction:column;gap:1.3rem;text-align:center;padding:2rem;}
.mobile-nav a{
  color:#FFF6E7;text-decoration:none;font-family:'Fraunces',serif;font-size:1.75rem;
  opacity:0;transform:translateY(18px);
  transition:opacity .5s var(--ease-out),transform .5s var(--ease-out),color .3s;
}
.mobile-nav a:hover{color:var(--olive-lt);}
.mobile-nav.open a{opacity:1;transform:none;}
.mobile-nav.open a:nth-child(1){transition-delay:.06s}
.mobile-nav.open a:nth-child(2){transition-delay:.11s}
.mobile-nav.open a:nth-child(3){transition-delay:.16s}
.mobile-nav.open a:nth-child(4){transition-delay:.21s}
.mobile-nav.open a:nth-child(5){transition-delay:.26s}
.mobile-nav.open a:nth-child(6){transition-delay:.31s}
.mobile-nav.open a:nth-child(7){transition-delay:.36s}
.mobile-nav.open a:nth-child(8){transition-delay:.41s}
.mobile-nav .btn{align-self:center;margin-top:.6rem;font-family:'Work Sans',sans-serif;font-size:1rem;}

@media (max-width:1080px){
  .nav-links{display:none;}
  /* No menu in the middle on a phone, so the bar goes back to two ends. */
  .topbar-inner{grid-template-columns:1fr auto;}
  .nav-toggle{display:block;}
  .topbar-actions .btn-solid{display:none;}
}

/* ---------------------------------------------------------------- hero */

.hero{
  position:relative;min-height:100svh;display:flex;align-items:flex-end;
  padding:calc(var(--topbar-h) + 40px) 0 clamp(64px,9vw,116px);
  overflow:hidden;color:#FFF6E7;isolation:isolate;
  background:var(--night);
}
.hero-media{position:absolute;inset:-9% 0;z-index:-3;will-change:transform;}
.hero-photo{
  width:100%;height:100%;object-fit:cover;object-position:center 62%;
  transform:scale(1.08);will-change:transform;backface-visibility:hidden;
  animation:kenburns 30s ease-in-out infinite alternate;
}
@keyframes kenburns{
  from{transform:scale(1.08) translate3d(0,0,0);}
  to{transform:scale(1.16) translate3d(-1.2%,-1.4%,0);}
}

.hero-veil{
  position:absolute;inset:0;z-index:-2;
  background:
    /* The headline sits at about 55 percent up. The kitchen behind it is white
       tile and steel, so the tint has to carry further up the frame than it did
       over the old, darker photo, or the words wash out. */
    linear-gradient(to top,rgba(14,10,7,.95) 0%,rgba(16,11,7,.82) 34%,rgba(18,12,8,.62) 58%,rgba(20,14,9,.42) 78%,rgba(14,10,7,.62) 100%),
    /* A soft pool of shade under the words themselves. Soft on purpose: it has
       to fall away before it reaches the pans on the right, so it reads as
       light in the room and not as a panel laid over the picture. */
    radial-gradient(85% 65% at 24% 66%,rgba(10,7,5,.62),rgba(10,7,5,.28) 55%,transparent 76%),
    radial-gradient(110% 70% at 18% 96%,rgba(85,107,47,.2835),transparent 62%),
    radial-gradient(80% 60% at 78% 8%,rgba(143,160,90,.2025),transparent 66%);
}
/* The bulb glow that used to sit above the pass has been removed at Johan's
   request. The tint over the photo is the .hero-veil gradient above, which
   stays. */
/* shafts of light coming in from the left, like a service door propped open */
.hero-rays{position:absolute;inset:-20% -10%;z-index:-2;pointer-events:none;overflow:hidden;}
.hero-rays span{
  position:absolute;top:-30%;height:170%;width:clamp(90px,13vw,190px);
  background:linear-gradient(to right,transparent,rgba(255,206,128,.16) 40%,rgba(255,206,128,.16) 60%,transparent);
  transform:rotate(14deg);opacity:.2;will-change:opacity,transform;
  animation:rayGlow 11s ease-in-out infinite;
}
.hero-rays span:nth-child(1){left:6%;}
.hero-rays span:nth-child(2){left:22%;width:clamp(60px,8vw,120px);animation-delay:-3.6s;}
.hero-rays span:nth-child(3){left:38%;width:clamp(40px,5vw,80px);animation-delay:-7.2s;}
@keyframes rayGlow{
  0%,100%{opacity:.12;transform:rotate(14deg) translateY(0);}
  50%{opacity:.4;transform:rotate(14deg) translateY(-24px);}
}

/* flour in the light, and the odd ember, drawn on a canvas in main.js */
/* A canvas is a replaced element, so inset:0 on its own leaves it at its
   intrinsic 300x150 and the specks get stretched across the hero. The size has
   to be stated. */
.hero-dust{position:absolute;inset:0;width:100%;height:100%;z-index:-1;pointer-events:none;}

/* flour and ember specks, drawn in js */

.hero-inner{position:relative;z-index:2;}
.hero .eyebrow{color:var(--olive-lt);text-shadow:0 1px 4px rgba(0,0,0,.75), 0 2px 12px rgba(0,0,0,.5);}
.hero-title{max-width:16ch;margin:0 0 1.4rem;}
.hero-title .word{
  display:inline-block;opacity:0;
  transform:translateY(44px) rotate(3deg) scale(.96);
  /* No filter here. A blur on the word puts it on its own compositing layer,
     and everything painted against that layer stops at the layer's rectangle,
     which shows up as a hard edged box around the word. The words rise and fade
     in instead, which reads the same and leaves nothing to clip. */
  text-shadow:0 1px 3px rgba(0,0,0,.55), 0 6px 26px rgba(0,0,0,.5);
  animation:wordIn 1.05s var(--ease-out) forwards;
  animation-delay:calc(.1s * var(--i) + .2s);
}
@keyframes wordIn{to{opacity:1;transform:none;}}
.hero-lede{max-width:46ch;font-size:1.09rem;color:rgba(255,246,231,.88);}
.hero-actions{display:flex;gap:.9rem;flex-wrap:wrap;margin-top:2rem;}

.hero-chips{list-style:none;display:flex;gap:.6rem;flex-wrap:wrap;padding:0;margin:2.4rem 0 0;}
.hero-chips a{
  display:inline-block;text-decoration:none;font-size:.82rem;letter-spacing:.02em;
  padding:.52rem 1.05rem;border-radius:999px;border:1px solid rgba(255,246,231,.28);
  color:rgba(255,246,231,.85);
  transition:background .3s,border-color .3s,color .3s,box-shadow .4s,transform .3s;
}
.hero-chips a:hover{
  background:rgba(255,246,231,.14);border-color:rgba(240,201,107,.8);color:#FFF6E7;
  box-shadow:0 0 22px rgba(240,201,107,.2363);transform:translateY(-2px);
}

.hero-scroll{
  position:absolute;right:clamp(20px,5vw,44px);bottom:clamp(28px,5vw,48px);z-index:2;
  display:flex;flex-direction:column;align-items:center;gap:.7rem;
  font-size:.68rem;letter-spacing:.26em;text-transform:uppercase;color:rgba(255,246,231,.62);
}
.hero-scroll-line{width:1px;height:66px;background:linear-gradient(to bottom,transparent,rgba(255,246,231,.7));position:relative;overflow:hidden;}
.hero-scroll-line::after{
  content:"";position:absolute;top:-40%;left:0;width:100%;height:40%;
  background:var(--olive-lt);box-shadow:0 0 10px var(--amber);
  animation:scrollRun 2.4s ease-in-out infinite;
}
@keyframes scrollRun{0%{top:-40%}60%,100%{top:100%}}
@media (max-width:700px){.hero-scroll{display:none;}}

/* ---------------------------------------------------------------- marquee */

.marquee{
  background:linear-gradient(90deg,var(--night),#2A1B10 50%,var(--night));
  color:#FFF6E7;padding:1rem 0;overflow:hidden;position:relative;
  border-top:1px solid rgba(240,201,107,.18);border-bottom:1px solid rgba(240,201,107,.18);
}
.marquee::before,.marquee::after{
  content:"";position:absolute;top:0;bottom:0;width:14%;z-index:2;pointer-events:none;
}
.marquee::before{left:0;background:linear-gradient(to right,var(--night),transparent);}
.marquee::after{right:0;background:linear-gradient(to left,var(--night),transparent);}
.marquee-track{
  display:flex;align-items:center;gap:2.2rem;width:max-content;
  animation:slide 64s linear infinite;will-change:transform;
  font-family:'Fraunces',serif;font-style:italic;font-size:1.18rem;
  color:rgba(255,246,231,.92);
}
.marquee.reverse .marquee-track{animation-direction:reverse;animation-duration:44s;}
.marquee .dot{width:5px;height:5px;border-radius:50%;background:var(--amber);flex:none;box-shadow:0 0 10px var(--amber);}
@keyframes slide{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ---------------------------------------------------------------- reveals */

.reveal{opacity:0;transform:translateY(30px);}
.reveal.in{
  opacity:1;transform:none;
  transition:opacity .95s var(--ease-out),transform .95s var(--ease-out);
  transition-delay:calc(.1s * var(--d,0));
}

/* headings that wipe up line by line */
/* The mask has to clip the line as it wipes up, but a tight line-height means
   it also clips the tail of a g, y or p. Padding gives the descender room and
   the matching negative margin keeps the spacing exactly as it was. */
/* Headings used to be revealed by sliding each line up out of a box with
   overflow:hidden. That box clipped the heading's text-shadow dead straight on
   all four sides, which is the dark rectangle that kept appearing behind the
   words. Nothing clips now: the lines rise and fade instead, so a shadow can
   spread as far as it likes. */
.line-mask{display:block;}
.line-mask > span{
  display:block;opacity:0;transform:translateY(26px);
  transition:transform 1s var(--ease-out), opacity .8s var(--ease-out);
  transition-delay:calc(.09s * var(--l,0));
}
.in .line-mask > span,.line-mask.in > span{transform:none;opacity:1;}

/* photos that uncover themselves */
.wipe{clip-path:inset(0 0 100% 0);transition:clip-path 1.2s var(--ease-out);}
/* The tribute photo and the pots in Gran's Kitchen used to roll down with the
   rest of the wipes. Against the quiet copy in that section it read as slow,
   so they fade up instead. The class stays so the observer still finds them,
   and the layer the rounded corners depend on is left alone. */
.grans .wipe{clip-path:none;opacity:0;transition:opacity .5s var(--ease-out);}
.grans .wipe.in{opacity:1;}

.wipe.in{clip-path:inset(0 0 0 0);}

/* ---------------------------------------------------------------- section head */

.section-head{
  display:flex;justify-content:space-between;align-items:flex-end;gap:2rem;
  flex-wrap:wrap;margin-bottom:clamp(34px,5vw,60px);position:relative;
}
.section-note{max-width:42ch;color:var(--ink-soft);margin:0;}

/* ---------------------------------------------------------------- about */

.about{
  background:linear-gradient(170deg,var(--paper),var(--paper-2) 60%,var(--cream));
  border-top:1px solid rgba(201,146,42,.22);border-bottom:1px solid rgba(201,146,42,.22);
  overflow:hidden;
}
.about-grid{display:grid;grid-template-columns:.85fr 1.15fr;gap:clamp(32px,6vw,80px);align-items:center;position:relative;z-index:1;}
.about-media{
  position:relative;border-radius:8px;overflow:hidden;
  aspect-ratio:3/4;box-shadow:var(--shadow-lg);
  isolation:isolate;transform:translateZ(0);
}
.about-media img{
  /* The picture drifts as you scroll, so it has to be taller than its frame or
     the frame shows through at one end and then the other. It was 112% tall and
     sitting flush with the top, which left dead space above and below on the way
     past. 136%, hung 18% above the frame, gives the drift room at both ends. */
  position:absolute;left:0;top:-18%;
  width:100%;height:136%;object-fit:cover;
  /* keep his head clear of the top edge rather than centring the crop */
  object-position:50% 30%;
  border-radius:inherit;
  will-change:transform;backface-visibility:hidden;
}
.about-media::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(200deg,rgba(255,196,102,.162),transparent 46%),
             radial-gradient(120% 80% at 50% 120%,rgba(60,30,10,.5),transparent 60%);
  mix-blend-mode:multiply;opacity:.75;
}
/* The badge used to be pinned into the corner with square edges, which left a
   sliver of photo curving around it where the frame's radius did not match.
   It floats clear of the corner now, rounded on all four sides. */
.about-media-badge{
  position:absolute;left:18px;bottom:18px;z-index:2;
  background:linear-gradient(135deg,var(--paper),var(--cream-2));
  padding:.85rem 1.25rem;border-radius:14px;
  display:flex;flex-direction:column;line-height:1.15;
  box-shadow:0 14px 34px -16px rgba(40,20,6,.6);
}
.badge-num{font-family:'Fraunces',serif;font-size:2rem;color:var(--copper);}
.badge-txt{font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-soft);}
/* Headings carry no margin of their own, which is right for a section head with
   an eyebrow above it, but here the name sits straight on top of the first line.
   Air above and below both, so the three pieces read as three pieces. */
.about-copy h2{margin-bottom:1.1rem;}
.about-copy .lede{
  font-family:'Fraunces',serif;font-style:italic;font-size:1.24rem;line-height:1.5;color:var(--ink-2);
  /* The opening line is set larger and italic, so it needs more air under it
     than the standard paragraph gap gives, or it crowds the paragraph beneath. */
  margin-bottom:1.7em;
}
/* Three columns rather than a wrapping flex row: the copy column is not wide
   enough for three 190px items plus the gaps, so the third one dropped to a
   line of its own. A grid cannot wrap, so they stay on one row. */
.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(12px,1.8vw,26px);margin-top:2.3rem;}
.stat{display:flex;flex-direction:column;gap:.25rem;position:relative;padding-left:1rem;}
.stat::before{
  content:"";position:absolute;left:0;top:.3rem;bottom:.3rem;width:2px;border-radius:2px;
  background:linear-gradient(to bottom,var(--amber),transparent);
}
.stat strong{font-family:'Fraunces',serif;font-size:2.2rem;color:var(--copper);line-height:1;}
.stat span{font-size:.85rem;color:var(--ink-soft);line-height:1.4;}

.offer{overflow:hidden;}

/* ---------------------------------------------------------------- cards */

.card-grid{display:grid;gap:clamp(18px,2.4vw,30px);grid-template-columns:repeat(auto-fill,minmax(310px,1fr));}
/* Workshops are CENTRED, including a short last row, and there are rarely a full
   six. Grid cannot do this: its items sit in fixed tracks, so three cards in a
   four track row leave a hole on the right and read as left aligned. That is
   what the earlier `repeat(4,1fr)` rule here did. A wrapping flex row hands the
   leftover space to justify-content instead, so any count from one to six
   centres, and so does a partial last row.
   NB this needs the same #workshops specificity to beat nothing else, but do not
   demote it to a class: it must stay stronger than .card-grid above. */
#workshops .card-grid{
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(14px,1.6vw,22px);
}
/* 286px basis, a tenth wider than it was, so a workshop name has room to sit on
   one line instead of dropping a word. Four across no longer fit the standard
   shell at that width, so this section alone gets a wider one rather than losing
   a card from the row. */
/* Wide enough for five across at 286 plus the gaps, and no wider: six would
   need 1826 and can never fit, so a row tops out at five and the rest wrap
   underneath, still centred. */
#workshops .shell{max-width:1620px;}
#workshops .session-card{flex:0 1 286px;}
/* And the name itself balances rather than filling the first line and leaving
   whatever is left underneath. */
#workshops .card-body h3,
#courses .card-body h3,
.table-main h3{text-wrap:pretty;text-wrap:balance;}
@media (max-width:640px){
  #workshops .session-card{flex:1 1 100%;}
}
.session-card{
  position:relative;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--radius-lg);overflow:hidden;display:flex;flex-direction:column;
  transform-style:preserve-3d;
  transition:transform .6s var(--ease-out),box-shadow .6s var(--ease-out),border-color .4s;
}
/* The pointer carries a soft pool of warm light across anything it passes over.
   Wider and much gentler than the first version, which read as a spotlight. */
.session-card::after,
.upnext-card::after,
.review::after,
.studio-media::after,
.cal-card::after,
.booking-panel::after,
.summary-card::after{
  content:"";position:absolute;inset:0;pointer-events:none;border-radius:inherit;z-index:2;
  background:radial-gradient(85% 70% at var(--mx,50%) var(--my,50%),rgba(255,196,102,.108),transparent 72%);
  opacity:0;transition:opacity .7s var(--ease-out);
}
/* The calendar panel is several times the size of a card, so the shared pool
   spread across the whole month and read as a wash. Its light is drawn tighter
   and dimmer so it stays a pool that follows the pointer. */
.cal-card::after{
  background:radial-gradient(280px 280px at var(--mx,50%) var(--my,50%),rgba(255,196,102,.0675),transparent 68%);
}

.session-card:hover::after,
.upnext-card:hover::after,
.review:hover::after,
.studio-card:hover .studio-media::after,
.cal-card:hover::after,
.booking-panel:hover::after,
.summary-card:hover::after{opacity:1;}

/* these need a positioning context for the light to sit in */
.cal-card,.booking-panel,.review,.upnext-card{position:relative;}
.session-card:hover{box-shadow:var(--shadow-md),0 0 60px -22px rgba(143,160,90,.5063);border-color:rgba(201,146,42,.4);}
.session-card:hover::after{opacity:1;}
.card-media{
  display:block;overflow:hidden;aspect-ratio:4/3;background:var(--cream-2);
  border-radius:var(--radius-lg) var(--radius-lg) 0 0;
  isolation:isolate;transform:translateZ(0);
}
.card-media img{
  width:100%;height:100%;object-fit:cover;border-radius:inherit;
  transition:transform 1.3s var(--ease-out);
}
.session-card:hover .card-media img{transform:scale(1.08);}
.card-body{padding:1.5rem 1.6rem 1.6rem;display:flex;flex-direction:column;gap:.6rem;flex:1;}
.card-when{font-size:.76rem;letter-spacing:.14em;text-transform:uppercase;color:var(--copper);margin:0;font-weight:600;}
.card-when span{color:var(--ink-soft);font-weight:400;letter-spacing:.06em;}
.card-body h3{margin:0;}
.card-body h3 a{text-decoration:none;transition:color .3s;}
.card-body h3 a:hover{color:var(--copper);}
.card-summary{color:var(--ink-soft);font-size:.94rem;margin:0;}
/* Two rows, always. As one wrapping row, a short price like R695 fitted beside
   the button and a longer one like R1 200 did not, so the same foot came out at
   two different heights across a row of cards and the rules above them did not
   line up. A grid holds the price and the seats on the first line and the button
   on the second, whatever the numbers are. */
.card-foot{
  margin-top:auto;padding-top:1rem;border-top:1px solid var(--line);
  display:grid;grid-template-columns:auto 1fr;align-items:center;
  gap:.35rem .6rem;
}
.price{font-family:'Fraunces',serif;font-size:1.38rem;color:var(--ink);}
.seats{font-size:.78rem;color:var(--olive);font-weight:600;letter-spacing:.04em;}
.seats.sold{color:var(--copper);}
.card-foot .price{grid-column:1;}
.card-foot .seats{grid-column:2;justify-self:start;}
/* The button takes the whole of the second line and sits to the right, so every
   card in a row puts it in the same place. */
.card-foot .btn{grid-column:1 / -1;justify-self:end;margin-top:.35rem;}
.empty-note{color:var(--ink-soft);}

/* ---------------------------------------------------------------- showcase */

.showcase{padding:0;background:var(--night);color:#FFF6E7;position:relative;}
.showcase::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(60% 40% at 50% 30%,rgba(143,160,90,.162),transparent 66%);
}
.showcase-sticky{position:relative;min-height:200vh;}
.showcase-frame{position:sticky;top:0;height:100svh;display:flex;align-items:center;justify-content:center;overflow:hidden;}
.showcase-frame img{
  /* Laid out at the size it ends at, and held scaled down until the scroll
     brings it up. Two things fall out of that. It is rasterised at the big size,
     so it is sharp at full zoom, which growing a small element was not. And the
     only thing that changes as you scroll is a transform, which the compositor
     handles on its own: setting width and height per frame, as this did for a
     few hours today, forces a layout on every frame and is felt on a phone as
     the scroll sticking. --peak has to match the figure in main.js. */
  --peak:2.45;
  width:calc(min(560px,86vw) * var(--peak));
  height:calc(min(74vh,740px) * var(--peak));
  /* The global img rule caps everything at the width of its container, which on
     a phone shrank this to the frame and left the piece smaller than it was
     meant to be. It is deliberately larger than its frame and clipped by it. */
  max-width:none;
  transform:scale(calc(1 / var(--peak)));
  object-fit:cover;border-radius:var(--radius-lg);
  transform-origin:center;
  box-shadow:0 40px 100px -30px rgba(0,0,0,.8),0 0 90px -20px rgba(143,160,90,.27);
}
.showcase-copy{
  position:absolute;left:50%;bottom:13vh;transform:translateX(-50%) translateY(24px);
  width:min(620px,88vw);text-align:center;z-index:3;opacity:0;
  transition:opacity .9s var(--ease-out),transform .9s var(--ease-out);
  /* The sugar piece behind this is pale grey and cream, so one wide soft shadow
     disappears into it. Layered instead: a tight one to give the letters an edge,
     then wider ones that pool into a dark glow behind the whole line. Nothing
     clips it now that the line masks are gone. */
  text-shadow:
    0 1px 2px rgba(0,0,0,.9),
    0 2px 8px rgba(0,0,0,.85),
    0 4px 22px rgba(0,0,0,.8),
    0 8px 48px rgba(0,0,0,.7),
    0 14px 80px rgba(0,0,0,.55);
}
.showcase-copy.in{opacity:1;transform:translateX(-50%) translateY(0);}
.showcase-copy .eyebrow{color:var(--olive-lt);justify-content:center;}
.showcase-copy p{
  color:#fff;margin-top:1rem;
  /* White, so it does not read as grey against its own glow. Small text does not
     need the full five layer glow the heading carries either; at this size the
     wide ones only smudge it. */
  text-shadow:0 1px 2px rgba(0,0,0,.9), 0 2px 10px rgba(0,0,0,.8), 0 6px 30px rgba(0,0,0,.6);
}

/* ---------------------------------------------------------------- tables */

.tables{position:relative;color:#FFF6E7;overflow:hidden;background:var(--night);}
.tables-bg{position:absolute;inset:-12% 0;z-index:0;}
.tables-bg img{
  width:100%;height:100%;object-fit:cover;opacity:.42;will-change:transform;backface-visibility:hidden;
}
.tables::after{
  content:"";position:absolute;inset:0;z-index:1;
  background:
    linear-gradient(to bottom,rgba(18,12,8,.88),rgba(22,15,9,.74)),
    radial-gradient(70% 50% at 82% 14%,rgba(143,160,90,.2025),transparent 62%),
    radial-gradient(80% 60% at 10% 100%,rgba(85,107,47,.189),transparent 60%);
}
.tables-inner{position:relative;z-index:2;}
.tables-copy{max-width:52ch;margin-bottom:clamp(30px,5vw,52px);}
.tables-copy .eyebrow{color:var(--olive-lt);}
.tables-copy p{color:rgba(255,246,231,.84);}

/* Three shots of what an evening at the counter actually looks like, between
   the copy and the dates. They sit on the dark ground, so a warm hairline edge
   keeps them from floating. */
.table-shots{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(12px,1.6vw,20px);margin-bottom:clamp(30px,4.5vw,48px);
}
.table-shots figure{margin:0;overflow:hidden;border-radius:14px;}
.table-shots img{
  width:100%;height:clamp(170px,17vw,250px);object-fit:cover;display:block;
  border:1px solid rgba(255,246,231,.16);border-radius:14px;
  transition:transform .8s var(--ease-out);
}
.table-shots figure:hover img{transform:scale(1.04);}
@media (max-width:720px){
  .table-shots{grid-template-columns:1fr 1fr;}
  .table-shots figure:last-child{display:none;}
}
.table-list{display:flex;flex-direction:column;}
.table-row{
  display:grid;grid-template-columns:86px 1fr auto;gap:clamp(16px,3vw,34px);align-items:center;
  padding:1.7rem 0;border-top:1px solid rgba(255,246,231,.16);
  transition:padding-left .5s var(--ease-out);
}
.table-row:last-child{border-bottom:1px solid rgba(255,246,231,.16);}
.table-row:hover{padding-left:.7rem;}
.table-date{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  width:78px;height:78px;border-radius:16px;
  background:linear-gradient(150deg,rgba(255,246,231,.12),rgba(255,246,231,.04));
  border:1px solid rgba(240,201,107,.34);
  transition:box-shadow .5s,transform .5s var(--ease-out);
}
.table-row:hover .table-date{box-shadow:0 0 34px -6px rgba(143,160,90,.3712);transform:translateY(-3px);}
.table-date .d{font-family:'Fraunces',serif;font-size:1.75rem;line-height:1;}
.table-date .m{font-size:.7rem;letter-spacing:.18em;text-transform:uppercase;color:var(--olive-lt);}
.table-main h3{margin-bottom:.35rem;}
.table-main h3 a{text-decoration:none;transition:color .3s;}
.table-main h3 a:hover{color:var(--olive-lt);}
.table-main p{color:rgba(255,246,231,.78);font-size:.95rem;margin:0;}
.table-meta{font-size:.82rem !important;color:rgba(255,246,231,.55) !important;margin-top:.4rem !important;}
.table-act{display:flex;flex-direction:column;align-items:flex-end;gap:.5rem;}
.table-act .price{color:#FFF6E7;}
.table-act .seats{color:var(--olive-lt);}

/* ---------------------------------------------------------------- calendar */

.calendar{background:linear-gradient(180deg,var(--cream-2),var(--cream));overflow:hidden;}
.cal-card{
  position:relative;z-index:1;
  background:linear-gradient(165deg,var(--paper),var(--paper-2));
  border:1px solid rgba(201,146,42,.28);border-radius:var(--radius-lg);
  padding:clamp(18px,3vw,34px);
  box-shadow:0 30px 70px -40px rgba(70,36,10,.6),inset 0 1px 0 rgba(255,255,255,.7);
}
.cal-toolbar{display:flex;align-items:center;justify-content:space-between;gap:1rem;}
.cal-toolbar h3{font-size:1.45rem;}
.cal-nav{
  width:44px;height:44px;border-radius:50%;border:1px solid var(--line-strong);background:var(--paper);
  color:var(--ink);display:grid;place-items:center;
  transition:background .3s,color .3s,border-color .3s,transform .3s,box-shadow .4s;
}
.cal-nav svg{width:18px;height:18px;}
.cal-nav:hover{background:var(--ink);color:var(--cream);border-color:var(--ink);transform:scale(1.06);box-shadow:var(--glow-soft);}
.cal-legend{display:none;gap:1.3rem;flex-wrap:wrap;margin:1.2rem 0 1rem;font-size:.8rem;color:var(--ink-soft);}
.cal-legend span{display:flex;align-items:center;gap:.45rem;}
.key{width:9px;height:9px;border-radius:50%;display:inline-block;}
.key.workshop{background:var(--copper);box-shadow:0 0 8px rgba(85,107,47,.405);}
.key.chefs_table{background:var(--olive);box-shadow:0 0 8px rgba(126,130,83,.405);}
.key.course{background:var(--gold);box-shadow:0 0 8px rgba(201,146,42,.405);}
.key.full{background:var(--line-strong);}
.cal-legend-hint{margin-left:auto;color:var(--ink-soft);font-style:italic;}

.cal-weekdays{
  display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px;margin-bottom:6px;
  font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-soft);text-align:center;
}
/* 1fr keeps a min-width of auto, so a long session name in a day cell forced
   the whole grid wider than a phone screen and took the page with it.
   minmax(0,1fr) lets the columns shrink and the label ellipsis do its job. */
.cal-grid{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:6px;}
/* The cells were only as tall as whatever was in them, which came out at 52px
   on a desktop and made the month look cramped. A floor of 78px gives the grid
   half as much height again and leaves room for a chip or two in a day without
   the row jumping about. */
.cal-day{min-width:0;min-height:78px;}
/* Wide screens keep the named chips. A phone gets a glowing dot per session
   instead, with the detail in the pop out, because a day cell that size cannot
   carry a title. Both are in the markup; these rules pick one. */
.cal-event{
  display:block;text-decoration:none;border:0;text-align:left;width:100%;border-radius:8px;
  padding:.32rem .5rem;font-size:.72rem;line-height:1.25;font-weight:600;color:#FFF6E7;
  background:linear-gradient(135deg,var(--copper),#6a8439);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;
  transition:transform .3s var(--ease-out),box-shadow .3s,filter .3s;
}
.cal-event.chefs_table{background:linear-gradient(135deg,#445627,var(--olive));}
.cal-event.course{background:linear-gradient(135deg,var(--gold),#B8892A);}
.cal-event.full{background:var(--cream-3);color:var(--ink-soft);}
.cal-event:hover{transform:translateY(-2px) scale(1.02);box-shadow:0 8px 18px -8px rgba(85,107,47,.9);filter:brightness(1.06);}

.cal-dots{display:none;flex-wrap:wrap;gap:5px;margin-top:auto;padding-top:4px;}
.cal-dot{
  width:9px;height:9px;border-radius:50%;display:block;flex:none;
  background:var(--copper);box-shadow:0 0 9px rgba(85,107,47,.5737);
}
.cal-dot.chefs_table{background:var(--olive);box-shadow:0 0 9px rgba(126,130,83,.5737);}
.cal-dot.course{background:var(--gold);box-shadow:0 0 9px rgba(201,146,42,.5737);}
.cal-dot.consultation{background:#7E6BA8;box-shadow:0 0 9px rgba(126,107,168,.8);}
.cal-dot.full{background:var(--line-strong);box-shadow:none;opacity:.7;}


.cal-day.picked{
  border-color:var(--amber);
  box-shadow:0 0 0 3px rgba(143,160,90,.22);
  background:var(--paper);
}

/* ---------------------------------------------------------------- day pop out */

.cal-pop{
  position:absolute;z-index:20;
  background:linear-gradient(165deg,var(--paper),var(--paper-2));
  border:1px solid rgba(201,146,42,.4);border-radius:16px;
  box-shadow:0 30px 70px -28px rgba(40,20,6,.7);
  padding:1.1rem 1.2rem 1.2rem;
  opacity:0;transform:translateY(-8px);
  transition:opacity .28s var(--ease-out),transform .28s var(--ease-out);
}
.cal-pop.open{opacity:1;transform:none;}
.cal-pop-close{
  position:absolute;top:.6rem;right:.6rem;width:30px;height:30px;border-radius:50%;
  border:0;background:transparent;color:var(--ink-soft);display:grid;place-items:center;
}
.cal-pop-close:hover{background:var(--cream-2);color:var(--ink);}
.cal-pop-close svg{width:15px;height:15px;}
.cal-pop-date{
  font-size:.72rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--copper);font-weight:600;margin:0 0 .7rem;padding-right:2rem;
}
.cal-pop-list{display:flex;flex-direction:column;gap:.9rem;}
.cal-pop-item{
  display:flex;flex-direction:column;gap:.3rem;
  padding-bottom:.9rem;border-bottom:1px solid var(--line);
}
.cal-pop-item:last-child{border-bottom:0;padding-bottom:0;}
.cal-pop-time{font-size:.78rem;letter-spacing:.08em;color:var(--ink-soft);}
.cal-pop-item h4{font-family:'Fraunces',serif;font-size:1.08rem;font-weight:500;margin:0;}
.cal-pop-summary{font-size:.88rem;color:var(--ink-soft);margin:0;}
.cal-pop-foot{display:flex;align-items:center;gap:.7rem;flex-wrap:wrap;margin-top:.35rem;}
.cal-pop-foot .price{font-size:1.1rem;}
.cal-pop-foot .btn{margin-left:auto;}

/* on a phone it arrives as a sheet from the bottom instead */
/* the back to top button lands on the sheet's own button, so it stands down */
body.cal-open .to-top{opacity:0;visibility:hidden;}

@media (max-width:720px){
  .cal-pop{
    position:fixed;left:0;right:0;bottom:0;top:auto;width:auto !important;
    border-radius:20px 20px 0 0;border-bottom:0;max-height:78svh;overflow-y:auto;
    padding:1.2rem 1.2rem calc(1.4rem + env(safe-area-inset-bottom,0px));
    transform:translateY(100%);
    box-shadow:0 -20px 60px -20px rgba(0,0,0,.6);
  }
  .cal-pop.open{transform:none;}
}

.cal-upnext{margin-top:clamp(24px,4vw,42px);display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));}
.upnext-card{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:1.15rem 1.25rem;display:flex;flex-direction:column;gap:.4rem;
  transition:transform .5s var(--ease-out),box-shadow .5s,border-color .3s;
}
.upnext-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:rgba(201,146,42,.4);}
.upnext-card .when{font-size:.76rem;letter-spacing:.12em;text-transform:uppercase;color:var(--copper);font-weight:600;}
.upnext-card h4{font-family:'Fraunces',serif;font-size:1.1rem;margin:0;font-weight:500;}
.upnext-card .row{display:flex;align-items:center;gap:.7rem;margin-top:.4rem;}
.upnext-card .row .btn{margin-left:auto;}

@media (max-width:720px){
  /* On a phone the chef's table rows become cards, so the section reads the
     same way as the up next cards under the diary: a card, a line of date, the
     title, then price, seats and the button on one row. Same shape and rhythm,
     the dark palette of this section instead of paper. */
  .table-list{gap:14px;}
  .table-row{
    display:block;
    background:rgba(255,246,231,.05);
    border:1px solid rgba(240,201,107,.22);
    border-radius:var(--radius);
    padding:1.15rem 1.25rem;
  }
  .table-row:last-child{border-bottom:1px solid rgba(240,201,107,.22);}
  /* no shifting about on tap, there is no pointer to follow here */
  .table-row:hover{padding-left:1.25rem;}
  /* a 78px date block does not belong on a card this size, so it becomes the
     same small line of capitals the up next cards open with */
  .table-date{
    flex-direction:row;align-items:baseline;justify-content:flex-start;gap:.4rem;
    width:auto;height:auto;background:none;border:0;border-radius:0;
    margin-bottom:.5rem;
  }
  .table-row:hover .table-date{box-shadow:none;transform:none;}
  .table-date .d{
    font-family:'Work Sans',sans-serif;font-size:.76rem;font-weight:600;
    letter-spacing:.12em;line-height:1.2;color:var(--olive-lt);
  }
  .table-date .m{font-size:.76rem;line-height:1.2;}
  .table-main h3{font-size:1.16rem;margin-bottom:.3rem;}
  .table-act{
    display:flex;flex-direction:row;align-items:center;gap:.7rem;margin-top:.85rem;
  }
  /* the markup runs price, button, seats. The up next card runs price, seats,
     button, so the order is set here rather than moved in the template. */
  .table-act .price{order:1;}
  .table-act .seats{order:2;}
  .table-act .btn{order:3;margin-left:auto;}

  /* the gran sign up button sits on its own line once the field wraps, and
     left against the edge it looked unfinished */
  .subscribe{justify-content:center;}
  .subscribe .btn{margin:0 auto;}

  /* the three shortcuts under the hero wrapped onto two lines on a phone and
     left the last one stranded. Trimmed just enough that all three sit on one
     row at 390px, still wrapping rather than overflowing on anything narrower. */
  .hero-chips{justify-content:center;gap:.38rem;}
  .hero-chips a{font-size:.72rem;padding:.46rem .68rem;}

  /* The pool of shade this used to carry read as a square block sitting on the
     petals. It wears the same glow as every other line of text on a phone
     instead: tight shadow for the edge, two mid blurs, one wide pool. Nothing
     behind it, so there is no shape to see. */
  .showcase-copy{
    bottom:9vh;width:min(560px,90vw);
    padding:0 clamp(18px,6vw,32px);
    background:none;
  }
  .showcase-copy,
  .showcase-copy p,
  .showcase-copy .eyebrow{
    text-shadow:
      0 1px 2px rgba(0,0,0,.85),
      0 2px 8px rgba(0,0,0,.8),
      0 5px 22px rgba(0,0,0,.7),
      0 10px 48px rgba(0,0,0,.55);
  }
  .showcase-copy p{color:#fff;}

  .cal-day{min-height:66px;padding:.35rem;}
  .cal-event{display:none;}
  .cal-dots{display:flex;}
  .cal-legend{display:flex;}
  .cal-day[data-day]{cursor:pointer;}
}

/* ---------------------------------------------------------------- courses, gran's */

.courses{position:relative;overflow:hidden;}
.courses-holding{
  background:linear-gradient(160deg,var(--paper),var(--cream-2));
  border:1px dashed rgba(201,146,42,.5);border-radius:var(--radius-lg);
  padding:clamp(28px,5vw,48px);text-align:center;color:var(--ink-soft);
}

.grans{
  background:linear-gradient(200deg,var(--paper),var(--paper-2) 55%,var(--cream-2));
  border-top:1px solid rgba(201,146,42,.22);overflow:hidden;
}
.grans-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(30px,6vw,72px);align-items:center;position:relative;z-index:1;}
.grans-media{
  position:relative;border-radius:8px;overflow:hidden;aspect-ratio:4/3;
  box-shadow:var(--shadow-lg);isolation:isolate;transform:translateZ(0);
}
.grans-media img{width:100%;height:112%;object-fit:cover;border-radius:inherit;will-change:transform;backface-visibility:hidden;}
.grans-media::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(80% 60% at 20% 10%,rgba(255,196,102,.216),transparent 60%);
}

.subscribe{display:flex;gap:.6rem;flex-wrap:wrap;margin-top:1.9rem;position:relative;}
.subscribe input[type=email]{
  flex:1 1 240px;padding:.95rem 1.15rem;border-radius:999px;border:1px solid var(--line-strong);
  background:var(--paper);font:inherit;color:var(--ink);
  transition:border-color .3s,box-shadow .3s;
}
.subscribe input[type=email]:focus{outline:none;border-color:var(--amber);box-shadow:0 0 0 4px rgba(143,160,90,.2);}
.form-msg{flex-basis:100%;margin:.4rem 0 0;font-size:.86rem;color:var(--olive);min-height:1.2em;}
.form-msg.error{color:var(--copper);}

/* ---------------------------------------------------------------- studio strip */

.gallery{background:linear-gradient(180deg,var(--paper),var(--paper-2));padding-bottom:clamp(60px,8vw,100px);}

/* The strip runs the full width of the window rather than sitting inside the
   1200px column the rest of the page uses. */
.studio-strip{padding:0 clamp(18px,2.6vw,40px);}
.studio-grid{
  display:grid;gap:clamp(14px,2vw,32px);
  /* Eight cards, so four across leaves two even rows rather than a short one. */
  grid-template-columns:repeat(4,1fr);
}
.studio-card{
  margin:0;position:relative;display:flex;flex-direction:column;gap:.7rem;
}
.studio-media{
  position:relative;overflow:hidden;border-radius:var(--radius-lg);
  isolation:isolate;transform:translateZ(0);
  background:var(--cream-2);box-shadow:var(--shadow-sm);
  height:clamp(400px,37vw,600px);
  transition:box-shadow .9s cubic-bezier(.4,0,.2,1);
}
.studio-card:hover .studio-media,
.studio-card:focus-within .studio-media{
  box-shadow:var(--shadow-md),0 0 54px -20px rgba(143,160,90,.5063);
}
.studio-media img,
.studio-media video{
  border-radius:inherit;
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  pointer-events:none;
}
.studio-media img{
  transition:opacity .9s cubic-bezier(.4,0,.2,1);
}

/* The clip sits over the still and fades in once it is actually playing, so a
   card never flashes a black frame while the file is still loading. */
.studio-clip{
  opacity:0;z-index:1;
  transition:opacity .9s cubic-bezier(.4,0,.2,1);
}
.studio-card.playing .studio-clip{opacity:1;}
/* The still fades out underneath as the clip fades up, so the two cross rather
   than one appearing over the other. Safe because .playing is only set once the
   video is rendering frames, and the clip's poster is that same still anyway. */
.studio-card.playing .studio-media img{opacity:0;}

/* This used to draw a small play marker on a touch screen, to say that a card
   held a clip. On a phone the card in view plays itself, so the marker was
   telling people to tap something that was already running, and it read as a
   video player rather than as part of the picture. Off everywhere now. The
   markup is left in place so it can be brought back with one rule. */
.studio-cue{display:none !important;}

.studio-card figcaption{
  font-size:.84rem;line-height:1.4;color:var(--ink-soft);padding:0 .2rem;
}
@media (max-width:1100px){
  .studio-grid{grid-template-columns:repeat(2,1fr);}
  .studio-media{height:clamp(360px,43vw,520px);}
}
@media (max-width:720px){
  .studio-grid{grid-template-columns:repeat(2,1fr);}
  .studio-media{height:clamp(300px,56vw,440px);}
}
@media (max-width:420px){
  .studio-grid{grid-template-columns:1fr;}
  .studio-media{height:min(104vw,480px);}
}

/* ---------------------------------------------------------------- reviews */

.reviews{background:linear-gradient(180deg,var(--cream-2),var(--cream));}
/* Centred, the same way the workshops are, and for the same reason: a grid puts
   its items in fixed tracks, so two reviews in a five track row sit hard left
   and read as a mistake. A wrapping flex row hands the leftover space to
   justify-content, so any number from one upwards centres, and so does a short
   last row. */
.review-grid{
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(14px,1.8vw,20px);
}
.review{
  flex:0 1 240px;
  margin:0;background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:1.3rem 1.35rem;display:flex;flex-direction:column;gap:.65rem;position:relative;
  overflow:hidden;
  transition:transform .5s var(--ease-out),box-shadow .5s;
}
/* Five across at that width plus the gaps needs 1280, and six would need 1540,
   which this cannot reach. So a row tops out at five. */
#reviews .shell{max-width:1380px;}
@media (max-width:640px){
  .review{flex:1 1 100%;}
}
.review:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);}
/* The accent used to be a short tab on the top edge, which reads as a stray
   mark until there are several cards in a row for it to rhyme with. Down the
   left instead, where it works as a quote's own margin rule: full height of the
   card, fading out at both ends so it does not fight the rounded corners. */
.review::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:linear-gradient(180deg,var(--amber),var(--olive-lt));
}
.review-platform{font-size:.64rem;letter-spacing:.16em;text-transform:uppercase;color:var(--copper);margin:0;font-weight:600;}
.review-quote{font-family:'Fraunces',serif;font-size:.97rem;line-height:1.48;color:var(--ink-2);margin:0;}
.review footer{margin-top:auto;font-size:.78rem;line-height:1.45;color:var(--ink-soft);}

/* ---------------------------------------------------------------- contact */

/* ---------------------------------------------------------------- faq
   Sits between the reviews and the contact form, so it carries the reviews'
   cream ground down to the contact band rather than introducing a third one.
   Built on <details>, which means no JavaScript: it opens with a click, with
   the keyboard and with scripting off, and the answer is in the markup whether
   it is open or shut, which is the whole point of putting it here. */
.faq{background:linear-gradient(180deg,var(--cream),var(--paper));border-top:1px solid rgba(201,146,42,.18);overflow:hidden;}
.faq-list{max-width:820px;margin:0 auto;position:relative;z-index:1;}
.faq-item{
  border:1px solid var(--line);border-radius:var(--radius);
  background:var(--paper);margin-bottom:.75rem;overflow:hidden;
  box-shadow:var(--shadow-sm);transition:border-color .3s,box-shadow .3s;
}
.faq-item[open]{border-color:rgba(85,107,47,.42);box-shadow:var(--shadow-md);}
.faq-item summary{
  display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:clamp(15px,2vw,20px) clamp(18px,2.4vw,26px);
  cursor:pointer;list-style:none;font-weight:600;color:var(--ink);
  font-size:clamp(.98rem,1.5vw,1.06rem);line-height:1.45;
}
.faq-item summary::-webkit-details-marker{display:none;}
.faq-item summary:hover{color:var(--olive);}
.faq-item summary:focus-visible{outline:2px solid var(--olive);outline-offset:-3px;}
.faq-item summary svg{width:20px;height:20px;flex:0 0 20px;color:var(--olive);transition:transform .3s;}
.faq-item[open] summary svg{transform:rotate(180deg);}
.faq-answer{padding:0 clamp(18px,2.4vw,26px) clamp(16px,2.2vw,22px);}
.faq-answer p{margin:0;color:var(--ink-soft);line-height:1.65;max-width:68ch;}

.contact{background:linear-gradient(180deg,var(--paper),var(--cream));border-top:1px solid rgba(201,146,42,.22);overflow:hidden;}
/* A touch more room for the words: "Talk to the kitchen" was breaking over two
   lines in a column sized for the form beside it. */
.contact-grid{display:grid;grid-template-columns:1fr 1.1fr;gap:clamp(30px,5vw,70px);align-items:start;position:relative;z-index:1;}
.contact-copy h2{text-wrap:balance;max-width:none;}
@media (min-width: 901px){
  /* Enough width that the heading holds one line at every desktop size, with the
     type easing down a little as the column narrows. */
  /* No nowrap: a heading that refuses to wrap will push the page sideways if the
     wording is ever changed to something longer. The wider column and the size
     below hold it on one line as it stands. */
  .contact-copy h2{font-size:clamp(1.9rem,3.2vw,3.1rem);}
}
/* The email and the WhatsApp number are a pair, so they are written as one:
   the same size, the same colour, marks riding on the line beside the words,
   and both turning olive on hover. */
.contact-direct a,
.contact-wa a{
  display:inline-flex;align-items:center;gap:.55rem;
  font-family:'Fraunces',serif;font-size:1.22rem;color:var(--copper);
  text-decoration:none;transition:color .3s var(--ease-out);
}
/* The contact block sits on the dark, where --copper is already the lighter
   olive, so the hover has to lift rather than deepen. --ember is that one shade
   up in both palettes. */
.contact-direct a:hover,
.contact-wa a:hover{color:var(--ember);}
.contact-direct svg,
.contact-wa svg{width:1.05em;height:1.05em;flex:none;}
.contact-form{display:flex;flex-direction:column;gap:1rem;position:relative;}
/* There is no card behind this form, so a light clipped to its box left a hard
   straight edge wherever the pool met a side. The mask fades the layer out
   towards its own edges, which means the pool is already gone by the time it
   reaches one. The pool is small for the same reason. */
.contact-form::after{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:2;
  background:radial-gradient(240px 240px at var(--mx,50%) var(--my,50%),rgba(255,196,102,.0878),transparent 70%);
  -webkit-mask-image:radial-gradient(closest-side,#000 38%,rgba(0,0,0,.4) 76%,transparent 100%);
  mask-image:radial-gradient(closest-side,#000 38%,rgba(0,0,0,.4) 76%,transparent 100%);
  opacity:0;transition:opacity .7s var(--ease-out);
}
.contact-form:hover::after{opacity:1;}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:1rem;}
.field{display:flex;flex-direction:column;gap:.4rem;}
.field label{font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-soft);}
.field input,.field select,.field textarea{
  font:inherit;color:var(--ink);background:var(--paper);
  border:1px solid var(--line-strong);border-radius:12px;padding:.88rem 1rem;
  transition:border-color .3s,box-shadow .3s,background .3s;
}
.field textarea{resize:vertical;min-height:120px;}
/* A native select takes its look from the operating system, which on Windows
   is a white box with a blue highlight and reads as borrowed from another site.
   The control is drawn here instead, with its own chevron. The list that drops
   down is still the browser's, but the colours below are honoured by Chrome,
   Edge and Firefox, so it stays in the same family. */
.field select{
  appearance:none;-webkit-appearance:none;-moz-appearance:none;
  padding-right:2.6rem;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%237A6653' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 1rem center;
  background-size:13px 8px;
}
.field select:hover{border-color:var(--line-strong);background-color:var(--paper);}
.field select option{
  background:var(--paper);color:var(--ink);
  padding:.5rem;
}
.field select option:checked{background:var(--cream-2);}
:root[data-theme="dark"] .field select{
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%23A99883' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* the whole control, so the drop down list follows the theme where the
   browser allows it */
.field select{color-scheme:light;}
:root[data-theme="dark"] .field select{color-scheme:dark;}

/* ---------------------------------------------------------- themed dropdown
   Only on a desktop, where the browser's own list cannot be themed. The real
   select is still there, hidden behind the drawn one, so the form submits the
   same and nothing breaks without the script. */

.select-shell{position:relative;}
.select-shell select{
  position:absolute;inset:0;width:100%;height:100%;
  opacity:0;pointer-events:none;
}
.select-button{
  width:100%;text-align:left;font:inherit;color:var(--ink);
  background:var(--paper);border:1px solid var(--line-strong);border-radius:12px;
  padding:.88rem 2.6rem .88rem 1rem;cursor:pointer;position:relative;
  transition:border-color .25s,box-shadow .25s,background .25s;
}
.select-button::after{
  content:"";position:absolute;right:1rem;top:50%;width:9px;height:9px;
  border-right:2px solid var(--ink-soft);border-bottom:2px solid var(--ink-soft);
  transform:translateY(-70%) rotate(45deg);
  transition:transform .3s var(--ease-out),border-color .25s;
}
.select-button:hover{border-color:var(--line-strong);background:var(--paper-2);}
.select-shell.open .select-button{
  border-color:var(--amber);box-shadow:0 0 0 4px rgba(143,160,90,.18);
}
.select-shell.open .select-button::after{transform:translateY(-20%) rotate(225deg);}

.select-list{
  position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:40;
  margin:0;padding:.35rem;list-style:none;
  background:var(--paper);border:1px solid rgba(201,146,42,.4);border-radius:12px;
  box-shadow:0 24px 50px -22px rgba(40,20,6,.55);
  max-height:280px;overflow-y:auto;
}
.select-option{
  padding:.62rem .8rem;border-radius:8px;cursor:pointer;font-size:.95rem;
  transition:background .18s,color .18s;
}
/* The same manners as the Atelier's dropdowns: hovering lifts the row a shade
   off the panel rather than filling it with colour, and only the option that is
   actually selected wears the olive. Solid olive on hover was too loud for a
   list you are only running your eye down. */
.select-option:hover,
.select-option:focus,
.select-option.active{background:rgba(85,107,47,.16);}
.select-option.chosen{background:var(--olive);color:#FFF6E7;}
.select-option.chosen:hover{background:var(--olive-dk);color:#FFF6E7;}

.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;border-color:var(--amber);box-shadow:0 0 0 4px rgba(143,160,90,.18);
  /* background-color, not the shorthand: the shorthand resets the chevron's
     repeat and position and leaves it tiling across the whole control */
  background-color:var(--paper-2);
}
.contact-form .btn{align-self:flex-start;}

/* ---------------------------------------------------------------- footer */

.site-footer{
  background:linear-gradient(180deg,var(--night-2),var(--night));
  color:rgba(255,246,231,.74);padding:clamp(54px,7vw,86px) 0 1.8rem;position:relative;overflow:hidden;
}
.site-footer::before{
  content:"";position:absolute;inset:auto 0 auto 0;top:-10%;height:60%;pointer-events:none;
  background:radial-gradient(50% 100% at 20% 0%,rgba(143,160,90,.1485),transparent 70%);
}
.footer-grid{display:grid;gap:clamp(26px,4vw,44px);grid-template-columns:1.4fr repeat(3,1fr) 1.25fr;align-items:start;position:relative;}
.footer-brand img{height:117px;width:auto;margin-bottom:1rem;opacity:.94;}
.footer-brand p{max-width:34ch;font-size:.92rem;}
.footer-col h3{
  font-family:'Work Sans',sans-serif;font-size:.74rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--olive-lt);margin-bottom:.9rem;font-weight:600;
}
.footer-col p{font-size:.92rem;margin-bottom:.5rem;}
.footer-col a{color:rgba(255,246,231,.82);text-decoration:none;transition:color .3s;}
.footer-col a:hover{color:var(--olive-lt);}
.footer-social{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:.5rem;font-size:.92rem;}
.footer-bottom{margin-top:clamp(34px,5vw,56px);padding-top:1.4rem;border-top:1px solid rgba(255,246,231,.14);position:relative;}
.footer-bottom p{
  font-size:.8rem;color:rgba(255,246,231,.5);text-align:center;margin:0;
  text-wrap:balance;
}
.footer-break{display:none;}
.footer-space{display:inline;}
/* The policy link above the credit. Its own space, and a shade brighter than
   the copyright line, because it is something to click rather than small print
   to skim past. */
.footer-legal{margin:0 0 .7rem !important;}
.footer-legal a{color:rgba(255,246,231,.78);text-decoration:none;border-bottom:1px solid rgba(255,246,231,.22);padding-bottom:1px;transition:color .3s,border-color .3s;}
.footer-legal a:hover{color:var(--olive-lt);border-color:var(--olive-lt);}
@media (max-width:560px){
  /* the credit gets a line to itself, and the long wording goes, so the line
     above cannot leave a single word stranded */
  .fo-long{display:none;}
  .footer-space{display:none;}
  .footer-break{display:block;}
  .footer-bottom p{font-size:.78rem;line-height:1.7;}
}
/* House standard: the KOPPEL credit is bold, in KOPPEL orange, and linked.
   Not a muted footer link. */
.dev-link{color:#E8650A;font-weight:700;text-decoration:none;transition:color .2s;}
.dev-link:hover{color:#FF8A3D;}

.to-top{
  position:fixed;right:1.4rem;bottom:1.4rem;z-index:90;width:48px;height:48px;border-radius:50%;
  border:0;background:linear-gradient(140deg,var(--copper),var(--copper-dk));color:#FFF6E7;
  display:grid;place-items:center;box-shadow:var(--shadow-warm);
  opacity:0;visibility:hidden;transform:translateY(10px) scale(.9);
  transition:opacity .35s,transform .35s var(--ease-out),visibility .35s,box-shadow .35s;
}
.to-top svg{width:20px;height:20px;}
.to-top.show{opacity:1;visibility:visible;transform:none;}
.to-top:hover{box-shadow:0 0 40px -6px rgba(143,160,90,.6075);transform:translateY(-3px);}

/* ---------------------------------------------------------------- inner pages */

/* Centred, and further down the page. With the trail of addresses gone there was
   nothing above the heading holding it clear of the fixed bar, and a heading on
   its own reads better in the middle than hard against the left edge. */
.page-head{
  padding:calc(var(--topbar-h) + clamp(70px,9vw,120px)) 0 0;
  background:linear-gradient(180deg,var(--cream-2),var(--cream));
  position:relative;overflow:hidden;text-align:center;
}
/* Close under the heading: the form or the detail below is the point of these
   pages, and a wide band of nothing between the two read as a gap rather than as
   space. */
.page-head .shell{padding-bottom:clamp(14px,2vw,24px);position:relative;z-index:1;}
.page-head + section{padding-top:clamp(24px,3vw,40px);}
.page-head h1{font-size:clamp(2.1rem,4.6vw,3.5rem);text-wrap:balance;margin:0 auto;max-width:20ch;}
.page-head .eyebrow{justify-content:center;}
.page-head .page-head-note{margin:1rem auto 0;max-width:52ch;color:var(--ink-soft);}

@media (max-width: 700px){
  /* The landing logo hangs below the bar on a phone, so measure from where it
     actually ends. Same reason as the unsubscribe page. */
  .page-head{padding-top:calc(var(--topbar-h) + 96px);}
}

.booking-grid{display:grid;grid-template-columns:1.2fr .8fr;gap:clamp(28px,4vw,54px);align-items:start;}
.booking-panel{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius-lg);padding:clamp(20px,3vw,34px);box-shadow:var(--shadow-sm);}
.summary-card{
  background:linear-gradient(165deg,var(--paper),var(--paper-2));
  border:1px solid rgba(201,146,42,.3);border-radius:var(--radius-lg);
  overflow:hidden;position:sticky;top:calc(var(--topbar-h) + 16px);box-shadow:var(--shadow-md);
}
.summary-card img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:inherit;}
.summary-body{padding:1.4rem 1.5rem;display:flex;flex-direction:column;gap:.6rem;}
.summary-line{display:flex;justify-content:space-between;gap:1rem;font-size:.92rem;color:var(--ink-soft);}
.summary-total{display:flex;justify-content:space-between;align-items:baseline;border-top:1px solid var(--line);padding-top:.9rem;margin-top:.4rem;}
.summary-total .price{font-size:1.65rem;}

.pay-options{display:grid;gap:.8rem;margin:.4rem 0 1.2rem;}
.pay-option{
  display:flex;align-items:center;gap:.9rem;padding:1rem 1.1rem;cursor:pointer;
  border:1.5px solid var(--line);border-radius:14px;background:var(--cream);
  transition:border-color .3s,background .3s,box-shadow .35s,transform .3s;
}
.pay-option:hover{border-color:var(--line-strong);transform:translateY(-2px);}
.pay-option input{accent-color:var(--copper);width:18px;height:18px;}
.pay-option.selected{border-color:var(--amber);background:var(--paper);box-shadow:0 0 0 4px rgba(143,160,90,.16);}
.pay-option .name{font-weight:600;}
.pay-option .hint{display:block;font-size:.8rem;color:var(--ink-soft);font-weight:400;}

.notice{border-radius:14px;padding:1rem 1.2rem;font-size:.92rem;margin-bottom:1.2rem;
  background:rgba(126,130,83,.14);border:1px solid rgba(126,130,83,.4);color:#4E5330;}
.notice.warn{background:rgba(85,107,47,.1);border-color:rgba(85,107,47,.4);color:var(--copper-dk);}

.result-hero{min-height:66vh;display:grid;place-items:center;text-align:center;padding:calc(var(--topbar-h) + 64px) 0 84px;}
.result-card{
  background:linear-gradient(165deg,var(--paper),var(--paper-2));
  border:1px solid rgba(201,146,42,.3);border-radius:var(--radius-lg);
  padding:clamp(28px,5vw,54px);max-width:640px;box-shadow:var(--shadow-lg);
}
/* The hero centres what sits directly in it, but that child is .shell, which is
   a full width container. The card inside it is a plain block with a max-width
   and no auto margin, so on a wide screen it hugged the left edge while the
   heading above it looked centred. return.php and cancel.php each carried an
   inline grid to work around it; the handoff page to the gateway did not, which
   is where it showed. Fixed once, here, for all three. Invisible on a phone,
   where the shell is narrower than the card anyway. */
.result-hero .shell{display:grid;place-items:center;}

.result-ref{font-family:'Fraunces',serif;font-size:1.65rem;color:var(--copper);letter-spacing:.05em;margin:.6rem 0 1.2rem;}

/* ---------------------------------------------------------------- responsive */

@media (max-width:960px){
  .about-grid,.grans-grid,.contact-grid,.booking-grid{grid-template-columns:1fr;}
  .about-media{aspect-ratio:4/3;max-height:470px;}
  .grans-media{order:-1;}
  .footer-grid{grid-template-columns:1fr 1fr;}
  .summary-card{position:static;}
}

@media (max-width:640px){
  /* the three figures read better centred under the copy on a narrow screen
     than strung along the left edge */
  .stats{grid-template-columns:1fr;justify-items:center;text-align:center;}
  .stat{align-items:center;padding-left:0;}
  .stat::before{display:none;}

  body{font-size:16px;}
  :root{--topbar-h:74px;}
  .brand-mark{height:104px;}
  .topbar.solid .brand-mark{height:46px;}
  .brand{margin-top:4px;}
  .topbar.solid .brand{margin-top:14px;}
  .field-row{grid-template-columns:1fr;}
  .footer-grid{grid-template-columns:1fr;}
  .footer-brand img{height:97px;}
  .hero{min-height:94svh;}
  .grain{opacity:.07;}
  /* Phones get the light without the running animations behind it. */
  .hero-rays{display:none;}
  /* the dust stays on phones, Johan asked for it */
}

/* ---------------------------------------------------------------- motion off */




/* =========================================================================
   Diagnostic switches, driven by ?fx=off and ?fx=bare on any page.
   These exist so a slow machine can be told apart from a slow page without
   guessing. Nothing here is used in normal browsing.
   ========================================================================= */

body.fx-off::before{display:none;}
body.fx-off .grain{display:none;}
body.fx-off .hero-rays{display:none;}
body.fx-off .session-card,
body.fx-off .review,
body.fx-off .upnext-card,
body.fx-off .summary-card,
body.fx-off .result-card,
body.fx-off .cal-card,
body.fx-off .about-media,
body.fx-off .grans-media,
body.fx-off .consult-media,
body.fx-off .gallery-item,
body.fx-off .showcase-frame img,
body.fx-off .btn{box-shadow:none;}

body.fx-bare::before{display:none;}
body.fx-bare .grain,
body.fx-bare .lamp,
body.fx-bare .hero-rays,
body.fx-bare .hero-veil{display:none;}
body.fx-bare *{
  box-shadow:none !important;
  border-radius:0 !important;
  transition:none !important;
  animation:none !important;
  text-shadow:none !important;
}
body.fx-bare .reveal,
body.fx-bare .wipe,
body.fx-bare .showcase-copy{opacity:1 !important;transform:none !important;clip-path:none !important;}
body.fx-bare .line-mask > span{transform:none !important;}
body.fx-bare .marquee-track{animation:none !important;}


/* =========================================================================
   Dark mode. Same kitchen, lights down: the palette moves, the olive and
   gold stay exactly where they are. Driven by data-theme on the html element,
   which is set before first paint so there is no flash of the light theme.
   ========================================================================= */

:root[data-theme="dark"]{
  --paper:#1F1811;
  --paper-2:#261D15;
  --cream:#15100B;
  --cream-2:#1E1710;
  --cream-3:#2A2018;

  --ink:#F7EEE1;
  --ink-2:#E0D2BE;
  --ink-soft:#A99883;

  --gold:#C4B06A;
  --gold-lt:#D8CC8A;
  --amber:#8fa05a;
  --ember:#a3b56c;
  --copper:#8fa05a;
  --copper-dk:#556b2f;
  --olive:#556b2f;
  --olive-lt:#8fa05a;
  --olive-dk:#3e4f22;

  --night:#100C08;
  --night-2:#171009;

  --line:rgba(247,238,225,.13);
  --line-strong:rgba(247,238,225,.26);

  --glow-soft:0 0 60px rgba(143,160,90,.1485);
  --shadow-sm:0 2px 12px rgba(0,0,0,.45);
  --shadow-md:0 20px 44px -24px rgba(0,0,0,.8);
  --shadow-lg:0 46px 90px -44px rgba(0,0,0,.9);
  --shadow-warm:0 18px 50px -26px rgba(85,107,47,.6);
}

/* the room light is warm but much dimmer with the lights off */
:root[data-theme="dark"] body::before{
  background:
    radial-gradient(70% 46% at 8% 0%,rgba(143,160,90,.108),transparent 62%),
    radial-gradient(58% 40% at 100% 22%,rgba(85,107,47,.081),transparent 60%),
    radial-gradient(80% 52% at 50% 100%,rgba(70,75,40,.108),transparent 66%);
}
:root[data-theme="dark"] .grain{opacity:.06;}

/* sections that were already dark need to separate from the new background */
:root[data-theme="dark"] .about{
  background:linear-gradient(170deg,var(--paper),var(--paper-2) 60%,var(--cream));
  border-color:rgba(143,160,90,.18);
}
:root[data-theme="dark"] .tables::after{
  background:
    linear-gradient(to bottom,rgba(8,6,4,.9),rgba(10,7,4,.78)),
    radial-gradient(70% 50% at 82% 14%,rgba(143,160,90,.1485),transparent 62%),
    radial-gradient(80% 60% at 10% 100%,rgba(85,107,47,.135),transparent 60%);
}
:root[data-theme="dark"] .showcase::before{
  background:radial-gradient(60% 40% at 50% 30%,rgba(143,160,90,.108),transparent 66%);
}
:root[data-theme="dark"] .site-footer{border-top:1px solid rgba(247,238,225,.08);}

/* photographs are a touch bright against a dark page */
:root[data-theme="dark"] .card-media img,
:root[data-theme="dark"] .studio-media img,
:root[data-theme="dark"] .about-media img,
:root[data-theme="dark"] .grans-media img,
:root[data-theme="dark"] .summary-card img{filter:brightness(.92);}

:root[data-theme="dark"] .footer-brand img{opacity:.9;}
:root[data-theme="dark"] .topbar.solid{
  box-shadow:0 1px 0 rgba(143,160,90,.22),0 16px 34px -30px rgba(0,0,0,.9);
}

/* ---------------------------------------------------------- theme toggle */



/* ---------------------------------------------------------------- tribute */

.tribute{
  margin:0 0 clamp(38px,5vw,64px);
  display:grid;grid-template-columns:1.05fr .95fr;
  gap:clamp(24px,4vw,52px);align-items:center;
}
.tribute-photo{
  border-radius:8px;overflow:hidden;box-shadow:var(--shadow-lg);
  isolation:isolate;transform:translateZ(0);
}
.tribute-photo img{width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block;}
.tribute figcaption{max-width:46ch;}
.tribute .eyebrow{margin-bottom:.7rem;}
.tribute-line{
  font-family:'Fraunces',serif;font-size:clamp(1.5rem,2.6vw,2.1rem);
  line-height:1.2;color:var(--ink);margin:0 0 .9rem;
}
.tribute-note{color:var(--ink-soft);font-size:1rem;margin:0 0 1.3rem;}
.tribute-dedication{
  font-family:'Fraunces',serif;font-style:italic;font-size:1.02rem;color:var(--copper);
  margin:0;padding-top:1rem;border-top:1px solid var(--line);display:inline-block;
}

@media (max-width:860px){
  .tribute{grid-template-columns:1fr;}
}

/* the press that replaces the blue box */
.btn:active,button:active,.cal-nav:active,.cal-event:active{transform:scale(.97);}
.brand:active,.studio-card:active,.cal-day:active{opacity:.82;}


/* --------------------------------------------------------------- credits

   The photographers, in the contact section, under the direct address. Their
   marks are their own, so nothing here recolours them: the tile keeps its red,
   the wordmark keeps its white, and both simply come up to full strength when
   pointed at. */
.credits-list{
  list-style:none;display:flex;align-items:center;gap:clamp(18px,3vw,32px);
  flex-wrap:wrap;padding:0;margin:0;
}
.credits-list a{display:block;line-height:0;}
.credits-list img{
  width:auto;opacity:.9;
  transition:opacity .3s var(--ease),transform .3s var(--ease);
}
/* A square tile and a wide wordmark cannot share one height: matched by height
   the wordmark's name shrinks to nothing. Sized so both read as the same weight
   on the page instead. */
.credit-dpc img{height:46px;border-radius:8px;}
.credit-wh img{height:46px;}
.credits-list a:hover img,
.credits-list a:focus-visible img{opacity:1;transform:translateY(-2px);}

@media (max-width: 780px){
  .credit-dpc img{height:40px;}
  .credit-wh img{height:40px;}
}


/* The photographers sit as their own footer column, beside Follow: the two
   marks side by side, matched in height so neither outweighs the other. */
.footer-credits .credits-list{flex-direction:row;align-items:center;gap:18px;}
.footer-credits h3{margin-bottom:1rem;}

/* -------------------------------------------------- the iOS play button

   A video that is not playing gets a play triangle drawn over it by the phone
   itself, not by us. These clips are decoration: they are muted, they have no
   controls, and they are started by the page when they come into view. The
   overlay makes them look like something to tap, and on a poster frame it never
   goes away. Hiding the shadow DOM control is the only way to be rid of it.
   Written for every video on the site rather than one class, so a clip added
   later cannot bring the triangle back. */
video::-webkit-media-controls,
video::-webkit-media-controls-panel,
video::-webkit-media-controls-start-playback-button,
video::-webkit-media-controls-play-button,
video::-webkit-media-controls-overlay-play-button{
  display:none !important;
  -webkit-appearance:none;
  opacity:0 !important;
}
.studio-clip,
.st-visual video{
  /* Safari on a phone otherwise treats the frame as a tappable player. These
     two are decoration; the card around the studio clip stays tappable. */
  pointer-events:none;
}


/* The narrow layout grows less, so its resting scale is different. */
@media (max-width: 820px){
  .showcase-frame img{--peak:1.95;}
}

/* The tribute photograph is what the Gran's Kitchen link lands on, so it clears
   the fixed header by the same amount every other anchor does. */
#grans{scroll-margin-top:calc(var(--topbar-h) + 16px);}

/* ------------------------------------------------------------- unsubscribe

   A page of its own with nothing above it, so it has to clear the fixed header
   by itself: without this the eyebrow sat behind the logo. Centred, because
   there is one thing to read and one thing to press. */
.unsub{
  min-height:70svh;display:flex;align-items:center;
  padding:calc(var(--topbar-h) + clamp(40px,8vw,90px)) 0 clamp(60px,10vw,120px);
  text-align:center;
}
.unsub .shell{max-width:640px;}
.unsub .eyebrow{justify-content:center;}
.unsub h1{font-size:clamp(1.9rem,5vw,3rem);margin:0 0 1rem;text-wrap:balance;}
.unsub-note{margin:0 auto 1.8rem;max-width:44ch;color:var(--ink-soft);}
.unsub-act{display:flex;justify-content:center;}
.unsub-act form{display:inline;}

/* The two choices on the unsubscribe screen: stacked, centred, and big enough
   to hit with a thumb. */
.unsub-pick{margin:0 auto;max-width:22rem;}
.unsub-pick-lead{margin:0 0 1rem;color:var(--ink-soft);font-size:.95rem;}
.unsub-check{
  display:flex;align-items:center;gap:.7rem;justify-content:flex-start;
  padding:.7rem .9rem;margin:0 0 .6rem;text-align:left;
  border:1px solid var(--line);border-radius:12px;cursor:pointer;
  transition:border-color .2s var(--ease),background .2s var(--ease);
}
.unsub-check:hover{border-color:var(--olive);}
.unsub-check input{width:18px;height:18px;accent-color:var(--olive);flex:none;}
.unsub-act{display:flex;justify-content:center;margin-top:1.2rem;}

@media (max-width: 700px){
  /* The landing logo hangs below the bar on a phone, about 108px from the top,
     while the bar itself is only 74px. Padding measured from the bar alone left
     the heading almost touching the mark. Measured from where the logo actually
     ends instead. */
  .unsub{padding-top:calc(var(--topbar-h) + 96px);}
}

/* WhatsApp sits directly under the email and is set with it above. */
.contact-wa{margin-top:.45rem;}
