/* ═══════════════════════════════════════════════════════════════
   Roberto Ojeda — custom tattoo artist
   Design language: the black of the hero, all the way down. Pale type on
   black, a satin emerald accent. The hero is the name alone, tattooed in as you
   arrive; the same heavy condensed face, in capitals, for every heading.
   (The tokens keep their old names: --paper is the ground, --ink the type.)
   ═══════════════════════════════════════════════════════════════ */

/* ── fonts ────────────────────────────────────────────────── */
/* Self-hosted from Google Fonts (all SIL OFL), latin subset only — it covers
   the Spanish accents. Anton, one heavy weight, is every heading: the same
   face as the hero name, which is not set in a font at all but is inline
   glyph outlines (tools/gen-hero-name.mjs). Plus Jakarta Sans is variable:
   one file per style carries the whole weight range. Pinyon Script, a
   copperplate like fine-line tattoo lettering, sets the one quote, under
   the Studio heading. Newsreader, a book serif (its 16pt text cut: regular,
   medium for the lead-ins, italic), sets the running text; the sans keeps
   the labels, buttons, nav and footer. */
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/anton-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url("../assets/fonts/plus-jakarta-sans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: italic;
  font-weight: 200 500;
  font-display: swap;
  src: url("../assets/fonts/plus-jakarta-sans-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Pinyon Script";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/pinyon-script-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/newsreader-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/newsreader-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/newsreader-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── tokens ───────────────────────────────────────────────── */
:root {
  --paper:       #000000;   /* the hero's black, so the page runs on from it */
  --paper-raise: #0e100f;   /* card cores, image wells */
  --ink:         #f2f0eb;   /* type: a warm off-white, softer than the hero's pure white */
  --ink-70:      rgba(242, 240, 235, .74);
  /* .64 is the floor: about 7:1 on --paper and --paper-raise. It is used on
     10px uppercase labels, so it has to clear AA on both. */
  --ink-dim:     rgba(242, 240, 235, .64);
  /* the accent is emerald, from a deep #001e17. That deep tone is ~1.1:1 on
     black, so it is the shadow of a satin sheen, not a colour type stands in.
     --emerald is the flat mid-tone for rules, rings and dots: 6:1 on black. */
  --emerald-deep: #001e17;
  --emerald:      #2a9a76;
  /* type sheens run straight down each line (an angle would shade wide labels
     left to right). Large type: a soft highlight high on the letter, falling
     to the deep tone below the baseline */
  --satin: linear-gradient(180deg, #23906d 0%, #7fdcba 28%, #2a9a76 55%, #136b51 82%, var(--emerald-deep) 100%);
  /* 10–11px labels: the same sheen, held above 4.5:1 all the way down */
  --satin-fine: linear-gradient(180deg, #5cc9a3 0%, #9be8cb 35%, #2f9e7a 70%, #22906d 100%);
  /* fills under dark type (the ink button's hover, the menu's middle sheet) */
  --satin-fill: linear-gradient(172deg, #3aa984 0%, #84ddbc 28%, #2a9a76 58%, #17785a 100%);

  /* the hero — main.js reads these two for the canvas */
  --tattoo:      #121b16;   /* black tattoo ink as it sits in skin */
  --stencil:     #4d3fae;   /* thermal transfer purple */

  --hair:        rgba(255, 255, 255, .14);
  --hair-soft:   rgba(255, 255, 255, .08);
  --shell-wash:  rgba(255, 255, 255, .03);
  --inner-lift:  inset 0 1px 0 rgba(255, 255, 255, .06);
  --tint:        rgba(255, 255, 255, .08);   /* icon wells, hover washes */
  --tint-dark:   rgba(0, 0, 0, .1);          /* the same, on a pale fill */

  /* captions and controls laid over photographs keep a dark scrim */
  --on-photo:    #ffffff;
  --on-photo-70: rgba(255, 255, 255, .78);

  --display: "Anton", Impact, "Arial Narrow Bold", sans-serif;
  --ui:      "Plus Jakarta Sans", ui-sans-serif, system-ui, "Segoe UI", sans-serif;
  --text:    "Newsreader", Georgia, "Times New Roman", serif;

  --ease: cubic-bezier(.32, .72, 0, 1);
  --ease-menu: cubic-bezier(.65, .01, .05, .99);   /* slow out, fast through, long settle */

  --r-shell: 2rem;      /* outer bezel radius */
  --r-pad:   .375rem;   /* outer bezel padding */
  --r-core:  calc(var(--r-shell) - var(--r-pad));

  /* never narrower than the notch: viewport-fit=cover lets the page run
     under it on a phone held sideways (the insets are 0 everywhere else) */
  --gut: max(clamp(1.25rem, 5vw, 3.5rem), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));
  --shell-w: 78rem;

  /* the page is dark already: scrollbars and controls match, and Chrome's
     and Samsung's forced dark have nothing left to do */
  color-scheme: dark;
}

/* ── reset ────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--ui);
  font-size: clamp(1rem, .35vw + .92rem, 1.0625rem);
  font-weight: 400;
  line-height: 1.68;
  letter-spacing: .004em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

/* a phone holds the page still while the hero plays (see the <head> script) */
html.ink-hold, html.ink-hold body { overflow: hidden; }
html.ink-hold body { touch-action: none; overscroll-behavior: none; }

img, svg { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { list-style: none; padding: 0; }

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

::selection { background: var(--emerald); color: var(--paper); }

.skip {
  position: fixed; z-index: 90;
  top: max(.75rem, env(safe-area-inset-top, 0px)); left: max(.75rem, env(safe-area-inset-left, 0px));
  padding: .7rem 1.2rem; border-radius: 999px;
  background: var(--ink); color: var(--paper);
  font-size: .8rem; font-weight: 500;
  transform: translateY(-160%);
  transition: transform .5s var(--ease);
}
.skip:focus-visible { transform: translateY(0); }
main:focus { outline: none; }

/* ── fixed film grain (never on scrolling content) ────────── */
/* overlay blending leaves pure white untouched; it only textures photographs */
.grain {
  position: fixed; inset: 0; z-index: 60;
  pointer-events: none; opacity: .038;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
}

/* ── the cursor: his machine ─────────────────────────────────
   The pointer is a tattoo machine drawn light, needle first: the needle tip
   (1 35) is the point that clicks. Over anything pressable it turns emerald,
   in place of the hand, except on the ink buttons, whose hover fill is emerald
   and would swallow it. 36px; past 32, Chrome shows the plain arrow where
   the cursor would cross its toolbar (the top ~35px). Made by
   tools/gen-cursor.py. Each rule says it twice so a browser without
   image-set() keeps the 1x image. */
html, .btn.btn--ink {
  cursor: url("../assets/img/cursor/gun.png") 1 35, auto;
  cursor: image-set(url("../assets/img/cursor/gun.png") 1x, url("../assets/img/cursor/gun@2x.png") 2x) 1 35, auto;
}
a[href], button:not(:disabled), summary, label[for] {
  cursor: url("../assets/img/cursor/gun-press.png") 1 35, pointer;
  cursor: image-set(url("../assets/img/cursor/gun-press.png") 1x, url("../assets/img/cursor/gun-press@2x.png") 2x) 1 35, pointer;
}

/* ── layout primitives ────────────────────────────────────── */
.shell {
  width: 100%;
  max-width: var(--shell-w);
  margin-inline: auto;
  padding-inline: var(--gut);
}

.section { padding-block: clamp(5.5rem, 12vw, 11rem); position: relative; }
/* a page's own first section (Aftercare, Booking, Visit) clears the fixed nav */
main > .section:first-child { padding-top: calc(clamp(5.5rem, 12vw, 11rem) + 2rem); }

/* anchor jumps must clear the floating nav island */
/* running text in the book serif: every paragraph but the labels (by
   class, not "no class": the reveals add .is-in), and the lines of the
   aftercare and journal lists. Labels, captions,
   buttons, the nav and the footer stay in the sans. font-size-adjust sizes
   it by its x-height, so the sizes set for the sans still read the same. */
:where(main) :is(p:not(.eyebrow, .gal__cap, .work__more, .studio__quote, .lightbox__cap,
  .event__price, .posts__meta, .post__more, .post__by), .care__list li > div, .skin__key li, .post__body li) {
  font-family: var(--text); font-size-adjust: .5; line-height: 1.55;
}
:is(section, main, .studio__lede, .footer__facts)[id] { scroll-margin-top: 6.5rem; }
/* the nav's Visit lands on the footer's details: they fade in, but never
   slide, or the jump would land where they were 40px ago */
.footer__facts[data-reveal] { transform: none; }

.display {
  font-family: var(--display);
  font-weight: 400;
  line-height: .95;
  letter-spacing: .01em;
  text-transform: uppercase;
  text-wrap: balance;
}
/* Anton has no italic; the stressed word is satin emerald instead */
.display em { font-style: normal; }

/* Satin emerald type: the sheen is a gradient clipped to the letters, laid
   per line so a wrapped word gets the whole sheen on each line. --emerald
   is the colour wherever background-clip: text is missing. */
:is(.display em, .studio__quote, .hero__go span, .book__title) { background-image: var(--satin); }
:is(.nav__mark-glyph, .step__num, .visit__k) { background-image: var(--satin-fine); }
:is(.display em, .studio__quote, .hero__go span, .book__title,
    .nav__mark-glyph, .step__num, .visit__k) {
  color: var(--emerald);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}

.eyebrow {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .32rem .8rem;
  border: 1px solid var(--hair);
  border-radius: 999px;
  background: var(--shell-wash);
  font-size: 10px; font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .2em;
  color: var(--ink-70);
}
.dot {
  width: 3px; height: 3px; border-radius: 50%;
  background: var(--emerald); display: inline-block;
}
.link {
  position: relative;
  color: var(--ink-70);
  padding-bottom: 2px;
  background-image: linear-gradient(var(--emerald), var(--emerald));
  background-size: 0% 1px; background-position: 0 100%;
  background-repeat: no-repeat;
  transition: color .5s var(--ease), background-size .5s var(--ease);
}
.link:hover { color: var(--ink); background-size: 100% 1px; }
.nobr { white-space: nowrap; }

/* ── double-bezel (outer shell + inner core) ──────────────── */
.bezel {
  padding: var(--r-pad);
  border-radius: var(--r-shell);
  background: var(--shell-wash);
  border: 1px solid var(--hair);
  transition: border-color .7s var(--ease), background-color .7s var(--ease), transform .7s var(--ease);
}
.bezel__core {
  border-radius: var(--r-core);
  background: linear-gradient(168deg, var(--paper), var(--paper-raise) 62%);
  box-shadow: var(--inner-lift), 0 1px 2px rgba(0, 0, 0, .4);
  height: 100%;
}

/* ── buttons: pill + nested icon island ───────────────────── */
.btn {
  --btn-fg: var(--ink);
  position: relative;
  display: inline-flex; align-items: center; gap: .9rem;
  padding: .55rem .55rem .55rem 1.5rem;
  border: 1px solid var(--hair);
  border-radius: 999px;
  background: var(--shell-wash);
  color: var(--btn-fg);
  font-size: .875rem; font-weight: 500; letter-spacing: .005em;
  transition: transform .55s var(--ease), background-color .55s var(--ease),
              border-color .55s var(--ease), color .55s var(--ease);
}
.btn__icon {
  display: grid; place-items: center;
  width: 2.15rem; height: 2.15rem;
  border-radius: 50%;
  background: var(--tint);
  transition: transform .55s var(--ease), background-color .55s var(--ease);
}
.btn__icon svg { width: 1rem; height: 1rem; }
/* the marks from the sprite at the top of each page: booking gets a
   calendar on an emerald disc, Instagram and WhatsApp their own colours */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.btn .btn__icon.btn__icon--cal, .btn:hover .btn__icon.btn__icon--cal { background: var(--emerald); color: #fff; }
:is(.btn__icon--ig, .btn__icon--wa) svg { width: 1.15rem; height: 1.15rem; }
.btn--sm :is(.btn__icon--ig, .btn__icon--wa) svg { width: .95rem; height: .95rem; }

/* Hover fills the pill from the bottom — pale ink into a dark button, emerald
   into a pale one — while the word slides out and its twin slides in under it
   (main.js doubles the label into .btn__t). */
.btn { isolation: isolate; overflow: hidden; }   /* clips the fill while it waits below */
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: var(--ink);
  transform: translateY(101%);
  transition: transform .7s var(--ease);
}
.btn__t { display: block; position: relative; overflow: hidden; }
.btn__t > span { display: block; transition: transform .7s var(--ease); }
.btn__t > span + span { position: absolute; left: 0; top: 100%; }

/* a real pointer only: on a touch screen a tap leaves :hover stuck on */
@media (hover: hover) and (pointer: fine) {
  .btn:hover  { color: var(--paper); border-color: var(--ink); }
  .btn:hover::before { transform: none; }
  .btn:hover .btn__t > span { transform: translateY(-100%); }
  .btn:hover .btn__icon { transform: translate(3px, -1px) scale(1.06); background: var(--tint-dark); }
}
.btn:active { transform: scale(.977); }

.btn--ink {
  background: var(--ink); color: var(--paper); border-color: var(--ink);
}
.btn--ink .btn__icon { background: var(--tint-dark); }
.btn--ink::before { background: var(--satin-fill); }
@media (hover: hover) and (pointer: fine) {
  .btn--ink:hover { border-color: var(--emerald); }
  .btn--ink:hover .btn__icon { background: rgba(0, 0, 0, .14); }
}

.btn--ghost { background: transparent; }

.btn--sm {
  padding: .5rem .5rem .5rem 1.15rem;
  font-size: .8125rem; gap: .7rem;
}
.btn--sm .btn__icon { width: 1.75rem; height: 1.75rem; }
.btn--sm .btn__icon svg { width: .85rem; height: .85rem; }

/* ══════════════════════════════════════════════════════════
   NAV — floating glass island, detached from the top
   ══════════════════════════════════════════════════════════ */
.nav {
  position: fixed; inset: 0 0 auto; z-index: 70;
  display: flex; justify-content: center;
  padding: clamp(.75rem, 2vw, 1.5rem) var(--gut);
  pointer-events: none;
}
.nav__pill {
  pointer-events: auto;
  display: flex; align-items: center; gap: clamp(.75rem, 2vw, 2rem);
  width: max-content; max-width: 100%;
  padding: .45rem .45rem .45rem 1.15rem;
  border: 1px solid var(--hair);
  border-radius: 999px;
  /* light glass: what's behind stays visible, softened just enough that
     the links still read over a photo */
  background: rgba(14, 16, 15, .32);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  box-shadow: var(--inner-lift), 0 22px 50px -28px rgba(0, 0, 0, .8);
  transition: background-color .7s var(--ease), border-color .7s var(--ease),
              transform .7s var(--ease), box-shadow .7s var(--ease);
}
.nav.is-stuck .nav__pill {
  background: rgba(14, 16, 15, .45);
  border-color: rgba(255, 255, 255, .18);
}
/* retracted, unless a key has moved focus into it */
.nav.is-hidden:not(:focus-within) { transform: translateY(-140%); transition: transform .7s var(--ease); }
/* over the menu's ink, glass would only go grey: plain paper instead */
.is-locked .nav .nav__pill { background: var(--paper); }

/* While the hero inks the name, the nav is only its contact icons, sitting
   bare in the top-right corner — no pill, no links, no Book. When the name is
   finished (main.js sets .is-inked) the whole island assembles and fades in. */
.ink:not(.is-inked) .nav { justify-content: flex-end; }
.ink:not(.is-inked) .nav__pill {
  padding: 0; border-color: transparent; background: transparent; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.ink:not(.is-inked) :is(.nav__mark, .nav__links, .nav__cta, .menu-btn) { display: none; }
.ink.is-inked .nav__pill { animation: navIn .9s var(--ease) both; }
@keyframes navIn {
  from { opacity: 0; transform: translateY(-1rem); }
  to   { opacity: 1; transform: none; }
}

.nav__mark {
  display: flex; align-items: center; gap: .6rem;
  /* pad the hit area out to 46px without moving the glyph */
  padding: .5rem; margin: -.5rem;
}
.nav__mark-glyph {
  display: grid; place-items: center;
  width: 1.9rem; height: 1.9rem;
  border: 1px solid var(--hair);
  border-radius: 50%;
  font-family: var(--display); font-size: .95rem;
  color: var(--emerald);
  transition: border-color .6s var(--ease), transform .6s var(--ease);
}
.nav__mark:hover .nav__mark-glyph { border-color: var(--emerald); transform: rotate(-8deg); }
.nav__mark-word {
  font-family: var(--display);
  font-size: 1.05rem; letter-spacing: .01em;
}

.nav__links { display: flex; align-items: center; gap: clamp(.9rem, 1.6vw, 1.6rem); }
.nav__links a {
  position: relative;
  font-size: .8125rem; font-weight: 400;
  color: var(--ink-70);
  transition: color .5s var(--ease);
}
.nav__links a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -6px; height: 1px;
  background: var(--emerald);
  transform: scaleX(0); transform-origin: left;
  transition: transform .55s var(--ease);
}
.nav__links a:hover { color: var(--ink); }
/* where you are: the page (the portfolio), or the section on screen (main.js
   marks it as you scroll) — lit, and underlined in emerald */
.nav__links a[aria-current] { color: var(--ink); }
.nav__links a[aria-current]::after { transform: none; }
.nav__links a:hover::after { transform: scaleX(1); }

/* Instagram · phone · map */
.nav__social { display: flex; align-items: center; }
.nav__icon {
  display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem;
  border-radius: 50%;
  color: var(--ink-70);
  transition: color .5s var(--ease), background-color .5s var(--ease);
}
.nav__icon:hover { color: var(--ink); background: var(--tint); }
.nav__icon svg { width: 1.15rem; height: 1.15rem; }

/* the menu button (phones and tablets): a round black hamburger. Open, the
   outer bars swing into an x and the middle one draws away */
.menu-btn {
  display: none; place-items: center;
  width: 2.875rem; height: 2.875rem;   /* Book's height beside it */
  border: 1px solid var(--hair); border-radius: 50%;
  background: var(--paper);
  box-shadow: 0 18px 40px -24px rgba(0, 0, 0, .8);
  color: var(--ink);
}
.menu-btn__bars { position: relative; width: 1.1rem; height: .75rem; }
.menu-btn__bars > span {
  position: absolute; left: 0; top: 0; width: 100%; height: 1.5px;
  border-radius: 1px; background: currentColor;
  transition: transform .7s var(--ease-menu);
}
.menu-btn__bars > :nth-child(2) { top: calc(50% - .75px); }
.menu-btn__bars > :nth-child(3) { top: calc(100% - 1.5px); }
[aria-expanded="true"] > .menu-btn__bars > :nth-child(1) { transform: translateY(calc(.375rem - .75px)) rotate(45deg); }
[aria-expanded="true"] > .menu-btn__bars > :nth-child(2) { transform: scaleX(0); }
[aria-expanded="true"] > .menu-btn__bars > :nth-child(3) { transform: translateY(calc(.75px - .375rem)) rotate(-45deg); }
/* high contrast would paint the bars' fill away */
@media (forced-colors: active) { .menu-btn__bars > span { background: ButtonText; forced-color-adjust: none; } }

/* The menu. Opening (.is-open): the shade fades up, three layers sweep in
   from the right 0.12s apart (stencil purple, emerald, then the ink the words
   sit on), the words rise from a 10deg tilt out of their own clipped lines,
   and the two links at the foot float up last. Closing (.is-closing) slides
   the panel out whole; main.js hides it after, which resets every part out
   of sight for the next opening. */
.menu {
  position: fixed; inset: 0; z-index: 65;
  color: var(--on-photo);
}
.menu[hidden] { display: none; }

.menu__shade {
  position: absolute; inset: 0;
  background: rgba(18, 27, 22, .4);
  opacity: 0;
  transition: opacity .7s var(--ease-menu);
}
.menu.is-open .menu__shade { opacity: 1; }
.menu.is-closing .menu__shade { opacity: 0; }

.menu__panel {
  position: absolute; inset: 0 0 0 auto;
  width: 100%;
  transition: transform .7s var(--ease-menu);
}
.menu.is-closing .menu__panel { transform: translateX(120%); }

.menu__bg { position: absolute; inset: 0; }
.menu__layer {
  position: absolute; inset: 0;
  border-radius: 1.25rem 0 0 1.25rem;
  background: var(--tattoo);
  transform: translateX(101%);
}
.menu__layer:nth-child(1) { background: var(--stencil); }
.menu__layer:nth-child(2) { background: var(--satin); }
.menu.is-open .menu__layer { transform: none; transition: transform .575s var(--ease-menu); }
.menu.is-open .menu__layer:nth-child(2) { transition-delay: .12s; }
.menu.is-open .menu__layer:nth-child(3) { transition-delay: .24s; }

/* one drawing per word, built by main.js; only the pointed word's shows */
.menu__shape {
  position: absolute; inset: 0;
  width: 100%; height: 100%; max-width: none;
  opacity: 0; pointer-events: none;
}
.menu__shape.is-on { opacity: 1; }
.menu__shape > * { transform-box: fill-box; transform-origin: center; }
.menu__shape .a { color: rgba(255, 255, 255, .1); }
.menu__shape .b { color: rgba(138, 106, 46, .5); }
.menu__shape .c { color: rgba(77, 63, 174, .6); }

.menu__body {
  position: relative; height: 100%;
  display: flex; flex-direction: column; justify-content: space-between; gap: 2rem;
  padding: 6.5rem 0 2rem;
  overflow-y: auto; overscroll-behavior: contain;
}
.menu__links li { overflow: hidden; }   /* each word rises out of its own line */
.menu__links a {
  position: relative; display: block;
  isolation: isolate;   /* keeps the hover wash above the ink layer */
  padding: .06em var(--gut);
  font-family: var(--display);
  /* eight words fit a phone held either way, and never outgrow a tablet */
  font-size: clamp(1.5rem, min(16vw, calc((100svh - 20rem) / 9.7)), 5.5rem);
  line-height: .95;
  text-transform: uppercase;
  transform: translateY(140%) rotate(10deg);
}
.menu.is-open .menu__links a {
  transform: none;
  transition: transform .7s var(--ease-menu) calc(.35s + var(--i, 0) * .05s);
}
/* a pale wash rises behind the word under the pointer */
.menu__links a::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: rgba(255, 255, 255, .08);
  transform: scaleY(0); transform-origin: bottom;
  transition: transform .5s cubic-bezier(.65, .05, 0, 1);
}
.menu__links a:is(:hover, :focus-visible)::before { transform: none; }
.menu__links a[aria-current] { color: var(--emerald); }
.menu :focus-visible { outline-color: var(--on-photo); outline-offset: -3px; }

.menu__foot {
  display: flex; flex-wrap: wrap; gap: 0 1.5rem;
  margin-inline: var(--gut); padding-top: .55rem;
  border-top: 1px solid rgba(255, 255, 255, .16);
  font-size: .8125rem;
}
.menu__foot a {
  padding: .7rem 0;   /* 44px targets: the nav's contact icons moved here */
  color: rgba(255, 255, 255, .76);
  opacity: 0; transform: translateY(50%);
}
.menu.is-open .menu__foot a {
  opacity: 1; transform: none;
  transition: opacity .7s var(--ease-menu) calc(.55s + var(--i, 0) * .04s),
              transform .7s var(--ease-menu) calc(.55s + var(--i, 0) * .04s),
              color .5s var(--ease);
}
.menu.is-open .menu__foot a:hover { color: var(--on-photo); transition-delay: 0s; }

/* a tablet keeps the page in view beside the panel */
@media (min-width: 40em) {
  .menu__panel { width: 35rem; }
}
/* on a phone the panel is the whole screen: its rounded edge sweeps in,
   then rests just off the left side */
@media (max-width: 39.99em) {
  .menu__bg { left: -1.25rem; }
}

/* ══════════════════════════════════════════════════════════
   HERO — the name, tattooed in

   Every letter is a patch of skin with its own black & grey tattoo. The SVG
   is generated into the markup: the art cut to the letters (two images —
   the finished tattoos and their stencil print — tinted by SVG filters) under
   the letter outlines. Without JS, or with reduced motion, it shows the
   finished tattoos. With motion on (the <head> script sets .ink) it first
   shows the stencil print, then main.js lays a canvas over it and tattoos
   the letters; .is-live hides the SVG once the canvas has its art.

   On a desktop (.ink-scrub) the section is --travel taller than the viewport
   and the stage is sticky, so the name holds still while that distance
   scrolls past. The first three quarters (90vh) drive the inking; the last
   quarter (30vh, about three wheel notches) holds the finished name still
   while the nav comes in, so the page only moves on once it is done. A phone, a tablet or a touch screen
   gets a plain one-screen hero and plays it.
   ══════════════════════════════════════════════════════════ */
.hero { --travel: 120vh; position: relative; background: #000; color: var(--on-photo); }

/* The lockup — name and disciplines — centres in the room left above the
   corner pieces on the stage's bottom edge: the machine bottom left, the
   shop label just right of it. The bottom padding reserves the taller of
   the two. --name-h caps the name so the line always fits under it.
   Below 64em his face is a panel --face-h tall, in the flow above the
   lockup, and the two centre together under the nav's corner buttons; from
   64em the face takes the right half, the lockup and the machine keep the
   left half, the shop label still beside the machine (see the media query). */
.hero__stage {
  --pad-t: clamp(4.5rem, 11vh, 6.5rem);
  --corner: clamp(4.5rem, 3.5rem + 2vw, 6rem);   /* the shop label's height; it grows with the screen */
  --machine: clamp(6rem, 10vw, 9rem);
  --face-h: 38svh;
  --name-h: min(24svh, 32rem);
  --name-w: calc((100vw - 2 * var(--gut)) * .92);  /* the lockup's column */
  position: relative; position: sticky; top: 0;
  display: grid; align-content: center; justify-items: center;
  height: 100vh; height: 100svh;
  /* the corner pieces' inset: clear of an iPhone's home indicator too */
  --gut-b: max(var(--gut), env(safe-area-inset-bottom, 0px));
  padding: var(--pad-t) var(--gut) calc(max(var(--corner), var(--machine)) + var(--gut) * .5 + var(--gut-b));
  overflow: hidden;
}

/* ── his face ─────────────────────────────────────────────────
   The hero is black, and so is the portrait's backdrop, its edges dissolved
   into pure black (tools/gen-hero-face.py): the panel has no edge at all.
   The whole portrait shows on every screen, hair to shoulders; below 64em
   the nav is two corner buttons, so nothing sits over him. Still: only the
   name moves. */
.hero__face {
  width: 100%; height: var(--face-h);
  margin-bottom: clamp(1rem, 3svh, 2rem);
  overflow: hidden;
}
.hero__face img { width: 100%; height: 100%; object-fit: contain; }

/* the contact icons sit bare on the black until the name is inked */
.ink:not(.is-inked) .nav__icon { color: var(--on-photo-70); }
.ink:not(.is-inked) .nav__icon:hover { color: var(--on-photo); background: rgba(255, 255, 255, .1); }

.hero__title { display: grid; place-items: center; width: 100%; margin: 0; }
.hero__title > * { grid-area: 1 / 1; }

/* The heading's real words, stacked over the drawn name and painted with a
   transparent fill: screen readers, find-in-page and selection get the text
   where the name is, and the browser sees the heading paint — an SVG or a
   canvas is never an LCP candidate, so without this the page reports none.
   (A transparent `color` or opacity:0 would be skipped; a transparent fill
   is still painted text.) Sized to about the name's width. */
.hero__text {
  position: relative; z-index: 1;
  width: min-content;
  font: 700 calc(min(var(--name-w), var(--name-h) * 1.46) / 5.2) / .92 var(--ui);
  text-transform: uppercase; text-align: center;
  -webkit-text-fill-color: transparent;
}

/* as wide as the frame allows, unless the screen is too short for that */
.hero__ink {
  position: relative; display: block;
  width: min(100%, calc(var(--name-h) * var(--ar)));
}
.hero__name { width: 100%; height: auto; overflow: visible; }
.hero__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* ── under the name: what he does ─────────────────────────────
   Three disciplines on one tracked line, parted by emerald hairlines. Plain
   sans, small and spaced — the name is the display. Where is the shop label's. */
.hero__tag {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  margin-top: clamp(1.25rem, 3svh, 2.25rem);
  /* capped by its column (the line is ~25em at a phone's tracking, ~28em at
     a desktop's), so it never wraps with a hairline starting a line */
  --tag: clamp(.8rem, .7rem + .45vw, 1.05rem);
  font-size: min(var(--tag), (100vw - 2 * var(--gut)) / 25); font-weight: 500;
  letter-spacing: .24em; text-transform: uppercase;
  color: var(--on-photo);
}
/* on a desktop the line is held to about 3/4 of the name's drawn width, so
   on a short, wide screen (where the name is capped by height) it never
   outgrows the name above it */
@media (min-width: 64em) { .hero__tag { font-size: min(var(--tag), min(var(--name-w), var(--name-h) * 1.4627) / 37); } }
.hero__tag li { display: flex; align-items: center; white-space: nowrap; }
.hero__tag li + li::before {
  content: ""; display: block;
  width: 1px; height: 1em; margin: 0 1.1em;
  background: var(--emerald);
}

/* ── the corners ────────────────────────────────────────────
   Bottom left, the machine, needle down into the corner — it buzzes
   (.is-buzzing, set by main.js) while the needle is in the skin. Right
   beside it, the shop: a flat label on an emerald rule, opening the map,
   its foot level with the needle. Both sit --gut-b up, clear of a home
   indicator. */
.hero__machine {
  position: absolute; left: var(--gut); bottom: var(--gut-b);
  width: var(--machine); height: auto;
  pointer-events: none; user-select: none;
  /* a steel-grey drawing would sink into the black: drawn light instead */
  filter: invert(1) brightness(.92);
}
.hero__machine.is-buzzing { animation: buzz .08s steps(2) infinite; }
@keyframes buzz {
  from { transform: translate(0, 0) rotate(0); }
  to   { transform: translate(.7px, -.5px) rotate(.5deg); }
}

.hero__badge {
  position: absolute; bottom: var(--gut-b);
  left: calc(var(--gut) + var(--machine) + clamp(.75rem, 2vw, 1.5rem));
  max-width: calc(100% - var(--gut) * 2 - var(--machine) - clamp(.75rem, 2vw, 1.5rem));
  display: grid; justify-items: start;
  padding-left: clamp(.75rem, .5rem + .6vw, 1.25rem); border-left: 1px solid var(--emerald);
  text-align: left;
  /* sized off the screen like the lockup, so it grows on a big display */
  gap: clamp(.4rem, .3rem + .3vw, .7rem);
  font-size: clamp(10px, .55rem + .25vw, 14px); font-weight: 500; line-height: 1;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--on-photo-70);
  transition: color .5s var(--ease);
}
.hero__badge:hover { color: var(--on-photo); }
.hero__shop {
  font-family: var(--display); font-size: clamp(1.25rem, .8rem + 1vw, 2.1rem);
  letter-spacing: .01em; color: var(--on-photo);
}
.hero__town { line-height: 1.3; }
.hero__go {
  display: inline-flex; gap: .4em;
  font-size: clamp(.75rem, .6rem + .25vw, 1rem); letter-spacing: .005em; text-transform: none;
  color: var(--on-photo);
}
.hero__go span { transition: transform .5s var(--ease); }
.hero__badge:is(:hover, :focus-visible) .hero__go span { transform: translate(2px, -2px); }

/* ── depth: the lockup leans with the pointer ────────────────
   main.js writes --px/--py (each -1…1) on the stage from the pointer's
   place in it. The name tilts like a card pressed where the pointer is;
   the lines under it drift a little the other way; the machine and the
   badge, nearest the eye, slide furthest. `translate` rather than
   `transform` on the machine, so its buzz keeps its own transform. */
.hero__stage { --px: 0; --py: 0; }
@media (hover: hover) and (pointer: fine) {
  .hero__ink {
    transform: perspective(70rem)
      rotateY(calc(var(--px) * 5deg)) rotateX(calc(var(--py) * -4deg))
      translate3d(calc(var(--px) * .5rem), calc(var(--py) * .4rem), 0);
  }
  .hero__tag { translate: calc(var(--px) * -.35rem) calc(var(--py) * -.25rem); }
  .hero__machine { translate: calc(var(--px) * -1.4rem) calc(var(--py) * -1rem); }
  .hero__badge   { translate: calc(var(--px) * -1.1rem) calc(var(--py) * -.8rem); }
}

/* Stroke widths here are viewBox units (6531 across the name), so outlines
   scale with the name: 22 is about 2px at a 600px-wide name, the canvas's
   own w/320. Not `vector-effect: non-scaling-stroke`: WebKit copies a
   path's matched styles into every <use> clone of it, and the skin and the
   clip below are clones, so the skin's 60 became 60 screen px — a white
   sticker round the whole name in Safari. */
.hero__letters { fill: none; stroke: var(--tattoo); stroke-width: 22px; }
.hero__art--stencil { display: none; }

/* the stencil print, until the canvas takes over — drawn the way it draws it */
.ink .hero__art--ink { display: none; }
.ink .hero__art--stencil { display: inline; opacity: .85; }
.ink .hero__letters { stroke: var(--stencil); stroke-width: 15px; stroke-opacity: .8; }
/* the skin runs a hair past each letter's tattoo outline, so the letters
   keep a crisp silhouette on the black even where the art is dark at the
   edge (viewBox units: about 3px at a 650px-wide name) */
.hero__skin { fill: #fff; stroke: #fff; stroke-width: 60px; stroke-linejoin: round; }
.hero__ink.is-live :is(.hero__art, .hero__letters) { visibility: hidden; }

/* ══════════════════════════════════════════════════════════
   STUDIO / ABOUT — editorial split
   ══════════════════════════════════════════════════════════ */
.studio__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2.5rem, 6vw, 5rem);
}
/* on one line, sized to fit its column at every width */
.studio__title { font-size: clamp(2.2rem, 5vw, 4.6rem); margin-top: 1.2rem; white-space: nowrap; }
/* the quote under the heading, in tattoo script; its small x-height needs
   the larger size */
.studio__quote {
  margin: 1.1rem 0 0; max-width: 22ch;
  font-family: "Pinyon Script", "Snell Roundhand", "Segoe Script", cursive;
  font-size: clamp(2rem, 3.2vw, 3rem); line-height: 1.2;
  color: var(--emerald);
  text-wrap: balance;
  /* room for the script's swashes, which would fall outside the clipped sheen */
  padding: .15em .25em; margin-inline: -.25em; margin-top: calc(1.1rem - .15em);
  /* a block, so the sheen is tiled one line-height at a time to light every line */
  background-origin: content-box; background-size: 100% 1.2em; background-repeat: repeat;
}
.studio__body { display: grid; gap: 1.35rem; }
.studio__body p { color: var(--ink); max-width: 44ch; }

.stats {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  margin-top: 2.5rem;
  background: var(--hair-soft);
  border: 1px solid var(--hair-soft);
  border-radius: 1.25rem;
  overflow: hidden;
}
.stat { padding: 1.4rem 1.5rem; background: var(--paper); }
.stat dt {
  font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .2em;
  color: var(--ink-dim);
}
.stat dd {
  display: flex; flex-wrap: wrap; align-items: baseline; column-gap: .25em;
  margin: .4rem 0 0;
  font-family: var(--display);
  font-size: clamp(2rem, 4vw, 2.75rem);
  line-height: 1;
  letter-spacing: -.02em;
}
/* words, not numbers: a smaller Anton, so four of them still read as a row */
.stat--text dd { font-size: clamp(1.15rem, 1.9vw, 1.45rem); letter-spacing: -.01em; }

/* ── him at work ──────────────────────────────────────────────
   Narrow: on top, full width, fading into the text below. Wide (64em+):
   absolute, the left part of the section, bleeding off the page's left
   edge and masked to dissolve into the paper on the other three sides
   (two gradients, intersected). Black and white until the pointer is on
   it; in colour where there is no pointer. */
.studio { overflow: hidden; }
.studio__photo {
  position: relative; margin: 0;
  aspect-ratio: 682 / 1024;          /* the whole photo, nothing cropped */
  --cap-b: clamp(3rem, 12%, 5rem);   /* the caption's (and pause button's) lift */
}
/* the mask is on the image, so the caption under it stays crisp */
.studio__photo :is(img, video) {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 22%;
  filter: grayscale(1) contrast(1.03);
  transition: filter 1.1s var(--ease), transform 1.6s var(--ease), opacity .9s var(--ease);
  -webkit-mask-image: linear-gradient(180deg, #000 70%, transparent);
          mask-image: linear-gradient(180deg, #000 70%, transparent);
}
/* the clip lies over the photo and takes its place once it is moving (both
   go, or the photo would show through the fades); a pause keeps its frame */
.studio__photo video { position: absolute; inset: 0; opacity: 0; object-position: 50% 38%; }
.studio__photo.has-clip video { opacity: 1; }
.studio__photo.has-clip img { opacity: 0; }
.studio__photo:hover :is(img, video) { filter: none; transform: scale(1.02); }
@media (hover: none) { .studio__photo :is(img, video) { filter: none; } }
.studio__photo figcaption {
  position: absolute; left: var(--gut); bottom: var(--cap-b);
  display: flex; align-items: center; gap: .75rem;
  min-height: 2rem;
  padding: .4rem .8rem; border-radius: 999px;
  background: rgba(0, 0, 0, .72);
  font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .2em; color: var(--on-photo);
}
.studio__photo figcaption span + span::before { content: "·"; margin-right: .75rem; color: var(--emerald); }
/* the clip's pause button, just left of the caption: a 44px target whose
   circle is drawn as tall as the caption (32px) */
.studio__pause {
  position: absolute; left: calc(var(--gut) - .375rem); bottom: calc(var(--cap-b) - .375rem);
  display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem; padding: 0;
  border: 0; border-radius: 50%;
  background: none; color: var(--on-photo);
}
.studio__pause::before {
  content: ""; position: absolute; inset: .375rem;
  border: 1px solid transparent; border-radius: 50%;
  background: rgba(0, 0, 0, .72);
}
.studio__pause svg { position: relative; width: .75rem; height: .75rem; }
.studio__photo.has-pause figcaption { left: calc(var(--gut) + 2.5rem); }
/* the words tuck under the fade */
.studio__photo + .studio__grid { margin-top: -3rem; position: relative; }

/* ══════════════════════════════════════════════════════════
   SECTION HEADS
   ══════════════════════════════════════════════════════════ */
.sec-head {
  display: grid; gap: 1.5rem;
  margin-bottom: clamp(2.5rem, 6vw, 4.5rem);
}
.sec-head__title { font-size: clamp(2.25rem, 5.4vw, 4.25rem); margin-top: 1.2rem; }
.sec-head__note { color: var(--ink-dim); max-width: 34ch; }

/* ══════════════════════════════════════════════════════════
   PORTFOLIO — a bento strip you drag

   Two rows of tiles that run off the right of the screen: a reel stands
   two rows tall, the two big photos two by two (tall, on a phone), the
   rest square. The strip
   scrolls sideways (trackpad, touch, a tab moving focus; the scrollbar is
   hidden) and main.js adds a mouse drag with a little run-on. Pointing at
   a tile, or tabbing to it, lifts it and brings its caption up over a
   shade; a touch screen has every caption showing. A photo opens full size
   in the lightbox at the end of <main>; a reel goes to Instagram, and
   plays its clip under the pointer. The tiles arrive once the strip is on
   screen (data-reveal on .gal), one after the other from the left (--d,
   from main.js). Left alone it drifts, looping on copies of the tiles
   (.gal__slide--echo) that main.js adds; .gal__pause stops it. Without
   JS they are simply there, in the same strip, still.
   ══════════════════════════════════════════════════════════ */
.gal { --tile: 15rem; position: relative; }
/* the strip's own reveal is off: its tiles do the arriving */
.gal[data-reveal] { opacity: 1; transform: none; filter: none; transition: none; }
.gal__track {
  display: grid; grid-auto-flow: column; grid-auto-columns: var(--tile);
  grid-template-rows: repeat(2, var(--tile)); gap: 1rem;
  margin: 0; padding: .5rem var(--gut) 2rem; list-style: none;
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none; user-select: none; -webkit-user-select: none;
  /* its own layer: at 100% scaling Chrome would otherwise repaint every
     tile, shadows and all, on each frame of a sideways scroll */
  will-change: scroll-position;
}
.gal__track::-webkit-scrollbar { display: none; }
.gal__slide {
  position: relative;
  opacity: 0; transform: translateY(20px) scale(.95);
  transition: opacity .7s var(--ease) var(--d, 0ms), transform .8s cubic-bezier(.2, .9, .3, 1.12) var(--d, 0ms);
}
.gal.is-in .gal__slide { opacity: 1; transform: none; }
.gal__slide--reel, .gal__slide--big { grid-row: span 2; }
/* the two big photos: two by two, where there is room; tall on a phone,
   so the tiles still pair up and the second row has no holes */
@media (min-width: 48em) { .gal__slide--big { grid-column: span 2; } }
.gal__open {
  position: relative; display: block; width: 100%; height: 100%;
  padding: 0; border: 1px solid var(--hair); border-radius: 1rem; overflow: hidden;
  background: var(--paper-raise);
  box-shadow: 0 30px 60px -30px rgba(0, 0, 0, .9);
  transition: transform .5s var(--ease), box-shadow .5s var(--ease), border-color .5s var(--ease);
}
.gal__open img, .gal__open video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.gal__open img { transition: transform .7s var(--ease); }
.gal__open video { opacity: 0; transition: opacity .4s var(--ease); }
.gal__slide.is-playing video { opacity: 1; }
/* a shade over the foot of the photo for the caption to sit on */
.gal__open::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, .82), rgba(0, 0, 0, .4) 45%, transparent 75%);
  opacity: 0; transition: opacity .5s var(--ease); pointer-events: none;
}
/* a play mark so a reel reads as video, not photo, until its clip runs */
.gal__slide--reel .gal__open::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 3.25rem; height: 3.25rem; margin: -1.625rem 0 0 -1.625rem;
  border-radius: 50%; border: 1px solid var(--on-photo-70);
  background: rgba(12, 16, 14, .5) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 10'%3E%3Cpath d='M0 0l8 5-8 5z' fill='%23ffffff'/%3E%3C/svg%3E") 56% 50% / .75rem no-repeat;
  pointer-events: none; transition: opacity .3s var(--ease);
}
.gal__slide.is-playing .gal__open::after { opacity: 0; }
.gal__cap {
  position: absolute; inset: auto 0 0 0; z-index: 1;
  display: grid; gap: .35rem; margin: 0; padding: 1rem 1.1rem;
  pointer-events: none; opacity: 0; transform: translateY(1rem);
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.gal__cap a { pointer-events: auto; }
.gal__name { font-size: clamp(1.15rem, 1.6vw, 1.5rem); line-height: 1; }
.gal__meta { font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: .18em; color: var(--on-photo-70); }
/* 10px type, but a 44px target: the padding is taken back by the margins */
.gal__ig {
  display: inline-block; padding: 16px 0; margin: -16px 0;
  text-decoration: underline; text-underline-offset: .3em;
}
.gal__ig:hover { color: var(--ink); }
/* under the pointer, or with keyboard focus (a mouse click must not leave
   a tile lit): the tile lifts, the photo grows a
   little and the caption comes up over its shade */
.gal__slide:is(:hover, :has(:focus-visible)) .gal__open {
  transform: scale(1.02); border-color: var(--on-photo-70);
  box-shadow: 0 40px 70px -30px #000;
}
.gal__slide:is(:hover, :has(:focus-visible)) .gal__open img { transform: scale(1.05); }
.gal__slide:is(:hover, :has(:focus-visible)) .gal__open::before,
.gal__slide:is(:hover, :has(:focus-visible)) .gal__cap { opacity: 1; transform: none; }
/* no pointer to point with: the shade and the captions are simply there */
@media (hover: none) { .gal__open::before, .gal__cap { opacity: 1; transform: none; } }

/* under the strip (main.js): the drift's pause and where you are, a
   hairline with a thumb as wide as the share of the work on screen. The
   line is 2px, the band you press or drag 44px. Hidden when it all fits */
.gal__ctl {
  display: flex; align-items: center; justify-content: center; gap: .75rem;
  margin-top: -.75rem; padding: 0 var(--gut);
}
.gal__ctl[hidden] { display: none; }
.gal__bar {
  position: relative; overflow: hidden;
  width: min(18rem, 60vw); height: 2.75rem;
  cursor: pointer; touch-action: pan-y;
}
.gal__bar::before, .gal__bar i {
  position: absolute; top: 50%; height: 2px; margin-top: -1px; border-radius: 2px;
}
.gal__bar::before { content: ""; left: 0; right: 0; background: var(--hair); }
/* the second thumb is the first, a bar's width to the left: on the loop
   it carries the thumb on from the start as it runs off the end */
.gal__bar i {
  left: calc(var(--a, 0) * 100%); width: calc(var(--w, .3) * 100%);
  background: var(--ink); transition: height .2s var(--ease), margin .2s var(--ease);
}
.gal__bar i + i { left: calc((var(--a, 0) - 1) * 100%); }
.gal__bar:hover i, .gal__bar.is-held i { height: 4px; margin-top: -2px; }
@media (forced-colors: active) {
  .gal__bar { forced-color-adjust: none; }
  .gal__bar::before { background: GrayText; }
  .gal__bar i { background: CanvasText; }
}

.work__more { display: flex; justify-content: center; margin-top: clamp(1.75rem, 3.5vw, 2.75rem); }
/* the drift's pause, at the bar's left end, and the skin drawing's, in
   its corner (main.js): a 44px target whose ring is drawn at 32px */
.gal__pause, .skin__pause {
  position: relative; display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem; padding: 0;
  border: 0; border-radius: 50%; background: none; color: var(--ink);
}
.gal__pause::before, .skin__pause::before {
  content: ""; position: absolute; inset: .375rem;
  border: 1px solid var(--hair); border-radius: 50%; background: rgba(0, 0, 0, .6);
  transition: border-color .3s var(--ease);
}
:is(.gal__pause, .skin__pause):hover::before { border-color: var(--ink); }
:is(.gal__pause, .skin__pause) svg { position: relative; width: .75rem; height: .75rem; }
/* the carousel is compact, so the gap from it down to Styles is too: about
   half a section's usual padding either side */
.work { padding-bottom: clamp(2.5rem, 5vw, 4rem); }
.work + .styles { padding-top: clamp(3rem, 6vw, 5rem); }

/* ── lightbox: full-size photo in the top layer ── */
.lightbox {
  width: 100vw; height: 100dvh; max-width: none; max-height: none;
  margin: 0; padding: 1.5rem; border: 0; background: transparent;
  color: var(--ink);
  display: none; place-items: center; place-content: center; gap: 1rem;
}
.lightbox[open] { display: grid; }
/* the page behind holds still while it is open (main.js sets .is-boxed); the
   gutter stays reserved so nothing jumps under a classic scrollbar */
html.is-boxed, html.is-boxed body { overflow: hidden; }
html.is-boxed { scrollbar-gutter: stable; }
.lightbox { overscroll-behavior: contain; }
.lightbox::backdrop { background: rgba(0, 0, 0, .95); }
.lightbox__img {
  width: auto; height: auto;
  max-width: min(92vw, 62rem); max-height: 82dvh;
  border-radius: 1rem;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .9);
}
.lightbox__cap { font-size: .875rem; color: var(--ink-dim); text-align: center; }
.lightbox__close {
  position: fixed;
  top: max(1.25rem, env(safe-area-inset-top, 0px)); right: max(1.25rem, env(safe-area-inset-right, 0px));
  display: grid; place-items: center;
  width: 2.75rem; height: 2.75rem; border-radius: 50%;
  border: 1px solid var(--hair); background: rgba(14, 16, 15, .8);
  transition: border-color .5s var(--ease), background-color .5s var(--ease);
}
.lightbox__close:hover { border-color: var(--emerald); background: var(--paper); }
.lightbox__close svg { width: 1rem; height: 1rem; }

/* ══════════════════════════════════════════════════════════
   AFTERCARE — precautions, the skin, the healing, the rules

   Four rows under the heading, drawn rather than photographed. The skin: a
   cross-section (inline SVG; the layers in ink and hairlines, the ink and
   the needle's tip in emerald) beside the why of it, with a numbered key.
   The healing: five stages on one line, their icons sitting on the line, a
   column down the left on a phone. The rules: two lists, before and after,
   a line each with its icon; a "don't" has a slash through it from CSS. Then
   when to get help. The icons come from one <symbol> sprite at the top of
   the section (.care__defs), stroke only, so they take the text's colour.
   ══════════════════════════════════════════════════════════ */
.care__defs { position: absolute; }
.care__row { display: grid; gap: clamp(1.5rem, 3.5vw, 2.5rem); margin-top: clamp(3rem, 7vw, 5.5rem); }
.care__row:first-of-type { margin-top: 0; }
.care__h {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.75rem, 3vw, 2.4rem); line-height: 1; letter-spacing: -.01em;
  padding-bottom: 1rem; border-bottom: 1px solid var(--hair);
}
/* an icon in a ring; its symbol is stroke only, so it is the ring's colour */
.care__ic {
  flex: none; width: 3rem; height: 3rem; padding: .8rem;
  border: 1px solid var(--hair); border-radius: 50%;
  background: var(--paper); color: var(--emerald);
  fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
  overflow: visible; position: relative; z-index: 1;
}
/* a don't: a slash in ink across the ring */
.care__ic--no { position: relative; }
.care__ic--no + div::before,
.care__ic--no ~ ::before { content: none; }
.care__slash, .care__ic--no { background:
  linear-gradient(135deg, transparent calc(50% - .9px), var(--ink) calc(50% - .9px), var(--ink) calc(50% + .9px), transparent calc(50% + .9px)) 50% 50% / 62% 62% no-repeat,
  var(--paper); }

