/* =========================================================================
   Portfolio. Tokens and motion are specified in /DESIGN.md.
   One shape system: radius 0. One accent, used only for interaction feedback.
   ========================================================================= */

:root {
  color-scheme: light dark;

  --bg: #fbfbfa;
  --bg-raised: #ffffff;
  --ink: #12110f;
  --ink-2: #5c5954;
  --ink-3: #8d8983;
  --line: #e4e2dd;
  --accent: #8a3324;
  --scrim: rgba(18, 17, 15, .94);

  --serif: "Cormorant Garamond", "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --sans: "Outfit", "Avenir Next", "Segoe UI", system-ui, sans-serif;

  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-soft: cubic-bezier(.32, .72, 0, 1);
  --ease-swift: cubic-bezier(.4, 0, .2, 1);

  --gutter: clamp(10px, 1.4vw, 22px);
  --margin: clamp(16px, 4vw, 56px);

  --z-grain: 1;
  --z-progress: 60;
  --z-header: 50;
  --z-lightbox: 80;
  --z-cursor: 90;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0d0d0c;
    --bg-raised: #161614;
    --ink: #f2f0ec;
    --ink-2: #a8a49d;
    --ink-3: #736f69;
    --line: #292825;
    --accent: #d4674f;
    --scrim: rgba(5, 5, 4, .96);
  }
}

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

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

body {
  margin: 0;
  background: var(--bg);
  touch-action: manipulation;              /* no 300ms double-tap delay */
  -webkit-tap-highlight-color: rgba(138, 51, 36, .12);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 300;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

a { color: inherit; }

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

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

.skip {
  position: absolute; top: -100px; left: var(--margin);
  z-index: 100; padding: 12px 20px;
  background: var(--ink); color: var(--bg);
  font-size: .8rem; letter-spacing: .1em; text-transform: uppercase;
  text-decoration: none;
  transition: top .2s var(--ease-swift);
}
.skip:focus { top: 12px; }

/* ------------------------------------------------------- scroll progress */
/* Scroll-driven CSS animation. No scroll listener, no main-thread work. */

.progress {
  position: fixed; inset: 0 0 auto 0; height: 2px;
  z-index: var(--z-progress); pointer-events: none;
}

.progress__bar {
  display: block; height: 100%;
  background: var(--accent);
  transform-origin: 0 50%;
  transform: scaleX(0);
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .progress__bar {
      animation: progress-grow linear;
      animation-timeline: scroll(root block);
    }
  }
}

@keyframes progress-grow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* --------------------------------------------------------------- masthead */

.masthead {
  position: sticky; top: 0; z-index: var(--z-header);
  padding: clamp(24px, 4vw, 44px) var(--margin) 0;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  transition: padding .5s var(--ease-soft);
}

/* Condenses once the page has moved, giving the photographs more room. */
.masthead[data-condensed="true"] {
  padding-top: 14px;
}

.wordmark {
  margin: 0;
  text-align: center;
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.6rem, 4.5vw, 2.6rem);
  letter-spacing: .22em;
  text-transform: uppercase;
  line-height: 1.1;
  transition: font-size .5s var(--ease-soft);
}
.masthead[data-condensed="true"] .wordmark { font-size: clamp(1.15rem, 2.4vw, 1.5rem); }

.wordmark a { text-decoration: none; }

/* ------------------------------------------------------------------- nav */

.nav {
  position: relative;
  margin-top: clamp(14px, 2vw, 26px);
  padding-bottom: 14px;
}

/* The track is the scroll container and the indicator's containing block, so
   the underline scrolls with the links instead of drifting away from them. */
.nav__track { position: relative; }

.nav__list {
  display: flex; flex-wrap: nowrap;
  justify-content: center;
  gap: clamp(14px, 2.6vw, 40px);
  margin: 0; padding: 0; list-style: none;
}

/* Below the point where eight items stop fitting, the nav becomes one
   horizontally scrollable line rather than wrapping to three rows and pushing
   the photographs off the screen. Every item stays one tap away, with no menu
   to open. */
@media (max-width: 860px) {
  .nav__track {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Fade the edges so it reads as scrollable. */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 22px,
                                        #000 calc(100% - 22px), transparent);
            mask-image: linear-gradient(90deg, transparent, #000 22px,
                                        #000 calc(100% - 22px), transparent);
  }
  .nav__track::-webkit-scrollbar { display: none; }

  .nav__list {
    justify-content: flex-start;
    width: max-content;
    padding: 0 22px;
    gap: 26px;
  }
}

.nav__link {
  display: inline-block;
  position: relative;
  padding: 4px 2px;
  font-family: var(--serif);
  font-size: .82rem;
  letter-spacing: .19em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-2);
  transition: color .4s var(--ease-out);
}

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

