/* ============================================================
   DANIEL AKSELROD · PHOTOGRAPHY
   A combined world: the landing, type treatment and bordered
   form panel come from Monochrome Designer Showcase; the top
   nav, left-hand category filter and works index come from
   Game Portfolio Showcase. One charcoal ground, no accent hue:
   the photographs carry all the colour on the page.
   ============================================================ */

@font-face {
  font-family: 'Archivo';
  src: url('../fonts/archivo-var.woff2') format('woff2-variations');
  font-weight: 300 800;
  font-stretch: 62% 125%;
  font-display: swap;
}
@font-face {
  font-family: 'Sometype Mono';
  src: url('../fonts/sometype-mono-400.woff2') format('woff2');
  font-weight: 400; font-display: swap;
}
@font-face {
  font-family: 'Sometype Mono';
  src: url('../fonts/sometype-mono-500.woff2') format('woff2');
  font-weight: 500; font-display: swap;
}

:root {
  --bg:        #0c0c0e;
  --bg-2:      #111113;   /* panels, cards */
  --bg-3:      #17171a;   /* image wells */
  --line:      #232327;
  --line-soft: #1b1b1f;
  --fg:        #ffffff;
  --fg-2:      #c3c3c9;   /* body prose, 12.1:1 */
  --fg-3:      #9a9aa2;   /* muted labels, 7.3:1 */
  --fg-4:      #82828c;   /* resting nav + placeholders, 5.1:1 on --bg */

  /* The one accent. Sampled from the blue in his own cars, then pushed
     off the cyan his photographs already own so it reads as interface
     rather than as part of a picture. 7.4:1 on the ground. */
  --accent:     #6ba3e0;
  --accent-dim: rgba(107, 163, 224, .34);

  --sans: 'Archivo', system-ui, -apple-system, sans-serif;
  --mono: 'Sometype Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --ease: cubic-bezier(.16, 1, .3, 1);
  /* For the two surfaces that travel rather than fade: the consent bar and
     the mobile menu. Flatter through the middle than --ease, so a panel the
     size of a hand reads as sliding instead of snapping into place. */
  --ease-drawer: cubic-bezier(.32, .72, 0, 1);

  --pad:  clamp(18px, 3.4vw, 46px);
  --rail: 148px;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }

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

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

.skip {
  position: fixed; left: 10px; top: -60px; z-index: 300;
  background: var(--fg); color: var(--bg); padding: 10px 16px;
  font: 500 12px/1 var(--mono); letter-spacing: .06em;
  transition: top .18s var(--ease);
}
.skip:focus { top: 10px; }

/* the film grain that sits under the whole site */
.grain {
  position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 0; opacity: .42;
}
/* Lift the page above the grain. This names the three structural elements
   rather than using `body > *:not(.grain)`, whose specificity
   beat the `position: fixed` on the skip link, the frame viewer and the
   consent bar, and dropped all three into normal flow at the foot of the
   document, where nothing could see them. */
.nav, main, .foot { position: relative; z-index: 1; }

/* --- shared type ------------------------------------------- */
.label {
  font-family: var(--sans); font-weight: 500;
  font-size: clamp(10px, 1.05vw, 11.5px);
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--fg-3);
}
/* mono is reserved for measurement: counts, EXIF, dates, character limits */
.meas {
  font-family: var(--mono); font-weight: 500;
  font-size: clamp(9.5px, 1vw, 11px);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg-3);
}
.display {
  font-stretch: 116%; font-weight: 600;
  text-transform: uppercase; letter-spacing: .015em; line-height: 1;
}
.prose { color: var(--fg-2); max-width: 68ch; }
.prose p { margin: 0 0 1.15em; }
.prose p:last-child { margin-bottom: 0; }

/* ============================================================
   TOP NAV  (Game Portfolio Showcase)
   ============================================================ */