/* ── the skin ── */
.skin { position: relative; margin: 0; color: var(--ink); }
/* moving (main.js): the drops grow from their own middles, the flakes
   turn on theirs; the pause sits over the empty air above the skin */
.skin__ink circle, .skin__flakes path { transform-box: fill-box; transform-origin: center; }
.skin__flakes path { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; }
.skin__pause { position: absolute; top: -.375rem; left: -.375rem; }
.skin > svg { display: block; width: 100%; height: auto; }
.skin__fat path { fill: var(--paper-raise); }
.skin__cells { fill: none; stroke: var(--hair); }
.skin__dermis { fill: var(--shell-wash); }
.skin__epi { fill: url(#skin-hatch); }
.skin__line { fill: none; stroke: currentColor; stroke-width: 1.5; }
.skin__edge { fill: none; stroke: var(--hair); stroke-width: 1; }
.skin__ink { fill: var(--emerald); }
.skin__needle { fill: none; stroke: var(--ink); stroke-width: 3; }
.skin__tip { fill: var(--emerald); }
.skin__t { fill: currentColor; font: 500 11px var(--ui); letter-spacing: .15em; }
.skin__num circle { fill: var(--paper); stroke: var(--hair); }
.skin__num text { fill: var(--emerald); font: 500 11px var(--ui); letter-spacing: .1em; text-anchor: middle; }
.skin__why p { margin-top: 1.25rem; color: var(--ink-dim); max-width: 48ch; }
.skin__key { display: grid; margin-top: 1.5rem; }
.skin__key li {
  display: grid; grid-template-columns: 1.75rem 1fr; gap: 1rem; align-items: start;
  padding-block: .9rem; border-top: 1px solid var(--hair-soft);
  font-size: .9375rem; color: var(--ink-dim);
}
.skin__key li:last-child { border-bottom: 1px solid var(--hair-soft); }
.skin__key span {
  display: grid; place-items: center; width: 1.75rem; height: 1.75rem;
  border: 1px solid var(--hair); border-radius: 50%;
  font-size: 10px; font-weight: 500; letter-spacing: .1em; color: var(--emerald);
}
.skin__key strong, .care__list strong { color: var(--ink); font-weight: 500; }

/* ── the healing: a line through the icons ── */
.heal { position: relative; display: grid; gap: 1.75rem; }
.heal::before {
  content: ""; position: absolute; left: 1.5rem; top: 1.5rem; bottom: 1.5rem;
  border-left: 1px solid var(--hair);
}
/* align-content: the stages stand in a row of equal height, and the rows
   inside one must not spread into the spare height */
.heal__t { display: grid; grid-template-columns: 3rem 1fr; column-gap: 1.1rem; align-items: start; align-content: start; }
.heal__t .care__ic { grid-row: span 3; }
.heal__when {
  padding-top: .55rem;
  font-size: 10px; font-weight: 500; letter-spacing: .22em; text-transform: uppercase; color: var(--emerald);
}
.heal h4 {
  margin-top: .35rem;
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.4rem, 2.2vw, 1.75rem); line-height: 1.05;
}
.heal p, .care__list li, .care__help p { font-size: .9375rem; color: var(--ink-dim); }
.heal p { margin-top: .5rem; max-width: 44ch; }

/* ── the rules ── */
.care__list { display: grid; margin-top: .25rem; }
.care__list li {
  display: grid; grid-template-columns: 3rem 1fr; gap: 1.1rem; align-items: start;
  padding-block: 1.1rem; border-bottom: 1px solid var(--hair-soft);
}
.care__list strong { display: block; margin-bottom: .2rem; }

.care__help {
  display: grid; grid-template-columns: 3rem 1fr; gap: 1.1rem; align-items: center;
  margin-top: clamp(2rem, 4vw, 3rem); padding: 1.1rem 1.4rem;
  border: 1px solid var(--hair); border-left: 2px solid var(--emerald); border-radius: 1rem;
  background: var(--shell-wash);
}

@media (min-width: 56em) {
  .care__row--skin { grid-template-columns: 1.15fr 1fr; align-items: center; gap: clamp(2rem, 5vw, 4.5rem); }
  .care__row--2 { grid-template-columns: 1fr 1fr; align-items: start; gap: clamp(2rem, 5vw, 4.5rem); }
  .heal { grid-template-columns: repeat(5, 1fr); gap: 1.5rem; }
  .heal::before { left: 1.5rem; right: 1.5rem; top: 1.5rem; bottom: auto; border-left: 0; border-top: 1px solid var(--hair); }
  .heal__t { grid-template-columns: 1fr; }
  .heal__t .care__ic { grid-row: auto; }
  .heal__when { padding-top: 1rem; }
}

/* ══════════════════════════════════════════════════════════
   PROCESS
   ══════════════════════════════════════════════════════════ */
.process__grid { display: grid; gap: clamp(2.5rem, 6vw, 5rem); grid-template-columns: 1fr; }
.process__title { font-size: clamp(2.25rem, 5.4vw, 4rem); margin-top: 1.2rem; }
.process__note { margin-top: 1.25rem; color: var(--ink-dim); max-width: 32ch; }
/* the ways in, under Booking's heading */
.process__ways { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.75rem; }

.steps { display: grid; }
.step {
  display: grid; grid-template-columns: auto 1fr;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  padding-block: clamp(1.75rem, 3vw, 2.25rem);
  border-top: 1px solid var(--hair);
  transition: border-color .7s var(--ease);
}
.step:last-child { border-bottom: 1px solid var(--hair); }
.step__num {
  font-variant-numeric: tabular-nums;
  font-size: 10px; font-weight: 500;
  letter-spacing: .22em; color: var(--emerald);
  padding-top: .5rem;
}
.step__body h3 {
  font-family: var(--display); font-weight: 400;
  font-size: clamp(1.4rem, 2.6vw, 1.85rem);
  line-height: 1.1; letter-spacing: -.015em;
  transition: transform .7s var(--ease);
}
.step__body p { margin-top: .65rem; color: var(--ink-dim); max-width: 52ch; font-size: .9375rem; }
.step:hover { border-color: rgba(255, 255, 255, .28); }
.step:hover .step__body h3 { transform: translateX(.4rem); }

/* ══════════════════════════════════════════════════════════
   EVENT — one dated flash panel, flyer beside the facts
   ══════════════════════════════════════════════════════════ */
.event { padding-top: 0; }
.event__core {
  display: grid; grid-template-columns: 1fr; align-items: center;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  padding: clamp(1.1rem, 2.5vw, 2rem);
}
.event__art { border-radius: calc(var(--r-core) - .6rem); overflow: hidden; }
.event__art img { width: 100%; aspect-ratio: 1; object-fit: cover; }
.event__body { padding-inline: clamp(0rem, 1vw, 1rem); }
.event__title { font-size: clamp(2rem, 4.6vw, 3.4rem); margin-top: 1rem; }
.event__facts { display: grid; margin-top: 1.5rem; }
.event__facts li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding: .75rem 0; border-top: 1px solid var(--hair-soft);
  font-size: .9375rem;
}
.event__facts li:last-child { border-bottom: 1px solid var(--hair-soft); }
.event__facts span {
  font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .2em; color: var(--ink-dim);
}
.event__price {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  margin-top: 1.5rem;
  font-family: var(--display); font-size: clamp(2rem, 4.4vw, 3rem); line-height: 1;
}
.event__price .dot { align-self: center; }
.event__price small {
  flex-basis: 100%;
  font-family: var(--ui); font-size: .8125rem; color: var(--ink-dim); line-height: 1.5; }