/* The moving underline. One element for the whole nav: it translates and
   resizes between items rather than each link owning a border. */
.nav__indicator {
  position: absolute;
  bottom: 8px; left: 0;
  height: 1px; width: 0;
  background: var(--accent);
  transform: translateX(0);
  opacity: 0;
  transition:
    transform .55s var(--ease-out),
    width .55s var(--ease-out),
    opacity .3s var(--ease-swift);
  pointer-events: none;
}
.nav__indicator[data-ready="true"] { opacity: 1; }

/* Fallback: if JS has not positioned the indicator, the current page still
   reads as current. aria-current carries it for assistive tech regardless. */
.nav:not([data-js="true"]) .nav__link[aria-current="page"] {
  border-bottom: 1px solid var(--accent);
}

/* ------------------------------------------------------------------ main */

main { padding: clamp(28px, 5vw, 56px) var(--margin) clamp(80px, 10vw, 140px); }

.pagehead { margin: 0 0 clamp(28px, 5vw, 52px); }

.display {
  margin: 0;
  text-wrap: balance;
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2rem, 6vw, 3.4rem);
  line-height: 1.08;
  letter-spacing: .01em;
}

.pagehead__blurb {
  margin: 14px 0 0;
  max-width: 52ch;
  color: var(--ink-2);
  font-size: 1rem;
}

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

/* ---- editorial rhythm -----------------------------------------------------
   A six-column grid running a nine-tile cycle: a pair of large frames, two
   rows of three, then one full-bleed. Rows stay whole, so the grid never has
   a hole in it, and the page has a cadence instead of a metronome. */

.grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--gutter);
  margin: 0; padding: 0; list-style: none;
}

.tile { grid-column: span 2; }                       /* three up, the default */
.tile:nth-child(9n + 1),
.tile:nth-child(9n + 2) { grid-column: span 3; }     /* a pair, larger */
.tile:nth-child(9n + 9) { grid-column: span 6; }     /* full width */

/* The overview opens on one frame at full width, so the work introduces itself
   rather than starting mid-grid. That shifts every following tile by one, so
   the cycle is re-phased here: without this the second row is a 3 + 2 and
   leaves a column-wide hole on the right. */
body[data-page="overview"] .tile:first-child { grid-column: span 6; }
body[data-page="overview"] .tile:first-child .tile__frame { aspect-ratio: 21 / 9; }

body[data-page="overview"] .tile:nth-child(9n + 1) { grid-column: span 6; }
body[data-page="overview"] .tile:nth-child(9n + 2),
body[data-page="overview"] .tile:nth-child(9n + 3) { grid-column: span 3; }
body[data-page="overview"] .tile:nth-child(9n + 4),
body[data-page="overview"] .tile:nth-child(9n + 5),
body[data-page="overview"] .tile:nth-child(9n + 6),
body[data-page="overview"] .tile:nth-child(9n + 7),
body[data-page="overview"] .tile:nth-child(9n + 8),
body[data-page="overview"] .tile:nth-child(9n + 9) { grid-column: span 2; }

body[data-page="overview"] .tile:nth-child(9n + 1) .tile__frame { aspect-ratio: 21 / 9; }
body[data-page="overview"] .tile:nth-child(9n + 2) .tile__frame,
body[data-page="overview"] .tile:nth-child(9n + 3) .tile__frame { aspect-ratio: 1 / 1; }
body[data-page="overview"] .tile:nth-child(9n + 9) .tile__frame { aspect-ratio: 4 / 5; }

@media (max-width: 1100px) {
  .grid { grid-template-columns: repeat(4, 1fr); }
  .tile,
  .tile:nth-child(9n + 1),
  .tile:nth-child(9n + 2),
  body[data-page="overview"] .tile:nth-child(9n + 2),
  body[data-page="overview"] .tile:nth-child(9n + 3) { grid-column: span 2; }
  .tile:nth-child(9n + 9),
  body[data-page="overview"] .tile:first-child,
  body[data-page="overview"] .tile:nth-child(9n + 1) { grid-column: span 4; }
}

@media (max-width: 700px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
  .tile,
  .tile:nth-child(9n + 1),
  .tile:nth-child(9n + 2),
  body[data-page="overview"] .tile:nth-child(9n + 2),
  body[data-page="overview"] .tile:nth-child(9n + 3) { grid-column: span 1; }
  .tile:nth-child(9n + 9),
  body[data-page="overview"] .tile:first-child,
  body[data-page="overview"] .tile:nth-child(9n + 1) { grid-column: span 2; }
}

