/* ==========================================================================
   Breathing grid — gallery dark
   Colours, spacing and type are driven by CSS variables that site.js sets
   from content.js, so the Studio can change them without touching this file.
   ========================================================================== */

:root {
  --canvas: #0e0e0e;
  --surface: #161616;
  --line: #262626;
  --ink: #f2f0ed;
  --ink-dim: #a3a09b;
  --ink-faint: #7a7772;
  --accent: #e8c547;

  --gutter: 24px;
  --logo-opacity: 0.72;

  --font: "Inter Tight", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;

  --pad: clamp(20px, 5vw, 64px);
  --display: clamp(2.5rem, 7vw, 6rem);
  --h1: clamp(2rem, 4.5vw, 3.5rem);
  --h2: clamp(1.35rem, 2.4vw, 2rem);
  --lead: clamp(1.05rem, 1.5vw, 1.3rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--canvas);
  color-scheme: dark;
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img,
svg {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

h1,
h2,
h3,
p {
  margin: 0;
}

::selection {
  background: var(--accent);
  color: #000;
}

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

.wrap {
  width: 100%;
  max-width: 1600px;
  margin: 0 auto;
  padding-inline: var(--pad);
}

.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

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

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 20px var(--pad);
  transition: background 0.3s var(--ease), border-color 0.3s var(--ease);
  border-bottom: 1px solid transparent;
}

.site-header.is-stuck {
  background: rgba(14, 14, 14, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
}

.brand {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.01em;
}


.brand span {
  color: var(--ink-faint);
  font-weight: 400;
}

.site-nav {
  display: flex;
  gap: clamp(16px, 3vw, 34px);
  font-size: 14px;
  color: var(--ink-dim);
}

.site-nav a {
  position: relative;
  transition: color 0.2s var(--ease);
}

.site-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s var(--ease);
}

.site-nav a:hover,
.site-nav a[aria-current="page"] {
  color: var(--ink);
}

.site-nav a:hover::after,
.site-nav a[aria-current="page"]::after {
  transform: scaleX(1);
}

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

.hero {
  padding-top: clamp(140px, 22vh, 240px);
  padding-bottom: clamp(48px, 8vh, 96px);
}

.hero h1 {
  margin-top: 22px;
  max-width: 16ch;
  font-size: var(--display);
  font-weight: 500;
  line-height: 0.98;
  letter-spacing: -0.035em;
  text-wrap: balance;
}

.hero-body {
  display: flex;
  align-items: center;
  gap: clamp(32px, 6vw, 88px);
}

.hero-text {
  flex: 1;
  min-width: 0;
}

.hero-top {
  display: flex;
  align-items: center;
  gap: 14px;
}

/* A frame with the photo inside it, rather than a round photo. The frame is
   the circle; the image is free to be scaled and slid around within it, which
   is what makes an ordinary square photo sit properly in a round hole. */
.hero-face {
  display: block;
  border-radius: 50%;
  overflow: hidden;
  background: var(--surface);
  flex: none;
  aspect-ratio: 1;
}

.hero-face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform-origin: center;
}

/* Out to the right of the whole text block, once there's room for it. */
.hero-face--lg {
  width: clamp(124px, 15vw, 210px);
}

/* On a narrow screen it rides the eyebrow line instead, which costs about
   thirty pixels of height — the point is to keep the work near the top. */
.hero-face--sm {
  width: 46px;
  display: none;
}

.hero .standfirst {
  margin-top: 28px;
  max-width: 46ch;
  font-size: var(--lead);
  line-height: 1.55;
  color: var(--ink-dim);
  text-wrap: pretty;
}

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

