/* pmotor — the data-theme bundle of photomotor, John Clark's bespoke theme.
 *
 * This is an extraction of backend/api/templates/site/static/photomotor.css
 * into the theme.json bundle format (docs/runbooks/themes.md), not a
 * redesign — the design below is unchanged from the code theme it is drawn
 * from. It exists as a separate, additively-registered name (``pmotor``, not
 * ``photomotor``) so John can opt into it later without his live
 * ``photomotor`` theme changing under him; nothing here touches the code
 * theme or its registration.
 *
 * Ported from johnclark.photo's hand-written stylesheet: right-aligned
 * header, a 1140px measure, a four-up photo grid, and quiet blue-greys under
 * a condensed grot.
 *
 * Two deliberate departures from the source site:
 *
 * 1. The faces are Archivo Narrow and Archivo, not Trade Gothic Pro. Trade
 *    Gothic is a licensed MyFonts kit, so it cannot be one of the theme's
 *    own faces — those are served on wholeshoot.com hostnames too. John's
 *    real type arrives as uploaded faces, served only on his own domain
 *    under his own licence; a theme still has to render before he has
 *    uploaded any. Archivo Narrow is
 *    the closest freely-licensed match for Trade Gothic Condensed 18 —
 *    grotesque, narrow, and stable in uppercase at the letter-spacing this
 *    design leans on.
 *
 * 2. The greys are one step darker than the source. johnclark.photo sets body
 *    text at #b1b8c2 on white, which measures 2.0:1 — well under WCAG AA's
 *    4.5:1. The palette here keeps his hues and his relationships and moves
 *    them into range: --ink at 4.6:1, and his own hover colour promoted to
 *    the resting --accent at 7.5:1. This is what a *default* looks like
 *    when the theme has to be defensible for whoever renders it — and it is
 *    the whole palette now, since no artist-stored colour overrides it.
 *
 * --canvas/--ink/--accent/--font-display/--font-grot are the app's shared
 * skin vocabulary, the same five names frontend/src/lib/skin.ts writes onto a
 * branded gallery and base.html.j2 writes from SharedBranding. Everything
 * else below is photomotor's own and takes no skin override.
 */

:root {
  /* Canonical — shared vocabulary with the app's skin (see header comment). */
  --canvas: #ffffff;
  --ink: #6b7688;
  --accent: #48566a;
  --font-display: "Archivo Narrow", "Helvetica Neue", Helvetica, sans-serif;
  --font-grot: "Archivo", "Helvetica Neue", Helvetica, sans-serif;

  /* photomotor-only, no skin override. */
  --hover: #2b3543;
  --rule: #e3e6ea;
  --surface: #f6f7f9;
  --measure: 1140px;
  --gutter: 15px;
  --track: 0.1em;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}

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

html {
  overflow-y: scroll;
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font-grot);
  font-size: 16px;
  font-weight: 300;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img,
video {
  max-width: 100%;
  height: auto;
}

a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.3s ease;
}

a:hover {
  color: var(--hover);
}

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

h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 500;
  margin: 0;
}

p {
  margin: 0 0 1em;
}

.wrap {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--canvas);
  color: var(--accent);
  padding: 10px 14px;
  z-index: 10;
}

.skip-link:focus-visible {
  left: 0;
}

/* ── Header ──────────────────────────────────────────────────────────── */
/* Right-aligned, and that is the signature of this design: the wordmark,
   the contact line and the nav all rag right against the same edge the
   photo grid ends on. */

.site-header {
  max-width: var(--measure);
  margin: 28px auto 40px;
  padding: 8px var(--gutter) 0;
  font-size: 0.9rem;
  letter-spacing: var(--track);
  line-height: 1.65;
  text-align: right;
}

.wordmark {
  font-family: var(--font-display);
  display: inline-block;
  font-size: 1.6rem;
  font-weight: 500;
  margin: 0 0 4px;
  text-transform: uppercase;
  color: var(--accent);
}