@media (max-width: 520px) {
  .grid { grid-template-columns: 1fr; }
  .tile,
  .tile:nth-child(9n + 1),
  .tile:nth-child(9n + 2),
  .tile:nth-child(9n + 9),
  body[data-page="overview"] .tile:first-child,
  body[data-page="overview"] .tile:nth-child(9n + 1),
  body[data-page="overview"] .tile:nth-child(9n + 2),
  body[data-page="overview"] .tile:nth-child(9n + 3) { grid-column: span 1; }
  body[data-page="overview"] .tile:first-child .tile__frame,
  body[data-page="overview"] .tile:nth-child(9n + 1) .tile__frame { aspect-ratio: 16 / 9; }
}

.tile {
  margin: 0;
  /* Galleries run well past 50 items. Skips layout and paint for tiles that are
     nowhere near the viewport. contain-intrinsic-size reserves the space so the
     scrollbar stays honest. */
  content-visibility: auto;
  /* Two axes, not one. A single value sets BOTH width and height, which forces
     skipped tiles to a fixed width and breaks the grid on narrow screens.
     Width is left unconstrained so the grid track decides it. */
  contain-intrinsic-width: none;
  contain-intrinsic-height: auto 400px;
}

.tile__btn {
  display: block; width: 100%;
  padding: 0; border: 0; background: none;
  font: inherit; color: inherit; text-align: left;
  cursor: pointer;
}

/* Index tiles share one crop so the grid reads as a single field with no holes.
   Photographs keep their true proportions in the lightbox, which is where they
   are actually looked at. A ragged grid of mixed ratios reads as a bug. */
.tile__frame {
  display: block;
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;              /* the frame never moves, only the image inside */
  background: var(--line);
}


.tile:nth-child(9n + 1) .tile__frame,
.tile:nth-child(9n + 2) .tile__frame { aspect-ratio: 1 / 1; }

.tile:nth-child(9n + 9) .tile__frame { aspect-ratio: 12 / 5; }

@media (max-width: 700px) {
  .tile:nth-child(9n + 9) .tile__frame { aspect-ratio: 16 / 9; }
}
@media (max-width: 520px) {
  .tile:nth-child(9n + 9) .tile__frame { aspect-ratio: 4 / 5; }
}

.tile__img {
  width: 100%; height: 100%;
  object-fit: cover;
  background-size: cover;        /* the inlined LQIP sits here until load */
  background-position: center;
  transform: scale(1);
  transition:
    transform 1.05s var(--ease-soft),
    filter .7s var(--ease-out);
  will-change: transform;
}

.tile__meta {
  display: block;
  margin-top: 10px;
  font-size: .7rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
  overflow: hidden;
}

.tile__meta > span {
  display: block;
  transform: translateY(105%);
  opacity: 0;
  transition:
    transform .6s var(--ease-out),
    opacity .5s var(--ease-out);
}

/* ---- the spotlight -------------------------------------------------------
   Hovering one tile dims and desaturates the others. Applied from the grid so
   the effect is a property of the field, not of each tile. */

@media (hover: hover) and (pointer: fine) {
  .grid[data-hovering="true"] .tile {
    opacity: .45;
    filter: saturate(.4);
    transition: opacity .6s var(--ease-out), filter .6s var(--ease-out);
  }

  .grid[data-hovering="true"] .tile[data-hovered="true"] {
    opacity: 1;
    filter: none;
  }

  .tile__btn:hover .tile__img,
  .tile__btn:focus-visible .tile__img {
    transform: scale(1.045);
  }

  .tile__btn:hover .tile__meta > span,
  .tile__btn:focus-visible .tile__meta > span {
    transform: translateY(0);
    opacity: 1;
  }
}

/* Touch and keyboard-only users get the caption without needing hover. */
@media (hover: none), (pointer: coarse) {
  .tile__meta > span { transform: none; opacity: 1; }
}

/* ---- scroll reveal ----------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .tile,
  .reveal {
    opacity: 0;
    transform: translateY(28px);
    filter: blur(6px);
    transition:
      opacity .9s var(--ease-soft) var(--delay, 0ms),
      transform .9s var(--ease-soft) var(--delay, 0ms),
      filter .9s var(--ease-soft) var(--delay, 0ms);
  }

  .tile.is-in,
  .reveal.is-in {
    opacity: 1;
    transform: none;
    filter: none;
  }

  /* If JS fails, nothing stays invisible. */
  .no-js .tile,
  .no-js .reveal { opacity: 1; transform: none; filter: none; }
}

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

