/* Benjamin Salon New York
   Black and white, Janson for reading, Gotham capitals for names and labels.
   Motion lives at the end of the file: the opening cut, scroll reveals, page transitions. */

@font-face { font-family: "Janson Text"; src: url(/assets/fonts/janson-text-roman.woff2) format("woff2"); font-style: normal; font-weight: 400; font-display: swap; }
@font-face { font-family: "Janson Text"; src: url(/assets/fonts/janson-text-italic.woff2) format("woff2"); font-style: italic; font-weight: 400; font-display: swap; }
@font-face { font-family: "Gotham"; src: url(/assets/fonts/gotham-medium.woff2) format("woff2"); font-style: normal; font-weight: 500; font-display: swap; }
/* No letters, only a space and a line: it sets the subscribe box's line, so the cursor is centred on the words
   (tools/field_font.py says why) */
@font-face { font-family: "Field Line"; src: url(/assets/fonts/field-line.ttf) format("truetype"); font-style: normal; font-weight: 400; }

:root {
  --ink: #000;
  --paper: #fff;
  --stone: #f5f5f5;
  --line: #d9d9d9;
  --mute: #666;
  --mute-dark: #9a9a9a;
  --serif: "Janson Text", "Iowan Old Style", Georgia, serif;
  --sans: "Gotham", "Montserrat", "Helvetica Neue", Arial, sans-serif;
  --gutter: max(clamp(20px, 4.2vw, 64px), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px));   /* clear of the Dynamic Island on a phone held sideways (the page draws edge to edge: viewport-fit=cover) */
  --max: 1520px;
  --hd: 84px;
  --out: cubic-bezier(.16, 1, .3, 1);     /* long, soft landing */
  --cut: cubic-bezier(.76, 0, .24, 1);    /* the blade: slow, sharp, slow */
  --sp: 1;                                /* reveal speed; main.js lowers it on elements revealed during fast scrolls */
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-padding-top: calc(var(--hd) + 20px); }
body { margin: 0; overflow-x: clip; background: var(--paper); color: var(--ink); font: 400 18px/1.62 var(--serif); font-kerning: normal; font-variant-numeric: lining-nums; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
img, video { display: block; max-width: 100%; height: auto; }
picture { display: block; }
a { color: inherit; text-decoration: none; }
p { margin: 0 0 1em; }
h1, h2, h3 { margin: 0; font-weight: 400; }
ul { margin: 0; padding: 0; list-style: none; }
button, input { font: inherit; color: inherit; }
::selection { background: var(--ink); color: var(--paper); }
:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
[tabindex="-1"]:focus { outline: none; }

.sr { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.skip { position: fixed; top: 8px; left: 8px; z-index: 200; padding: 12px 18px; background: var(--ink); color: var(--paper); font: 500 11px var(--sans); letter-spacing: .16em; text-transform: uppercase; transform: translateY(-160%); }
.skip:focus { transform: none; }
.wrap { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 var(--gutter); }
.sec { padding: clamp(80px, 11vw, 168px) 0; }
.pg:not(.pg--home):not(.pg--services) main { padding-top: var(--hd); }

/* ---------------------------------------------------------------- type */

.eyebrow { margin: 0 0 22px; font: 500 11px/1.4 var(--sans); letter-spacing: .2em; text-transform: uppercase; }
.eyebrow--light { color: #fff; }
.display { margin: 0 0 .62em; max-width: 15ch; font: italic 400 clamp(40px, 5.6vw, 92px)/1.02 var(--serif); letter-spacing: -.012em; }
.display--sm { font-size: clamp(34px, 3.6vw, 56px); }
.display--light { color: #fff; }
.lead { font-size: clamp(21px, 1.75vw, 27px); line-height: 1.42; }
.small { font-size: 15px; line-height: 1.5; color: var(--mute); }
.cols-2 { columns: 2 340px; column-gap: clamp(32px, 5vw, 88px); }
.cols-2 p { break-inside: avoid; }
.rule { height: 1px; margin-bottom: 26px; background: currentColor; transform-origin: 0 50%; }

/* Gotham capitals whose ink fills their box edge to edge. The box is a size container; --w is
   the ink width in em and --l the blank space before the first letter (build.py, from the font). */
.big { display: block; container-type: inline-size; font: 500 1em/.8 var(--sans); letter-spacing: -.01em; text-transform: uppercase; }
.big__t { display: block; margin-left: calc(var(--l, 0) * -1em); white-space: nowrap; font-size: calc(100cqw / var(--w)); }
/* Words that rise letter by letter (.l) or word by word (.w > span) inside a clipping box */
.lw { display: inline-block; overflow: hidden; padding: .1em .02em .02em; margin: -.1em -.02em -.02em; vertical-align: top; }
.l { display: inline-block; }
.w { display: inline-block; overflow: hidden; padding: .1em .06em .14em; margin: -.1em -.06em -.14em; vertical-align: top; }
.w > span { display: inline-block; }

/* ---------------------------------------------------------------- buttons and links
   Every kind of link answers the pointer its own way, and its words stay readable throughout:
   nothing fills in behind them and they never change colour or roll.
   - the opening's Meet the team (btn--go): its border leaves a copy of itself inside, a hairline mat, while
     the words step aside and an arrow draws in beside them. It leans a few pixels toward the pointer (main.js).
   - every other button has a hover of its own, named by its modifier:
     btn--marks   Book now, Call to book and Meet the team (and the 404's two): four corner marks
                  close in, like a viewfinder finding focus
     btn--stack   Services: the border darkens and a second frame slides out from behind
     btn--more    Read more: the border darkens, the words step aside and three full stops type out after them
     btn--gift    Buy a gift card: ribbons run down and across the card, like a gift's, and a bow draws in
                  where they cross
     btn--fold    Subscribe: the top right corner folds down, like the corner of a letter
     Every button gives a little when pressed.
   - header links: the letter-spacing opens and a small square appears (it stays on the current page)
   - the logo in the header and footer: its letters part
   - links that leave the site: a ring draws round the mark after them, a location dot on the address
     and map links and the arrow on the rest
   - footer links: slide aside for a short dash; links in running text: the underline drops away
   - team index names: a square marks the one whose portrait is in the frame, and the name steps in
   - photos: team cards cut open into colour from the middle; the founder's pulls in to a black mat
   Hovers apply only with a mouse or trackpad, so nothing stays stuck on touch screens. */

.btn { --fill: var(--ink); --edge: var(--ink); --text: var(--paper); --mat: 5px; --px: 30px; --aw: 16px; --gap: 10px; position: relative; display: inline-flex; align-items: center; justify-content: center; min-height: 54px; padding: 0 var(--px); border: 1px solid var(--edge); background: transparent; color: var(--text); font: 500 11px/1 var(--sans); letter-spacing: .2em; text-transform: uppercase; white-space: nowrap; cursor: pointer; isolation: isolate; transition: color .5s, border-color .5s, scale .45s var(--out); }
/* the fill runs under the border too (inset -1px), so no light seam shows where they meet at fractional pixels */
.btn::before { content: ""; position: absolute; inset: -1px; z-index: -1; background: var(--fill); clip-path: inset(0); transition: clip-path .7s var(--out), background-color .5s; }
.btn:focus-visible { outline-color: var(--trace, var(--edge)); }
.btn__l { display: block; line-height: 1.25; transition: translate .7s var(--out); }
/* the arrow sits where the words end once they have stepped aside, so words and arrow end up centred together */
.btn__go { position: absolute; top: 50%; right: calc(var(--px) - (var(--aw) + var(--gap)) / 2); width: var(--aw); height: calc(var(--aw) * .625); margin-top: calc(var(--aw) * -.3125); }
:is(.btn__go, .btn__bow) svg { display: block; width: 100%; height: 100%; overflow: visible; fill: none; stroke: currentColor; stroke-width: 1; stroke-linecap: round; stroke-linejoin: round; }
/* hidden, each stroke sits wholly inside a gap of its dash pattern: touching a dash would leave a round cap showing as a dot */
:is(.btn__go, .btn__bow) path { stroke-dasharray: 1 2; stroke-dashoffset: 1.001; transition: stroke-dashoffset .25s var(--out); }
.btn--sm { --mat: 4px; --px: 22px; --aw: 12px; --gap: 8px; min-height: 42px; }
.btn--block { display: flex; width: 100%; }
.btn--light { --fill: #fff; --edge: #fff; --text: var(--ink); }
/* Outline buttons: a pale border at rest. --trace is the colour it takes on hover, and the focus ring's. */
.btn--line { --fill: transparent; --edge: rgba(0, 0, 0, .2); --trace: var(--ink); --text: var(--ink); }
.btn--ghost { --fill: transparent; --edge: rgba(255, 255, 255, .45); --trace: #fff; --text: #fff; }
/* btn--go on the outline button: ::before is a hairline frame lying on the border, which pulls in to the mat */
.btn--go.btn--ghost::before { border: 1px solid transparent; background: none; clip-path: none; pointer-events: none; transition: inset .7s var(--out), border-color .5s; }
/* btn--marks: eight hairlines on one layer just outside the button, two to a corner, in its hover colour */
.btn--marks::after { --m: linear-gradient(var(--trace, var(--edge)), var(--trace, var(--edge))); content: ""; position: absolute; inset: -13px; pointer-events: none; opacity: 0; background: var(--m) 0 0 / 7px 1px, var(--m) 0 0 / 1px 7px, var(--m) 100% 0 / 7px 1px, var(--m) 100% 0 / 1px 7px, var(--m) 0 100% / 7px 1px, var(--m) 0 100% / 1px 7px, var(--m) 100% 100% / 7px 1px, var(--m) 100% 100% / 1px 7px; background-repeat: no-repeat; transition: inset .7s var(--out), opacity .35s; }
/* btn--stack: a paper fill inside the border hides the frame behind, so it shows only where it sticks out */
.btn--stack { --fill: var(--paper); }
.btn--stack::before { inset: 0; }
.btn--stack::after { content: ""; position: absolute; inset: -1px; z-index: -2; border: 1px solid var(--trace); opacity: 0; pointer-events: none; transition: translate .7s var(--out), opacity .3s; }
/* btn--more: the full stops hang just past the words, shown one at a time by a stepped clip, like typing.
   --dots is their width, so the words step aside by half of it and the two end up centred together. */
.btn--more { --dots: 1.4em; }
.btn--more .btn__l { position: relative; }
.btn--more .btn__l::after { content: "..."; content: "..." / ""; position: absolute; bottom: 0; left: 100%; clip-path: inset(0 100% 0 0); transition: clip-path .2s steps(3, jump-end); }
/* btn--gift: a ribbon down the card in its left padding (::after) and one across it above the words
   (.btn__l::after), with the bow (build.py BOW) knotted where they cross. They stop inside the border,
   so on the dark section they read as printed on the card. */
.btn--gift { --rib: 3px; --rx: calc(var(--px) / 2); --ry: 10px; }
.btn--gift::after, .btn--gift .btn__l::after { content: ""; position: absolute; background: var(--text); pointer-events: none; }
.btn--gift::after { top: 0; bottom: 0; left: calc(var(--rx) - var(--rib) / 2); width: var(--rib); clip-path: inset(0 0 100% 0); transition: clip-path .3s var(--out); }
.btn--gift .btn__l::after { top: calc(var(--ry) - var(--rib) / 2); right: 0; left: 0; height: var(--rib); clip-path: inset(0 100% 0 0); transition: clip-path .3s var(--out); }
.btn__bow { position: absolute; top: calc(var(--ry) - 5.5px); left: calc(var(--rx) - 11px); width: 22px; height: calc(22px * 2 / 3); }
.btn__bow svg { stroke-width: 1.1; }
/* btn--fold: the fill loses its corner while a flap, the back of the paper, folds over in its place */
.btn--fold { --fold: 12px; border-color: transparent; }
.btn--fold::before { clip-path: polygon(0 0, 100% 0, 100% 0, 100% 100%, 0 100%); }
.btn--fold::after { content: ""; position: absolute; top: -1px; right: -1px; width: var(--fold); height: var(--fold); background: var(--line); clip-path: polygon(0 0, 0 100%, 100% 100%); scale: 0; transform-origin: 100% 0; pointer-events: none; transition: scale .7s var(--out); }

/* Capitals whose letter-spacing opens on hover (build.py tracked): the visible words sit over a hidden
   copy that keeps the width. padding-left follows the letter-spacing, so the words stay centred. */
.trk { position: relative; display: inline-block; }
.trk__w { visibility: hidden; }
.trk__t { position: absolute; inset: 0 -3em; display: flex; justify-content: center; padding-left: .2em; white-space: nowrap; pointer-events: none; transition: letter-spacing .7s var(--out), padding-left .7s var(--out); }

.link { display: inline-flex; align-items: center; gap: .8em; font: 500 11px/1.4 var(--sans); letter-spacing: .18em; text-transform: uppercase; }
/* The mark after a link that leaves the site (build.py ARROW, DOT) and the ring that draws round it from the top */
.ico { position: relative; flex: none; width: 9px; height: 9px; }
.ico svg { position: absolute; display: block; overflow: visible; fill: none; stroke: currentColor; }
.ico svg:first-child { inset: 0; width: 100%; height: 100%; stroke-width: 1.1; }
.ico--dot svg:first-child circle { fill: currentColor; stroke: none; }
.ico__ring { top: 50%; left: 50%; width: 17px; height: 17px; margin: -8.5px 0 0 -8.5px; rotate: -90deg; stroke-width: 1.15; }
.ico__ring circle { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset .35s var(--out); }
main p:not(.pp__crumb) a:not([class]), .menu__info a, .ft__base a { text-decoration-line: underline; text-decoration-thickness: 1px; text-underline-offset: .22em; transition: text-underline-offset .5s var(--out), text-decoration-color .5s; }
.ft__grid a { position: relative; display: inline-block; transition: translate .55s var(--out); }
.ft__grid a::before { content: ""; position: absolute; top: .78em; left: -18px; width: 11px; height: 1px; background: currentColor; scale: 0 1; transform-origin: 0 50%; transition: scale .55s var(--out); }

@media (hover: hover) and (pointer: fine) {
  .btn--go.btn--ghost:is(:hover, :focus-visible), .btn--go.btn--ghost:is(:hover, :focus-visible)::before { border-color: var(--trace); }
  .btn--go.btn--ghost:is(:hover, :focus-visible)::before { inset: var(--mat); }
  .btn--go:is(:hover, :focus-visible) .btn__l { translate: calc((var(--aw) + var(--gap)) / -2) 0; }
  .btn--go:is(:hover, :focus-visible) .btn__go path { stroke-dashoffset: 0; transition: stroke-dashoffset .4s var(--out) .15s; }
  .btn--go:is(:hover, :focus-visible) .btn__go path + path { transition-delay: .42s; }
  .btn--marks:is(:hover, :focus-visible)::after { inset: -6px; opacity: 1; }
  :is(.btn--line.btn--marks, .btn--stack, .btn--more):is(:hover, :focus-visible) { border-color: var(--trace); }
  .btn--stack:is(:hover, :focus-visible)::after { translate: 6px 6px; opacity: 1; }
  .btn--more:is(:hover, :focus-visible) .btn__l { translate: calc(var(--dots) / -2) 0; }
  .btn--more:is(:hover, :focus-visible) .btn__l::after { clip-path: inset(0); transition: clip-path .6s steps(3, jump-start) .12s; }
  .btn--gift:is(:hover, :focus-visible)::after { clip-path: inset(0); transition: clip-path .5s var(--out); }
  .btn--gift:is(:hover, :focus-visible) .btn__l::after { clip-path: inset(0); transition: clip-path .7s var(--out) .1s; }
  .btn--gift:is(:hover, :focus-visible) .btn__bow path { stroke-dashoffset: 0; transition: stroke-dashoffset .45s var(--out) .45s; }
  .btn--gift:is(:hover, :focus-visible) .btn__bow path:nth-child(n + 3) { transition-delay: .7s; }
  .btn--fold:is(:hover, :focus-visible)::before { clip-path: polygon(0 0, calc(100% - var(--fold)) 0, 100% var(--fold), 100% 100%, 0 100%); }
  .btn--fold:is(:hover, :focus-visible)::after { scale: 1; }
  .link:is(:hover, :focus-visible) .ico__ring circle { stroke-dashoffset: 0; transition: stroke-dashoffset .9s var(--out); }
  :is(main p:not(.pp__crumb) a:not([class]), .ft__base a):is(:hover, :focus-visible) { text-underline-offset: .55em; text-decoration-color: transparent; }
  .ft__grid a:is(:hover, :focus-visible) { translate: 18px 0; }
  .ft__grid a:is(:hover, :focus-visible)::before { scale: 1; }
}
/* Pressed (touch screens too): the button gives a little, and the opening's one pulls its mat in further */
.btn:active { scale: .97; transition-duration: .15s; }
.btn--go.btn--ghost:active::before { inset: calc(var(--mat) + 2px); border-color: var(--trace); transition-duration: .3s; }

/* ---------------------------------------------------------------- header */

/* The header's colours change along an edge passing through it: the bottom of the photo it sits over, as the
   page scrolls, and the phone menu's panel as it opens and shuts. Faded, they went through a grey where the
   letters matched their ground and the bar seemed to vanish; switched at once, they jumped. Below the edge,
   main.js shows a copy of the bar in the plain look (.hd__copy, from --split down); above it the header keeps
   its look over the photo, or the menu's black, drawn down to --black. */
.hd { position: fixed; inset: 0 0 auto; z-index: 60; height: var(--hd); background: var(--paper); color: var(--ink); view-transition-name: hd; }
.hd::before { content: ""; position: absolute; inset: 0; background: var(--ink); clip-path: inset(0 0 calc(100% - var(--black, 0px)) 0); pointer-events: none; }
.hd__bar { position: relative; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; height: 100%; padding: 0 var(--gutter); }
.hd__nav { display: flex; gap: clamp(20px, 2.6vw, 40px); }
.hd__nav a, .hd__link { position: relative; padding: 10px 0; font: 500 11px/1 var(--sans); letter-spacing: .2em; text-transform: uppercase; }
/* the marker: a small square under the word, centred on its letters (not their trailing spacing) */
.hd__nav a::after, .hd__link::after { content: ""; position: absolute; left: 50%; bottom: 0; width: 4px; height: 4px; margin-left: calc(-2px - .1em); background: currentColor; scale: 0; transition: scale .5s var(--out); }
.hd__nav a[aria-current]::after { scale: 1; }
@media (hover: hover) and (pointer: fine) {
  :is(.hd__nav a, .hd__link):is(:hover, :focus-visible)::after { scale: 1; }
  :is(.hd__nav a, .hd__link):is(:hover, :focus-visible) .trk__t { letter-spacing: .36em; padding-left: .36em; }
}
.hd__brand { display: flex; flex-direction: column; align-items: center; gap: 7px; }
.hd .logo { width: clamp(150px, 12.5vw, 196px); }
.hd__city { margin-right: -.5em; font: 500 9px/1 var(--sans); letter-spacing: .5em; text-transform: uppercase; }
.hd__actions { display: flex; justify-content: flex-end; align-items: center; gap: clamp(18px, 2vw, 30px); }
.hd .btn { --fill: var(--ink); --edge: var(--ink); --text: var(--paper); transition: scale .45s var(--out); }
.hd .btn:active { transition-duration: .15s; }
.hd .btn::before { transition: clip-path .7s var(--out); }
.hd--over .btn, :is(.menu-open, .menu-closing) .hd .btn { --fill: #fff; --edge: #fff; --text: var(--ink); }
.hd__menu { display: none; width: 44px; height: 44px; margin-right: -10px; padding: 0; border: 0; background: none; cursor: pointer; }
.hd__burger, .hd__burger::before { position: relative; display: block; width: 24px; height: 1px; margin: 0 auto; background: currentColor; transition: transform .5s var(--cut); }
.hd__burger { transform: translateY(-4px); }
.hd__burger::before { content: ""; transform: translateY(8px); }
.hd__menu[aria-expanded="true"] .hd__burger { transform: rotate(45deg); }
.hd__menu[aria-expanded="true"] .hd__burger::before { transform: rotate(-90deg); }
.hd__line { position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--line); }
.hd__line i { display: block; height: 100%; background: var(--ink); transform: scaleX(0); transform-origin: 0 50%; }
.hd--over { background: transparent; color: #fff; }
.hd--over .hd__line { opacity: 0; }
.hd__copy { position: absolute; inset: 0; visibility: hidden; color: var(--ink); clip-path: inset(var(--split, 100%) 0 0 0); pointer-events: none; }
.hd__copy > div { position: absolute; inset: 0; background: var(--paper); }
.hd--split .hd__copy { visibility: visible; }
/* Moved by the page's own scrolling (main.js says when): the copy's box goes down by the photo's part of the
   header and hides what falls outside it, and the bar inside goes back up by as much, so it stays put. Both run
   on the same frame as the scrolling. .hd-edge, the header's height just above the photo's edge, gives the
   place: its top reaching the top of the screen is the edge coming into the header, its bottom the edge leaving. */
.hd-edge { position: absolute; left: 0; bottom: 0; width: 1px; height: var(--hd); visibility: hidden; pointer-events: none; view-timeline: --hd-edge block; view-timeline-inset: 0; }
.cover > * + * > .hd-edge { bottom: auto; top: calc(-1 * var(--hd)); }
body { timeline-scope: --hd-edge; }
.hd--edge .hd__copy { overflow: hidden; clip-path: none; }
.hd--edge.hd--over .hd__copy { visibility: visible; }
.hd--edge .hd__copy, .hd--edge .hd__copy > div { animation: hd-edge linear both; animation-timeline: --hd-edge; animation-range: exit 0% exit 100000%; }
.hd--edge .hd__copy > div { animation-name: hd-edge-bar; }
/* Done in the first thousandth: the range runs on a thousand header heights past the edge, further than any page
   here scrolls, so the animation is never over. Chrome moves a running animation with the scrolling itself, but
   one that has ended waits for the page script to start it again, a frame or two late. */
@keyframes hd-edge { from { transform: translateY(100%); } 0.1%, to { transform: none; } }
@keyframes hd-edge-bar { from { transform: translateY(-100%); } 0.1%, to { transform: none; } }

.menu { position: fixed; inset: 0; z-index: 55; display: flex; flex-direction: column; padding: calc(var(--hd) + 28px) var(--gutter) calc(24px + env(safe-area-inset-bottom)); overflow-y: auto; background: var(--ink); color: #fff; clip-path: inset(0 0 100% 0); transition: clip-path .8s var(--cut); }
.menu.is-open { clip-path: inset(0); }
.menu__links { margin-bottom: 36px; }
.menu__links a { display: block; padding: 6px 0; font: italic 400 clamp(44px, 12vw, 64px)/1.08 var(--serif); }
.menu .w > span { transform: translateY(120%); transition: transform .9s var(--out); transition-delay: calc(.25s + var(--i) * 70ms); }
.menu.is-open .w > span { transform: none; }
.menu__info { margin-top: auto; color: var(--mute-dark); font-size: 16px; opacity: 0; transition: opacity .8s .5s; }
.menu.is-open .menu__info { opacity: 1; }
.menu__info a { color: #fff; }
.menu__book { width: 100%; }
.menu-open { overflow: hidden; }
/* The menu's black is .hd::before, all of it while the menu is up; opening and shutting, main.js draws it to the panel's edge */
:is(.menu-open, .menu-closing) .hd { background: transparent; color: #fff; }
.menu-open .hd { --black: 100%; }
:is(.menu-open, .menu-closing) .hd__line { opacity: 0; }
/* The copy keeps the plain look whatever the header's own */
.hd .hd__copy .hd__actions .btn { --fill: var(--ink); --edge: var(--ink); --text: var(--paper); }
.hd .hd__copy .hd__line { opacity: 1; }

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

.hero { position: relative; height: 100vh; height: 100svh; min-height: 560px; overflow: hidden; background: #000; color: #fff; }
.hero__media { position: absolute; inset: -8% 0 0; }
.hero__poster img, .hero__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero__video { opacity: 0; transition: opacity 1.4s; }
.hero__video.is-playing { opacity: 1; }
.hero__still { position: absolute; inset: 0; width: 100%; height: 100%; }   /* the video's frame, held while leaving (main.js) */
.hero__shade { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0, 0, 0, .38) 0, rgba(0, 0, 0, 0) 26%, rgba(0, 0, 0, 0) 52%, rgba(0, 0, 0, .58) 100%); }
.hero__content { position: absolute; left: 0; right: 0; bottom: 0; padding: 0 var(--gutter) clamp(24px, 4.5vh, 52px); }
.hero__eyebrow { margin-bottom: clamp(10px, 2vh, 22px); }
.hero__foot { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px; margin-top: clamp(18px, 3.4vh, 40px); }
.hero__addr { margin: 0; font: 500 11px/1.4 var(--sans); letter-spacing: .2em; text-transform: uppercase; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; }
/* The opening picture holds still and the page comes up over it, white over the photo: the About section on the
   home page, the rest of the page on Services. Scrolled away with the page, the picture's big words ran up into
   the header and were cut off there, a frame late on a quick scroll, and the page under it showed blank before
   its words came in. A picture taller than the screen (its 560 or 420 px floor) scrolls to its bottom edge
   first, so its words are never left below the fold. */
.cover > .hero { position: sticky; top: 0; top: min(0px, 100svh - 560px); }
.cover > .mast { position: sticky; top: 0; top: min(0px, 100svh - clamp(420px, 80vh, 900px)); }   /* the same on Services */
.cover > :first-child + * { position: relative; z-index: 1; background: var(--paper); }

/* ---------------------------------------------------------------- home sections */

.about .display { max-width: 19ch; }
.about__body { max-width: 1180px; margin-top: clamp(36px, 5vw, 72px); }

.founder { padding-top: 0; }
.founder__grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: clamp(32px, 6vw, 112px); align-items: center; }
.founder__pt { display: block; }
.founder__pt .pt__img { clip-path: inset(0); transition: clip-path .8s var(--out); }
@media (hover: hover) and (pointer: fine) {
  .founder__pt:is(:hover, :focus-visible) .pt__img { clip-path: inset(3cqw); }
  html.motion .founder__pt:is(:hover, :focus-visible) .pt.in img { scale: 1.05; transition-delay: 0s; }
}
.founder .lead { max-width: 34em; }

/* Full-width pictures with a title set in big capitals along the bottom edge */
.banner { position: relative; height: clamp(400px, 78vh, 900px); overflow: hidden; background: #000; color: #fff; }
.banner--short { height: clamp(340px, 62vh, 700px); }
.banner__img, .mast__img { position: absolute; inset: -12% 0; }
.banner__img img, .mast__img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.banner__title { position: absolute; left: var(--gutter); right: var(--gutter); bottom: 0; }
.banner__title .big__t, .mast--image .mast__title .big__t { margin-bottom: -.04em; }   /* baseline on the edge */
.banner + .wrap { padding-top: clamp(56px, 7vw, 104px); }
.services-ov { padding-top: 0; }

.ov { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(28px, 4vw, 72px); }
.ov__h { margin-bottom: 18px; font: italic 400 clamp(28px, 2.4vw, 38px)/1.1 var(--serif); }
.ov__more, .ix__more { margin: clamp(40px, 5vw, 64px) 0 0; }

.prices > li { position: relative; display: flex; align-items: baseline; justify-content: space-between; gap: 24px; padding: 15px 0; }
.prices > li::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--line); transform-origin: 0 50%; }
.prices__p { flex: none; font-variant-numeric: lining-nums tabular-nums; white-space: nowrap; }
.tt__svc { display: block; }
.tt__desc { display: block; max-width: 46em; margin-top: 4px; color: var(--mute); font-size: 15px; line-height: 1.5; }

.ix { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) minmax(240px, .78fr) minmax(0, 1fr); gap: clamp(28px, 4vw, 72px); margin-top: clamp(28px, 4vw, 56px); }
.ix__h { margin-bottom: 10px; font: 500 11px/1.4 var(--sans); letter-spacing: .2em; text-transform: uppercase; }
.ix__list a { position: relative; display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 11px 0; }
.ix__list a::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--line); transform-origin: 0 50%; }
.ix__name { font: italic 400 clamp(22px, 1.9vw, 30px)/1.15 var(--serif); transition: transform .6s var(--out); }
.ix__title { flex: none; color: var(--mute); font: 500 9.5px/1.3 var(--sans); letter-spacing: .16em; text-transform: uppercase; }
.ix__list a::before { content: ""; position: absolute; top: 50%; left: 0; width: 6px; height: 6px; margin-top: -3px; background: currentColor; scale: 0; transition: scale .5s var(--out); }
.ix__list li.is-on a::before { scale: 1; }
.ix__list li.is-on .ix__name { transform: translateX(22px); }
@media (hover: hover) and (pointer: fine) {
  .ix__list a:is(:hover, :focus-visible)::before { scale: 1; }
  .ix__list a:is(:hover, :focus-visible) .ix__name { transform: translateX(22px); }
}
/* The portrait's frame stands between the two lists and stays on screen while the names scroll past: centred in
   the space under the header, from the top of the index to the end of the longer list. It never moves with the
   pointer. Pinned on the right, it used to slide on past the end of the shorter list into empty space; made to
   come level with the name pointed at, it went down the page with the pointer and off the bottom of the screen.
   Between the lists there are names beside it the whole way down. 62.5cqw is half the frame's height (4:5). */
.ix > .ix__col + .ix__col { grid-column: 3; grid-row: 1; }
.ix__preview { grid-column: 2; grid-row: 1; container-type: inline-size; }
.ix__frame { position: sticky; top: max(var(--hd) + 24px, 50vh + var(--hd) / 2 - 62.5cqw); aspect-ratio: 4 / 5; overflow: hidden; background: var(--stone); }
.ix__frame > .pt { position: absolute; inset: 0; }   /* each new portrait wipes up over the last */

.visit { padding-top: 0; }
.visit__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(28px, 4vw, 72px); }
.visit__maps { display: flex; gap: 24px; }
.visit__hours { margin: 0; }
.visit__row { display: flex; justify-content: space-between; gap: 20px; padding: 12px 0; border-bottom: 1px solid var(--line); font-size: clamp(19px, 1.5vw, 23px); }
.visit__row:first-child { padding-top: 0; }
.visit__row dd { margin: 0; white-space: nowrap; }
.visit__mail { font-size: .82em; overflow-wrap: anywhere; }

.gift { background: var(--ink); color: #fff; }
.gift__in { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); align-items: end; gap: 20px clamp(32px, 5vw, 96px); }
.gift .display { margin: 0; }
.gift p:last-child { margin: 0; }

.duo__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, .8fr); gap: clamp(32px, 5vw, 88px); align-items: start; }
.duo__img { position: relative; aspect-ratio: 4 / 5; overflow: hidden; background: var(--stone); }

/* ---------------------------------------------------------------- portraits
   The name is printed across the bottom in white capitals, as on the salon's own photos:
   one word per line, set in from the left, right and bottom edges by the same margin (--pad). */

.pt { position: relative; aspect-ratio: 4 / 5; overflow: hidden; background: #000; color: #fff; container-type: inline-size; }
.pt__img, .pt__img img { position: absolute; inset: 0; width: 100%; height: 100%; }
.pt__img img { object-fit: cover; object-position: 50% 30%; }
.pt__blank { position: absolute; inset: 0; background: #000; }
.pt__shade { position: absolute; inset: auto 0 0; height: 46%; background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, .44)); pointer-events: none; }
.pt--blank .pt__shade { display: none; }
.pt__name { --pad: 6cqw; position: absolute; left: 0; right: 0; bottom: 0; margin: 0; padding: 0 var(--pad) var(--pad); font: 500 1em/.9 var(--sans); letter-spacing: -.01em; text-transform: uppercase; }
.nm__in { display: block; margin-bottom: -.09em; font-size: min(calc((100cqw - 2 * var(--pad)) / var(--w)), 15cqw); }   /* baseline on the margin */
.nm__line { display: block; margin-left: calc(var(--l, 0) * -1em); white-space: nowrap; }

/* ---------------------------------------------------------------- inner page mastheads */

.mast { padding: clamp(56px, 9vw, 132px) 0 clamp(40px, 5vw, 72px); }
.mast__in { max-width: none; }
.mast--image { position: relative; height: clamp(420px, 80vh, 900px); padding: 0; overflow: hidden; overflow: clip; background: #000; color: #fff; }   /* clip: hidden would make it a scroller of its own, and .hd-edge would be measured in it */
.mast--image::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0, 0, 0, .3), rgba(0, 0, 0, 0) 30%, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, .4)); }
.mast--image .mast__in { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; }
.nf__links { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(24px, 3vw, 40px); }
.mast--404 .mast__title { max-width: max(320px, (100svh - 440px) * 2.47); }   /* short screens: the 404 shrinks so its links stay on the first screen */