.nav {
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: 20px;
  padding: 20px var(--pad);
  border-bottom: 1px solid var(--line-soft);
}
.nav .brand {
  font-stretch: 116%; font-weight: 700; font-size: 18px;
  letter-spacing: .04em; text-transform: uppercase;
}
.nav .menu { display: flex; gap: clamp(16px, 2.6vw, 38px); font-size: 13.5px; }
.nav .menu a { color: var(--fg-4); transition: color .18s var(--ease); }
.nav .menu a:hover { color: var(--fg); }
.nav .menu a[aria-current='page'] { color: var(--accent); font-weight: 600; }
.nav .end { justify-self: end; display: flex; align-items: center; gap: 16px; }
.nav .end a { color: var(--fg-4); transition: color .18s var(--ease); }
.nav .end a:hover { color: var(--fg); }

.burger {
  display: none; background: none; border: 0; padding: 6px; cursor: pointer;
  color: var(--fg); transition: color .18s var(--ease);
}
/* The icon is one path of three strokes, so it cannot morph into a cross
   without new markup on seven pages. Colour carries the state instead. */
.burger[aria-expanded='true'] { color: var(--accent); }
.burger svg { display: block; }

@media (max-width: 780px) {
  /* The menu is row two of the nav grid. Taking that row from 0fr to 1fr
     opens it to exactly its content height with nothing measured in JS —
     `overflow: hidden` on the menu is what lets the row collapse below
     min-content. `visibility` keeps the links out of the tab order while
     the menu is shut, and is delayed on the way out so they do not blink
     off mid-slide. */
  .nav {
    grid-template-columns: auto 1fr;
    grid-template-rows: auto 0fr;
    row-gap: 0;
    transition: grid-template-rows .3s var(--ease-drawer);
  }
  .nav.open { grid-template-rows: auto 1fr; }
  /* Placed explicitly. The menu is a grid item at every width now, and left
     to auto-placement it takes row two and shunts the icons down to a row
     three of their own, away from the wordmark. */
  .nav .brand { grid-area: 1 / 1; }
  .nav .menu {
    grid-area: 2 / 1 / 3 / -1;
    display: flex; flex-direction: column; gap: 2px;
    /* The nav centres its items, and a centred item keeps its content height
       and ignores the row — so the links would sit there full size while the
       row grew around them. Stretching is what hands the height back to the
       row and gives `overflow: hidden` something to clip. */
    align-self: stretch;
    min-height: 0; overflow: hidden;
    visibility: hidden;
    transition: visibility 0s linear .3s;
  }
  .nav.open .menu { visibility: visible; transition: visibility 0s; }
  .nav .menu a { padding: 9px 0; font-size: 15px; }
  /* Breathing room lives on the links, not on the menu box: padding on a
     collapsing grid item survives the collapse and leaves a gap. */
  .nav .menu a:first-child { padding-top: 18px; }
  .nav .menu a:last-child { padding-bottom: 4px; }
  .nav .end { grid-area: 1 / 2; }
  .burger { display: block; }
}

/* ============================================================
   LANDING HERO  (Monochrome Designer Showcase)
   ============================================================ */

.hero {
  min-height: min(calc(100svh - 66px), 720px);
  display: grid; place-items: center;
  text-align: center; padding: clamp(40px, 8vw, 90px) var(--pad);
}
.hero h1 {
  margin: 0;
  font-size: clamp(30px, 6.2vw, 86px);
  font-stretch: 116%; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; line-height: 1.02;
  text-wrap: balance;
}
.hero .role { margin: clamp(18px, 2.4vw, 30px) 0 0; }
.hero .scroll {
  margin-top: clamp(30px, 5vw, 58px);
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--fg-3); transition: color .18s var(--ease);
}
.hero .scroll:hover { color: var(--accent); }
.hero .scroll svg { animation: nudge 2.4s var(--ease) infinite; }
@keyframes nudge { 0%,100% { transform: translateY(0) } 50% { transform: translateY(4px) } }

/* the two fixed marks that frame the landing */
.social {
  position: fixed; left: var(--pad); bottom: 26px; z-index: 40;
  display: flex; flex-direction: column; gap: 14px;
}
.social a { color: var(--fg-4); transition: color .18s var(--ease); }
.social a:hover { color: var(--fg); }
/* Both marks belong to the landing, not to the page. main.js retires them
   once the hero has left, or they trail the reader down over the work and
   end up sitting on top of the footer — and the one that says SCROLL is
   still saying it at the bottom of the document. `visibility` is delayed
   out so the two links leave the tab order only once they have faded. */