.lightbox {
  width: 100vw; max-width: 100vw;
  height: 100dvh; max-height: 100dvh;
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
  border: 0;
  background: transparent;
  overflow: hidden;
  overscroll-behavior: contain;   /* no scroll chaining to the page behind */
}

.lightbox::backdrop {
  background: var(--scrim);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.lightbox[open] {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  z-index: var(--z-lightbox);
}

.lightbox__stage {
  margin: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 16px;
  height: 100dvh;
  padding: 4dvh 0;
}

.lightbox__stage img {
  max-width: min(92vw, 1600px);
  max-height: 82dvh;
  width: auto; height: auto;
  object-fit: contain;
  opacity: 0;
  transform: scale(.985);
  transition: opacity .5s var(--ease-out), transform .7s var(--ease-soft);
}
.lightbox__stage img.is-loaded { opacity: 1; transform: none; }

.lightbox__caption {
  display: flex; align-items: baseline; gap: 18px;
  color: #d8d5cf;
  font-size: .72rem;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.lightbox__count {
  color: #86827b;
  font-variant-numeric: tabular-nums;  /* the counter must not jitter */
}

.lightbox__close,
.lightbox__nav {
  display: grid; place-items: center;
  width: 56px; height: 56px;
  border: 0; background: none;
  color: #e8e5df;
  cursor: pointer;
  transition: opacity .3s var(--ease-swift), transform .4s var(--ease-out);
  opacity: .6;
}
.lightbox__close:hover, .lightbox__nav:hover,
.lightbox__close:focus-visible, .lightbox__nav:focus-visible { opacity: 1; }

.lightbox__nav--prev:hover { transform: translateX(-3px); }
.lightbox__nav--next:hover { transform: translateX(3px); }

.lightbox__close {
  position: absolute; top: 18px; right: 18px;
  z-index: 2;
}

.lightbox svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor;
  stroke-width: 1.1; stroke-linecap: round; stroke-linejoin: round;
}

@media (max-width: 700px) {
  .lightbox[open] { grid-template-columns: 1fr; }
  .lightbox__nav { position: absolute; bottom: 16px; z-index: 2; }
  .lightbox__nav--prev { left: 18px; }
  .lightbox__nav--next { right: 18px; }
}

/* ----------------------------------------------------------- cursor label */

.cursor {
  position: fixed; top: 0; left: 0;
  z-index: var(--z-cursor);
  display: grid; place-items: center;
  width: 74px; height: 74px;
  margin: -37px 0 0 -37px;
  border-radius: 50%;
  background: var(--ink); color: var(--bg);
  font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  transform: scale(.4);
  transition: opacity .35s var(--ease-swift), transform .45s var(--ease-out);
}
.cursor[data-visible="true"] { opacity: 1; transform: scale(1); }

@media (hover: none), (pointer: coarse) { .cursor { display: none; } }
@media (prefers-reduced-motion: reduce) { .cursor { display: none; } }

/* ------------------------------------------------------------ prose pages */

.prose { max-width: 62ch; }
.prose p { color: var(--ink-2); margin: 1.35em 0; }
.prose .display { margin-bottom: .6em; }
.prose__cta { margin-top: 2.4em; }

.link {
  color: var(--ink);
  text-decoration: none;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-size: 0% 1px;
  background-position: 0 100%;
  transition: background-size .45s var(--ease-out);
  padding-bottom: 2px;
}
.link:hover, .link:focus-visible { background-size: 100% 1px; }

/* ------------------------------------------------------------------ forms */

.form { margin-top: 2.6em; display: grid; gap: 22px; max-width: 34rem; }

.field { display: grid; gap: 8px; }

.field label {
  font-size: .7rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-2);
}

.field input, .field textarea {
  font: inherit; color: var(--ink);
  padding: 12px 14px;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: 0;
  transition: border-color .3s var(--ease-swift);
}
.field input:focus, .field textarea:focus { border-color: var(--accent); }
.field textarea { resize: vertical; }

.field__err { margin: 0; color: var(--accent); font-size: .82rem; }

.btn {
  justify-self: start;
  display: inline-block;
  padding: 14px 30px;
  border: 1px solid var(--ink);
  background: var(--ink); color: var(--bg);
  font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  text-decoration: none; cursor: pointer;
  transition: transform .3s var(--ease-out), opacity .3s var(--ease-swift);
}
.btn:hover { opacity: .86; }
.btn:active { transform: translateY(1px); }