.wordmark:hover {
  color: var(--hover);
}

.contact-info {
  list-style: none;
  margin: 0;
  padding: 0;
  text-transform: uppercase;
  color: var(--accent);
}

.contact-info li {
  display: inline-block;
  margin-left: 0.75em;
}

.contact-icons a {
  margin-left: 6px;
}

.icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
}

.site-nav {
  margin-top: 4px;
  font-family: var(--font-display);
  text-transform: lowercase;
}

.site-nav a {
  margin-left: 12px;
  letter-spacing: var(--track);
  padding-bottom: 3px;
  color: var(--ink);
}

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

/* The hero treatment aurea uses for a full-bleed home feature. photomotor
   keeps the header on the page rather than laying it over the image — the
   source site's home slideshow sits below the header, not behind it — so
   `over-hero` only softens the margin it would otherwise reserve. */
.site-header.over-hero {
  margin-bottom: 24px;
}

/* ── Page furniture ──────────────────────────────────────────────────── */

main {
  max-width: var(--measure);
  margin: 0 auto 64px;
  padding: 0 var(--gutter);
}

main.wrap {
  padding: 0 var(--gutter);
}

/* johnclark.photo prints no page heading — the nav already says where you
   are. page.html.j2 renders <h1 class="page-title"> on every page, so this
   theme takes it out of the picture visually and leaves it in the document
   for screen readers and search, the same treatment as .visually-hidden
   above. Not display:none, which would remove the page's one <h1>. */
.page-title {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.page-intro {
  text-align: right;
  max-width: 46em;
  margin-left: auto;
}

/* Display family, like every other uppercase-accent rule here (.wordmark,
   .blk-kv-list span b). It was inheriting --font-grot, which reads fine in
   the stand-in Archivo — a real 500 — and would have put the artist's *body*
   face at a synthesized 500 the moment he uploaded his own. His source site
   asks 500 of the condensed display face only. */
.section-title {
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  text-align: right;
  margin: 48px 0 16px;
}

.section-title::after {
  content: "";
  display: block;
  height: 1px;
  background: var(--rule);
  margin-top: 8px;
}

/* ── Hero (home's first feature block) ───────────────────────────────── */
/* The source site's home is a right-aligned slideshow capped at 641px tall,
   letterboxed rather than cropped. A single feature block is that shape with
   the crossfade removed. */

.hero {
  display: grid;
  text-align: right;
  margin-bottom: 40px;
}

.hero img,
.hero video {
  width: 100%;
  max-height: 641px;
  object-fit: contain;
  object-position: right top;
}

.hero-name {
  text-align: right;
}

.hero-name h1 {
  font-size: 1.6rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  margin: 12px 0 0;
}

.hero-name p {
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  margin: 2px 0 0;
}

/* ── feature ─────────────────────────────────────────────────────────── */

.blk-feature {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  margin: 0 0 40px;
}

.blk-feature-media img {
  display: block;
  width: 100%;
}

/* 16/9 is only the fallback for when the resolved video carries no known
   dimensions — `_feature.j2` writes `style="aspect-ratio: W / H"` from
   `ResolvedVideo.width`/`.height` when they're known, and inline wins over
   this rule. `object-fit: contain` on the video below is what stops a
   square or vertical clip being cropped inside either ratio. */
.blk-feature-media.frame {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--surface);
}

.blk-feature-media.frame video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.blk-feature-copy {
  text-align: right;
}

.blk-feature-copy h2 {
  font-size: 1.2rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  margin: 0 0 4px;
}

.blk-feature-copy .meta {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  margin: 0;
}

/* ── entry ───────────────────────────────────────────────────────────── */

.entry-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 2em 30px;
  margin: 0 0 40px;
}

.entry-item h3 {
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  margin: 0 0 2px;
}

.entry-item .when {
  display: block;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  margin-bottom: 6px;
}

.entry-item p {
  margin: 0 0 0.5em;
}