.marquee {
  margin-top: clamp(48px, 8vh, 88px);
  padding-block: 26px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

/* Driven from site.js rather than by a CSS animation. Hovering slows the
   strip instead of stopping it, and easing between two speeds is something
   an animation-duration swap cannot do — changing the duration recomputes
   the animation's progress and the strip visibly jumps. */
.marquee-track {
  display: flex;
  align-items: center;
  gap: clamp(44px, 6vw, 88px);
  width: max-content;
  will-change: transform;
}

/* --------------------------------------------------------------- logo mark */

/* Every logo is flattened to a single silhouette and repainted in the site's
   ink, so twelve brand guidelines end up looking like one set.

   This uses a filter on a plain <img> rather than a CSS mask. A mask would be
   the tidier tool, but mask images obey same-origin rules, and opening the
   site straight from disk (file://) gives every file its own opaque origin —
   which makes Chrome drop all of them and leaves the logos invisible.
   Filters have no such restriction, so this works from disk and from a host.

   brightness(0) crushes the artwork to black, invert(1) lifts it to white.
   Alpha is untouched throughout, so transparent stays transparent. */
.mark {
  display: inline-block;
  object-fit: contain;
  object-position: center;
  filter: brightness(0) invert(1);
  opacity: var(--logo-opacity);
  transition: opacity 0.3s var(--ease);
}

.marquee .mark {
  width: 104px;
  height: 40px;
  flex: none;
  opacity: calc(var(--logo-opacity) * 0.75);
}

.marquee .mark-link {
  flex: none;
}

/* A mark becomes a link only when that client has a project behind it.
   Lifting to full opacity on hover is the whole affordance: the clients
   whose work can't be shown stay dim and inert, which is the honest signal
   and saves anyone a dead click. */
.mark-link {
  display: inline-flex;
}

.mark-link:hover .mark,
.mark-link:focus-visible .mark {
  opacity: 1;
}

.logowall .mark-link {
  display: block;
  width: 100%;
}

/* The strip drifts, and touch has no hover to pause it — a moving tap target
   is a bad one, so on touch the marquee goes back to being decoration. The
   same logos are tappable in the static wall further down. */
@media (hover: none) {
  .marquee .mark-link {
    pointer-events: none;
  }
}

/* --------------------------------------------------------------- work grid */

.work {
  padding-top: clamp(48px, 9vh, 110px);
  padding-bottom: clamp(60px, 10vh, 130px);
}

.grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--gutter);
  align-items: start;
}

/* Deliberately NOT grid-auto-flow: dense.

   Dense backfills holes by pulling later tiles forward, which quietly
   reorders the work — a project sitting seventh in the Studio can render
   sixth on the page. The Studio promises that the order you drag is the
   order a visitor sees, and that promise is worth more than a filled hole.

   It also made the holes worse, not better: dropping a tall tile alongside
   two squares set the row's height by the tall one and left dead space
   under the squares. Without dense, a row that can't be filled simply ends
   early, which reads as whitespace rather than as a mistake. */

/* Sizing lives on .cell / .cell-face, not on .tile, so that project tiles and
   the cards you drop between them are measured by exactly the same rules and
   land level with each other. .tile and .card carry only their own looks. */
.cell {
  display: block;
  grid-column: span 2;
  position: relative;
}

.cell[data-size="full"] {
  grid-column: span 6;
}
.cell[data-size="wide"] {
  grid-column: span 4;
}

.cell-face {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border-radius: 2px;
  aspect-ratio: 1 / 1;
}

.cell[data-size="full"] .cell-face {
  aspect-ratio: 16 / 9;
}
.cell[data-size="wide"] .cell-face {
  aspect-ratio: 3 / 2;
}
.cell[data-size="tall"] .cell-face {
  aspect-ratio: 3 / 4;
}

/* Reserves the caption's height on every cell. A tile fills this with its
   title; a card leaves it empty. Without it a card would sit 38px shorter
   than the tile beside it and put the ragged row back. */
/* Flex, not a plain block. A block foot inherits a text strut from the body's
   17px/1.6, so a foot with a caption in it stands 27.2px tall while an empty
   one falls back to the 24px minimum — and a captioned cell ends up 3px taller
   than its neighbour. Flex items generate their own boxes, so the height comes
   from the caption alone and every foot in a row agrees. */
.cell-foot {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 14px;
  min-height: 24px;
}

/* Pinning the pair that is meant to read as level.

   A wide and a tall look like they should line up, and by pure ratio they
   would — but a span-4 tile contains three internal gutters where a span-2
   contains one, so the wide ends up exactly two thirds of a gutter taller.
   At the default 24px that is a 16px near-miss: too small to look deliberate,
   large enough to look like a mistake.

   So at the six-column layout both are given the same computed height instead
   of an aspect ratio. --u is the width of one column track, measured by
   site.js, because CSS cannot read it. Every other combination still lands
   wherever it lands — that stagger is the point of the design; it just has to
   be an obvious amount rather than an accidental one. */