/* The category bar sticks under the header, which stays on screen, so a jump to a section stops below both:
   the header's room is in html's scroll-padding, the bar's 48px (11px words, 18px above and below, 1px line) here */
.chips { position: sticky; top: var(--hd); z-index: 20; background: var(--paper); border-bottom: 1px solid var(--line); }
.chips ~ section[id] { scroll-margin-top: 48px; }
.chips__in { display: flex; gap: 28px; overflow-x: auto; scrollbar-width: none; }
.chips__in::-webkit-scrollbar { display: none; }
.chips a { position: relative; flex: none; padding: 18px 0; font: 500 11px/1 var(--sans); letter-spacing: .2em; text-transform: uppercase; color: var(--mute); transition: color .3s; }
.chips a::after { content: ""; position: absolute; left: 50%; bottom: 6px; width: 4px; height: 4px; margin-left: calc(-2px - .1em); background: currentColor; scale: 0; transition: scale .45s var(--out); }
.chips a.is-here { color: var(--ink); }
.chips a.is-here::after { scale: 1; }
@media (hover: hover) and (pointer: fine) {
  .chips a:hover { color: var(--ink); }
  .chips a:hover::after { scale: 1; }
}
.intro-note { padding-bottom: 0; }
.intro-note .wrap { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 24px; }