.event__cta { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1.75rem; }

/* ══════════════════════════════════════════════════════════
   FAQ
   ══════════════════════════════════════════════════════════ */
.faq__grid { display: grid; gap: clamp(2.5rem, 6vw, 5rem); grid-template-columns: 1fr; }
.faq__title { font-size: clamp(2.25rem, 5.4vw, 4rem); margin-top: 1.2rem; }

.qa { border-top: 1px solid var(--hair); }
.qa:last-child { border-bottom: 1px solid var(--hair); }
.qa summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  padding-block: 1.35rem;
  list-style: none;
  font-size: 1.0625rem;
  transition: color .5s var(--ease), padding-inline-start .6s var(--ease);
}
.qa summary::-webkit-details-marker { display: none; }
@media (hover: hover) and (pointer: fine) { .qa summary:hover { color: var(--emerald); padding-inline-start: .4rem; } }

.qa__mark { position: relative; flex: none; width: 12px; height: 12px; }
.qa__mark::before, .qa__mark::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  background: currentColor; border-radius: 2px;
  transform: translate(-50%, -50%);
  transition: transform .6s var(--ease), opacity .4s var(--ease);
}
.qa__mark::before { width: 12px; height: 1.25px; }
.qa__mark::after  { width: 1.25px; height: 12px; }
.qa[open] .qa__mark::after { transform: translate(-50%, -50%) rotate(90deg); opacity: 0; }
@media (forced-colors: active) {
  .qa__mark::before, .qa__mark::after { background: CanvasText; forced-color-adjust: none; }
}