/* ------------------------------------------------------------------ empty */

.empty { max-width: 46ch; padding: clamp(40px, 8vw, 90px) 0; }
.empty__body { color: var(--ink-2); margin: 1.2em 0 2.4em; }

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

.footer {
  border-top: 1px solid var(--line);
  margin: 0 var(--margin);
  padding: 40px 0 60px;
  display: flex; flex-wrap: wrap; gap: 14px;
  align-items: baseline; justify-content: space-between;
}

.footer__name {
  margin: 0;
  font-family: var(--serif);
  font-size: .95rem; letter-spacing: .2em; text-transform: uppercase;
}

.footer__meta {
  margin: 0; display: flex; gap: 22px;
  font-size: .72rem; letter-spacing: .12em; text-transform: uppercase;
}
.footer__meta a { color: var(--ink-2); text-decoration: none; }
.footer__meta a:hover { color: var(--ink); }

/* -------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .tile, .reveal { opacity: 1 !important; transform: none !important; filter: none !important; }
  .tile__meta > span { transform: none; opacity: 1; }
  .grid[data-hovering="true"] .tile { opacity: 1; filter: none; }
}

/* =========================================================================
   Flair. Added after the first pass read as austere rather than restrained.
   Everything here is type, rhythm and surface: nothing competes with a
   photograph for attention, and nothing delays one appearing.
   ========================================================================= */

/* ---------------------------------------------------------- section head */

.pagehead {
  padding-bottom: clamp(18px, 3vw, 30px);
  border-bottom: 1px solid var(--line);
}

.pagehead__eyebrow {
  margin: 0 0 clamp(8px, 1.4vw, 14px);
  font-size: .66rem;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Larger than the old page title by a wide margin. A section heading on a
   photography site is a title page, not a label. */
.display--xl {
  font-size: clamp(2.6rem, 9vw, 6.5rem);
  line-height: .95;
  letter-spacing: -.01em;
}

.display--xl::first-letter { letter-spacing: .02em; }

/* ------------------------------------------------------------- sub-nav */

.subnav {
  display: flex; flex-wrap: wrap;
  gap: clamp(14px, 2.2vw, 30px);
  margin-top: clamp(18px, 3vw, 30px);
}

.subnav__link {
  position: relative;
  padding-bottom: 4px;
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-3);
  transition: color .4s var(--ease-out);
}

.subnav__link::after {
  content: "";
  position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .5s var(--ease-out);
}

.subnav__link:hover,
.subnav__link:focus-visible,
.subnav__link.is-current { color: var(--ink); }

.subnav__link:hover::after,
.subnav__link:focus-visible::after,
.subnav__link.is-current::after { transform: scaleX(1); }

/* ---------------------------------------------------------------- grain */

/* Fixed and pointer-events:none, per the performance rule: a noise layer on a
   scrolling container repaints continuously and wrecks mobile frame rates.
   Sits under the lightbox so it never sits on top of a photograph at full size. */
.grain {
  position: fixed; inset: 0;
  z-index: var(--z-grain);   /* under header, lightbox and cursor */
  pointer-events: none;
  opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark) { .grain { opacity: .05; } }

/* ------------------------------------------------------- tile refinement */

/* A hairline that draws itself under a hovered frame. Small, but it makes the
   grid feel handled rather than printed. */
.tile__frame::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .7s var(--ease-out);
  z-index: 2;
}

@media (hover: hover) and (pointer: fine) {
  .tile__btn:hover .tile__frame::after,
  .tile__btn:focus-visible .tile__frame::after { transform: scaleX(1); }
}

.tile__meta {
  display: flex; align-items: baseline; gap: 10px;
}

/* A rule that grows with the caption, so the row has a baseline to sit on. */
.tile__meta::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .8s var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .tile__btn:hover .tile__meta::after,
  .tile__btn:focus-visible .tile__meta::after { transform: scaleX(1); }
}

/* ------------------------------------------------------------- prose lift */

.prose .display { font-size: clamp(2.4rem, 7vw, 4.6rem); line-height: .98; }

.prose > p:first-of-type {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  color: var(--ink);
  line-height: 1.55;
}

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

.footer { padding-top: 56px; }

@media (prefers-reduced-motion: reduce) {
  .subnav__link::after,
  .tile__frame::after,
  .tile__meta::after { transition: none; }
  .subnav__link.is-current::after { transform: scaleX(1); }
}
