:root {
  --color-bg: #ffffff;
  --color-ink: #000000;
  --color-ink-soft: #4a4a4a;
  --font-display: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-ui: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --page-max-width: 30rem;
  --logo-h: 1.1rem;
  /* Space under the logo before the header line. On phones it is chosen so the
     line sits at the same height as the top of the home page's hero image. */
  --header-pad-bottom: 1.125rem;
  --header-h: calc(1.25rem + var(--logo-h) + var(--header-pad-bottom) + 1px);
  --nav-gap-bottom: 1.25rem;
}

/* iOS Safari 26+ has a floating toolbar with its own clearance, so the nav
   needs almost no extra gap there; every other browser has a toolbar flush
   with the page edge (or none). Class is set by the inline script in <head>. */
html.ios-floating-toolbar {
  --nav-gap-bottom: 0.25rem;
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  overflow-anchor: none;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
  /* Safari (and Chrome on iOS, which is Safari underneath) doesn't reliably
     honor overscroll-behavior on <html>/<body> itself — the page can still
     be dragged past its top/bottom edge and bounce back, which on the
     projects page pulls the nav row up past its resting position. Pinning
     body in place and making .page the actual scrolling element sidesteps
     that: overscroll-behavior is well-supported on an ordinary element. */
  position: fixed;
  inset: 0;
  overflow: hidden;
}

a {
  color: inherit;
}

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

/* ---------- Page layout (mobile first) ---------- */

.page {
  min-height: 100vh;
  /* Not 100dvh: that grows as Safari's address bar auto-hides while
     scrolling, so the page's minimum height would grow with it and never
     shrink back — leaving a gap below short content (e.g. the projects
     accordion closed again) instead of the nav row sitting right after it,
     like at the very start. svh is the viewport size with browser chrome
     fully shown, so it stays fixed regardless of scroll/chrome state. */
  min-height: 100svh;
  max-width: var(--page-max-width);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  /* The actual scrolling element now (see body, above). Bounce/rubber-band
     at top and bottom is intentionally left on (no overscroll-behavior-y
     here) — the min-height: 100svh above already keeps this element's
     resting height (and so the nav row's resting position) stable regardless
     of scroll/chrome state or bounce, independent of whether bounce itself
     is allowed. */
  height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* The projects page always spans the whole window, so the dividing lines and
   the header line run from the left edge to the right edge at any width. */
.page--projects {
  max-width: none;
}

.page--home {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

.wordmark-row {
  padding: 1.25rem 1.5rem 0.75rem;
  text-align: center;
}

/* Fixed rather than sticky: on iOS a sticky header is repainted at a stale
   position for a frame when layout height and scroll offset change together
   (the accordion's off-screen close). --header-h must equal padding-top +
   logo height + padding-bottom + border, and is reused as the spacer. */
.wordmark-row--fixed {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  height: var(--header-h);
  margin: 0 auto;
  padding-bottom: var(--header-pad-bottom);
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-ink);
}

.wordmark {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.6rem;
  line-height: 1.15;
  letter-spacing: 0.01em;
}

.logo-image {
  display: block;
  height: var(--logo-h);
  width: auto;
  max-width: 80%;
  object-fit: contain;
  margin: 0 auto;
}

.page--maintenance {
  height: 100vh;
  height: 100dvh;
}

.maintenance-row {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

.hero-row {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.375rem 0.75rem;
  min-height: 0;
}

.hero-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Rotating hero: two images stacked per frame; the next one dissolves in on top
   of the current one, then the old one is hidden. --hero-fade is the dissolve
   length and is read by js/main.js so its wait always matches. */
.hero-row {
  --hero-fade: 0.25s;
}

.hero-stack {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.hero-stack .hero-image {
  position: absolute;
  inset: 0;
  opacity: 0;
  z-index: 1;
  transition: opacity var(--hero-fade) ease-in-out;
}

.hero-stack .hero-image.is-current {
  opacity: 1;
}

.hero-stack .hero-image.is-entering {
  z-index: 2;
}

.contact-block {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  text-align: center;
  line-height: 1.8;
}

.contact-block p {
  margin: 0 0 1.75rem;
}

.contact-block p:last-child {
  margin-bottom: 0;
}

.nav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1.75rem calc(var(--nav-gap-bottom) + env(safe-area-inset-bottom, 0px));
}

.nav-link {
  font-family: var(--font-ui);
  font-size: 0.9rem;
  font-weight: 400;
  text-decoration: none;
  letter-spacing: 0.01em;
}

.nav-link:focus-visible,
.wordmark:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 4px;
}

/* ---------- Project list (accordion) ---------- */

.projects-main {
  flex: 1;
  padding-top: var(--header-h);
}

.project-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.project-item + .project-item {
  border-top: 1px solid var(--color-ink);
}

.project-list > .project-item:last-child {
  border-bottom: 1px solid var(--color-ink);
}

.project-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  margin: 0;
  padding: 0.625rem 1.5rem;
  background: none;
  border: 0;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.project-toggle:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 4px;
}