.more-link {
  font-family: var(--font-display);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
}

/* ── quote ───────────────────────────────────────────────────────────── */

blockquote.blk-quote {
  margin: 0 0 32px;
  padding: 0;
  text-align: right;
}

blockquote.blk-quote .quote {
  font-family: var(--font-display);
  font-size: 1.15rem;
  line-height: 1.45;
  color: var(--accent);
  margin: 0 0 6px;
}

blockquote.blk-quote cite {
  display: block;
  font-size: 0.85rem;
  font-style: normal;
  text-transform: uppercase;
  letter-spacing: var(--track);
}

blockquote.blk-quote cite i {
  font-style: italic;
}

/* ── kv_list ─────────────────────────────────────────────────────────── */

.blk-kv-list {
  list-style: none;
  margin: 0 0 32px;
  padding: 0;
  text-align: right;
  font-size: 0.9rem;
  letter-spacing: var(--track);
}

.blk-kv-list li {
  padding: 6px 0;
  border-bottom: 1px solid var(--rule);
}

.blk-kv-list span b {
  font-family: var(--font-display);
  font-weight: 500;
  text-transform: uppercase;
  color: var(--accent);
  margin-right: 0.6em;
}

.blk-kv-list .aside {
  display: block;
  font-size: 0.85rem;
}

/* ── table ───────────────────────────────────────────────────────────── */

table.blk-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 32px;
  font-size: 0.9rem;
}

table.blk-table td {
  padding: 8px 12px 8px 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

table.blk-table td:first-child {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  white-space: nowrap;
}

table.blk-table td:last-child {
  padding-right: 0;
  text-align: right;
}

/* ── file ────────────────────────────────────────────────────────────── */

.blk-file-btn {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  border: 1px solid var(--rule);
  padding: 8px 16px;
  margin-bottom: 32px;
}

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

.updated-note {
  font-size: 0.85rem;
  letter-spacing: var(--track);
}

.updated-note a {
  color: var(--accent);
}

/* ── text ────────────────────────────────────────────────────────────── */

.blk-text {
  max-width: 46em;
  margin: 0 0 32px auto;
  text-align: right;
}

.blk-text a {
  border-bottom: 1px solid var(--rule);
}

/* ── media: video ────────────────────────────────────────────────────── */
/* One column, ported from johnclark.photo/motion/ — every video there is a
   `.col-12 video-box`, one per row, with 2em under it. A film is not a
   contact sheet: half a measure is not enough frame to watch one in, and
   the source has never put two side by side. So there is no column count
   to step down at a breakpoint either.

   The frame matches the source's own player placement too: a 640px
   measure, flush against the grid's right edge (`margin-left: auto`, the
   same trick `nav[aria-label="Item"]` uses further down to pin a lone
   link), `max-width: 100%` so a narrow viewport gets the full column
   instead of an overflow. */

.video-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2em;
  margin: 0 0 40px;
}

/* Every video in the column shares one height; width follows each clip's
   own aspect ratio, so a 16:9 reel is wide and a square upload is not
   stretched to match it. Height, not width, is now the fixed dimension:
   `height` is set and `width` is left `auto`, so the browser derives width
   from `aspect-ratio` and the used height — the same mechanism as an `img`
   with a height and no width, just spelled out for a non-replaced element.

   Ratio comes from the resolved media, not a fixed guess: `_media.j2`'s
   hosted branch writes `style="aspect-ratio: W / H"` from the item's own
   width/height when they are known (`ResolvedVideo.width`/`.height`,
   site_renderer.py), so a square upload gets a square frame and
   `object-fit: contain` on the `<video>` below shows it with no letterbox.
   The 16/9 here is only the fallback for when they are not known — which is
   a video transcoded before 2026-08-23 (no dimensions on its Media
   record) — and it is also what an unembeddable YouTube/Vimeo facade or
   iframe below renders at, since those never carry a resolved ratio.

   624px is the live site's own frame height. Every clip shares it, and the
   ratio decides the width: a 16:9 clip comes out 1109px — the whole 1110
   column, which reads as centered — while a square clip is 624×624, flush
   right. `min(624px, calc((100vw - 30px) * 0.5625))` holds 624 while the
   column (1140 max, 15px padding each side) can fit a 16:9 clip, and below
   that scales the height with the viewport so the widest ratio always
   fits without bars. `max-width: 100%` is the backstop
   for a wider-than-16:9 clip, whose derived width could still overrun the
   column; if it clamps, the video keeps its fixed height and letterboxes
   inside the narrower frame via `object-fit: contain` below, rather than
   spilling past the grid's right edge. */