/* ---------------------------------------------------------------- services page */

.menu-group { padding-bottom: 0; }
.menu-note { margin-top: -18px; color: var(--mute); }
.menu-text { max-width: 44em; margin-bottom: 32px; }
.tt { width: 100%; border-collapse: collapse; font-size: 18px; }
.tt th, .tt td { padding: 18px 0; text-align: left; vertical-align: top; }
.tt thead th { padding: 0 0 14px; color: var(--mute); font: 500 10px/1.3 var(--sans); letter-spacing: .16em; text-transform: uppercase; }
.tt thead th:not(:first-child), .tt td { width: calc(46% / var(--cols)); padding-left: 16px; text-align: right; font-variant-numeric: lining-nums tabular-nums; white-space: nowrap; }
.tt tbody th { padding-right: 24px; font-weight: 400; }
.tt tbody tr { border-bottom: 1px solid var(--line); }
.tt__na { color: var(--line); }
.tt__founder { max-width: 560px; margin-top: 44px; }
.prices--menu { margin-top: 36px; }
.policies__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px clamp(32px, 5vw, 88px); }
.policy__h { margin-bottom: 10px; font: 500 11px/1.4 var(--sans); letter-spacing: .2em; text-transform: uppercase; }

/* ---------------------------------------------------------------- team page */