/* Plus when the project is closed, minus when it is open. Two thin bars: the
   vertical one turns down onto the horizontal one, so the plus folds into a
   minus (and back). Follows aria-expanded however the panel got opened/closed. */
.project-toggle-icon {
  position: relative;
  flex: none;
  width: 13px;
  height: 13px;
}

.project-toggle-icon::before,
.project-toggle-icon::after {
  content: '';
  position: absolute;
  top: 6px;
  left: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transition: transform 0.25s ease;
}

.project-toggle-icon::after {
  transform: rotate(90deg);
}

.project-toggle[aria-expanded="true"] .project-toggle-icon::after {
  transform: rotate(0deg);
}

.project-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}

.project-panel.is-open {
  grid-template-rows: 1fr;
}

.project-panel--instant {
  transition: none;
}

.project-panel-inner {
  overflow: hidden;
  padding: 0.625rem 1.5rem 0;
}

.project-description {
  margin: 0 0 0.75rem;
  color: var(--color-ink-soft);
  font-size: 0.95rem;
  line-height: 1.5;
  /* Line breaks typed in the CMS text field show up as breaks here too
     (a plain browser default collapses them all into one line). */
  white-space: pre-line;
}

.project-description.is-truncated {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.read-more-toggle {
  display: inline-block;
  margin: 0 0 1.25rem;
  padding: 0;
  background: none;
  border: 0;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--color-ink-soft);
  text-decoration: underline;
  cursor: pointer;
}

.media-gallery {
  margin: 1.25rem 0 2rem;
}

.media-slider {
  display: flex;
  gap: 0.25rem;
  /* On phones: exactly tall enough that a 2:3 portrait image is as wide as the
     text column (screen width minus the panel's 1.5rem padding on each side),
     so the first image is fully visible; never taller than 78vh. */
  height: min(78vh, calc((100vw - 3rem) * 1.5));
  margin: 0;
  overflow-x: auto;
  scrollbar-width: none;
}

.media-slider::-webkit-scrollbar {
  display: none;
}

.media-scrollbar {
  position: relative;
  height: 1px;
  margin-top: 0.5rem;
  background: rgba(0, 0, 0, 0.12);
  overflow: hidden;
}

.media-scrollbar.is-hidden {
  display: none;
}

.media-scrollbar-thumb {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: var(--color-ink);
}

.media-slide {
  flex: 0 0 auto;
  height: 100%;
  width: auto;
  max-width: none; /* the global img max-width would squeeze wide images */
  object-fit: cover;
}

.media-slide--portrait {
  aspect-ratio: 2 / 3;
}

.media-slide--wide {
  /* No fixed aspect-ratio: width is flexible, following the image's
     own natural ratio (e.g. ~21:9) at the slider's fixed height. */
}

/* ---------- Newsletter signup (last accordion row on the projects page) ---------- */

.newsletter {
  margin: 0 0 1.25rem;
  max-width: 30rem;
}

.newsletter-heading {
  margin: 0 0 0.5rem;
  color: var(--color-ink-soft);
  font-size: 0.95rem;
  line-height: 1.5;
}

.newsletter-subtext {
  margin: 0 0 1rem;
  color: var(--color-ink-soft);
  font-size: 0.95rem;
  line-height: 1.5;
}