.qa__body { padding: 0 2.5rem 1.6rem 0; }
.qa__body p { color: var(--ink-dim); font-size: .9375rem; max-width: 62ch; }
.qa[open] .qa__body { animation: qaIn .6s var(--ease) both; }
@keyframes qaIn {
  from { opacity: 0; transform: translateY(-.5rem); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ══════════════════════════════════════════════════════════
   SHOP — the studio floor at VVV Tattoo, held still behind Recent work
   → Aftercare, the way the chest piece is below: each section paints
   the same viewport-sized fixed layer and clips it to its own box, so
   the two read as one photo the page slides over. Black and white and
   darkened in the file itself (tools/gen-shop-backdrop.py), so nothing
   is filtered while the page scrolls. It waits for html.shop-on
   (main.js); About's plain black runs into it at the top of Recent
   work, and it gives way to black again at the foot of Aftercare.
   ══════════════════════════════════════════════════════════ */
.shopdrop { position: relative; isolation: isolate; clip-path: inset(0); }
.shopdrop::before {
  content: ""; position: fixed; inset: 0 0 auto; height: 100vh; height: 100lvh; z-index: -2;
  pointer-events: none; background: var(--paper);
}
.shop-on .shopdrop::before {
  background:
    radial-gradient(80% 70% at 50% 50%, rgba(0,0,0,.5), rgba(0,0,0,0)),
    linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5)),
    url("../assets/img/shop-backdrop-1200.webp") 50% 50% / cover no-repeat;
}
@media (min-width: 64em) {
  .shop-on .shopdrop::before {
    background-image:
      radial-gradient(80% 70% at 50% 50%, rgba(0,0,0,.5), rgba(0,0,0,0)),
      linear-gradient(rgba(0,0,0,.5), rgba(0,0,0,.5)),
      url("../assets/img/shop-backdrop-2400.webp");
  }
}
/* Where a held photo begins or ends on a page, its section fades it
   from or to black (.fade-top, .fade-bottom, set on the page's first and
   last section with that photo). The fade reaches 3px past the section's
   edge: where that edge falls between two screen pixels, a fade starting
   on it is only partly drawn there, and a hairline of the photo shows
   through (at 100% and 125%). Shared by the chest piece below. */