.social, .edge {
  transition: opacity .35s var(--ease), visibility 0s linear 0s;
}
.social[data-gone], .edge[data-gone] {
  opacity: 0; visibility: hidden;
  transition: opacity .35s var(--ease), visibility 0s linear .35s;
}
.edge {
  position: fixed; right: 18px; top: 50%; z-index: 40;
  transform: translateY(-50%) rotate(180deg); writing-mode: vertical-rl;
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  letter-spacing: .3em; text-transform: uppercase; color: var(--fg-4);
}
@media (max-width: 860px) { .social, .edge { display: none; } }
@media (max-width: 780px) { .hero { min-height: 74svh; } }

/* ============================================================
   SECTIONS
   ============================================================ */

.band { padding: clamp(54px, 8vw, 118px) var(--pad); }
.band + .band { border-top: 1px solid var(--line-soft); }

.band-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 18px; flex-wrap: wrap; margin-bottom: clamp(26px, 4vw, 52px);
}
.band-head h2 {
  margin: 0; font-size: clamp(20px, 3.2vw, 38px);
  font-stretch: 116%; font-weight: 600;
  text-transform: uppercase; letter-spacing: .015em; line-height: 1.05;
}
.band-head .more { color: var(--fg-3); font-size: 13px; transition: color .18s var(--ease); }
.band-head .more:hover { color: var(--accent); }

/* --- featured trio: three shoots, middle one raised --------- */
.trio {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(14px, 2.4vw, 32px); align-items: end;
}
.trio .shoot:nth-child(2) { transform: translateY(-34px); }
@media (max-width: 900px) {
  .trio { grid-template-columns: 1fr; }
  .trio .shoot:nth-child(2) { transform: none; }
}

/* --- a shoot card ------------------------------------------ */
.shoot { display: block; }
.shoot .well {
  aspect-ratio: 3 / 4; overflow: hidden; background: var(--bg-3);
  border: 1px solid var(--line-soft);
}
.shoot .well img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .85s var(--ease), filter .5s var(--ease);
}
.shoot:hover .well img, .shoot:focus-visible .well img { transform: scale(1.045); }
.shoot .meta { padding-top: 14px; display: flex; justify-content: space-between; gap: 14px; }
.shoot h3 {
  margin: 0 0 4px; font-size: clamp(15px, 1.7vw, 19px);
  font-weight: 500; letter-spacing: -.005em;
}
.shoot .where { margin: 0; color: var(--fg-3); font-size: 13px; }
.shoot .n {
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  color: var(--accent); white-space: nowrap; font-variant-numeric: tabular-nums;
}

/* ============================================================
   WORKS INDEX  (Game Portfolio Showcase)
   ============================================================ */

.works { display: grid; grid-template-columns: var(--rail) minmax(0, 1fr); }
.works .rail {
  border-right: 1px solid var(--line-soft);
  padding: clamp(30px, 5vw, 60px) 0 clamp(30px, 5vw, 60px) var(--pad);
}
.works .rail .sticky { position: sticky; top: 28px; display: flex; flex-direction: column; gap: 13px; }
.works .rail .label { margin-bottom: 6px; }
.filters { display: flex; flex-direction: column; gap: 13px; align-items: flex-start; }
.filters button {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--sans); font-size: 13.5px; color: var(--fg-4);
  transition: color .18s var(--ease);
}
.filters button:hover { color: var(--fg-2); }
.filters button[aria-pressed='true'] { color: var(--accent); font-weight: 600; }
.filters .c {
  font-family: var(--mono); font-size: 10.5px; margin-left: 7px; color: var(--fg-4);
  font-variant-numeric: tabular-nums;
}

.works .grid-wrap {
  padding: clamp(30px, 5vw, 60px) 0 clamp(30px, 5vw, 60px) var(--pad);
  min-width: 0;
}