.video-item .frame {
  position: relative;
  display: block;
  width: auto;
  max-width: 100%;
  height: min(624px, calc((100vw - 30px) * 0.5625));
  margin-left: auto;
  aspect-ratio: 16 / 9;
  background: var(--surface);
}

.video-item video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The click-to-play facade (poster + play button, swapped for a real
   iframe by facade.js) and a directly-rendered embed both land inside
   `.frame` too — the facade's `<a>` itself carries `class="frame facade"`
   (_media.j2), so the sizing above already applies to it before a click.
   These two only need to fill the box the rule above already drew. */
.video-item .frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.video-item .frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Sizing/position are already covered by `.video-item .frame` above — this
   element always carries both classes (_media.j2: `class="frame facade"`).
   Declared again here only so this rule reads complete on its own. */
a.facade {
  position: relative;
  display: block;
}

a.facade .play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

a.facade .play::after {
  content: "";
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 13px 0 13px 22px;
  border-color: transparent transparent transparent var(--canvas);
  filter: drop-shadow(0 1px 3px rgb(0 0 0 / 45%));
  transition: transform 0.3s var(--ease-out);
}

a.facade:hover .play::after {
  transform: scale(1.12);
}

/* The caption on a motion page: under its frame and hung off the right, the
   source site's `.list-video-comments`. Sentence case rather than this
   theme's usual uppercase, because these read as sentences ("Eyelids
   Accidental Falls record release teaser no 1") and the source sets them as
   typed. Its tracking is looser than the theme's --track for the same
   reason — uppercase needs the air, a sentence does not. */
.video-item h3 {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 400;
  letter-spacing: 0.05em;
  text-align: right;
  color: var(--ink);
  margin: 8px 0 0;
}

.video-item .meta {
  font-size: 0.85rem;
  letter-spacing: var(--track);
  text-align: right;
  margin: 2px 0 0;
}

.external-watch h3 {
  font-family: var(--font-display);
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  margin: 8px 0 0;
}

/* Same measure and right edge as `.video-item .frame` above, so an
   unembeddable link (iSpot.tv, say) reads as one more card in the same
   right-aligned column instead of a full-width outlier — `text-align:
   right` here is what the h3/p/a rules around it inherit. */
.external-watch {
  width: 640px;
  max-width: 100%;
  margin-left: auto;
  border: 1px solid var(--rule);
  padding: 16px;
  text-align: right;
}

.external-watch p {
  font-size: 0.85rem;
  margin: 4px 0 8px;
}

.external-watch a {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: var(--track);
}

/* ── media: photo grids ──────────────────────────────────────────────── */
/* Four up, stepping to three, two and one — the source site's isotope grid
   without isotope. `grid`, one of this theme's two arrangements
   (site_templates.PHOTOMOTOR_SITE.layouts) and the default a section
   renders as untouched — every page but home uses it. Frames keep their
   own proportions; nothing is cropped. */

.photo-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2em 30px;
  list-style: none;
  margin: 0 0 40px;
  padding: 0;
}

.photo-grid img {
  display: block;
  width: 100%;
  transition: opacity 0.3s ease;
}

.photo-grid a {
  display: block;
}

.photo-grid a:hover img {
  opacity: 0.85;
}