:is(.fade-top, .fade-bottom)::after {
  content: ""; position: absolute; inset: -3px 0; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg,
    var(--paper) 3px, rgba(0,0,0,0) calc(3px + var(--ft, 0px)),
    rgba(0,0,0,0) calc(100% - 3px - var(--fb, 0px)), var(--paper) calc(100% - 3px));
}
.fade-top::after { --ft: min(40vh, 22rem); }
.fade-bottom::after { --fb: min(40vh, 22rem); }

/* ══════════════════════════════════════════════════════════
   BACKDROP — one chest piece held still behind Flash → Footer.
   Each section paints the same viewport-sized fixed layer and clips it
   to its own box, so the page reads as one photo the text slides over
   (background-attachment: fixed would do this, but not on iOS).
   The photo itself waits for html.backdrop-on (main.js), like a lazy img.
   ══════════════════════════════════════════════════════════ */
.backdrop { position: relative; isolation: isolate; clip-path: inset(0); }
.backdrop::before {
  /* the photo's height: wide enough to fill the width (738×1600, so 216.8vw),
     and tall enough that its middle can sit at mid-screen (143vh) */
  --bd-h: max(216.8vw, 143vh);
  content: ""; position: fixed; inset: 0; z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(70% 60% at 50% 50%, rgba(0,0,0,.8), rgba(0,0,0,0)),
    linear-gradient(rgba(0,0,0,.78), rgba(0,0,0,.78));
}
/* the doves, 35% down the photo, held at the middle of the screen */
.backdrop-on .backdrop::before {
  background:
    radial-gradient(70% 60% at 50% 50%, rgba(0,0,0,.8), rgba(0,0,0,0)),
    linear-gradient(rgba(0,0,0,.78), rgba(0,0,0,.78)),
    url("../assets/img/chest-backdrop.jpg") 50% calc(50vh - var(--bd-h) * .35) / auto var(--bd-h) no-repeat;
  filter: grayscale(1) contrast(1.05);
}