/* The work reads as one continuous rail you push sideways, divided by
   hairlines, exactly as the reference does it. The last card is padded
   so the rail bleeds off the right edge instead of stopping dead, which
   is the affordance that says there is more. */
.shoot-grid {
  display: flex;
  gap: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  padding: 2px var(--pad) 14px 0;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.shoot-grid::-webkit-scrollbar { height: 3px; }
.shoot-grid::-webkit-scrollbar-track { background: transparent; }
.shoot-grid::-webkit-scrollbar-thumb { background: var(--line); }
.shoot-grid:hover::-webkit-scrollbar-thumb { background: var(--fg-4); }

.shoot-grid .shoot {
  flex: 0 0 clamp(216px, 24vw, 320px);
  padding: 0 clamp(16px, 2vw, 30px);
  border-left: 1px solid var(--line-soft);
  scroll-snap-align: start;
}
.shoot-grid .shoot[hidden] { display: none; }

/* A filter change brings cards in that were not there a moment ago. They
   rise in rather than appearing already placed, staggered in main.js so the
   rail fills left to right the way it reads. Transitions, not keyframes:
   hammering the four buttons retargets instead of restarting. */
.shoot-grid .shoot {
  transition: opacity .34s var(--ease), transform .34s var(--ease);
}
.shoot-grid .shoot[data-enter] { opacity: 0; transform: translateY(12px); }

.rail-hint {
  display: flex; align-items: center; gap: 9px;
  margin-top: 16px; padding-right: var(--pad);
  color: var(--fg-4);
  transition: opacity .25s var(--ease);
}
.rail-hint svg { flex: none; }

.empty { color: var(--fg-3); font-size: 14px; padding: 20px var(--pad) 20px 0; }

@media (max-width: 860px) {
  .works { grid-template-columns: 1fr; }
  .works .rail {
    border-right: 0; border-bottom: 1px solid var(--line-soft);
    padding: 20px var(--pad);
  }
  .works .rail .sticky { position: static; }
  .filters { flex-direction: row; flex-wrap: wrap; gap: 8px 20px; }
  .works .grid-wrap { padding-left: var(--pad); }
  .shoot-grid .shoot { flex-basis: clamp(200px, 62vw, 260px); }
}

/* ============================================================
   SHOOT DETAIL
   ============================================================ */

.shoot-hero { position: relative; }
.shoot-hero img {
  width: 100%; height: clamp(42svh, 60svh, 74svh); object-fit: cover;
}
/* On a phone a tall band crops a landscape frame to a sliver, so the band
   gets shallower and the whole car survives. */
@media (max-width: 780px) {
  .shoot-hero img { height: clamp(30svh, 42svh, 50svh); }
}
.shoot-hero .cap {
  position: absolute; inset: auto 0 0 0; padding: clamp(24px, 5vw, 56px) var(--pad) clamp(18px, 3vw, 34px);
  background: linear-gradient(to top, rgba(8,8,10,.9), rgba(8,8,10,0));
}
.shoot-hero h1 {
  margin: 0; font-size: clamp(26px, 5.4vw, 62px);
  font-stretch: 116%; font-weight: 600;
  text-transform: uppercase; letter-spacing: .015em; line-height: 1;
}
.shoot-hero .sub { margin: 12px 0 0; }

.crumb { padding: 16px var(--pad); border-bottom: 1px solid var(--line-soft); }
.crumb a { color: var(--fg-3); font-size: 13px; }
.crumb a:hover { color: var(--accent); }

.plates {
  display: grid; grid-template-columns: repeat(12, 1fr);
  grid-auto-rows: clamp(74px, 8.4vw, 122px);
  gap: clamp(10px, 1.6vw, 20px);
  padding: clamp(28px, 5vw, 62px) var(--pad);
}
.plate {
  grid-column: span var(--c, 6); grid-row: span var(--r, 3);
  position: relative; overflow: hidden; background: var(--bg-3);
  border: 0; padding: 0; margin: 0; cursor: zoom-in; display: block; width: 100%;
}
.plate img { width: 100%; height: 100%; object-fit: cover; transition: transform .85s var(--ease); }
.plate:hover img, .plate:focus-visible img { transform: scale(1.035); }
.plate .rec {
  position: absolute; inset: auto 0 0 0; padding: 22px 12px 10px;
  background: linear-gradient(to top, rgba(8,8,10,.82), transparent);
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: .1em; text-transform: uppercase; color: var(--fg-2);
  opacity: 0; transform: translateY(5px);
  transition: opacity .28s var(--ease), transform .28s var(--ease);
}
.plate:hover .rec, .plate:focus-visible .rec { opacity: 1; transform: none; }
@media (hover: none) { .plate .rec { opacity: 1; transform: none; } }

@media (max-width: 780px) {
  .plates { grid-auto-rows: clamp(62px, 13vw, 104px); }
  .plate { grid-column: span min(12, calc(var(--c) * 1.6)); }
}
@media (max-width: 500px) {
  .plate { grid-column: span 12; }
}

.pager {
  display: flex; justify-content: space-between; gap: 18px; flex-wrap: wrap;
  padding: clamp(26px, 4vw, 52px) var(--pad) clamp(50px, 7vw, 90px);
  border-top: 1px solid var(--line-soft);
}
.pager a { display: grid; gap: 5px; max-width: 46%; }
.pager .dir { font-family: var(--mono); font-size: 10px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--fg-4); }
.pager .nm { font-size: clamp(15px, 2vw, 21px); font-weight: 500; }
.pager a:hover .nm { color: var(--accent); text-decoration: underline; text-underline-offset: 4px; }
.pager .next { text-align: right; }