.photo-grid figure {
  margin: 0;
}

/* A contact sheet carries no captions. The source site names a frame on the
   frame's own page and nowhere else, and four columns of uppercase captions
   is the thing that stops a grid of photographs reading as photographs. The
   text stays in the markup and comes back below 576px — one column there, and
   the caption is then the only thing naming what you are looking at. The
   source does exactly this with `.list-photo-comments`. */
.photo-grid figcaption {
  display: none;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  margin-top: 6px;
}

/* ── media: slideshow ────────────────────────────────────────────── */
/* photomotor's other arrangement, and home's only one: ported from
   johnclark.photo's #slideshow, rule for rule. Every figure sits in the
   same grid cell (`grid-area: 1 / 1`), so the stage is as tall as the
   tallest frame and nothing is positioned out of flow; only `.active`
   (written by _media.j2 on the first, moved by slideshow.js) is opaque,
   and the transition between them is what crossfades.

   It crops nothing — `contain` under a 641px ceiling, hung off the right
   like the source. A slideshow shows one photo at a time, so there is no
   row to line up and no reason to force a shared frame; a fixed landscape
   stage would crop every portrait frame put through it. Photos keep their
   own proportions here exactly as they do in `.photo-grid` above.

   `prefers-reduced-motion` already kills the transition (this file's
   global rule, further down); slideshow.js is what stops the interval that
   would still swap `.active` without it. */

.slideshow {
  display: grid;
  text-align: right;
  margin: 0 0 40px;
}

.slideshow figure {
  grid-area: 1 / 1;
  margin: 0;
  opacity: 0;
  transition: opacity 1s var(--ease-out);
}

.slideshow figure.active {
  opacity: 1;
}

.slideshow a,
.slideshow picture {
  display: block;
}

.slideshow img {
  display: block;
  width: 100%;
  max-height: 641px;
  object-fit: contain;
  object-position: right top;
}

/* The live home page carries no caption at all, at any width — unlike the
   contact sheet's `.list-photo-comments`, which comes back at 575px because
   it is the only thing naming a frame once the grid drops to one column.
   The slideshow has no such need (there is exactly one photo on screen
   either way), so this stays hidden everywhere rather than reappearing
   below 576px. The text stays in the markup (and as each `<img>`'s alt)
   for anyone not reading it visually. */
.slideshow figcaption {
  display: none;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  margin-top: 6px;
}

/* ── item-detail pages ───────────────────────────────────────────────── */
/* One photograph on the measure, its caption and its position under it and
   hung off the right — johnclark.photo's detail page, rule for rule. Its
   image ceiling steps 641 → 575 → 440 with the container (the Responsive
   section below) and then gives the ceiling up entirely on a phone, where a
   641px cap on a 375px-wide frame would only be letterboxing.

   `contain` under that ceiling, not `cover`: a detail page is the one place
   a photograph is shown whole, so nothing here crops. Portrait frames stay
   portrait and simply reach the ceiling sooner. */

.photo-detail {
  margin: 0 0 40px;
  text-align: right;
}

.photo-detail a,
.photo-detail picture {
  display: block;
}

.photo-detail img {
  display: block;
  width: 100%;
  max-height: 641px;
  object-fit: contain;
  object-position: right top;
}

/* Inset from the image's right edge by the source's 14px, which is what
   keeps the caption from reading as part of the frame. */
.photo-detail figcaption {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  margin-top: 8px;
  padding-right: 14px;
}

/* "2 / 34". Secondary to the caption by size and tracking rather than by a
   paler ink — the source's own #b1b8c2 is below AA on white, and this
   theme's whole palette exists because those greys were promoted into range
   (see the file header). Losing that here to match a hex would undo it. */
.photo-count {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  text-align: right;
  margin: 4px 0 0;
  padding-right: 14px;
}