/* ══════════════════════════════════════════════════════════
   BOOK — call to action over a piece of work, washed into the paper.
   Booking itself happens on Venue, so this is a doorway, not a form.
   ══════════════════════════════════════════════════════════ */
.book {
  text-align: center;
  padding-block: clamp(7rem, 16vw, 14rem);
}
.book__inner { max-width: 48rem; margin-inline: auto; display: grid; justify-items: center; gap: 1.25rem; }
.book__title { font-size: clamp(2.6rem, 7.2vw, 5.75rem); }
.book__note { color: var(--ink-70); max-width: 40ch; }
.book__cta { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem; margin-top: .75rem; }

.book__facts {
  width: 100%; max-width: 42rem;
  margin-top: 2.5rem;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px;
  background: var(--hair); border: 1px solid var(--hair);
  border-radius: 1.25rem; overflow: hidden;
}
.book__facts li {
  display: grid; gap: .3rem; text-align: left;
  padding: 1.05rem 1.25rem;
  background: rgba(10, 12, 11, .82);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.book__facts li > span {
  font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .2em; color: var(--ink-dim);
}
.book__facts strong { font-weight: 400; font-size: .9375rem; }

/* ══════════════════════════════════════════════════════════
   VISIT
   ══════════════════════════════════════════════════════════ */
.visit__grid { display: grid; gap: clamp(2.5rem, 6vw, 5rem); grid-template-columns: 1fr; }
/* the lede sits beside the three columns only once they have room for an
   address; below that it heads them (at 1024px they were ~80px wide) */
@media (min-width: 90em) { .visit__grid { grid-template-columns: .8fr 1.2fr; align-items: start; } }
.visit__title { font-size: clamp(2.25rem, 5.4vw, 4rem); margin-top: 1.2rem; }
.visit__note { margin-top: 1.25rem; color: var(--ink-dim); max-width: 44ch; }

.visit__cols { display: grid; gap: 2.25rem; grid-template-columns: 1fr; }
.visit__col { display: grid; gap: .75rem; align-content: start; padding-top: 1.35rem; border-top: 1px solid var(--hair); }
.visit__k {
  font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .2em; color: var(--emerald);
}
.visit__v { font-style: normal; line-height: 1.6; }
.visit__hours { display: grid; gap: .45rem; }
.visit__hours li { display: flex; justify-content: space-between; gap: 1rem; font-size: .9375rem; }
.visit__hours li span:first-child { white-space: nowrap; }
.visit__hours li span:last-child { color: var(--ink-dim); }
.visit__contact { display: grid; font-size: .9375rem; }
/* stacked contact links are prime tap targets on a phone — give them height */
.visit__contact li a { display: inline-flex; align-items: center; min-height: 44px; }

/* ══════════════════════════════════════════════════════════
   FOOTER — a shop card, minis behind it, his name off the bottom

   A satin hairline along the top. The ways in beside the studio's details;
   the pages and the work in two columns; the copyright line.
   Behind it, a sheet of old-school flash (.footer__ground), one picture
   covering the footer, faint: fetched only once the footer is on screen
   (data-reveal's .is-in), and all but cleared behind the middle where the
   text sits.
   Last, his name, huge and dim, in three layers on one spot (the outline;
   the emerald fill let across left to right; a bright outline that shows
   only through a soft circle at the pointer, --mx/--my from main.js).
   ══════════════════════════════════════════════════════════ */
.footer { position: relative; overflow: hidden; padding-block: clamp(3.5rem, 7vw, 5.5rem) clamp(4.5rem, 8vw, 6rem); }
.footer::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--emerald) 30%, #9be8cb 50%, var(--emerald) 70%, transparent);
  opacity: .75;
}
.footer > .shell { position: relative; z-index: 1; }