@media (min-width: 1024px) {
  .cell[data-size="wide"] .cell-face,
  .cell[data-size="tall"] .cell-face {
    aspect-ratio: auto;
    height: calc((2 * var(--u, 0px) + var(--gutter)) * 4 / 3);
  }

  /* Falls back to the plain ratios if script hasn't measured yet. */
  .grid:not([style*="--u"]) .cell[data-size="wide"] .cell-face {
    aspect-ratio: 3 / 2;
    height: auto;
  }
  .grid:not([style*="--u"]) .cell[data-size="tall"] .cell-face {
    aspect-ratio: 3 / 4;
    height: auto;
  }
}

/* :not(.mark) matters — the client logo is also an <img> inside this box,
   and without it the cover rule stretches the logo to fill the whole panel. */
.cell-face img:not(.mark) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.001);
  transition: transform 0.75s var(--ease), filter 0.5s var(--ease);
}

.tile:hover .cell-face img:not(.mark) {
  transform: scale(1.035);
}

/* The client mark, worn in the corner of the panel. */
.tile-mark {
  position: absolute;
  top: 16px;
  left: 16px;
  width: 74px;
  height: 28px;
  z-index: 2;
  pointer-events: none;
  /* Repeats the recolour from .mark because filter does not accumulate —
     the shadow is chained on the end so it reads against a pale image. */
  filter: brightness(0) invert(1) drop-shadow(0 1px 8px rgba(0, 0, 0, 0.55));
}

.tile:hover .tile-mark {
  opacity: 1;
}

.tile-caption {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 14px;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.6s var(--ease) 0.15s, transform 0.6s var(--ease) 0.15s;
}

.tile.is-in .tile-caption {
  opacity: 1;
  transform: none;
}

.tile-caption .t {
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.tile-caption .c {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-left: auto;
  white-space: nowrap;
}

/* ------------------------------------------------------------ previews */

/* The four visuals sit stacked; only one is ever shown. */
.tile-frame {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.14s linear;
}

.tile-frame.is-on {
  opacity: 1;
}

/* The segment bar is the entire affordance. Nobody discovers that a tile
   holds four visuals unless something says so, and at rest it has to be
   quiet enough not to compete with the image it sits on. */
.tile-steps {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 12px;
  z-index: 2;
  display: flex;
  gap: 4px;
  opacity: 0.3;
  pointer-events: none;
  transition: opacity 0.3s var(--ease);
}

.tile:hover .tile-steps {
  opacity: 1;
}

.tile-steps i {
  flex: 1;
  height: 2px;
  border-radius: 1px;
  background: var(--ink);
  opacity: 0.35;
  transition: opacity 0.14s linear;
}

.tile-steps i.is-on {
  opacity: 1;
}

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

/* No panel, no frame — the card sits directly on the page so it reads as a
   pause in the grid rather than another object in it. With the border gone
   the text has to carry the shape itself, so it centres both ways. */
.card-face {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: 12px;
  padding: clamp(18px, 2.2vw, 32px);
  background: transparent;
  border: 0;
}

a.card:hover .card-line {
  color: var(--accent);
}

.card-line {
  font-size: clamp(1.1rem, 1.9vw, 1.9rem);
  font-weight: 500;
  line-height: 1.14;
  letter-spacing: -0.025em;
  text-wrap: balance;
  transition: color 0.25s var(--ease);
}

.card-sub {
  color: var(--ink-dim);
  font-size: 15px;
  line-height: 1.5;
  text-wrap: pretty;
}

.card-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
  font-size: 14px;
  color: var(--ink-dim);
  transition: color 0.25s var(--ease);
}

a.card:hover .card-cta {
  color: var(--accent);
}

.card-cta em {
  font-style: normal;
  display: inline-block;
  transition: transform 0.3s var(--ease);
}

a.card:hover .card-cta em {
  transform: translateX(4px);
}

/* Kinetic type. Letters are displaced by their distance from the cursor.
   Deliberately transform-only — no variable-font weight — because only two
   static weights are requested from Google Fonts, and the effect has to work
   whichever of the five typefaces is chosen. */
