/* ── tokens ─────────────────────────────────────────────── */
:root{
  --ink:#0a0a0b;
  --ink-2:#111113;
  --line:#26262a;
  --fg:#f2f0ec;
  --fg-dim:#8c8a86;
  --accent:#d8a24a;
  --serif:"Cormorant Garamond",Georgia,serif;
  --sans:"Inter",-apple-system,system-ui,sans-serif;
  --pad:clamp(1.25rem,5vw,5rem);
  --ease:cubic-bezier(.16,1,.3,1);
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;background:var(--ink);color:var(--fg);
  font:400 clamp(1rem,.95rem + .3vw,1.125rem)/1.6 var(--sans);
  -webkit-font-smoothing:antialiased;overflow-x:hidden;
}
img{display:block;max-width:100%}
a{color:inherit}
h1,h2,h3{margin:0;font-family:var(--serif);font-weight:300;line-height:.95;letter-spacing:-.01em;text-wrap:balance}
h1{font-size:clamp(3rem,11vw,9.5rem)}
h2{font-size:clamp(2.25rem,6.5vw,5.5rem)}
em{font-style:italic;color:var(--accent)}
::selection{background:var(--accent);color:var(--ink)}

.eyebrow{
  margin:0 0 1.5rem;font:500 .7rem/1 var(--sans);
  letter-spacing:.22em;text-transform:uppercase;color:var(--fg-dim);
}

.btn{
  padding:.9rem 1.9rem;border:1px solid var(--fg);
  border-radius:100px;background:var(--fg);color:var(--ink);
  font:500 .8rem/1 var(--sans);letter-spacing:.08em;text-transform:uppercase;
  text-decoration:none;transition:background .4s var(--ease),color .4s var(--ease);
}
.btn:hover{background:transparent;color:var(--fg)}
.btn--ghost{background:transparent;color:var(--fg);border-color:var(--line)}
.btn--ghost:hover{background:var(--fg);color:var(--ink);border-color:var(--fg)}

/* ── scroll progress (native scroll timeline) ───────────── */
.scroll-progress{
  position:fixed;inset:0 0 auto;height:2px;z-index:100;
  background:var(--accent);transform-origin:0 50%;transform:scaleX(0);
  animation:progress linear;animation-timeline:scroll(root);
}
@keyframes progress{to{transform:scaleX(1)}}

/* ── nav ────────────────────────────────────────────────── */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:90;
  display:flex;align-items:center;justify-content:space-between;gap:2rem;
  /* Shorter bar: it is a camera mark and one button, and on a phone the browser already takes
     its own share of the top. 79px of chrome over a photograph was too much of it. */
  padding:.7rem var(--pad);
  backdrop-filter:blur(14px);
  background:color-mix(in srgb,var(--ink) 55%,transparent);
  border-bottom:1px solid transparent;
  transition:border-color .4s,background .4s;
}
.nav.is-stuck{border-bottom-color:var(--line);background:color-mix(in srgb,var(--ink) 88%,transparent)}
.nav__mark{display:inline-flex;align-items:center;color:var(--fg);text-decoration:none;
  transition:color .3s}
.nav__mark:hover{color:var(--accent)}
.nav__mark svg{display:block}
.nav__links{display:flex;gap:2rem}
.nav__links a{
  font:400 .78rem/1 var(--sans);letter-spacing:.12em;text-transform:uppercase;
  text-decoration:none;color:var(--fg-dim);transition:color .3s;
}
.nav__links a:hover{color:var(--fg)}

/* ── the menu, wide ─────────────────────────────────────────
   Above 760 the <details> is transparent scaffolding: the burger is hidden and the list is the
   row it always was. `display:contents` so the wrapper adds no box of its own to the flex bar. */