/* the flash sheet: original white line art on black, made for the site
   (assets/img/flash-sheet-wide.webp, -tall on phones and tablets), one
   picture covering the footer, dimmed so the lines read as a faint grey
   ground; the mask thins it behind the middle, where the text is */
.footer__ground { position: absolute; inset: 0; pointer-events: none; }
/* the element itself stays fully there (its reveal is off); the sheet is
   dimmed by black laid over it in the same background, to a fifth */
.footer__ground[data-reveal] { opacity: 1; transform: none; filter: none; transition: none; }
.footer__ground.is-in {
  background:
    linear-gradient(rgba(0, 0, 0, .82), rgba(0, 0, 0, .82)),
    url("../assets/img/flash-sheet-wide.webp") 50% 50% / cover no-repeat;
  -webkit-mask-image: radial-gradient(72% 62% at 50% 42%, rgba(0, 0, 0, .12), #000 88%);
          mask-image: radial-gradient(72% 62% at 50% 42%, rgba(0, 0, 0, .12), #000 88%);
}
@media (max-width: 63.99em) {
  .footer__ground.is-in {
    background-image:
      linear-gradient(rgba(0, 0, 0, .82), rgba(0, 0, 0, .82)),
      url("../assets/img/flash-sheet-tall.webp");
  }
}

.footer__top { display: grid; gap: 2.25rem; align-items: start; }
.footer__ways { display: flex; flex-wrap: wrap; gap: .75rem; }
.footer__facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.4rem 2.5rem; margin: 0; }
.footer__facts dt, .footer__k {
  /* a lighter emerald than the token: 10px over the flash sheet needs 4.5:1
     where a line runs under it (about 5.3:1 measured; 7.6:1 on black) */
  font-size: 10px; font-weight: 500; letter-spacing: .22em; text-transform: uppercase; color: #3aae88;
}
.footer__k { margin-bottom: 1rem; }
.footer__facts dd { margin: .45rem 0 0; font-size: .9375rem; line-height: 1.55; color: var(--ink); }
.footer__facts address { font-style: normal; }
/* 15px type, but a 44px target: the padding is taken back by the margins */
.footer__facts a { display: inline-block; padding: 12px 0; margin: -12px 0; text-decoration: underline; text-underline-offset: .25em; text-decoration-color: var(--hair); }
.footer__facts a:hover { text-decoration-color: var(--emerald); }

.footer__cols {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem); padding-top: clamp(2rem, 4vw, 2.75rem);
  border-top: 1px solid var(--hair);
}
.footer__list { display: grid; gap: .6rem; font-size: .9375rem; color: var(--ink-dim); }
.footer__list [aria-current] { color: var(--ink); }
/* 15px type, but a 44px target: the padding is taken back by the margins */
.footer__list a { display: inline-block; min-width: 44px; padding: 12px 0; margin: -12px 0; transition: color .5s var(--ease); }
.footer__list a:hover { color: var(--ink); }
.footer__bottom {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem;
  margin-top: clamp(2.5rem, 5vw, 3.5rem); padding-top: 1.5rem;
  border-top: 1px solid var(--hair);
  font-size: .8125rem; color: var(--ink-dim);
}
.footer__tag {
  display: flex; align-items: center; gap: .6rem;
  font-size: 10px; font-weight: 500; text-transform: uppercase; letter-spacing: .2em; color: var(--ink-dim);
}