.card-kinetic {
  display: flex;
  flex-wrap: wrap;
  font-size: clamp(1.5rem, 3.4vw, 3rem);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.02em;
}

.card-kinetic span {
  display: inline-block;
  transition: transform 0.28s var(--ease);
}

.card-kinetic .sp {
  width: 0.3em;
}

/* ---------------------------------------------------------------- logowall */

.logowall {
  padding-block: clamp(56px, 9vh, 110px);
  border-top: 1px solid var(--line);
}

.logowall-grid {
  margin-top: 40px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: clamp(28px, 4vw, 56px) clamp(20px, 3vw, 40px);
  align-items: center;
}

.logowall .mark {
  width: 100%;
  height: 42px;
}


.logowall ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: contents;
}

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

.site-footer {
  padding-block: clamp(56px, 9vh, 100px);
  border-top: 1px solid var(--line);
}

.footer-top {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
}

.footer-mail {
  font-size: clamp(1.6rem, 4vw, 3rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  transition: color 0.25s var(--ease);
}

.footer-mail:hover {
  color: var(--accent);
}

.footer-links {
  display: flex;
  gap: 22px;
  font-size: 14px;
  color: var(--ink-dim);
}

.footer-links a:hover {
  color: var(--ink);
}

.footer-bottom {
  margin-top: 48px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ------------------------------------------------------------ project page */

.proj-hero {
  padding-top: clamp(120px, 18vh, 190px);
}

.proj-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 28px;
  align-items: center;
  margin-bottom: 20px;
}

.proj-meta .mark {
  width: 88px;
  height: 32px;
  opacity: 1;
}

.proj-hero h1 {
  max-width: 20ch;
  font-size: var(--h1);
  font-weight: 500;
  line-height: 1.02;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

/* Covers arrive in every shape — a 16:9 still, a 3:4 poster, a square crop.
   Filling the width would make a portrait cover taller than the screen, so
   it is capped by height and centred on the surface instead. */
.proj-cover {
  margin-top: clamp(32px, 5vh, 56px);
  border-radius: 2px;
  overflow: hidden;
  background: var(--surface);
  display: flex;
  justify-content: center;
}

.proj-cover img {
  width: 100%;
  height: auto;
  max-height: 78vh;
  object-fit: contain;
}

.proj-body {
  padding-block: clamp(36px, 6vh, 72px);
}

.proj-writeup {
  max-width: 62ch;
}

/* Client / year / role read across as one line of credits under the opening
   image. Wraps to two on a narrow screen rather than squeezing. */
.proj-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 20px clamp(32px, 6vw, 88px);
  padding-top: clamp(24px, 4vh, 40px);
  border-top: 1px solid var(--line);
  margin-top: clamp(28px, 5vh, 56px);
}

.fact .k {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.fact .v {
  margin-top: 6px;
  font-size: 16px;
}

.proj-writeup p {
  font-size: var(--lead);
  line-height: 1.65;
  color: var(--ink-dim);
  text-wrap: pretty;
}

.proj-writeup p + p {
  margin-top: 1.2em;
}

/* A gallery item is a grid cell exactly like a project tile, so a project
   page tiles by the same rules as the homepage — including the wide/tall
   pinning — and there is only one layout system in the whole site. */
.shot {
  margin: 0;
  cursor: zoom-in;
}

.shot .cell-foot .t {
  font-size: 14px;
  color: var(--ink-dim);
}

/* ------------------------------------------------------------------ video */

.cell-face video,
.proj-cover video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  background: var(--surface);
}

.proj-cover video {
  height: auto;
  max-height: 78vh;
  object-fit: contain;
}

video.tile-frame {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.14s linear;
}

video.tile-frame.is-on {
  opacity: 1;
}

.proj-next {
  border-top: 1px solid var(--line);
  padding-block: clamp(40px, 7vh, 80px);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
}

.proj-next .n {
  font-size: clamp(1.4rem, 3.4vw, 2.4rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  transition: color 0.25s var(--ease);
}

.proj-next:hover .n {
  color: var(--accent);
}

/* --------------------------------------------------------------- info page */

.info {
  padding-top: clamp(130px, 20vh, 210px);
  padding-bottom: clamp(56px, 9vh, 110px);
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(32px, 6vw, 90px);
  align-items: start;
}

.info-portrait {
  border-radius: 2px;
  overflow: hidden;
  background: var(--surface);
}

.info h1 {
  font-size: var(--h1);
  font-weight: 500;
  line-height: 1.03;
  letter-spacing: -0.03em;
  margin-bottom: 26px;
}

.info-bio p {
  font-size: var(--lead);
  line-height: 1.65;
  color: var(--ink-dim);
  text-wrap: pretty;
}

.info-bio p + p {
  margin-top: 1.15em;
}

.info-looking {
  margin-top: 34px;
  padding: 22px 24px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--surface);
}

.info-looking p {
  margin-top: 8px;
  color: var(--ink-dim);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-top: 30px;
  padding: 13px 22px;
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: 14px;
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}

.btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* ---------------------------------------------------------------- lightbox */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 4vmin;
  background: rgba(8, 8, 8, 0.94);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s var(--ease), visibility 0.3s;
  cursor: zoom-out;
}