/* ============================================================
   ABOUT
   ============================================================ */

.about { display: grid; grid-template-columns: 1fr; gap: clamp(28px, 5vw, 70px); }
@media (min-width: 900px) { .about { grid-template-columns: 0.85fr 1fr; align-items: start; } }
.about .portrait { border: 1px solid var(--line-soft); overflow: hidden; background: var(--bg-3); }
/* height:auto is load-bearing. The img height attribute is a presentational
   hint that otherwise beats aspect-ratio and blows the box out to 4128px */
.about .portrait img { width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; }
.about .prose { font-size: clamp(15.5px, 1.6vw, 17.5px); line-height: 1.8; }
.about h1 {
  margin: 0 0 clamp(20px, 3vw, 34px); font-size: clamp(26px, 4.4vw, 52px);
  font-stretch: 116%; font-weight: 600; text-transform: uppercase;
  letter-spacing: .015em; line-height: 1.02;
}
.facts { margin: clamp(26px, 4vw, 44px) 0 0; display: grid; gap: 0; }
.facts div {
  display: flex; justify-content: space-between; gap: 20px;
  padding: 11px 0; border-bottom: 1px solid var(--line-soft);
}
.facts dt { color: var(--fg-3); font-family: var(--mono); font-size: 11px;
  letter-spacing: .16em; text-transform: uppercase; }
.facts dd { margin: 0; font-size: 14px; }

/* ============================================================
   CONTACT  (Monochrome Designer Showcase panel)
   ============================================================ */

.contact { display: grid; grid-template-columns: 1fr; gap: clamp(28px, 5vw, 72px); align-items: start; }
@media (min-width: 900px) { .contact { grid-template-columns: 1fr 1fr; } }

.contact .left h1 {
  margin: 0 0 clamp(18px, 2.6vw, 30px); font-size: clamp(26px, 4.4vw, 52px);
  font-stretch: 116%; font-weight: 600; text-transform: uppercase;
  letter-spacing: .015em; line-height: 1.02;
}
.contact .left .prose { max-width: 46ch; }
.contact .direct { margin-top: clamp(26px, 4vw, 42px); display: grid; gap: 0; }
.contact .direct div {
  display: flex; justify-content: space-between; gap: 20px;
  padding: 11px 0; border-bottom: 1px solid var(--line-soft);
}
.contact .direct dt { color: var(--fg-3); font-family: var(--mono); font-size: 11px;
  letter-spacing: .16em; text-transform: uppercase; }