/* his name, as wide as the page and dim, whole, above the Ask pill's corner */
.footer__wordmark {
  position: relative; z-index: 0;
  margin: clamp(1.5rem, 4vw, 3rem) 0 0; padding-inline: var(--gut);
  font-family: var(--display); text-transform: uppercase; text-align: center;
  font-size: clamp(2.5rem, 15.6vw, 19rem); line-height: .82; letter-spacing: -.02em;
  white-space: nowrap; -webkit-user-select: none; user-select: none;
}
/* its own reveal is off: the fill does the arriving */
.footer__wordmark[data-reveal] { opacity: 1; transform: none; filter: none; transition: none; }
.footer__wordmark > span { display: block; }
/* dim: it is the footer's ground, not its text (the glow at the pointer stays bright) */
.footer__stroke, .footer__fill { opacity: .32; }
.footer__stroke { color: transparent; -webkit-text-stroke: 1px rgba(42, 154, 118, .5); }
.footer__fill, .footer__glow { position: absolute; inset: 0; padding-inline: var(--gut); }
.footer__fill {
  color: transparent;
  /* emerald, with the hairline's sheen through the middle */
  background-image: linear-gradient(100deg, #1f7a5c, var(--emerald) 30%, #9be8cb 50%, var(--emerald) 70%, #1f7a5c);
  -webkit-background-clip: text; background-clip: text;
  clip-path: inset(0 100% 0 0);
  transition: clip-path 2.4s var(--ease) .15s;
}
/* the fill goes with the ground: the name itself sits too low on the screen
   for the reveals ever to count it in */
.footer__ground.is-in ~ .footer__wordmark .footer__fill, .footer__wordmark.is-in .footer__fill { clip-path: inset(0 0 0 0); }
.footer__glow {
  color: transparent; -webkit-text-stroke: 1px var(--ink); opacity: 0;
  -webkit-mask-image: radial-gradient(11rem circle at var(--mx, 50%) var(--my, 50%), #000, transparent 70%);
          mask-image: radial-gradient(11rem circle at var(--mx, 50%) var(--my, 50%), #000, transparent 70%);
  transition: opacity .5s var(--ease);
}
@media (hover: hover) and (pointer: fine) { .footer__wordmark:hover .footer__glow { opacity: 1; } }

@media (min-width: 56em) {
  .footer__top { grid-template-columns: auto minmax(0, 1fr); gap: 3rem; }
  .footer__cols { gap: 3rem; }
}

/* ══════════════════════════════════════════════════════════
   ASK — quick answers, bottom right

   A pill that rises in once the hero has gone by (main.js, .is-up) and a
   panel above it: the thread, a row of questions to tap, the box. On a
   phone the panel takes the width. Hidden entirely without JS.
   ══════════════════════════════════════════════════════════ */
.ask {
  position: fixed; z-index: 55;
  right: max(1.25rem, env(safe-area-inset-right, 0px)); bottom: max(1.25rem, env(safe-area-inset-bottom, 0px));
  display: grid; justify-items: end; gap: .75rem;
}
.ask__btn {
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .7rem 1.1rem .7rem .9rem;
  border: 1px solid var(--hair); border-radius: 999px;
  background: rgba(14, 16, 15, .85); color: var(--ink);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  font-size: .875rem; font-weight: 500;
  box-shadow: 0 20px 40px -20px rgba(0, 0, 0, .9);
  opacity: 0; transform: translateY(1rem); pointer-events: none;
  transition: opacity .5s var(--ease), transform .6s var(--ease), border-color .4s var(--ease);
}
.ask.is-up .ask__btn { opacity: 1; transform: none; pointer-events: auto; }
.ask__btn:hover { border-color: var(--emerald); }
.ask__btn svg { width: 1.1rem; height: 1.1rem; color: var(--emerald); }
.ask__panel {
  width: min(22rem, calc(100vw - 2.5rem)); max-height: min(70vh, 34rem);
  display: grid; grid-template-rows: auto 1fr auto auto; overflow: hidden;
  border: 1px solid var(--hair); border-radius: 1.25rem;
  background: rgba(14, 16, 15, .94);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 40px 80px -30px #000;
  opacity: 0; transform: translateY(.75rem) scale(.98); transform-origin: bottom right;
  transition: opacity .3s var(--ease), transform .4s var(--ease);
}
.ask__panel[hidden] { display: none; }
.ask.is-open .ask__panel { opacity: 1; transform: none; }
.ask__head {
  display: grid; grid-template-columns: 1fr auto; gap: .3rem 1rem; align-items: center;
  padding: 1rem 1.1rem .85rem; border-bottom: 1px solid var(--hair-soft);
}
.ask__title { font-family: var(--display); font-size: 1.35rem; line-height: 1; text-transform: uppercase; }
.ask__sub { grid-column: 1; font-size: .75rem; line-height: 1.4; color: var(--ink-dim); }
.ask__close {
  grid-column: 2; grid-row: 1 / span 2;
  display: grid; place-items: center; width: 2.75rem; height: 2.75rem;
  border: 1px solid var(--hair); border-radius: 50%; background: none; color: var(--ink-70);
}
.ask__close:hover { color: var(--ink); border-color: var(--emerald); }
.ask__close svg { width: 1rem; height: 1rem; }
.ask__log {
  display: grid; gap: .75rem; align-content: start; overflow-y: auto;
  padding: 1rem 1.1rem; font-size: .875rem; line-height: 1.5;
}
.ask__q {
  justify-self: end; max-width: 85%; padding: .55rem .9rem;
  border-radius: 1rem 1rem .25rem 1rem; background: var(--ink); color: var(--paper);
}
.ask__a {
  justify-self: start; max-width: 92%; padding: .6rem .9rem;
  border: 1px solid var(--hair-soft); border-radius: 1rem 1rem 1rem .25rem;
  background: var(--shell-wash); color: var(--ink-70);
}
.ask__a a { color: var(--ink); text-decoration: underline; text-underline-offset: .25em; }
.ask__chips { display: flex; gap: .4rem; overflow-x: auto; padding: 0 1.1rem .75rem; scrollbar-width: none; }
.ask__chips::-webkit-scrollbar { display: none; }
/* a mouse can't swipe the row: the chips wrap instead */
@media (hover: hover) and (pointer: fine) { .ask__chips { flex-wrap: wrap; overflow: visible; } }
.ask__chips button {
  flex: none; padding: .45rem .8rem;
  border: 1px solid var(--hair); border-radius: 999px; background: none; color: var(--ink-70);
  font-size: .75rem; white-space: nowrap;
}
.ask__chips button:hover { color: var(--ink); border-color: var(--emerald); }
.ask__form {
  display: grid; grid-template-columns: 1fr auto; gap: .5rem; align-items: center;
  padding: .75rem 1.1rem 1rem; border-top: 1px solid var(--hair-soft);
}
.ask__form input {
  min-width: 0; padding: .7rem .9rem;
  border: 1px solid var(--hair); border-radius: 999px; background: var(--paper); color: var(--ink);
  font-size: 1rem;
}
.ask__form input::placeholder { color: var(--ink-dim); }
.ask__form button {
  display: grid; place-items: center; width: 2.75rem; height: 2.75rem;
  border: 0; border-radius: 50%; background: var(--emerald); color: var(--paper);
}
.ask__form button svg { width: 1rem; height: 1rem; }
@media (max-width: 40em) {
  .ask { left: max(1rem, env(safe-area-inset-left, 0px)); right: max(1rem, env(safe-area-inset-right, 0px)); }
  .ask__panel { width: 100%; max-height: 75vh; max-height: 75svh; }
}

/* ══════════════════════════════════════════════════════════
   SCROLL REVEAL
   ══════════════════════════════════════════════════════════ */
[data-reveal] {
  opacity: 0;
  transform: translateY(2.5rem);
  filter: blur(8px);
  transition:
    opacity .95s var(--ease) var(--d, 0ms),
    transform 1.05s var(--ease) var(--d, 0ms),
    filter .95s var(--ease) var(--d, 0ms);
  will-change: transform, opacity;
}
[data-reveal].is-in { opacity: 1; transform: translateY(0); filter: blur(0); will-change: auto; }
/* (a <noscript> block in the head un-hides these when JS is off) */

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — everything above md collapses to one column
   ══════════════════════════════════════════════════════════ */
@media (min-width: 40em) {
  .visit__cols { grid-template-columns: repeat(3, 1fr); }
  .book__facts { grid-template-columns: repeat(4, 1fr); }
  .event__core { grid-template-columns: .85fr 1.15fr; }
}

/* a desktop scrolls through the inking rather than watching it play */
.ink-scrub .hero { height: calc(100vh + var(--travel)); height: calc(100svh + var(--travel)); }

@media (min-width: 64em) {
  :root { --gut: clamp(2rem, 5vw, 4rem); }

  /* the hero splits: the lockup, the machine and the shop label keep the
     left half; his face takes the right half */
  /* The lockup starts below the nav island (a 24px inset, a 62px pill, a
     gap), so the pill never lands on the name when it fades in; on a short
     laptop the name shrinks instead, so its line (3rem) still clears the
     corner pieces. The bottom keeps the base rule's --gut-b, so an iPad in
     landscape still clears its home indicator. */
  .hero__stage {
    --pad-t: 6.5rem;
    /* a smaller machine and a lower corner leave the height to the name */
    --machine: clamp(5.5rem, 7vw, 8rem);
    --gut-b: max(2.5rem, env(safe-area-inset-bottom, 0px));
    --name-h: min(64svh, 48rem, 100svh - var(--pad-t) - max(var(--corner), var(--machine)) - var(--gut) * .5 - var(--gut-b) - 3rem);
    --name-w: calc(50vw - var(--gut));
    padding: var(--pad-t) 50% calc(max(var(--corner), var(--machine)) + var(--gut) * .5 + var(--gut-b)) var(--gut);
  }
  .hero__face { position: absolute; inset: 0 0 0 auto; width: 50%; height: auto; margin: 0; }

  /* the photo takes the left half of the section; the words stack on the right */
  .studio__grid  { grid-template-columns: 1fr 1fr; align-items: start; gap: 0 clamp(2rem, 5vw, 5rem); }
  .studio__lede, .studio__body { grid-column: 2; }
  .studio__body { margin-top: 2.5rem; }
  /* as tall as the section, never taller, at the photo's own proportions,
     at most half the width; past that the image crops a little instead of
     growing */
  .studio__photo {
    position: absolute; left: 0; top: 0;
    height: 100%; width: auto; max-width: 50%; margin: 0;
  }
  .studio__photo img { object-position: 55% 25%; }
  .studio__photo :is(img, video) {
    -webkit-mask-image: linear-gradient(90deg, #000 55%, transparent), linear-gradient(180deg, transparent, #000 12% 86%, transparent);
            mask-image: linear-gradient(90deg, #000 55%, transparent), linear-gradient(180deg, transparent, #000 12% 86%, transparent);
    -webkit-mask-composite: source-in;
            mask-composite: intersect;
  }
  .studio__photo { --cap-b: clamp(5.5rem, 12vw, 11rem); }
  /* the grid's empty left cell lies over the photo: let the pointer through
     to it, while the words on the right stay selectable */
  .studio__photo + .studio__grid { margin-top: 0; pointer-events: none; }
  .studio__grid > * { pointer-events: auto; }
  .process__grid { grid-template-columns: .8fr 1.2fr; align-items: start; }
  .faq__grid     { grid-template-columns: .8fr 1.2fr; align-items: start; }

  .sec-head {
    grid-template-columns: 1.4fr 1fr;
    align-items: end; gap: 3rem;
  }
  .sec-head__note { justify-self: end; }

}

/* the full nav (links + icons + Book) only fits beside the wordmark from 80em;
   below that the glyph alone links home — the hero has just spelled the name */
@media (min-width: 64em) and (max-width: 79.99em) {
  .nav__mark-word { display: none; }
}

/* Phones and tablets: no bar. Book in the top-left corner, the hamburger in
   the top-right, both clear of his face; the contact icons live in the
   menu's foot. Book stays: once the name is inked it is the entire
   conversion path above the fold. The menu has its own Book. */
@media (max-width: 63.99em) {
  :is(.nav__mark, .nav__links, .nav__social) { display: none; }
  .menu-btn { display: grid; }
  #nav .nav__pill {
    width: 100%; justify-content: space-between; padding: 0;
    border-color: transparent; background: none; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  .is-locked .nav__cta { visibility: hidden; }
  /* the Book pill and the menu button stay put: on a phone the nav doesn't
     retract on the way down (iOS read it as the button jumping about) */
  .nav.is-hidden:not(:focus-within) { transform: none; }
}

@media (max-width: 40em) {
  /* the corner pieces shrink so the lockup keeps the middle of a short screen */
  .hero__stage { --corner: 5.25rem; --machine: 4rem; }   /* the town wraps to two lines */
  .hero__tag { letter-spacing: .18em; }
  .hero__tag li + li::before { margin: 0 .8em; }

  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .qa__body { padding-right: 0; }
}

/* ══════════════════════════════════════════════════════════
   REDUCED MOTION
   ══════════════════════════════════════════════════════════ */
/* two stats side by side need ~21em: at 320px, or with a large default font
   size on any phone, they stack */
@media (max-width: 21em) { .stats, .book__facts { grid-template-columns: 1fr; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal], .gal__slide { opacity: 1; transform: none; filter: none; }
  /* the menu's staggers would still wait out their delays */
  .menu *, .menu-btn * { transition-delay: 0s !important; }
  /* (the portfolio never turns on its own under reduced motion; the hero
     never gets .ink, so the name renders already inked) */
}

/* ══════════════════════════════════════════════════════════
   PRINT — everything shown at rest, the fixed chrome left off
   ══════════════════════════════════════════════════════════ */
@media print {
  /* paper prints white whatever the screen is: the page goes back to dark
     ink on white, the emerald at its deep #001e17 (about 17:1 there), flat */
  :root {
    --paper: #fff; --paper-raise: #f7f7f5;
    --ink: #17201b; --ink-70: rgba(23, 32, 27, .74); --ink-dim: rgba(23, 32, 27, .64);
    --emerald: var(--emerald-deep);
    --satin: linear-gradient(var(--emerald-deep), var(--emerald-deep));
    --satin-fine: var(--satin); --satin-fill: var(--satin);
    --hair: rgba(23, 32, 27, .12); --hair-soft: rgba(23, 32, 27, .07); --shell-wash: transparent;
    color-scheme: light;
  }
  .footer__stroke { -webkit-text-stroke-color: rgba(23, 32, 27, .2); }
  .footer__fill, .footer__glow, .footer__ground, .ask { display: none !important; }
  .nav, .menu, .grain, .skip, .lightbox, .backdrop::before, .shopdrop::before, .fade-top::after, .fade-bottom::after, .hero__badge,
  .studio__photo video, .studio__pause, .skin__pause { display: none !important; }
  /* the clip doesn't print: its photo does */
  .studio__photo img { opacity: 1 !important; }
  /* the portfolio prints as a grid of every piece, each with its caption */
  .gal__open video, .gal__open::after, .gal__open::before, .gal__slide--echo, .gal__ctl { display: none !important; }
  .gal .gal__track { grid-auto-flow: row; grid-template-columns: repeat(3, 1fr); grid-template-rows: none; overflow: visible; }
  .gal .gal__slide { grid-column: auto !important; grid-row: auto !important; opacity: 1 !important; transform: none !important; }
  .gal .gal__open { height: 10rem; }
  .gal .gal__cap { position: static; padding: .5rem 0 0; opacity: 1; transform: none; }
  .gal .gal__open { height: 13rem; }
  [data-reveal] {
    opacity: 1 !important; transform: none !important; filter: none !important;
    transition: none !important;
  }
  .hero { height: auto !important; background: none; color: var(--ink); }
  .gal__slide, .heal__t, .care__list li, .step, .qa, .visit__col { break-inside: avoid; }
}

/* ── short screens (a phone on its side) ───────────────────────────
   The menu's words are sized to the screen's height above; here they go to
   two columns so the contact row stays in view. The hero grows past one
   screen rather than printing the shop label over the tag line. */
@media (orientation: landscape) and (max-height: 30em) {
  .menu__body { padding-top: 4.5rem; gap: 1rem; }
  .menu__links { columns: 2; }
  .menu__links li { break-inside: avoid; }
}
@media (max-width: 63.99em) and (max-height: 30em) {
  :root:not(.ink-scrub) .hero__stage { height: auto; min-height: 100vh; min-height: 100svh; }
}