.nav__menu{display:contents}
.nav__burger{display:none}
.nav__cta{
  font:500 .72rem/1 var(--sans);letter-spacing:.12em;text-transform:uppercase;
  text-decoration:none;padding:.7rem 1.2rem;border:1px solid var(--line);border-radius:100px;
  transition:border-color .3s,color .3s;
}
.nav__cta:hover{border-color:var(--accent);color:var(--accent)}
@media(max-width:760px){
  /* Tighter bar, but the booking button keeps a 44px touch target — it is the one thing on the
     page that has to be hit with a thumb, and shrinking it with the bar took it to 34px. */
  .nav{padding:.3rem var(--pad)}
  .nav__cta{min-height:44px;padding-inline:1rem;display:inline-flex;align-items:center}

  /* ── the menu, narrow ── */
  .nav__menu{display:block;position:relative}
  .nav__burger{
    display:inline-flex;align-items:center;gap:.55rem;cursor:pointer;list-style:none;
    min-width:44px;min-height:44px;justify-content:center;   /* a full 44x44 thumb target */
    font:500 .72rem/1 var(--sans);letter-spacing:.12em;text-transform:uppercase;color:var(--fg-dim);
  }
  .nav__burger::-webkit-details-marker{display:none} /* Safari's own triangle */
  .nav__burger::marker{content:''}
  .nav__menu[open] .nav__burger{color:var(--fg)}
  /* three lines drawn from one box: the middle is the box, the others are its pseudo-elements */
  .nav__burger-bars,
  .nav__burger-bars::before,
  .nav__burger-bars::after{
    display:block;width:18px;height:1.5px;background:currentColor;transition:transform .3s var(--ease)}
  .nav__burger-bars{position:relative}
  .nav__burger-bars::before,.nav__burger-bars::after{content:'';position:absolute;left:0}
  .nav__burger-bars::before{top:-5px}
  .nav__burger-bars::after{top:5px}
  .nav__menu[open] .nav__burger-bars{background:transparent}
  .nav__menu[open] .nav__burger-bars::before{transform:translateY(5px) rotate(45deg)}
  .nav__menu[open] .nav__burger-bars::after{transform:translateY(-5px) rotate(-45deg)}
  .nav__burger-word{display:none}                    /* the icon says it; the label is for a11y */

  .nav__links{
    position:absolute;top:calc(100% + .55rem);left:0;z-index:95;
    display:grid;gap:0;min-width:11rem;
    background:color-mix(in srgb,var(--ink) 96%,transparent);
    border:1px solid var(--line);border-radius:8px;
    box-shadow:0 18px 40px #0009;overflow:hidden;
  }
  .nav__links a{
    padding:.95rem 1.1rem;                            /* 44px rows, same reason as the button */
    font-size:.8rem;color:var(--fg);
  }
  .nav__links a + a{border-top:1px solid var(--line)}
  .nav__links a:active{background:var(--ink-2)}
}

/* ── hero ───────────────────────────────────────────────── */
.hero{position:relative;min-height:100svh;display:grid;align-items:end;overflow:clip}
.hero__media{position:absolute;inset:0;z-index:-1}
.hero__media img{width:100%;height:100%;object-fit:cover;
  animation:heroPan linear both;animation-timeline:view();animation-range:exit 0% exit 100%}
