/* HTS studio v2 — structure after Manège. Quiet white field, one ink, one hanko-red accent. */
:root {
  --bg: #ffffff;
  --ink: #0e0e0e;
  --mute: #737370;
  --line: #e7e6e2;
  --field: #f3f2ef;
  --red: #c8321e;
  --pad: clamp(12px, 1.15vw, 20px);
  --gap: clamp(10px, 1.1vw, 18px);
  --sans: "Switzer", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mono: "DM Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --ease: cubic-bezier(.22, .8, .2, 1);
  --display: clamp(44px, 6.4vw, 104px);
  color-scheme: light;
  /* the raised pill and the pressed one (see "Nav pills" below); also the discipline buttons on the Works page */
  --pill-up:
    7px 6px 10px 0 rgba(0, 0, 0, .26),
    0 0 2px 0 rgba(0, 0, 0, .06),
    -1px -1px 3px -1px rgba(255, 255, 255, .35),
    -2px -2px 8px -2px rgba(255, 255, 255, .25),
    inset 2px 1px 3px 1px rgba(115, 115, 115, 0),
    inset -1px -1px 4px 0 rgba(255, 255, 255, 0);
  --pill-in:
    1px 1px 2px 0 rgba(0, 0, 0, 0),
    0 0 2px 0 rgba(0, 0, 0, 0),
    -1px -1px 3px -1px rgba(255, 255, 255, 0),
    -2px -2px 8px -2px rgba(255, 255, 255, 0),
    inset 2px 1px 3px 1px rgba(115, 115, 115, .255),
    inset -1px -1px 4px 0 rgba(255, 255, 255, .84);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-padding-top: calc(var(--pad) + 104px); }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 400 13px/1.55 var(--sans); letter-spacing: .03em; text-transform: uppercase;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "ss01", "kern";
}
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
p { margin: 0; }
ul { list-style: none; margin: 0; padding: 0; }
figure { margin: 0; }
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
/* Everything the site says is set in capitals, by style rather than in the copy, so the source and the
   page titles keep normal case. Browsers reset the case of form controls, so buttons and menus opt back
   in; what a visitor types into a field stays exactly as typed. */
button, select { text-transform: uppercase; }
input, textarea { text-transform: none; letter-spacing: 0; }
::selection { background: var(--ink); color: var(--bg); }
:focus-visible { outline: 1.5px solid var(--red); outline-offset: 3px; }

.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip { position: fixed; left: var(--pad); top: -60px; z-index: 50; background: var(--ink); color: var(--bg); padding: 8px 12px; }
.skip:focus { top: var(--pad); }

.mono { font-family: var(--mono); font-size: 11px; line-height: 1.5; letter-spacing: .045em; text-transform: uppercase; font-weight: 400; }
.label { color: var(--mute); margin-bottom: 14px; }
.display { font-family: var(--sans); font-weight: 400; font-size: var(--display); line-height: .92; letter-spacing: -0.035em; margin: 0; }

/* page transitions (cross-document) */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out .32s var(--ease) both; }
::view-transition-new(root) { animation: vt-in .5s var(--ease) both; }
/* The nav is its own group so it holds steady while the page fades. No geometry animation: where a classic
   scrollbar appears between pages the nav takes its new position at once instead of sliding by its width. */
::view-transition-group(hdr-nav) { animation: none; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; } }

/* ---------------------------------------------------------------- paper
   The ground every page sits on: drafting paper. A plain square grid with a heavier rule every fifth line, held at
   30% across the whole window and let go softly just before its edges, so it is felt more than seen.
   This is the sheet at rest. With a mouse, the squares under the pointer press into it: site.js then draws the
   whole sheet on a canvas instead (.is-live), from the measures below, so the two can never disagree. */