/* Prev/next. The source has no text links at all here — it navigates by two
   invisible half-width click zones with arrow cursors, which is a gesture a
   keyboard cannot reach and a screen reader cannot announce. This theme
   keeps the named links and sets them in its own vocabulary instead: a rule
   above them, one at each end. `margin-left: auto` is what pins a lone
   "next →" to the right when there is no "← prev" for space-between to push
   it away from. */
nav[aria-label="Item"] {
  display: flex;
  justify-content: space-between;
  gap: 30px;
  margin: 24px 0 40px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
  font-size: 0.85rem;
}

nav[aria-label="Item"] a {
  margin-left: 0;
  color: var(--accent);
}

nav[aria-label="Item"] a:last-child {
  margin-left: auto;
}

/* ── galleries listing ───────────────────────────────────────────────── */

.blk-galleries {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2em 30px;
  list-style: none;
  margin: 0 0 40px;
  padding: 0;
}

.blk-galleries img {
  display: block;
  width: 100%;
}

.blk-galleries h3 {
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  margin: 8px 0 0;
}

/* ── Detail pages ────────────────────────────────────────────────────── */
/* Right-aligned and height-capped, matching the source site's photo page. */

.photo-page {
  text-align: right;
}

.photo,
.photo-page img,
.photo-page video {
  width: auto;
  max-width: 100%;
  max-height: 641px;
  margin-left: auto;
}

.photo-caption,
.photo-count {
  font-family: var(--font-display);
  letter-spacing: var(--track);
  text-transform: uppercase;
  font-size: 0.85rem;
  margin: 8px 0 0;
  color: var(--accent);
}

.photo-count {
  color: var(--ink);
  margin-top: 2px;
}

nav[aria-label="Item"] {
  display: flex;
  justify-content: flex-end;
  gap: 16px;
  font-family: var(--font-display);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  margin-top: 16px;
}

/* ── Footer ──────────────────────────────────────────────────────────── */
/* The source site has no footer — its contact details live in the header,
   which photomotor's header partial reproduces. What is here is the quiet
   restatement a published page still needs, and the colophon. */

.site-footer {
  border-top: 1px solid var(--rule);
  padding: 32px 0 48px;
  font-size: 0.85rem;
  letter-spacing: var(--track);
}

.site-footer .wrap {
  text-align: right;
}

.footer-cols {
  display: flex;
  justify-content: flex-end;
  gap: 48px;
}

.footer-cols h3 {
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--track);
  color: var(--accent);
  margin: 0 0 6px;
}

.footer-cols p {
  margin: 0 0 2px;
}

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

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

.colophon {
  margin: 24px 0 0;
  text-transform: uppercase;
}

.site-footer.slim .footer-cols {
  display: none;
}

.site-footer.slim .colophon {
  margin-top: 0;
}

/* ── Responsive ──────────────────────────────────────────────────────── */

/* The detail image's ceiling tracks the container, exactly as the source's
   `img#large-image` does: 641 at the full measure, then 575, then 440. */
@media (max-width: 1199px) {
  .photo-detail img {
    max-height: 575px;
  }
}

@media (max-width: 991px) {
  .photo-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  .blk-galleries {
    grid-template-columns: repeat(2, 1fr);
  }

  .photo-detail img {
    max-height: 440px;
  }
}

@media (max-width: 767px) {
  .photo-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .entry-grid {
    grid-template-columns: 1fr;
  }

  .photo,
  .photo-page img,
  .photo-page video {
    width: 100%;
    max-height: none;
  }

  /* No ceiling on a phone: the frame is narrower than any of them, so one
     would only letterbox the photograph it was meant to contain. */
  .photo-detail img {
    height: auto;
    max-height: none;
  }
}

@media (max-width: 575px) {
  .photo-grid,
  .blk-galleries {
    grid-template-columns: 1fr;
  }

  /* One column: the caption is now the only thing naming the frame, so it
     comes back. See `.photo-grid figcaption` above. */
  .photo-grid figcaption {
    display: block;
  }

  .footer-cols {
    flex-direction: column;
    gap: 20px;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}