.lightbox.is-open {
  opacity: 1;
  visibility: visible;
}

.lightbox img {
  max-width: 100%;
  max-height: 92vh;
  object-fit: contain;
}

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

[data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.85s var(--ease), transform 0.85s var(--ease);
}

[data-reveal].is-in {
  opacity: 1;
  transform: none;
}

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

/* Laptop-height windows.

   The ticker is the first sign the page has any depth to it, and on a 13" or
   14" MacBook it was landing just past the fold — so the one thing telling a
   visitor to keep going was the thing they couldn't see.

   Height is what varies here, not width, so the rhythm tightens on short
   viewports rather than at some width breakpoint. A 27" display keeps the
   generous version. The portrait shrinks too: it is the tallest thing in the
   hero, so it sets how far down everything below it starts. */
@media (max-height: 860px) {
  .hero {
    padding-top: clamp(112px, 15vh, 140px);
    padding-bottom: clamp(28px, 4vh, 40px);
  }
  .hero .standfirst {
    margin-top: 22px;
  }
  .hero-face--lg {
    width: clamp(104px, 11vw, 160px);
  }
  .marquee {
    margin-top: clamp(28px, 4vh, 40px);
  }
}

@media (max-width: 767px) {
  .hero-face--lg {
    display: none;
  }
  .hero-face--sm {
    display: block;
  }
  .hero-aside {
    display: none;
  }
}

@media (max-width: 1023px) {
  .grid {
    grid-template-columns: repeat(4, 1fr);
  }
  .cell[data-size="full"],
  .cell[data-size="wide"] {
    grid-column: span 4;
  }
  .proj-body,
  .info {
    grid-template-columns: minmax(0, 1fr);
  }
  .info-portrait {
    max-width: 340px;
  }
}

@media (max-width: 639px) {
  body {
    font-size: 16px;
  }
  .grid {
    grid-template-columns: 1fr;
  }
  .cell,
  .cell[data-size="full"],
  .cell[data-size="wide"] {
    grid-column: span 1;
  }
  /* On a phone the captions never hide — there is no hover to reveal them,
     and a recruiter scrolling one-handed should not have to hunt. */
  .tile-caption {
    opacity: 1;
    transform: none;
  }
  .shot:nth-child(3n + 1) {
    grid-column: span 1;
  }
  .site-nav {
    font-size: 13px;
  }
  .site-header {
    padding: 14px var(--pad);
  }
  /* Name plus discipline plus three nav items will not fit across a phone —
     the discipline is the part a visitor can lose without missing anything. */
  .brand span {
    display: none;
  }
  /* The bar sits lower and wider on a phone, where the tile is full width
     and the scrub is driven by scrolling rather than hovering. */
  .tile-steps {
    left: 12px;
    right: 12px;
    bottom: 10px;
    opacity: 0.55;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  [data-reveal],
  .tile-caption {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .marquee-track {
    animation: none;
  }
  .tile:hover .cell-face img {
    transform: none;
  }
  /* Frames still switch on a desktop, because that is the visitor's own
     cursor doing it — but they cut rather than crossfade, and the phone's
     scroll-driven scrub is switched off entirely in site.js. */
  .tile-frame {
    transition: none;
  }
  .card-kinetic span {
    transition: none;
    transform: none !important;
  }
}