.paper {
  --g: 36px;              /* one square */
  --paper-minor: .3;      /* ink in a grid line, before the sheet's 30% */
  --paper-major: .34;     /* added on every fifth line */
  --paper-well: .42;      /* ink in a square pressed all the way in */
  --gx: 50%; --gx5: 50%;  /* site.js replaces these with whole pixels, so a rule never lands between two */
  --ex: clamp(36px, 8vw, 130px);   /* how far in from the left and right edges the sheet takes to arrive */
  --ey: clamp(36px, 10vh, 110px);  /* and from the top and bottom */
  position: fixed; inset: 0; z-index: -1; pointer-events: none; opacity: .3;
  background:
    linear-gradient(to right, rgb(14 14 14 / var(--paper-major)) 0 1px, transparent 1px) var(--gx5) 0 / calc(var(--g) * 5) calc(var(--g) * 5),
    linear-gradient(to bottom, rgb(14 14 14 / var(--paper-major)) 0 1px, transparent 1px) var(--gx5) 0 / calc(var(--g) * 5) calc(var(--g) * 5),
    linear-gradient(to right, rgb(14 14 14 / var(--paper-minor)) 0 1px, transparent 1px) var(--gx) 0 / var(--g) var(--g),
    linear-gradient(to bottom, rgb(14 14 14 / var(--paper-minor)) 0 1px, transparent 1px) var(--gx) 0 / var(--g) var(--g);
  /* Full strength everywhere but a band along each edge, where it eases away to nothing. One fade across, one
     down; where the two cross (the corners) the sheet is thinnest of all, which is where the brand and the nav sit. */
  --across: linear-gradient(to right, transparent, rgb(0 0 0 / .1) calc(var(--ex) * .25), rgb(0 0 0 / .36) calc(var(--ex) * .5),
    rgb(0 0 0 / .72) calc(var(--ex) * .75), #000 var(--ex), #000 calc(100% - var(--ex)), rgb(0 0 0 / .72) calc(100% - var(--ex) * .75),
    rgb(0 0 0 / .36) calc(100% - var(--ex) * .5), rgb(0 0 0 / .1) calc(100% - var(--ex) * .25), transparent);
  --down: linear-gradient(to bottom, transparent, rgb(0 0 0 / .1) calc(var(--ey) * .25), rgb(0 0 0 / .36) calc(var(--ey) * .5),
    rgb(0 0 0 / .72) calc(var(--ey) * .75), #000 var(--ey), #000 calc(100% - var(--ey)), rgb(0 0 0 / .72) calc(100% - var(--ey) * .75),
    rgb(0 0 0 / .36) calc(100% - var(--ey) * .5), rgb(0 0 0 / .1) calc(100% - var(--ey) * .25), transparent);
  -webkit-mask-image: var(--across), var(--down); mask-image: var(--across), var(--down);
  -webkit-mask-composite: source-in; mask-composite: intersect;
}
.paper.is-live { background: none; }
.paper canvas { position: absolute; left: 0; top: 0; }
@media (prefers-contrast: more), (forced-colors: active), print { .paper { display: none; } }
/* The paper is open ground. Where pictures are laid edge to edge, or a form's own rules would tangle with the
   grid's, the content stands on a plain white plate whose edge is feathered, so the grid thins out toward it. */
.pgal, .brief, .press { background: var(--bg); box-shadow: 0 0 30px 26px var(--bg); }

/* ---------------------------------------------------------------- header / footer */
/* The header itself is not positioned: brand and nav are fixed separately, each above the page. */
.hdr { font-size: clamp(11.5px, .93vw, 13.5px); font-weight: 500; letter-spacing: .07em; --pill-h: 36px; }
.hdr__brand, .hdr__nav { position: fixed; top: var(--pad); z-index: 20; }
/* Paper behind the header. On pages that scroll, content fades out before it reaches the brand and the
   pills, so the embossed brand always has white behind it and text never runs between the pills.
   A project opens on a full-bleed photograph, so there the paper only comes in once the hero has passed
   (the script adds .past-hero). The home page does not scroll and needs none. */
.hdr::before {
  content: ""; position: fixed; inset: 0 0 auto 0; z-index: 15; pointer-events: none;
  height: calc(var(--pad) + 104px);
  background: linear-gradient(to bottom,
    #fff calc(var(--pad) + 50px), rgba(255, 255, 255, .8) calc(var(--pad) + 62px),
    rgba(255, 255, 255, .45) calc(var(--pad) + 78px), rgba(255, 255, 255, .15) calc(var(--pad) + 92px),
    rgba(255, 255, 255, 0));
  opacity: 0; transition: opacity .35s var(--ease);
}
.is-works .hdr::before, .is-about .hdr::before, .is-contact .hdr::before, .past-hero .hdr::before { opacity: 1; }
/* The brand is embossed like the crest: page-white forms raised off the page and lit from the top-left.
   Each form shows a crisp pale side wall on its lower right (three hard steps), then a soft cast shadow and
   a wide faint one; the lit side stays open, as on the crest. Sizes are in em so the relief scales with the
   type. The mark is an image with the same layers baked in (emboss.brand_mark). Over photographs the same
   white forms simply read as a white logo. */