.team { padding-bottom: 0; }
.team:last-of-type { padding-bottom: clamp(80px, 11vw, 168px); }
.cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(28px, 3vw, 44px) clamp(12px, 1.6vw, 24px); }
.card__a { display: block; }
.card__title { display: block; margin-top: 14px; color: var(--mute); font: 500 10px/1.3 var(--sans); letter-spacing: .18em; text-transform: uppercase; }
/* Black and white throughout, hover included: 14 of the photos are black and white to begin with, so a colour
   hover worked on only some of the cards. On hover the photo pulls in closer. */
.cards .pt__img img { filter: grayscale(1); transition: scale 1.4s var(--out); }
@media (hover: hover) and (pointer: fine) {
  .card__a:is(:hover, :focus-visible) .pt__img img { scale: 1.06; }
}

/* ---------------------------------------------------------------- person page */

.pp__grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(32px, 6vw, 112px); padding-top: clamp(24px, 3.4vw, 48px); }
.pp__media { position: sticky; top: calc(var(--hd) + 24px); align-self: start; max-width: calc((100svh - var(--hd) - 40px) * .8); }   /* short screens: the whole 4:5 photo, name and all, stays in view while the words scroll past */
.pp__body { max-width: 640px; padding: clamp(8px, 4vw, 72px) 0 0; }
.pp__crumb { margin-bottom: 40px; font: 500 11px/1 var(--sans); letter-spacing: .2em; text-transform: uppercase; color: var(--mute); }
.pp__crumb a { transition: color .4s; }
.pp__crumb a::before { content: "←"; content: "←" / ""; display: inline-block; margin-right: .6em; transition: translate .5s var(--out); }
@media (hover: hover) and (pointer: fine) {
  .pp__crumb a:is(:hover, :focus-visible) { color: var(--ink); }
  .pp__crumb a:is(:hover, :focus-visible)::before { translate: -6px 0; }
}
.pp__title { margin-bottom: 10px; font: italic 400 clamp(30px, 2.8vw, 44px)/1.1 var(--serif); }
.pp__meta { display: flex; flex-wrap: wrap; gap: 8px 48px; overflow: hidden; margin: -6px -6px 34px; padding: 6px; color: var(--mute); font-size: 16px; }
/* A dot in the gap before each later item. An item that wraps to start a line takes its dot past the edge, where overflow hides it. */
.pp__meta > * + *::before { content: "·"; content: "·" / ""; display: inline-block; width: 48px; margin-left: -48px; text-align: center; }
.pp__meta a { color: var(--ink); }
.bio { margin-bottom: 56px; }
.bio__h { margin: 28px 0 10px; font: 500 11px/1.4 var(--sans); letter-spacing: .2em; text-transform: uppercase; }
.bio__list li { padding: 6px 0; border-bottom: 1px solid var(--line); }
.pp__h { margin-bottom: 4px; font: 500 11px/1.4 var(--sans); letter-spacing: .2em; text-transform: uppercase; }
.pp__cta { margin-top: 40px; }
.pp__cta .small { margin-top: 14px; }
.pn { display: grid; grid-template-columns: 1fr 1fr; margin-top: clamp(72px, 9vw, 140px); border-top: 1px solid var(--ink); }
.pn__a { display: block; padding: 28px 0 8px; }
.pn__a--next { text-align: right; }
.pn__a .eyebrow { display: block; margin-bottom: 8px; color: var(--mute); }
.pn__name { display: inline-block; font: italic 400 clamp(26px, 3vw, 46px)/1.1 var(--serif); transition: transform .6s var(--out); }
.pn__a--prev:hover .pn__name { transform: translateX(-12px); }
.pn__a--next:hover .pn__name { transform: translateX(12px); }
.pg--person .ft { margin-top: clamp(80px, 11vw, 168px); }

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