.contact .direct dd { margin: 0; font-size: 14px; }
.contact .direct a { border-bottom: 1px solid var(--line); }
.contact .direct a:hover { border-bottom-color: var(--accent); color: var(--accent); }

.panel {
  border: 1px solid var(--line); background: var(--bg-2);
  padding: clamp(22px, 3.2vw, 42px);
}
.panel h2 {
  margin: 0 0 clamp(20px, 3vw, 30px); font-size: clamp(17px, 2.4vw, 27px);
  font-stretch: 116%; font-weight: 600; text-transform: uppercase; letter-spacing: .02em;
}
.field { display: grid; gap: 7px; margin-bottom: 20px; }
.field label {
  font-family: var(--sans); font-size: 11px; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase; color: var(--fg-3);
}
.field .req { color: var(--fg); }
.field input, .field select, .field textarea {
  width: 100%; background: transparent; color: var(--fg);
  border: 0; border-bottom: 1px solid var(--line);
  font-family: var(--sans); font-size: 14.5px; padding: 10px 0; border-radius: 0;
}
.field select { cursor: pointer; }
.field select option { background: var(--bg-2); color: var(--fg); }
.field textarea { resize: vertical; min-height: 96px; line-height: 1.6; }
.field input::placeholder, .field textarea::placeholder { color: var(--fg-4); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: 0; border-bottom-color: var(--accent); border-bottom-width: 2px; padding-bottom: 9px;
}
.field input[aria-invalid='true'], .field textarea[aria-invalid='true'] { border-bottom-color: #ff6b6b; }
.err { color: #ff8a8a; font-size: 12px; min-height: 0; }
.err:empty { display: none; }

.count { text-align: right; font-family: var(--mono); font-size: 10.5px; color: var(--fg-4); }

/* Honeypot. Bots fill every input they find; people never see this one.
   Positioned off-canvas rather than display:none, because some bots skip
   fields that are not rendered at all. */
.hp {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}

.consent { display: flex; gap: 11px; align-items: flex-start; margin: 4px 0 22px; }
.consent input { margin: 3px 0 0; width: 15px; height: 15px; accent-color: var(--accent); flex: none; }
.consent label { font-size: 12.5px; line-height: 1.6; color: var(--fg-3); }
.consent a { color: var(--fg-2); border-bottom: 1px solid var(--line); }
.consent a:hover { color: var(--fg); border-bottom-color: var(--fg); }

.send {
  width: 100%; background: none; border: 1px solid var(--line);
  color: var(--fg-2); padding: 14px; cursor: pointer;
  font-family: var(--sans); font-weight: 500; font-size: 12px;
  letter-spacing: .22em; text-transform: uppercase;
  transition: background .2s var(--ease), color .2s var(--ease),
              border-color .2s var(--ease), transform .14s var(--ease);
}
.send:hover:not(:disabled) { background: var(--fg); color: var(--bg); border-color: var(--fg); }
/* Gentler than the usual .97 because the button runs the full width of the
   panel — the same ratio reads as a lurch at that size. */
.send:active:not(:disabled) { transform: scale(.985); }
.send:disabled { opacity: .45; cursor: not-allowed; }

.form-note {
  margin: 16px 0 0; font-size: 12px; line-height: 1.65; color: var(--fg-4);
}
/* The reply to a submitted form is the one moment on the page that is pure
   feedback, so it arrives rather than materialising already written. */
.status {
  margin-top: 16px; font-size: 13.5px; line-height: 1.6;
  transition: opacity .24s var(--ease), transform .24s var(--ease);
}
.status[data-fresh] { opacity: 0; transform: translateY(5px); }
.status[data-kind='ok'] { color: #7ee0a8; }
.status[data-kind='err'] { color: #ff8a8a; }

/* ============================================================
   POLICY PAGES
   ============================================================ */

.policy { max-width: 74ch; }
.policy h1 {
  margin: 0 0 10px; font-size: clamp(24px, 4vw, 44px);
  font-stretch: 116%; font-weight: 600; text-transform: uppercase; letter-spacing: .015em;
}
.policy .updated { margin: 0 0 clamp(28px, 4vw, 46px); }
.policy h2 {
  margin: clamp(30px, 4vw, 46px) 0 12px; font-size: clamp(15px, 1.9vw, 19px);
  font-weight: 600; letter-spacing: -.005em;
}
.policy p, .policy li { color: var(--fg-2); font-size: 15px; line-height: 1.8; }
.policy ul { padding-left: 1.1em; margin: 0 0 1.15em; }
.policy li { margin-bottom: .45em; }
.policy a { border-bottom: 1px solid var(--line); }
.policy a:hover { border-bottom-color: var(--accent); color: var(--accent); }
.todo {
  border: 1px dashed var(--line); padding: 14px 16px; margin: 20px 0;
  color: var(--fg-3); font-size: 13.5px; line-height: 1.7;
}
.todo b { color: var(--fg-2); }

/* ============================================================
   FOOTER
   ============================================================ */

.foot {
  border-top: 1px solid var(--line-soft);
  padding: clamp(40px, 6vw, 72px) var(--pad) clamp(24px, 3vw, 36px);
}
.foot-grid {
  display: grid; gap: clamp(24px, 4vw, 44px);
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  padding-bottom: clamp(26px, 4vw, 42px);
  border-bottom: 1px solid var(--line-soft); margin-bottom: 20px;
}
.foot h3 { margin: 0 0 12px; }
.foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.foot li a { color: var(--fg-3); font-size: 13.5px; transition: color .18s var(--ease); }
.foot li a:hover { color: var(--accent); }
.foot .mark {
  font-stretch: 116%; font-weight: 700; text-transform: uppercase;
  font-size: clamp(17px, 2.2vw, 24px); letter-spacing: .04em; line-height: 1.1;
}
.foot-base {
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}

/* On a phone the auto-fit grid put the two-line name in a tall first cell
   and left the link groups starting at different heights, which read as
   a mess. Below 720px the footer becomes one column: name, then each
   group as its own block with a hairline above it, everything sharing
   one left edge. */
@media (max-width: 720px) {
  .foot-grid {
    grid-template-columns: 1fr;
    gap: 0;
    padding-bottom: 4px;
    border-bottom: 0;
  }
  .foot-grid > div {
    padding: 20px 0;
    border-top: 1px solid var(--line-soft);
  }
  .foot-grid > div:first-child {
    padding: 0 0 22px;
    border-top: 0;
  }
  .foot .mark { font-size: 1.35rem; line-height: 1.15; }
  .foot h3 { margin-bottom: 14px; }
  .foot ul { gap: 12px; }
  .foot li a { font-size: 15px; }
  .foot-base {
    flex-direction: column; gap: 8px;
    border-top: 1px solid var(--line-soft); padding-top: 20px;
  }
}

/* ============================================================
   LIGHTBOX
   ============================================================ */

/* `allow-discrete` lets the display swap wait for the fade. Where it is not
   supported the viewer simply cuts in and out, which is what it did before,
   so nothing is staked on it. */
.lb {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(8,8,10,.97);
  display: none; flex-direction: column; gap: 12px;
  padding: clamp(12px, 2.4vw, 26px);
  opacity: 0; pointer-events: none;
  transition: opacity .24s var(--ease), display .24s allow-discrete;
}
.lb.on { display: flex; opacity: 1; pointer-events: auto; }
@starting-style { .lb.on { opacity: 0; } }

.lb-stage { flex: 1 1 auto; min-height: 0; display: grid; place-items: center; }
.lb-stage img {
  max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain;
  transform: scale(.98);
  transition: opacity .2s var(--ease), transform .24s var(--ease);
}
/* Opening settles the frame the last fraction into place; closing takes it
   back out the same way. */
.lb.on .lb-stage img { transform: scale(1); }
@starting-style { .lb.on .lb-stage img { transform: scale(.98); } }
/* main.js holds this while the next frame decodes, so one photograph
   dissolves into the next instead of cutting to it. */
.lb-stage img[data-loading] { opacity: 0; }
.lb-bar { display: flex; align-items: flex-end; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
.lb-bar .rec { font-family: var(--mono); font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--fg-3); }
.lb-bar .nm { margin: 0 0 5px; font-size: clamp(14px, 2vw, 19px); font-weight: 500; }
.lb-nav { display: flex; gap: 8px; }
.lb button {
  background: none; border: 1px solid var(--line); color: var(--fg);
  width: 42px; height: 42px; cursor: pointer; display: grid; place-items: center;
  transition: background .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), transform .14s var(--ease);
}
.lb button:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.lb button:active { transform: scale(.94); }
.lb button svg { width: 15px; height: 15px; }

/* ============================================================
   CONSENT BANNER
   ============================================================ */

/* Arrives from the bottom edge and leaves through it, so the swipe the
   shape suggests is the motion it actually makes. `visibility` rather than
   `display` gives the slide something to move and still keeps the two
   buttons out of the tab order while the bar is away; the delay on the way
   out holds them until it has gone. */
.consent-bar {
  position: fixed; inset: auto 0 0 0; z-index: 250;
  background: var(--bg-2); border-top: 1px solid var(--line);
  padding: clamp(16px, 2.4vw, 24px) var(--pad);
  display: flex; gap: clamp(16px, 3vw, 40px);
  align-items: center; justify-content: space-between; flex-wrap: wrap;
  transform: translateY(100%);
  visibility: hidden;
  transition: transform .42s var(--ease-drawer), visibility 0s linear .42s;
}
.consent-bar.on {
  transform: translateY(0); visibility: visible;
  transition: transform .42s var(--ease-drawer), visibility 0s;
}
.consent-bar p { margin: 0; font-size: 13px; line-height: 1.65; color: var(--fg-2); max-width: 72ch; }
.consent-bar a { border-bottom: 1px solid var(--line); }
.consent-bar a:hover { border-bottom-color: var(--accent); color: var(--accent); }
.consent-acts { display: flex; gap: 10px; flex-wrap: wrap; }
.consent-acts button {
  background: none; border: 1px solid var(--line); color: var(--fg-2);
  padding: 11px 20px; cursor: pointer;
  font-family: var(--sans); font-weight: 500; font-size: 11.5px;
  letter-spacing: .2em; text-transform: uppercase;
  transition: background .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), transform .14s var(--ease);
}
.consent-acts button:hover { border-color: var(--fg); color: var(--fg); }
.consent-acts button:active { transform: scale(.96); }
.consent-acts button.primary { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.consent-acts button.primary:hover { opacity: .86; }

/* Fewer and gentler, not none. Colour and opacity still carry meaning, so
   they stay; what goes is travel — the zooms, the slide, the rises, the
   endless nudge under the hero. The old blanket rule flattened every
   duration to nothing, which left the viewer and the consent bar cutting
   in exactly as hard for the people who asked for less. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .hero .scroll svg { animation: none; }

  .shoot .well img, .plate img { transition: none; }
  .shoot:hover .well img, .shoot:focus-visible .well img,
  .plate:hover img, .plate:focus-visible img { transform: none; }

  .plate .rec { transform: none; transition: opacity .2s var(--ease); }
  .plate:hover .rec, .plate:focus-visible .rec { transform: none; }

  .nav { transition: none; }

  .shoot-grid .shoot[data-enter] { transform: none; }

  /* Dropping transform from the list is what stops the scale, including the
     one @starting-style would otherwise run on open. */
  .lb-stage img { transform: none; transition: opacity .2s var(--ease); }

  .consent-bar {
    transform: none; opacity: 0;
    transition: opacity .24s var(--ease), visibility 0s linear .24s;
  }
  .consent-bar.on {
    transform: none; opacity: 1;
    transition: opacity .24s var(--ease), visibility 0s;
  }

  .status[data-fresh] { transform: none; }

  .send:active:not(:disabled), .lb button:active,
  .consent-acts button:active { transform: none; }
}