.hdr__brand { left: var(--pad); height: var(--pill-h); display: inline-flex; align-items: center; gap: .9em; color: #fff; }
.hdr__word {
  font-size: clamp(13.5px, 1.1vw, 16px); font-weight: 700; letter-spacing: .11em;
  text-shadow:
    .03em .03em 0 rgba(0, 0, 0, .3),
    .0625em .0625em 0 rgba(0, 0, 0, .3),
    .094em .094em 0 rgba(0, 0, 0, .16),
    .16em .19em .19em rgba(0, 0, 0, .16),
    .31em .375em .625em rgba(0, 0, 0, .08);
}
/* The image is a 152px glyph with 64px of room for its shadow on every side; the negative margin lets
   that room overhang, so the layout only sees the glyph. */
.hdr__mark {
  --mark-w: clamp(30px, 2.5vw, 38px);
  width: calc(var(--mark-w) * 1.8421); max-width: none; height: auto; flex: none;
  margin: calc(var(--mark-w) * -.4211);
}
/* Keyboard focus: the brand sits on an opaque white chip, so the ring is on white whatever is behind it. */
.hdr__brand:focus-visible {
  background: #fff; border-radius: 98px; box-shadow: 0 0 0 7px #fff;
  outline: 2px solid var(--red); outline-offset: 2px;
  padding: 0 .5em; margin: 0 -.5em; /* room at the ends of the chip; the two cancel, so the brand does not move */
}

/* Nav pills, after shadowbutton.framer.website: raised off the page at rest, pressed into it on hover and
   for the current page. Light comes from the top-left, same as the crest. Both shadow sets stay in the list
   (alpha 0 when off) so the press interpolates; an outset-to-inset swap would snap.
   Layers: cast shadow (the reference's), a contact line that closes the top-left edge on a white page,
   two soft highlights that only show over photos, then the two inset layers of the pressed state.
   The gap is wide enough for each cast shadow to fade out before the next pill.
   Depth is calibrated against the reference, measured from 1x renders: its shadow sits 23 L* below its grey
   page and its pressed rim 10.5 L* below the face. On white the reference's own .34 alphas read deeper than
   that (27 and 14), so the cast shadow is .26, the contact line .06 and the inset .255, which land on 23 and 10.5. */
.hdr__nav {
  right: var(--pad); display: flex; gap: clamp(14px, 1.1vw, 16px); view-transition-name: hdr-nav;
}
.hdr__nav a {
  display: inline-flex; align-items: center; height: var(--pill-h); padding: 0 1.3em;
  border-radius: 98px; background-color: #fff; color: #474747; white-space: nowrap;
  box-shadow: var(--pill-up);
  transition: box-shadow .26s var(--ease), background-color .26s var(--ease), color .26s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
/* a page inside this section (a project): the pill stays raised and pressable, only the label takes ink */
.hdr__nav a[aria-current="true"] { color: var(--ink); }
.hdr__nav a[aria-current="page"], .hdr__nav a:active {
  background-color: #f1f0ee; color: var(--ink); box-shadow: var(--pill-in);
}
@media (hover: hover) {
  .hdr__nav a:hover { background-color: #f1f0ee; color: var(--ink); box-shadow: var(--pill-in); }
}
/* the focus ring sits on the pill's own face, so it reads the same over any photo */
.hdr__nav a:focus-visible { outline: 2px solid var(--red); outline-offset: -5px; }
/* the label settles half a pixel with the press; only where half a pixel is a whole device pixel */
@media (min-resolution: 2dppx) {
  .hdr__nav a span { display: block; transition: transform .26s var(--ease); }
  .hdr__nav a[aria-current="page"] span, .hdr__nav a:active span { transform: translate(.5px, .5px); }
  @media (hover: hover) { .hdr__nav a:hover span { transform: translate(.5px, .5px); } }
}
@media (prefers-contrast: more) {
  .hdr__nav a, .wpill { border: 1px solid var(--ink); }
  .hdr__nav a[aria-current="page"], .wpill[aria-pressed="true"] { background-color: var(--ink); color: #fff; box-shadow: none; }
  /* white-on-white relief is the opposite of what was asked for here: solid ink on a white chip */
  .hdr__brand { color: var(--ink); background: #fff; border-radius: 98px; box-shadow: 0 0 0 7px #fff; }
  .hdr__word { text-shadow: none; }
  .hdr__mark { filter: invert(1); } /* black face; the baked shadow turns white and disappears into the chip */
}
@media (forced-colors: active) {
  .hdr__nav a, .wpill { border: 1px solid ButtonText; }
  .hdr__nav a[aria-current="page"], .wpill[aria-pressed="true"] {
    forced-color-adjust: none; background: Highlight; color: HighlightText; border-color: Highlight; box-shadow: none;
  }
  .hdr__nav a:focus-visible { outline-offset: 2px; }
  /* the mark keeps its baked wall and shadow here, so it stays visible on a light or a dark canvas */
}
/* narrow screens: the pills need the width, so the brand shows the mark alone (the link keeps its accessible name) */
@media (max-width: 539.98px) {
  .hdr__word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .hdr__brand { min-width: 44px; } /* a full-size touch target for the mark alone */
}
@media (max-width: 345px) {
  .hdr__nav { gap: 10px; }
  .hdr__nav a { padding: 0 1em; }
}
@media (max-width: 300px) {
  .hdr__nav { gap: 6px; }
  .hdr__nav a { padding: 0 .75em; }
}

.ftr {
  display: flex; justify-content: space-between; gap: 16px; padding: 48px var(--pad) var(--pad);
  color: var(--ink);
}
.ftr a { transition: color .3s; }
.ftr a:hover { color: var(--red); }

/* ---------------------------------------------------------------- reveal (visible by default; JS opts in) */
.js .rv { opacity: 0; transform: translateY(18px); transition: opacity .9s var(--ease), transform 1.1s var(--ease); }
.js .rv.in { opacity: 1; transform: none; }

/* ---------------------------------------------------------------- home / ring */
.is-home { height: 100svh; overflow: hidden; overscroll-behavior: none; }
.is-home .ftr { position: fixed; inset: auto 0 0 0; padding-top: 0; z-index: 5; }
.home { position: fixed; inset: 0; }
/* --cw is a panel's width (site.js sets it); --ch its height: panels are 16:10 (PANEL in build.py, which cuts the
   pictures to it); --cy is how far down the ring the centre of the row of panels sits. */
.ring {
  --ch: calc(var(--cw, 300px) * .625); --cy: 46%;
  position: absolute; inset: 0; outline: none; cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none;
}
.ring.is-drag { cursor: grabbing; }
.ring:focus-visible .ring__card[tabindex="0"], .ring__card:focus-visible { outline: 1.5px solid var(--red); outline-offset: 4px; }
html:not(.js) .ring__card { display: none; }
html:not(.js) .ring__cap { display: none; }
.home noscript p { position: absolute; left: 0; right: 0; top: 50%; }
.ring__stage { position: absolute; inset: 0; overflow: hidden; perspective-origin: 50% var(--cy); }
.ring__track { position: absolute; left: 50%; top: var(--cy); width: 0; height: 0; transform-style: preserve-3d; }
/* Each panel is a board floating above the page, in the same material as the crest and the brand: a pale
   side wall on its lower right (three hard steps), then two diffused cast shadows thrown well clear of it,
   which is what reads as height. Light comes from the top-left.
   --lift runs from 0 at the centre panel to 1 at the ends of the row (the script sets it from the panel's
   angle): the end panels are nearest the viewer, so their shadows fall farther, spread wider and are
   darker. --hover adds a little more under the pointer. All sizes follow the panel width. */
.ring__card {
  position: absolute; left: 0; top: 0; width: var(--cw, 300px); height: var(--ch);
  margin: calc(var(--ch) / -2) 0 0 calc(var(--cw, 300px) / -2);
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  border-radius: calc(var(--cw, 300px) * .009); overflow: hidden; background: var(--field);
  --l: calc(var(--lift, 0) + var(--hover, 0) * .3);
  box-shadow:
    1px 1px 0 rgba(0, 0, 0, .18), 2px 2px 0 rgba(0, 0, 0, .12), 3px 3px 0 rgba(0, 0, 0, .07),
    calc(var(--cw, 300px) * (.03 + .03 * var(--l))) calc(var(--cw, 300px) * (.045 + .045 * var(--l)))
      calc(var(--cw, 300px) * (.07 + .05 * var(--l))) rgb(0 0 0 / calc(.1 + .05 * var(--l))),
    calc(var(--cw, 300px) * (.06 + .06 * var(--l))) calc(var(--cw, 300px) * (.1 + .1 * var(--l)))
      calc(var(--cw, 300px) * (.2 + .16 * var(--l))) calc(var(--cw, 300px) * -.02) rgb(0 0 0 / calc(.2 + .12 * var(--l)));
  will-change: transform, opacity; -webkit-user-drag: none;
}
/* shadows ease only while the row is at rest (hover in and out); while it turns they follow it frame by frame */
.ring:not(.is-moving) .ring__card { transition: box-shadow .5s var(--ease), scale .5s var(--ease); }
.ring__card img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
/* panels turning away from the viewer fall into shade (the script sets --shade from the panel's angle) */
.ring__card::before { content: ""; position: absolute; inset: 0; background: #000; opacity: var(--shade, 0); pointer-events: none; }
/* A slight gloss, as if each panel were a print under glass. Three lights, all from the top-left: a soft fall of
   light down from the top edge; a narrow reflection that slides across the panel as it turns (--glare, set by the
   script from the panel's angle: it sits left of centre on the panel that faces you); and a little weight in the
   lower-right corner. Then the bright lit edge and the dark far edge of the glass itself. */
.ring__card::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background:
    linear-gradient(108deg, rgba(255, 255, 255, 0) 43%, rgba(255, 255, 255, .24) 48.5%, rgba(255, 255, 255, .07) 53%, rgba(255, 255, 255, 0) 58%)
      var(--glare, 40%) 0 / 300% 100% no-repeat,
    linear-gradient(to bottom, rgba(255, 255, 255, .07), rgba(255, 255, 255, .02) 26%, rgba(255, 255, 255, 0) 46%),
    linear-gradient(135deg, rgba(0, 0, 0, 0) 56%, rgba(0, 0, 0, .13));
  box-shadow: inset 1px 1px 0 rgba(255, 255, 255, .5), inset 2px 2px 3px -1px rgba(255, 255, 255, .22), inset -1px -1px 0 rgba(0, 0, 0, .2);
}
/* the centre panel lifts a little under the pointer */
@media (hover: hover) {
  .ring:not(.is-moving) .ring__card[tabindex="0"]:hover { scale: 1.02; --hover: 1; }
}
/* HT crest: glyph = 40% of the centre card width (glyph fills ~74% of each frame),
   centred in the space between the page top and the top edge of the centre card. */
.crest {
  position: absolute; left: 50%; top: calc((46% - var(--ch) / 2) / 2);
  width: calc(var(--cw, 300px) * .4 / .74); aspect-ratio: 640 / 598;
  transform: translate(-50%, -45%); pointer-events: none; isolation: isolate;
}
/* The three frames are the logo as solid paper with its shadows, on a clear ground: the paper's grid runs up to
   the crest and under its shadow, never across the logo itself. Each frame therefore has to leave as the next
   arrives (stacked, their shadows would add up); plus-lighter makes the two halves of each hand-over sum to exactly
   one picture instead of dipping lighter half way. */
.crest img { position: absolute; inset: 0; width: 100%; height: 100%; mix-blend-mode: plus-lighter; }
.crest img:nth-child(1) { animation: crest-in .7s var(--ease) .15s both, crest-out .6s ease-in-out .75s forwards; }
.crest img:nth-child(2) { animation: crest-in .6s ease-in-out .75s both, crest-out .7s ease-in-out 1.25s forwards; }
.crest img:nth-child(3) { animation: crest-in .7s ease-in-out 1.25s both; }
@keyframes crest-in { from { opacity: 0; } }
@keyframes crest-out { to { opacity: 0; } }
html:not(.js) .crest img:not(:last-child) { display: none; }
/* Under 640px the nav pills sit directly above the crest. It keeps its size and its midpoint wherever that
   clears the pills; on short screens it moves down just far enough, then shrinks to fit above the card.
   The glyph occupies about 12%-90% of the frame's height; 76px = pill bottom plus shadow plus breathing room. */
@media (max-width: 639.98px) {
  .crest {
    --card-top: calc(var(--rh, 100svh) * .46 - var(--ch) / 2);
    --crest-w: min(calc(var(--cw, 300px) * .4 / .74), calc((var(--card-top) - 92px) * 1.372));
    width: var(--crest-w);
    top: max(calc(var(--card-top) / 2), calc(76px + var(--crest-w) * .308));
  }
}
.ring__cap {
  /* set below the densest part of the centre panel's shadow */
  position: absolute; left: 0; right: 0; top: calc(var(--cy) + var(--ch) / 2 + var(--cw, 300px) * .1 + clamp(18px, 2.4vw, 34px));
  text-align: center; pointer-events: none;
  transition: opacity .45s var(--ease), transform .6s var(--ease);
}
.ring__cap.is-hidden { opacity: 0; transform: translateY(4px); transition-duration: .18s; }
.ring__title { font-size: clamp(12.5px, .95vw, 14px); font-weight: 500; letter-spacing: .07em; margin-bottom: 6px; }
.ring__meta i { font-style: normal; color: var(--line); margin: 0 .7em; }
.ring__meta { color: #3b3b3b; }

/* ---------------------------------------------------------------- works
   Four carousels, one to a discipline, each under its own button. Pressing a button keeps that carousel, opens it
   to full size and folds the other three down to their buttons, stacked where they were; pressing it again, or
   "All works", brings all four back (site.js). */
.pghead { padding: calc(var(--pad) + clamp(84px, 13vh, 132px)) var(--pad) clamp(20px, 2.4vw, 34px); display: flex; justify-content: space-between; align-items: flex-end; gap: 24px; flex-wrap: wrap; }
.wall { margin-bottom: 18px; animation: vt-in .5s var(--ease) both; }
.wall[hidden] { display: none; }
.wrow__head { position: relative; z-index: 2; display: flex; align-items: center; gap: 10px clamp(18px, 2vw, 30px); flex-wrap: wrap; padding: 0 var(--pad); min-height: 52px; }
.wrow__head h2 { margin: 0; font: inherit; }
.wpill {
  display: inline-flex; align-items: center; gap: 1em; height: 50px; padding: 0 1.6em; border: 0; cursor: pointer;
  border-radius: 98px; background-color: #fff; color: #474747; box-shadow: var(--pill-up);
  font-size: clamp(13px, 1.05vw, 15px); font-weight: 500; letter-spacing: .09em; white-space: nowrap;
  transition: box-shadow .26s var(--ease), background-color .26s var(--ease), color .26s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.wpill i { font-style: normal; color: var(--mute); }
.wpill[aria-pressed="true"], .wpill:active { background-color: #f1f0ee; color: var(--ink); box-shadow: var(--pill-in); }
@media (hover: hover) { .wpill:hover { background-color: #f1f0ee; color: var(--ink); box-shadow: var(--pill-in); } }
.wpill:focus-visible { outline: 2px solid var(--red); outline-offset: -5px; }
/* the projects of the open discipline, beside its button: each turns the carousel to itself */
.wrow__topics { display: none; flex-wrap: wrap; gap: 2px clamp(14px, 1.5vw, 24px); }
.wrow.is-open .wrow__topics { display: flex; animation: vt-in .6s var(--ease) both; }
.wtopic { background: none; border: 0; padding: 7px 0; cursor: pointer; color: var(--mute); transition: color .3s; }
.wtopic[aria-current], .wtopic:hover { color: var(--ink); }
.wtopic[aria-current] { text-decoration: underline; text-underline-offset: 5px; text-decoration-thickness: 1px; }
/* the fold: a grid row from 1fr to 0fr is a height that can be eased without knowing it */
.wrow__fold { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .75s var(--ease), opacity .45s var(--ease), visibility 0s; }
/* the panels' shadows fall past the foot of the row: they are let go softly there, not cut off in a line */
.wrow__in {
  min-height: 0; overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 64px), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - 64px), transparent);
}
/* visibility as well as inert: where inert is not understood, a folded carousel still leaves the tab order */
.wrow.is-shut .wrow__fold { grid-template-rows: 0fr; opacity: 0; visibility: hidden; transition: grid-template-rows .75s var(--ease), opacity .45s var(--ease), visibility 0s .75s; }
.wrow.is-shut .wrow__head { margin-bottom: 18px; }
.wrows.is-filtered .wrow:not(.is-shut) .wrow__in { padding-bottom: 8px; }
/* A row's ring is as tall as its panels need: the end panels stand nearer and so about half as tall again, then
   their shadows, then the caption. */
.ring--row {
  position: relative; inset: auto; touch-action: pan-y;
  --cy: calc(var(--ch) * .78 + 8px);
  height: calc(var(--ch) * 1.5 + var(--cw, 300px) * .3 + 58px);
}
.ring--row.is-wait .ring__card, .ring--row.is-wait .ring__cap { opacity: 0; }
.wrow__plain { padding: 8px var(--pad) 28px; display: flex; flex-wrap: wrap; gap: 6px 22px; }
.wrow__plain a { text-decoration: underline; }

/* The carousel's raised board, quieter: the same pale wall on the lower right, a shorter and lighter cast
   shadow, because these sit in a page of text rather than floating alone. Shared by the project galleries
   and the About portrait. */
.pgal__item, .about__portrait {
  position: relative; border-radius: 3px;
  --wall: 1px 1px 0 rgba(0, 0, 0, .18), 2px 2px 0 rgba(0, 0, 0, .12), 3px 3px 0 rgba(0, 0, 0, .07);
  box-shadow: var(--wall), 8px 12px 18px rgba(0, 0, 0, .07), 14px 22px 44px -6px rgba(0, 0, 0, .15);
}
.pgal__item::after, .about__portrait::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 1px 1px 0 rgba(255, 255, 255, .22), inset -1px -1px 0 rgba(0, 0, 0, .14);
}

/* ---------------------------------------------------------------- project */
.phero { position: relative; height: 100svh; min-height: 520px; overflow: hidden; background: #111; }
.phero__media { position: absolute; inset: 0; }
.phero__media picture { display: contents; }
.phero__media img { width: 100%; height: 100%; object-fit: cover; transform-origin: 50% 60%; animation: heroin 1.8s var(--ease) both; }
@keyframes heroin { from { transform: scale(1.06); filter: brightness(.8); } }
.phero.ink-light::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,.42), rgba(0,0,0,0) 38%); pointer-events: none; }
.phero--obj { background: var(--field); }
/* A hero that is a mark, not a photograph. Filling an upright screen would crop the logo down the middle, so there it
   is shown whole on its own colour (build.py reads it off the picture's edge), its top and bottom faded into that colour. */
@media (max-aspect-ratio: 1/1) {
  .phero--mark { background: var(--field); }
  .phero--mark .phero__media { display: grid; align-content: center; }
  .phero--mark .phero__media img {
    height: auto; /* its own shape, so the fade below lands on the picture's real top and bottom edges */
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 18%, #000 82%, transparent);
  }
  /* which part of a wide hero an upright phone keeps (build.py: focal_m) */
  .phero__media img { object-position: var(--fx, 50%) 50%; }
}
.phero__media--obj { background: var(--field); display: grid; place-items: center; padding: 11vh 0 15vh; }
.phero__media--obj img { width: auto; height: 100%; max-height: 74vh; max-width: 86vw; object-fit: contain; animation-name: objin; }
@keyframes objin { from { transform: translateY(18px); opacity: 0; } }
.phero__cap { position: absolute; left: var(--pad); right: var(--pad); bottom: var(--pad); z-index: 2; }
.phero__cap .display { margin-bottom: 12px; }
.phero__cap .mono i { font-style: normal; opacity: .45; margin: 0 .6em; }
.ink-light .phero__cap { color: #fff; }
.ink-dark .phero__cap { color: var(--ink); }
.ink-dark:not(.phero--obj)::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(255,255,255,.75), rgba(255,255,255,0) 40%); pointer-events: none; }

.pbody { display: grid; grid-template-columns: 1fr 2fr; gap: var(--gap); padding: var(--gap) var(--pad) 0; }
.pinfo__sticky { position: sticky; top: calc(var(--pad) + 108px); padding-top: 4px; }
.pinfo__text { max-width: 46ch; line-height: 1.6; }
.pinfo__text p + p { margin-top: .8em; }
.pinfo__specs { display: grid; grid-template-columns: 1fr 1fr; gap: 22px var(--gap); margin: 40px 0 0; max-width: 520px; }
.pinfo__specs dt { color: var(--mute); margin-bottom: 4px; }
.pinfo__specs dd { margin: 0; }
.pinfo .arrow, .pinfo__go { display: inline-block; margin-top: 36px; }
.pinfo__note { margin-top: 36px; max-width: 44ch; color: var(--mute); }
.pgal { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap); }
/* A gallery picture is a cell: the raised board, then an optional caption clear of the board's shadow.
   Cells are full width or half; build.py pairs the halves so none is left alone in a row. */
.pgal__cell { grid-column: span 2; margin: 0; min-width: 0; }
.pgal__cell.is-half { grid-column: span 1; }
/* a wall of small things: three or four to a row */
.pgal--c3 { grid-template-columns: repeat(3, 1fr); }
.pgal--c4 { grid-template-columns: repeat(4, 1fr); }
.pgal--c3 .pgal__cell { grid-column: span 3; }
.pgal--c4 .pgal__cell { grid-column: span 4; }
.pgal--c3 .pgal__cell.is-half, .pgal--c4 .pgal__cell.is-half { grid-column: span 1; }
.pgal__item { background: var(--field); overflow: hidden; }
.pgal__item img { width: 100%; }
/* a caption belongs to the picture above it: close under that one, well clear of the next */
.pgal__cap { padding-top: 14px; color: #5c5c59; }
.pgal--caps { row-gap: clamp(30px, 3.2vw, 46px); }

.pnext { display: flex; justify-content: flex-end; padding: clamp(60px, 9vw, 140px) var(--pad) 0; }
.arrow { position: relative; display: inline-flex; gap: .5em; align-items: center; transition: color .3s; }
.arrow::after { content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px; background: currentColor; transform: scaleX(0); transform-origin: right; transition: transform .5s var(--ease); }
.arrow:hover::after { transform: scaleX(1); transform-origin: left; }
.arrow:hover { color: var(--red); }

/* ---------------------------------------------------------------- about / contact */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap); padding: clamp(150px, 26vh, 260px) var(--pad) 0; }
.split__sticky { position: sticky; top: calc(var(--pad) + 108px); }
.split__l .display { margin-bottom: clamp(24px, 3vw, 44px); }
.about__portrait { width: min(62%, 420px); aspect-ratio: 4 / 5; overflow: hidden; background: var(--field); }
.about__portrait img { width: 100%; height: 100%; object-fit: cover; }
.split__r { max-width: 680px; padding-top: calc(var(--display) * .55); }
.lede { font-size: clamp(13px, 1.05vw, 15px); line-height: 1.65; letter-spacing: .045em; max-width: 58ch; }
.lede p + p { margin-top: 1em; }
.lede--sm { font-size: 13px; max-width: 44ch; }
.block { margin-top: clamp(64px, 7vw, 110px); }
.svc { display: grid; grid-template-columns: 1fr 1fr; gap: 32px var(--gap); }
.svc .label { color: var(--ink); margin-bottom: 8px; }
.svc li { line-height: 1.6; }
.clients { columns: 3; column-gap: var(--gap); }
.clients li { line-height: 1.75; break-inside: avoid; }
.press li { display: grid; grid-template-columns: 1.2fr 1.4fr auto; gap: var(--gap); align-items: baseline; padding: 12px 0; border-top: 1px solid var(--line); }
.press li:last-child { border-bottom: 1px solid var(--line); }
.press__t { font-weight: 500; letter-spacing: .05em; }
.press__v, .press__y { color: #4a4a4a; }
.contactgrid, .contactlist { display: grid; grid-template-columns: 1fr 1fr; gap: 28px var(--gap); }
.contactlist { max-width: 520px; }
.contactgrid a, .contactlist a { background: linear-gradient(currentColor, currentColor) 0 100% / 100% 1px no-repeat; transition: color .3s; }
.contactgrid a:hover, .contactlist a:hover { color: var(--red); }
.contactgrid .label, .contactlist .label { margin-bottom: 4px; }

.brief { margin-top: clamp(56px, 6vw, 96px); display: grid; gap: 22px; max-width: 560px; }
.brief label { display: grid; gap: 6px; }
.brief label .mono { color: var(--mute); }
.brief input, .brief select, .brief textarea {
  width: 100%; border: 0; border-bottom: 1px solid var(--line); background: transparent; padding: 8px 0 10px; border-radius: 0;
  font-size: 16px; outline: none; transition: border-color .35s var(--ease); appearance: none; -webkit-appearance: none;
}
.brief select { background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%230e0e0e' stroke-width='1.2'/%3E%3C/svg%3E") right 2px center no-repeat; cursor: pointer; }
.brief select { font-size: 14px; letter-spacing: .03em; }
@media (pointer: coarse) { .brief select { font-size: 16px; } } /* under 16px iOS zooms the page on focus */
.brief select:invalid { color: var(--mute); }
.brief textarea { resize: vertical; min-height: 120px; }
.brief input:focus, .brief select:focus, .brief textarea:focus { border-color: var(--ink); }
.brief .is-err { border-color: var(--red); }
.btn { justify-self: start; background: var(--ink); color: var(--bg); border: 0; padding: 14px 22px; cursor: pointer; transition: background .35s var(--ease); margin-top: 8px; }
.btn:hover { background: var(--red); }
.brief__note { color: var(--mute); min-height: 1.5em; }
.brief__hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; } /* a trap for form-filling scripts */
.btn[disabled] { opacity: .5; cursor: default; }

.nf { min-height: 80vh; display: flex; flex-direction: column; justify-content: flex-end; gap: 20px; padding: var(--pad); }

/* ---------------------------------------------------------------- responsive */
@media (max-width: 900px) {
  .pbody, .split { grid-template-columns: 1fr; }
  .pinfo__sticky, .split__sticky { position: static; }
  .pinfo { padding: 18px 0 36px; }
  .split__r { padding-top: 48px; }
  .about__portrait { width: 100%; max-width: 420px; }
  .clients { columns: 2; }
}
@media (max-width: 640px) {
  /* a gallery laid out in twos stays in twos (a wall of hats, a before beside its after); the rest stack */
  .pgal:not(.pgal--pairs, .pgal--c3, .pgal--c4) { grid-template-columns: 1fr; }
  .pgal:not(.pgal--pairs, .pgal--c3, .pgal--c4) .pgal__cell { grid-column: span 1; }
  .pgal--c3, .pgal--c4 { grid-template-columns: 1fr 1fr; }
  .pgal--c3 .pgal__cell, .pgal--c4 .pgal__cell { grid-column: span 2; }
  .pgal--c3 .pgal__cell.is-half, .pgal--c4 .pgal__cell.is-half { grid-column: span 1; }
  .pgal__cap { padding-top: 11px; }
  .pgal--caps { row-gap: 26px; }
  .press li { grid-template-columns: 1fr auto; }
  .press__v { grid-column: 1; grid-row: 2; }
  .press__y { grid-row: 1 / span 2; grid-column: 2; }
  .svc, .contactgrid, .contactlist { grid-template-columns: 1fr; }
  .ftr { flex-direction: column; gap: 6px; }
  .is-home .ftr { flex-direction: row; }
  .is-home .ftr .mono:first-child { max-width: 60%; }
  .pghead { flex-direction: column; align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  .crest img:not(:last-child) { display: none; }
  .crest img { animation: none; } /* else the last frame would wait out its 1.25s delay unseen */
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
  .js .rv { opacity: 1; transform: none; }
}