.newsletter-form {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.newsletter-email {
  flex: 1 1 14rem;
  max-width: 16rem;
  min-width: 0;
  padding: 0.5rem 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--color-ink-soft);
  border-radius: 0;
  outline: none;
  font: inherit;
  font-size: 0.95rem;
  color: var(--color-ink);
  caret-color: var(--color-ink-soft);
}

.newsletter-email::placeholder {
  color: var(--color-ink-soft);
  transition: opacity 0.15s ease;
}

/* No browser focus ring — the placeholder fading out and the blinking cursor
   are enough to show the field is active. */
.newsletter-email:focus::placeholder {
  opacity: 0;
}

.newsletter-hp {
  /* Hidden from people; left in the tab/DOM order so a bot's form-filler finds it. */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
  clip: rect(0, 0, 0, 0);
}

/* An arrow (shaft + chevron head, one stroked line), the same weight as the
   plus/minus accordion icon, and the same colour as the surrounding text. */
.newsletter-submit {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 0.9rem;
  padding: 0;
  background: none;
  border: 0;
  color: var(--color-ink-soft);
  cursor: pointer;
  transition: transform 0.15s ease;
}

.newsletter-submit svg {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
}

.newsletter-submit:hover {
  transform: translateX(0.15rem);
}

.newsletter-submit:disabled {
  cursor: default;
  opacity: 0.4;
  transform: none;
}

.newsletter-message {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--color-ink-soft);
  min-height: 1.2em;
}

.newsletter-message.is-error {
  color: #b42318;
}

/* ---------- Desktop ---------- */

@media (min-width: 700px) {
  :root {
    --page-max-width: 40rem;
    --logo-h: 1.25rem;
    --header-pad-bottom: 1.75rem;
    --header-h: calc(2rem + var(--logo-h) + var(--header-pad-bottom) + 1px);
    --nav-gap-bottom: 1.5rem;
  }

  /* The page spans the whole window so the nav can sit at its left and right
     edges; the content column is constrained individually instead. */
  .page {
    max-width: none;
  }

  .hero-row {
    width: 100%;
    max-width: var(--page-max-width);
    margin-inline: auto;
  }

  /* Home hero on wide windows: three equal frames (added by js/main.js). */
  .hero-row--triptych {
    max-width: none;
    gap: 0.75rem;
  }

  .hero-row--triptych .hero-stack {
    flex: 1 1 0;
    min-width: 0;
    width: auto;
  }

  /* Project list: uses the full window. The dividing lines (and the header's
     line) run edge to edge, titles and text start at the left inset (same as
     the nav), and galleries run from there to the right edge of the window. */
  :root {
    --column-pad: 2.5rem;
  }

  .wordmark-row {
    padding-top: 2rem;
  }

  .wordmark {
    font-size: 2rem;
  }

  .hero-row {
    padding: 0.75rem 2.5rem var(--nav-gap-bottom);
  }

  /* Nav at the top: on the logo's line, left and right of it. It is fixed so it
     stays level with the (fixed) header on the projects page, and lets clicks
     through the empty middle so the logo link still works. */
  .nav-row {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 11;
    height: calc(2rem + var(--logo-h));
    padding: 2rem 2.5rem 0;
    pointer-events: none;
  }

  .nav-row .nav-link {
    pointer-events: auto;
  }

  .nav-link,
  .contact-block {
    font-size: 1rem;
  }

  .project-toggle {
    padding: 0.625rem var(--column-pad);
  }

  /* The gap above the panel content is a real element rather than padding:
     padding can't collapse, so it left 0.625rem under every closed row and the
     title looked off-centre between the lines. */
  .project-panel-inner {
    padding: 0 var(--column-pad);
  }

  .project-panel-inner::before {
    content: '';
    display: block;
    height: 0.625rem;
  }

  .media-slider {
    height: 78vh;
  }

  /* Text keeps a readable line length even though the panel is full width,
     but reaches a bit past half the window instead of stopping at a fixed width. */
  .project-description {
    max-width: 55vw;
  }

  /* The newsletter text is short by design; without the mobile column width
     cap it naturally stays on one line instead of wrapping. */
  .newsletter {
    max-width: none;
  }
}

/* ---------- Reduced motion / accessibility floor ---------- */

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