@keyframes heroPan{to{transform:scale(1.14);filter:brightness(.45)}}
.hero__scrim{position:absolute;inset:0;
  background:
    linear-gradient(180deg,#0a0a0be6 0%,#0a0a0b40 30%,#0a0a0bf2 100%),
    linear-gradient(90deg,#0a0a0bb3 0%,#0a0a0b00 62%)}
/* Portrait hero on a phone shows the whole frame, so the copy lands on the bright white linen
   instead of the dark window: darken from the headline down, keep the daylight at the top. */
@media(max-width:760px){
  .hero__scrim{background:
    linear-gradient(180deg,#0a0a0bd9 0%,#0a0a0b33 12%,#0a0a0bd1 32%,#0a0a0bf7 100%),
    linear-gradient(90deg,#0a0a0b80 0%,#0a0a0b00 78%)}
  .hero .eyebrow{color:#cfccc7}   /* it lands on the daylight in the window; grey is not enough */
}
.hero__copy{padding:0 var(--pad) clamp(4rem,10vh,8rem);max-width:min(58rem,92vw)}
.hero__copy h1{margin-bottom:1.75rem}
.hero__sub{max-width:34rem;color:#d7d4ce;font-size:1.05rem;margin:0 0 2.5rem}
.hero__cue{
  position:absolute;right:var(--pad);bottom:clamp(4rem,10vh,8rem);
  display:flex;align-items:center;gap:.75rem;
  font:400 .68rem/1 var(--sans);letter-spacing:.24em;text-transform:uppercase;color:var(--fg-dim);
}
.hero__cue span{display:block;width:1px;height:52px;background:var(--line);position:relative;overflow:hidden}
.hero__cue span::after{content:"";position:absolute;inset:0;background:var(--accent);animation:cue 2.2s var(--ease) infinite}
@keyframes cue{0%{transform:translateY(-100%)}60%,100%{transform:translateY(100%)}}
@media(max-width:760px){.hero__cue{display:none}}

/* ── hero ticker ──────────────────────────────────────────
   Sits on the hero's bottom edge rather than as a band of its own below the fold. Translucent
   ink so the photograph still reads through it; clipped by the hero's own `overflow: clip`. */
.marquee{
  position:absolute;left:0;right:0;bottom:0;z-index:2;
  overflow:hidden;user-select:none;padding:.85rem 0;
  border-top:1px solid #ffffff1f;
  background:linear-gradient(180deg,#0a0a0b00 0%,#0a0a0bcc 45%,#0a0a0be6 100%);
}
/* No flex gap: spacing lives inside each item so the sequence is perfectly periodic and the
   seam lands exactly on a copy. A gap would leave the wrap point half a gap short. */
.marquee__track{display:flex;width:max-content}
/* The travel is one whole copy of the list in pixels, not a percentage of the track: appending
   copies to cover a wider screen then cannot move the wrap point, and the seam always lands on a
   copy boundary. --copy/--dur are set by the inline script in index.html once the geometry is
   final; until then, and under reduced motion, the strip simply stands still and full. */
.marquee__track.is-live{animation:slide var(--dur,20s) linear infinite}
.marquee__track span{font:300 clamp(1.05rem,2vw,1.7rem)/1 var(--serif);white-space:nowrap;
  padding-right:2.2rem;color:#e7e3dc}
.marquee__track i{color:var(--accent);font-style:normal;align-self:center;padding-right:2.2rem}
@keyframes slide{to{transform:translateX(calc(-1 * var(--copy,0px)))}}
@media(prefers-reduced-motion:reduce){.marquee__track.is-live{animation:none}}

/* ── hero claim ─────────────────────────────────────────────
   Was a full section of its own on black. It now sits in the hero's top-right quiet zone,
   over the dark window. */
.hero__claim{
  position:absolute;right:var(--pad);top:clamp(6rem,16vh,10rem);z-index:1;
  margin:0;max-width:22ch;text-align:right;
  font:300 clamp(1.35rem,2.4vw,2.15rem)/1.22 var(--serif);letter-spacing:-.01em;
  color:#e7e3dc;text-wrap:balance;
}
/* On a phone the frame is portrait and the top is the bright part of the room, so the claim
   moves under the copy where the scrim is deepest and reads left-aligned with everything else. */
@media(max-width:900px){
  /* The claim is now the last thing in the hero, so the bottom breathing room moves from the
     copy onto it — otherwise the last line sits flush on the viewport edge and reads as cut off. */
  .hero__copy{padding-bottom:clamp(1.5rem,4vh,2.5rem)}
  .hero__claim{position:static;right:auto;top:auto;text-align:left;max-width:28ch;
    margin:1.5rem 0 0;padding:0 var(--pad) clamp(2.5rem,7vh,4rem);
    font-size:clamp(1.2rem,4.6vw,1.6rem)}
  /* On a phone the claim is in the hero's flow, so the absolutely-positioned ticker sat on top of
     it — measured overlap at 390. Reserve the strip's height at the foot of the hero instead. */
  .hero__claim{padding-bottom:calc(clamp(2.5rem,7vh,4rem) + 3.4rem)}
}

/* Short phone — a 664px-tall iPhone, less once the browser's own toolbars take their share. The nav
   owns the top 79px and the ticker the bottom ~54, and between them the hero's five blocks stopped
   fitting: the hero grew past the viewport and, being bottom-aligned, pushed the headline up BEHIND
   the nav (44px behind it at 390x600). Height-gated, because at 390 the width alone is fine.
   Nothing is dropped — the headline comes down one notch, which also lets "Photography that" set in
   one line instead of two, and the space between the blocks tightens. The two font sizes are
   clamped against vh rather than fixed: 700px tall keeps the full step-down, while 560 — where
   every pixel is spoken for — takes a little more off. */
@media(max-width:900px) and (max-height:700px){
  .hero .eyebrow{margin-bottom:.6rem}
  .hero__copy h1{font-size:clamp(2.2rem,5.8vh,2.55rem);margin-bottom:.5rem}
  .hero__sub{font-size:1rem;margin-bottom:.85rem}
  .hero__copy{padding-bottom:.45rem}
  .hero__claim{margin-top:.6rem;font-size:clamp(1.05rem,2.9vh,1.2rem);
    padding-bottom:calc(.75rem + 3.4rem)}
}

/* ── sticky categories ──────────────────────────────────── */
.cats{
  display:grid;grid-template-columns:1fr 1.15fr;gap:clamp(2rem,6vw,7rem);
  /* Less at the foot than at the head: the gallery that follows opens on photographs, and
     226px of black between the last category and the first frame read as a dead screen. */
  padding:clamp(6rem,14vh,11rem) var(--pad) clamp(2rem,5vh,3.5rem);
}
.cats__sticky{position:sticky;top:22vh;align-self:start}
.cats__lede{margin:1.75rem 0 2rem;color:#b9b6b0;max-width:34ch;font-size:.98rem}
.cats__list{display:grid;gap:clamp(3rem,8vh,5.5rem)}
.cat h3{font-size:clamp(1.9rem,4vw,3rem);margin:.9rem 0 .8rem}
.cat p{margin:0 0 1.75rem;color:#b9b6b0;max-width:38ch}
.cat__img{margin:0;overflow:clip;border-radius:2px}
/* display:contents — <picture> is an inline box by default and would sit in the layout as a
   line, leaving a gap under the frame. */
.cat__img picture{display:contents}
/* Every frame is shown WHOLE, at its own aspect ratio. It used to sit in a fixed-height band with
   object-fit:cover — but the band's ratio grows with the window (1.11 at 1280 … 2.35 at 2560) while
   the photograph's does not, so something was always cut, and the wider the screen the worse:
   measured 12% off the top and bottom of the bar frame at 1920, 36% at 2560. A correctly oriented
   source per breakpoint is what keeps the shape sensible now, not a crop. */
/* The photograph settles in rather than tracking the scrollbar. It was a scroll-driven animation,
   which is fine when you drag slowly but runs its whole range in one frame when a flick moves a
   whole screen — so the effect existed and was never seen. A transition fired once when the block
   first comes into view is what the restaurant site does, and it reads as the picture arriving:
   a touch oversized and dim, easing to its true size over 1.6s. */
/* Bigger and dimmer to start with than before — at scale(1.06) the move was too small to register
   once the frame was already in front of you. Keyed off `.settled`, which app.js adds only when the
   frame is more than half in view; the reveal's `.in` fires on the first sliver of a block taller
   than the viewport, so the settle used to be over before you got there. */
.cat__img img{width:100%;height:auto;
  transform:scale(1.1);filter:brightness(.72) saturate(.9);
  transition:transform 1.5s cubic-bezier(.22,.61,.36,1), filter 1.2s ease-out}
.cat__img.settled img,
html:not(.reveal) .cat__img img{transform:scale(1);filter:none}
@media(max-width:900px){
  .cats{grid-template-columns:1fr}
  .cats__sticky{position:static}
}

/* ── gallery ────────────────────────────────────────────── */
.work{padding:clamp(2rem,5vh,3.5rem) var(--pad) clamp(5rem,12vh,9rem)}
.work__head{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:1.5rem;margin-bottom:2.5rem;
}
.work__head .eyebrow{margin:0}

/* Masonry via grid row spans, not `columns`. `columns` gives the same stagger for free but flows
   top-to-bottom per column, which left the four columns up to 400px apart at the foot — a big empty
   rectangle that read as broken. A grid flows row-major, so tall and short tiles interlock across
   the row and the foot stays close to flat. Two span sizes only (portrait vs landscape, set in
   app.js) so the spans are whole rows and the columns actually line up. */
/* `dense` matters here: app.js pins every tile to a column, and the default sparse algorithm never
   goes back to fill a hole, so a 3-row tile that did not fit the next slot left an empty gap and the
   column ran deeper than the sum of its tiles. Dense backfills, which is what levels the foot. */
.grid{display:grid;grid-template-columns:repeat(4,1fr);gap:.75rem;grid-auto-flow:row dense;
  grid-auto-rows:clamp(72px,9.5vw,150px)}
@media(max-width:900px){.grid{grid-template-columns:repeat(3,1fr)}}
@media(max-width:600px){.grid{grid-template-columns:repeat(2,1fr);gap:.5rem}}
.tile{
  grid-row:span 2;                    /* landscape */
  margin:0;position:relative;cursor:zoom-in;
  overflow:clip;background:var(--ink-2);border-radius:2px;
  /* button reset: the tile is a real button so it works from the keyboard */
  display:block;width:100%;padding:0;border:0;appearance:none;-webkit-appearance:none;
  color:inherit;font:inherit;text-align:left;
}
.tile--tall{grid-row:span 3}               /* portrait — the stagger comes from these two spans */
.tile:focus-visible{outline-offset:-4px}   /* the tile clips its own overflow, so inset the ring */
/* Fill the span the grid gave the tile; the lightbox shows the uncropped frame. */
.tile img{width:100%;height:100%;object-fit:cover;display:block;
  transition:transform .8s var(--ease),filter .5s}
.tile:hover img{transform:scale(1.05);filter:brightness(1.08)}
.tile::after{
  content:"View";position:absolute;inset:auto 0 0;padding:1.25rem 1rem .9rem;
  font:500 .68rem/1 var(--sans);letter-spacing:.18em;text-transform:uppercase;
  background:linear-gradient(transparent,#0a0a0bcc);
  opacity:0;transform:translateY(6px);transition:.4s var(--ease);
}
.tile:hover::after{opacity:1;transform:none}
.tile[hidden]{display:none}

.work__count{margin:1.75rem 0 0;color:var(--fg-dim);font-size:.78rem;letter-spacing:.06em}

/* Reveal once, on first sight, driven by the IntersectionObserver in app.js.
   Deliberately NOT a scroll-driven animation: with animation-timeline:view() the reveal is a pure
   function of scroll position, so any engine that runs the timeline backwards — iOS Safari during
   rubber-band overscroll — fades the text out again in the middle of the page. A class that is
   added once and never removed cannot do that.
   The .reveal gate is set by app.js, so if the script never runs the text stays visible instead of
   leaving a blank page. */
.reveal :is(.cat,.tile,.card){
  opacity:0;transform:translateY(48px);
  transition:opacity .9s var(--ease),transform .9s var(--ease);
}
/* Gallery tiles rise a little further and in a cascade — app.js sets --d from the column,
   so a row arrives left to right rather than appearing all at once. The delay is on the
   transition, not an animation, so the reveal stays one-way and cannot run backwards. */
.reveal .tile{transform:translateY(64px);transition-delay:var(--d,0ms)}
.reveal :is(.cat,.tile,.card).in{opacity:1;transform:none}

/* These two still use a scroll timeline (transform only, no text). Without timeline support they
   would run once against the document timeline on load, so switch them off there. */
@supports not (animation-timeline:view()){
  .cat__img img,.hero__media img{animation:none}
}

/* ── about + contact ────────────────────────────────────── */
.about{
  display:grid;gap:clamp(2rem,4vw,3.5rem);align-items:start;
  grid-template-columns:minmax(0,.62fr) minmax(0,1fr) minmax(0,1fr);
  padding:clamp(6rem,14vh,11rem) var(--pad);border-top:1px solid var(--line);
  max-width:96rem;margin-inline:auto;
}
/* Whole frame, no crop: the column is narrow enough that the full 2:3 portrait lands near the
   height of the text beside it. Cropping a portrait to a text's height cuts the head off. */
.about__img{margin:0;padding:6px;background:var(--ink-2);border:1px solid var(--line);border-radius:2px}
.about__img img{width:100%;height:auto;aspect-ratio:2/3;object-fit:contain}
.about__copy h2{font-size:clamp(1.9rem,3.4vw,3rem);margin-bottom:1.5rem}
.about__copy p{margin:0 0 1.05rem;color:#b9b6b0;max-width:44ch;font-size:.97rem}
.about__ask{border-left:1px solid var(--line);padding-left:clamp(1.5rem,2.5vw,2.5rem)}
/* One line: the 18ch cap was forcing the wrap, not the column width. Sized so the full
   sentence fits the form column at every breakpoint — measured, not estimated. */
.ask__h{font-size:clamp(1.15rem,1.55vw,1.55rem);margin-bottom:1.5rem;max-width:none;text-wrap:nowrap}
@media(max-width:1200px){.ask__h{font-size:clamp(1.3rem,2.6vw,1.9rem)}}
@media(max-width:420px){.ask__h{text-wrap:balance}}   /* narrow enough that one line would clip */
.about__ask .form{margin-top:0}
@media(max-width:1200px){
  /* portrait + copy share the first row, the form takes the full width beneath them */
  .about{grid-template-columns:minmax(0,.62fr) minmax(0,1fr)}
  .about__ask{grid-column:1 / -1;border-left:0;padding-left:0;
    border-top:1px solid var(--line);padding-top:clamp(2rem,4vh,3rem)}
}
@media(max-width:820px){
  .about{grid-template-columns:1fr}
  .about__img{max-width:22rem}
}

/* ── rates ──────────────────────────────────────────────── */
.rates{padding:clamp(6rem,14vh,11rem) var(--pad);border-top:1px solid var(--line)}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));
  gap:1rem;margin-top:clamp(3rem,7vh,5rem)}
.card{
  padding:2.25rem 2rem;border:1px solid var(--line);border-radius:4px;
  display:flex;flex-direction:column;background:var(--ink-2);position:relative;
}
.card--feature{border-color:var(--accent);background:#141210}
.card__flag{
  position:absolute;top:-.7rem;left:2rem;padding:.35rem .8rem;border-radius:100px;
  background:var(--accent);color:var(--ink);margin:0;
  font:500 .62rem/1 var(--sans);letter-spacing:.16em;text-transform:uppercase;
}
/* the discount badge is a fact, not the upsell — outline it instead of filling it */
.card__flag--quiet{background:var(--ink);border:1px solid var(--accent);color:var(--accent)}
.card h3{font-size:1.9rem;margin-bottom:.75rem}
.card__price{font:300 3rem/1 var(--serif);margin:0}
.card__price small{font-size:1rem;color:var(--fg-dim);font-family:var(--sans)}
.card__meta{margin:.4rem 0 1.75rem;color:var(--fg-dim);font-size:.85rem}
.card ul{list-style:none;margin:0 0 2rem;padding:0;display:grid;gap:.7rem;flex:1}
.card li{padding-left:1.3rem;position:relative;color:#b9b6b0;font-size:.92rem}
.card li::before{content:"—";position:absolute;left:0;color:var(--accent)}
.card .btn{text-align:center}
.rates__note{margin:2rem 0 0;color:var(--fg-dim);font-size:.82rem}

/* the mark is defined once as a <symbol>; this hides the definition itself */
.sprite{position:absolute;width:0;height:0;overflow:hidden}
/* every button that opens the chat carries it, sized to its own type */
/* WhatsApp green on the mark itself, on every button — the shape alone was not reading as
   WhatsApp on the outlined buttons, where it inherited the text colour. */
.i-wa{width:1.15em;height:1.15em;flex:0 0 auto;color:#25d366}
/* except on the filled green button, where green-on-green would vanish */
.btn--wa .i-wa{color:currentColor}
.btn,.nav__cta{display:inline-flex;align-items:center;justify-content:center;gap:.55em}

/* ── WhatsApp call to action ─────────────────────────────
   The whole enquiry happens in one chat now, so the form is gone. WhatsApp green only on this
   one button — enough to be recognised, not enough to fight the page. */
.ask__lede{margin:0 0 1.75rem;color:#b9b6b0;max-width:38ch;font-size:.97rem}
.btn--wa{
  background:#25d366;border-color:#25d366;color:#07130c}
.btn--wa:hover{background:transparent;border-color:#25d366;color:#25d366}
.btn--wa svg{display:block}
.about__ask .form__note{margin-top:1.1rem;max-width:34ch}

/* The QR is for a desktop visitor with a phone in hand. Below 1100 the button does the job and
   nobody scans their own screen, so it is hidden rather than shrunk. */
.qr{display:none}
@media(min-width:1101px){
  .qr{display:block;margin:2rem 0 0;max-width:9.5rem}
  .qr img{width:100%;height:auto;background:#fff;padding:.5rem;border-radius:4px}
  .qr figcaption{margin-top:.6rem;font-size:.72rem;color:var(--fg-dim);letter-spacing:.02em}
}

/* ── enquiry form (lives inside the about band) ─────────── */
.form{display:grid;gap:1.25rem;align-content:start}
.form label{display:grid;gap:.5rem;
  font:500 .68rem/1 var(--sans);letter-spacing:.16em;text-transform:uppercase;color:var(--fg-dim)}
.form input,.form textarea{
  padding:.9rem 0;border:0;border-bottom:1px solid var(--line);
  background:none;color:var(--fg);font:400 1rem/1.5 var(--sans);
  transition:border-color .3s;resize:vertical;
}
.form input:focus,.form textarea:focus{outline:0;border-bottom-color:var(--accent)}
.form .btn{justify-self:start;border:1px solid var(--fg);cursor:pointer;margin-top:.5rem}
.form__note{margin:0;color:var(--fg-dim);font-size:.75rem;letter-spacing:0;text-transform:none}
.form__note a{color:var(--accent)}

/* ── footer ─────────────────────────────────────────────── */
.foot{
  display:flex;justify-content:center;
  padding:2rem var(--pad);border-top:1px solid var(--line);
  font-size:.78rem;color:var(--fg-dim);
}
.foot a{text-decoration:none}
.foot a:hover{color:var(--fg)}

/* ── lightbox ───────────────────────────────────────────── */
.lb{
  border:0;padding:0;width:100vw;max-width:100vw;height:100svh;max-height:100svh;
  background:#050506;color:var(--fg);
  display:grid;place-items:center;grid-template-columns:1fr;
}
.lb::backdrop{background:#050506}
.lb:not([open]){display:none}
.lb__img{max-width:92vw;max-height:86svh;object-fit:contain;view-transition-name:lb-img}
.lb__close{
  position:absolute;top:1.25rem;right:1.25rem;z-index:2;
  width:44px;height:44px;border-radius:50%;border:1px solid var(--line);
  background:none;color:var(--fg);cursor:pointer;font-size:1rem;
}
.lb__close:hover{border-color:var(--accent);color:var(--accent)}
.lb__nav{
  position:absolute;top:50%;translate:0 -50%;z-index:2;
  width:56px;height:56px;border-radius:50%;border:1px solid var(--line);
  background:#0a0a0baa;color:var(--fg);cursor:pointer;font-size:1.9rem;line-height:0;
  transition:border-color .3s,color .3s;
}
.lb__nav:hover{border-color:var(--accent);color:var(--accent)}
.lb__nav--prev{left:1.25rem}
.lb__nav--next{right:1.25rem}
.lb__count{position:absolute;bottom:1.5rem;left:50%;translate:-50% 0;margin:0;
  font:400 .72rem/1 var(--sans);letter-spacing:.18em;color:var(--fg-dim)}
@media(max-width:600px){.lb__nav{width:44px;height:44px;font-size:1.4rem}}

::view-transition-old(lb-img),::view-transition-new(lb-img){animation-duration:.45s}

/* ── a11y ───────────────────────────────────────────────── */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition-duration:.01ms!important}
  .cat,.tile,.card{opacity:1!important;transform:none!important;transition-delay:0s!important}
  .cat__img img{transform:none!important;filter:none!important;transition:none!important}
}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