.ft { overflow: hidden; background: #000; color: #fff; padding-top: clamp(72px, 9vw, 128px); }
.ft__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 40px clamp(28px, 4vw, 64px); font-size: 16px; line-height: 1.55; }
.ft__h { margin: 0 0 14px; color: var(--mute-dark); font: 500 10px/1.4 var(--sans); letter-spacing: .22em; text-transform: uppercase; }
.ft__col p + .ft__h { margin-top: 30px; }
.ft__list li { margin-bottom: 4px; }
.news { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.news input { flex: 1 1 180px; min-height: 42px; padding: 0 14px; border: 1px solid #555; border-radius: 0; background: transparent; color: #fff; font: 400 16px "Field Line", var(--serif); }
.news input::placeholder { color: var(--mute-dark); }
.news input:focus { outline: none; border-color: #fff; }
.news__msg { flex-basis: 100%; min-height: 1.5em; margin: 6px 0 0; color: var(--mute-dark); font-size: 15px; }
.ft__mark { display: block; padding: clamp(64px, 9vw, 140px) var(--gutter) 0; }
.ft__base { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px 24px; margin-top: clamp(28px, 3vw, 44px); padding-top: 22px; padding-bottom: 26px; border-top: 1px solid #262626; color: var(--mute-dark); font: 500 10px/1.5 var(--sans); letter-spacing: .16em; text-transform: uppercase; }
.ft__base p { margin: 0; }
.ft__base a { color: #fff; }

/* The logo (a vector traced by tools/logo.py) as eight tiles, one letter each, so it can move letter by
   letter. In the header and footer the letters part from the middle on hover (cqw: a share of the logo's width). */
.sprite { position: relative; display: block; width: 100%; aspect-ratio: 2500 / 325; container-type: inline-size; }
.sprite__l { position: absolute; top: 0; bottom: 0; left: calc(var(--a) * 100%); width: calc((var(--b) - var(--a)) * 100%); overflow: hidden; transition: translate .9s var(--out); }
.sprite__l i { position: absolute; top: 0; bottom: 0; left: calc(var(--a) / (var(--b) - var(--a)) * -100%); width: calc(100% / (var(--b) - var(--a))); background: currentColor; -webkit-mask: url(/assets/img/site/logo.svg) 0 0 / 100% 100% no-repeat; mask: url(/assets/img/site/logo.svg) 0 0 / 100% 100% no-repeat; }
@media (hover: hover) and (pointer: fine) { :is(.hd__brand, .ft__mark):is(:hover, :focus-visible) .sprite__l { translate: calc((var(--i) - 3.5) * var(--part)) 0; } }
.hd__brand { --part: .9cqw; }
.ft__mark { --part: .55cqw; }

/* Phone booking bar. The footer keeps room for it, so it never covers anything (bug BSNY-06). */
.bookbar { display: none; }

/* ---------------------------------------------------------------- opening sequence ("the cut")
   The logo rises letter by letter, NEW YORK follows, a hairline cuts across and the black parts
   along it. Each half holds a full copy of the composition, so the cut runs straight through it.
   main.js adds intro-open as the halves part (the page underneath starts its entrance then),
   intro-skip on a click, key or scroll before that, and intro-done at the end. */

.opening { display: none; }
html.intro .opening { display: block; position: fixed; inset: 0; z-index: 100; cursor: pointer; }
html.intro-done .opening { display: none; }
.opening__half { position: absolute; left: 0; right: 0; height: 50%; overflow: hidden; background: #000; color: #fff; }
.opening__half--top { top: 0; }
.opening__half--bottom { bottom: 0; }
.opening__stage { position: absolute; left: 0; right: 0; height: 200%; }
.opening__half--top .opening__stage { top: 0; }
.opening__half--bottom .opening__stage { bottom: 0; }
.opening .sprite { position: absolute; top: 50%; left: 50%; width: min(78vw, 620px); transform: translate(-50%, -50%); }
.opening__city { position: absolute; left: 0; right: 0; top: calc(50% + min(78vw, 620px) * 325 / 2500 / 2 + clamp(18px, 3vw, 30px)); margin-right: -.62em; text-align: center; font: 500 clamp(10px, 1.3vw, 13px)/1 var(--sans); letter-spacing: .62em; }
.opening__cut { position: absolute; left: 0; right: 0; top: 50%; height: 1px; margin-top: -.5px; background: #fff; transform: scaleX(0); }

html.intro .opening .sprite__l i { animation: rise .95s var(--out) both; animation-delay: calc(.15s + var(--i) * 55ms); }
html.intro .opening__city .l { animation: fade-rise .8s var(--out) both; animation-delay: calc(.62s + var(--i) * 32ms); }
html.intro .opening__cut { animation: cut-draw .5s var(--cut) 1.28s both, fade-out .3s 1.8s both; }
html.intro .opening__half--top { animation: part-up 1s var(--cut) 1.74s both; }
html.intro .opening__half--bottom { animation: part-down 1s var(--cut) 1.74s both; }
html.intro .opening { animation: gone .01s 2.8s both; }
/* Skipped: the cut opens at once. New animation names, so they start fresh instead of retiming the old ones. */
html.intro-skip .opening__half--top { animation: part-up-now .7s var(--cut) both; }
html.intro-skip .opening__half--bottom { animation: part-down-now .7s var(--cut) both; }
html.intro-skip .opening__cut { animation: fade-out-now .2s both; }
html.intro-skip .opening { animation: gone-now .01s .75s both; }

@keyframes rise { from { transform: translateY(105%); } to { transform: none; } }
@keyframes fade-rise { from { opacity: 0; transform: translateY(70%); } to { opacity: 1; transform: none; } }
@keyframes cut-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes fade-out { to { opacity: 0; } }
@keyframes fade-out-now { to { opacity: 0; } }
@keyframes part-up { to { transform: translateY(-101%); } }
@keyframes part-up-now { to { transform: translateY(-101%); } }
@keyframes part-down { to { transform: translateY(101%); } }
@keyframes part-down-now { to { transform: translateY(101%); } }
@keyframes gone { to { visibility: hidden; pointer-events: none; } }
@keyframes gone-now { to { visibility: hidden; pointer-events: none; } }
@keyframes settle { from { scale: 1.16; } to { scale: 1; } }

/* Page openings (home hero, inner mastheads) play on load; behind the opening sequence they
   hold until the cut opens. */
html.motion :is(.hero__media, .mast__img) { animation: settle 2.6s var(--out) both; }
html.motion :is(.hero, .mast) .big .l { animation: rise 1.25s var(--out) both; animation-delay: calc(.1s + var(--i) * 55ms); }
html.motion :is(.hero__eyebrow, .mast .eyebrow) { animation: fade-rise 1.1s var(--out) .45s both; }
html.motion :is(.hero__foot, .nf__links) { animation: fade-rise 1.1s var(--out) .7s both; }
html.intro:not(.intro-open) :is(.hero__media, .mast__img, .hero .l, .mast .l, .hero__eyebrow, .mast .eyebrow, .hero__foot, .nf__links) { animation-play-state: paused; }

/* ---------------------------------------------------------------- scroll reveals
   Hidden states apply only under html.motion (set before first paint, dropped after 3.5 s if
   main.js never runs). main.js adds .in to each element, or each item of a group, as it comes
   into view; --d cascades whatever arrives together, --sp < 1 plays it quicker on fast scrolls. */

html.motion [data-reveal=""] { opacity: 0; transform: translateY(48px); transition: opacity calc(1.1s * var(--sp)) var(--out), transform calc(1.3s * var(--sp)) var(--out); transition-delay: calc(var(--d, 0s) * var(--sp)); }
html.motion [data-reveal=""].in { opacity: 1; transform: none; }

html.motion [data-reveal="rule"] { transform: scaleX(0); transition: transform calc(1.4s * var(--sp)) var(--cut); transition-delay: calc(var(--d, 0s) * var(--sp)); }
html.motion [data-reveal="rule"].in { transform: none; }

html.motion [data-reveal="words"] .w > span { transform: translateY(120%); transition: transform calc(1.2s * var(--sp)) var(--out); transition-delay: calc((var(--d, 0s) + .05s + var(--i) * 70ms) * var(--sp)); }
html.motion [data-reveal="words"].in .w > span { transform: none; }

html.motion [data-reveal="stagger"] > * { opacity: 0; transform: translateY(40px); transition: opacity calc(1.1s * var(--sp)) var(--out), transform calc(1.3s * var(--sp)) var(--out); transition-delay: calc(var(--d, 0s) * var(--sp)); }
html.motion [data-reveal="stagger"] > .in { opacity: 1; transform: none; }

/* Price rows and name lists: each row rises and its hairline draws across */
html.motion :is([data-reveal="rows"] .prices__row, [data-reveal="list"] > li) > * { opacity: 0; transform: translateY(24px); transition: opacity calc(.9s * var(--sp)) var(--out), transform calc(1s * var(--sp)) var(--out); transition-delay: calc((var(--d, 0s) + .12s) * var(--sp)); }
html.motion :is([data-reveal="rows"] .prices__row, [data-reveal="list"] > li).in > * { opacity: 1; transform: none; }
html.motion :is([data-reveal="rows"] li.prices__row, [data-reveal="list"] > li > a)::after { transform: scaleX(0); transition: transform calc(1.1s * var(--sp)) var(--cut); transition-delay: calc(var(--d, 0s) * var(--sp)); }
html.motion :is([data-reveal="rows"] li.prices__row.in, [data-reveal="list"] > li.in > a)::after { transform: none; }
html.motion [data-reveal="rows"] tr.prices__row { border-bottom-color: transparent; transition: border-color calc(1s * var(--sp)); transition-delay: calc((var(--d, 0s) + .3s) * var(--sp)); }
html.motion [data-reveal="rows"] tr.prices__row.in { border-bottom-color: var(--line); }

/* Banners open from a hairline across their middle; the photo settles, then the title rises */
html.motion [data-reveal="cut"] { clip-path: inset(calc(50% - .5px) 0 calc(50% - .5px) 0); transition: clip-path calc(1.5s * var(--sp)) var(--cut); transition-delay: calc(var(--d, 0s) * var(--sp)); }
html.motion [data-reveal="cut"] .banner__img img { scale: 1.3; transition: scale calc(2.2s * var(--sp)) var(--out); transition-delay: calc(var(--d, 0s) * var(--sp)); }
html.motion [data-reveal="cut"] .l { transform: translateY(105%); transition: transform calc(1.2s * var(--sp)) var(--out); transition-delay: calc((var(--d, 0s) + .6s + var(--i) * 60ms) * var(--sp)); }
html.motion [data-reveal="cut"].in { clip-path: inset(0); }
html.motion [data-reveal="cut"].in .banner__img img { scale: 1; }
html.motion [data-reveal="cut"].in .l { transform: none; }

/* Portraits: the frame wipes up from its bottom edge, the photo settles, the name rises letter by letter */
html.motion [data-reveal="portrait"] { clip-path: inset(100% 0 0 0); transition: clip-path calc(1.3s * var(--sp)) var(--cut); transition-delay: calc(var(--d, 0s) * var(--sp)); }
html.motion [data-reveal="portrait"] img { scale: 1.28; transition: scale calc(1.9s * var(--sp)) var(--out), filter .9s var(--out); transition-delay: calc(var(--d, 0s) * var(--sp)), 0s; }
html.motion [data-reveal="portrait"] .l { transform: translateY(108%); transition: transform calc(1s * var(--sp)) var(--out); transition-delay: calc((var(--d, 0s) + .5s + var(--i) * 34ms) * var(--sp)); }
html.motion [data-reveal="portrait"].in { clip-path: inset(0); }
html.motion [data-reveal="portrait"].in img { scale: 1; }
html.motion [data-reveal="portrait"].in .l { transform: none; }
html.motion :is(.card__a:hover, .card__a:focus-visible) .pt.in img { scale: 1.06; transition-delay: 0s; }

/* Team grids: each card plays the portrait reveal; main.js cascades them along the row */
html.motion [data-reveal="grid"] .card__title { opacity: 0; transform: translateY(14px); transition: opacity calc(.8s * var(--sp)) var(--out), transform calc(.9s * var(--sp)) var(--out); transition-delay: calc((var(--d, 0s) + .6s) * var(--sp)); }
html.motion [data-reveal="grid"] .card.in .card__title { opacity: 1; transform: none; }

/* Footer wordmark rises letter by letter, like the opening */
html.motion [data-reveal="mark"] .sprite__l i { transform: translateY(105%); transition: transform calc(1.3s * var(--sp)) var(--out); transition-delay: calc((var(--d, 0s) + var(--i) * 70ms) * var(--sp)); }
html.motion [data-reveal="mark"].in .sprite__l i { transform: none; }

/* A portrait arriving through a page-transition morph is shown finished, so the morph lands on it */
.vt-now, .vt-now * { transition: none !important; }
/* So is everything on screen when a page arrives through the cut (PAGE_REVEAL in build.py): the cut is its entrance.
   Safari drew these reveals finished inside the cut, then played them from the start once it ended. */
html.vt-in [data-reveal], html.vt-in [data-reveal] * { transition: none !important; }
/* So is the header a page is left with (main.js, pageswap), down to the button's fill and the burger's lines */
.hd.vt-now ::before, .hd.vt-now ::after { transition: none !important; }

/* ---------------------------------------------------------------- page transitions
   The new page is cut in from the bottom edge while the old one lifts away. A portrait clicked
   on the team page (or the home page) morphs into the large portrait on the person's page, and back. */

@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-lift .9s var(--cut) both; }
::view-transition-new(root) { animation: vt-cut .9s var(--cut) both; }
::view-transition-group(*) { animation-duration: .9s; animation-timing-function: var(--cut); }
::view-transition-group(hd) { z-index: 2; animation: none; }
::view-transition-old(hd) { animation: vt-hd-old .9s var(--cut) both; }
::view-transition-new(hd) { animation: vt-hd-new .9s var(--cut) both; }
@keyframes vt-lift { to { transform: translateY(-9vh); } }
@keyframes vt-cut { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(0); } }
/* The header changes where the cut crosses it, with the page under it: the old one above the line, the
   new one below. These clips are in screen pixels (100vh is the whole cut), so the header's line is the
   page's line. The new header used to show at once, and one that starts see-through (white letters for
   a dark photo, as on Services) sat on the old page's white until the cut came up to it: the bar seemed
   to vanish for half a second. */
@keyframes vt-hd-old { from { clip-path: inset(0 0 calc(var(--hd) - 100vh) 0); } to { clip-path: inset(0 0 var(--hd) 0); } }
@keyframes vt-hd-new { from { clip-path: inset(100vh 0 0 0); } to { clip-path: inset(0); } }

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

@media (max-width: 1100px) {
  .ix { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .ix > .ix__col + .ix__col { grid-column: auto; grid-row: auto; }
  .ix__preview { display: none; }
  .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .duo__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .duo__img { display: none; }
  .ft__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 899px) {
  :root { --hd: 64px; }
  body { font-size: 17px; }
  .hd__bar { grid-template-columns: auto 1fr; }
  .hd__nav, .hd__link { display: none; }
  .hd__brand { align-items: flex-start; gap: 5px; }
  .hd .logo { width: 132px; }
  .hd__city { font-size: 8px; }
  .hd__menu { display: block; }
  .ov, .visit__grid { grid-template-columns: minmax(0, 1fr); }
  .founder__grid, .pp__grid, .gift__in, .duo__grid, .policies__grid { grid-template-columns: minmax(0, 1fr); }
  .pp__media { position: relative; top: 0; max-width: none; margin: 0 calc(var(--gutter) * -1); }
  .pp__body { padding-top: 8px; }
  .pp__crumb { margin-bottom: 24px; }
  .bookbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 50; display: block; padding: 10px var(--gutter) calc(10px + env(safe-area-inset-bottom)); background: var(--paper); border-top: 1px solid var(--line); visibility: hidden; transform: translateY(101%); transition: transform .7s var(--cut), visibility 0s .7s; }
  .bookbar.is-on { visibility: visible; transform: none; transition: transform .7s var(--cut); }   /* hidden once off screen: iOS Safari draws the page under its glass toolbar */
  .ft { padding-bottom: calc(75px + env(safe-area-inset-bottom)); }
  .hero__foot { flex-direction: column; align-items: stretch; }
  .hero__cta .btn { flex: 1 1 auto; }
  .tt thead { display: none; }
  .tt, .tt tbody, .tt tr, .tt th, .tt td { display: block; width: auto; }
  .tt tbody tr { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: 0 12px; padding: 16px 0; }
  .tt tbody th { grid-column: 1 / -1; padding: 0 0 12px; }
  .tt td { padding: 0; text-align: left; white-space: normal; }
  .tt td::before { content: attr(data-tier); display: block; margin-bottom: 4px; color: var(--mute); font: 500 9px/1.3 var(--sans); letter-spacing: .14em; text-transform: uppercase; }
  .pn { grid-template-columns: minmax(0, 1fr); }
  .pn__a--next { text-align: left; border-top: 1px solid var(--line); }
}

/* A phone held sideways: the photo beside the words, as on a desktop, instead of a photo three screens tall
   (or, on the biggest phones, a pinned one with its name below the screen). It scrolls with the page. */
@media (orientation: landscape) and (max-height: 500px) {
  .founder__grid, .pp__grid { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
  .pp__media { position: relative; top: 0; max-width: none; margin: 0; }
}

/* Each banner photo is two pictures side by side. A tall banner on a narrow screen shows the seam between
   them, enlarged: portrait tablets get a shorter strip, phones the first picture alone, both sharp. The
   services masthead stays tall and on portrait screens centres the first picture's sitter instead. */
@media (min-width: 700px) and (max-aspect-ratio: 1/1) {
  .banner, .banner--short { height: 52vw; }
}
@media (max-aspect-ratio: 1/1) {
  .mast__img img { object-position: 23.5% 50%; }
}

@media (max-width: 699px) {
  .banner, .banner--short { height: 74vw; }
  .banner__img img { object-position: 0 50%; }
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ix { grid-template-columns: minmax(0, 1fr); }
  .ft__grid { grid-template-columns: minmax(0, 1fr); }
  .intro-note .btn { width: 100%; }
  .tt tbody tr { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 12px; }
  .tt tbody th { padding-bottom: 0; }
  .tt td { white-space: nowrap; }
}

/* Touch screens: every link answers a tap over about 44 px, Apple's size for a fingertip, and nothing on
   screen moves. A small link grows padding and gives it back as negative margin; an inline one (its letters
   about 1em tall) takes vertical padding without touching its line. Links stacked one above another
   (.links) are too close for that, so they open to 44 px lines, and the first and last give back the
   leading they gain, so the list's neighbours stay put. */
@media (pointer: coarse) {
  .link { padding-block: calc((44px - 1lh) / 2); margin-block: calc((1lh - 44px) / 2); }
  .pp__crumb a, .pp__meta a:not(.link), .duo__item p > a:not([class]) { padding-block: calc((44px - 1em) / 2); }
  .pp__meta { padding-block: 20px; margin-block: -20px 20px; }   /* room inside its clip for those */
  .pp__meta .link { position: relative; z-index: 1; }   /* its tap area reaches into a wrapped agency line below (Jeremy) */
  .ft__base a { padding: calc((44px - 1em) / 2) 6px; margin-inline: -6px; }
  .hd__brand { padding-block: 7px; margin-block: -7px; }
  /* tablets held sideways get the desktop header; its links had 10 px of padding round 1em of capitals */
  .hd__nav a, .hd__link { padding: calc((44px - 1em) / 2) 6px; margin: calc((1em - 24px) / 2) -6px; }
  .hd__nav a::after, .hd__link::after { bottom: calc((44px - 1em) / 2 - 10px); }
  .btn--sm, .news input { min-height: 44px; }
  .links a { display: block; width: fit-content; padding-block: calc((44px - 1lh) / 2); }
  .links > a:first-of-type, .links > li:first-child > a { margin-top: calc((1lh - 44px) / 2); }
  .links > a:last-of-type, .links > li:last-child > a { margin-bottom: calc((1lh - 44px) / 2); }
  .links > li { margin-bottom: 0; }
  .links > br { display: none; }
  .visit .lead:not(.links) > a { display: inline-block; }   /* the two-line address answers as one box, as in the footer */
}

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; animation-delay: 0s !important; }
}
