:root {
  --paper: #fff;
  --ink: #1a1a1a;
  --muted: #767676; /* 4.5:1 on white: meets WCAG AA for small text */
  --rule: #ececec;
  --serif: "Cormorant Garamond", Georgia, serif;
  --panel-w: clamp(290px, 27vw, 400px);
  /* One easing for everything that moves: long, soft landing. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --glide: 0.9s;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
}

html {
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
}

/* The page itself never scrolls (Thumbnails scrolls inside its own layer),
   so a scrollbar can never appear and shift the layout. */
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;
}

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

img {
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

.switch a.is-active,
.tabs .is-active,
.works a.is-current .title {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

@media (hover: hover) and (pointer: fine) {
  body.has-cursor,
  body.has-cursor a,
  body.has-cursor button {
    cursor: none;
  }
}

/* Text waits for the serif, then fades in once (no fallback-font swap). */
.masthead,
.switch,
.panel,
.touch-name {
  transition: opacity 0.7s ease;
}

.fonts-pending .masthead,
.fonts-pending .switch,
.fonts-pending .panel,
.fonts-pending .touch-name {
  opacity: 0;
}

/* Name (top centre) and view switch (bottom centre). With the panel open
   they glide left by half its width to stay centred: a transform, not a
   change of padding, so nothing re-lays out while it moves. */

.masthead,
.switch {
  position: fixed;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  justify-content: center;
  pointer-events: none;
  transition:
    transform var(--glide) var(--ease),
    opacity 0.7s ease;
}

.masthead {
  top: 0;
  padding: 24px 16px 14px;
}

.switch {
  bottom: 0;
  gap: 14px;
  padding: 10px 16px 18px;
  font-size: 15px;
}

/* At least 24px tall to tap (WCAG 2.2 target size). */
.switch a {
  display: inline-block;
  padding: 4px 4px;
}

/* Keyboard focus is always visible; the mouse never shows it. */
:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

:focus:not(:focus-visible) {
  outline: none;
}

body.panel-open .masthead,
body.panel-open .switch {
  transform: translate3d(calc(var(--panel-w) / -2), 0, 0);
}

body.is-thumbs .masthead {
  background: linear-gradient(var(--paper) 70%, rgba(255, 255, 255, 0));
}

body.is-thumbs .switch {
  background: linear-gradient(rgba(255, 255, 255, 0), var(--paper) 45%);
}

.masthead > *,
.switch > * {
  pointer-events: auto;
}

.name {
  margin: 0;
  font-size: 19px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Back (inside a project) and the step arrows. */

.back {
  position: fixed;
  top: 24px;
  left: 28px;
  z-index: 21;
  font-size: 15px;
  color: var(--muted);
  transition:
    color 0.3s ease,
    opacity 0.5s ease;
  animation: viewIn 0.6s ease both;
}

.back:hover {
  color: var(--ink);
}

.back[hidden] {
  display: none;
}

.arrow {
  position: fixed;
  top: 50%;
  z-index: 21;
  width: 44px;
  height: 64px;
  margin-top: -32px;
  font-size: 30px;
  line-height: 1;
  color: var(--muted);
  transition:
    color 0.3s ease,
    opacity 0.5s ease,
    transform var(--glide) var(--ease);
}

.arrow:hover {
  color: var(--ink);
}

.arrow.prev {
  left: 1.2vw;
}

.arrow.next {
  right: 1.2vw;
}

/* Arrows belong to the photograph: hidden in Thumbnails and while the panel is open. */
body.is-thumbs .arrow,
body.panel-open .arrow {
  opacity: 0;
  pointer-events: none;
}

.switch span {
  color: var(--muted);
}

/* Stage: one photograph, centred. Its frame never changes size; opening the
   panel moves and scales it with a single GPU transform (set in app.js). */

.stage {
  position: fixed;
  inset: 88px 6vw 82px;
  transform-origin: 50% 50%;
  transition: transform var(--glide) var(--ease);
  will-change: transform;
}

.stage img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  backface-visibility: hidden;
  /* Arriving: a soft fade in, only after the previous photograph has gone. */
  transition: opacity var(--in, 600ms) cubic-bezier(0.25, 0.1, 0.25, 1);
}

.stage img.is-shown {
  opacity: 1;
}

/* Leaving: fades out completely before the next one appears. */
.stage img.is-leaving {
  opacity: 0;
  transition: opacity var(--out, 300ms) cubic-bezier(0.4, 0, 1, 1);
}

/* The click halves are for the mouse only (keyboard users have the arrow
   buttons), so they never show a focus outline. */
.zones button:focus,
.zones button:focus-visible {
  outline: none;
}

.zones button {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: inherit;
}

.zones {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

/* View change: the stage and the thumbnails crossfade instead of cutting. */
.view-in {
  animation: viewIn 0.6s ease both;
}

.view-out {
  opacity: 0;
  transition: opacity 0.45s ease;
  pointer-events: none;
}

.stage[hidden],
.thumbs[hidden] {
  display: none;
}

/* Touch screens have no cursor label: the project name sits under the photo. */
.touch-name {
  display: none;
}

@media (hover: none) {
  .touch-name {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 52px;
    margin: 0;
    text-align: center;
    font-size: 14px;
    color: var(--muted);
    z-index: 19;
  }
}

/* Panel ------------------------------------------------------------------ */

.panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 30;
  width: var(--panel-w);
  padding: 22px 30px 40px;
  background: var(--paper);
  border-left: 1px solid var(--rule);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  transform: translate3d(100%, 0, 0);
  transition:
    transform var(--glide) var(--ease),
    opacity 0.7s ease;
  will-change: transform;
}

.panel::-webkit-scrollbar {
  display: none;
}

body.panel-open .panel {
  transform: none;
}

.tabs {
  display: flex;
  align-items: baseline;
  gap: 18px;
  margin-bottom: 48px;
  font-size: 15px;
}

.tabs .close {
  margin-left: auto;
  font-size: 19px;
  line-height: 1;
  color: var(--muted);
  transition: color 0.3s ease;
}

.tabs .close:hover {
  color: var(--ink);
}

[data-pane] {
  animation: viewIn 0.5s ease both;
}

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

.works a {
  display: block;
  padding: 3px 0;
  font-size: 15.5px;
  transition: color 0.35s ease;
}

.works:hover a {
  color: var(--muted);
}

.works a:hover,
.works:hover a.is-current {
  color: var(--ink);
}

.info p {
  margin: 0 0 1.1em;
  font-size: 16px;
  line-height: 1.45;
}

.info dl {
  display: grid;
  grid-template-columns: 84px 1fr;
  gap: 4px 12px;
  margin: 36px 0 0;
  font-size: 15px;
}

.info dt {
  color: var(--muted);
}

.info dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.info a:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Get in touch ------------------------------------------------------------ */

.contact h2 {
  margin: 0 0 6px;
  font-size: 22px;
  font-weight: 400;
}

.contact .lede {
  margin: 0 0 28px;
  color: var(--muted);
  font-size: 15px;
}

.enquiry {
  display: grid;
  gap: 18px;
}

.enquiry label {
  display: grid;
  gap: 4px;
  font-size: 14px;
  color: var(--muted);
}

.enquiry input,
.enquiry select,
.enquiry textarea {
  width: 100%;
  padding: 6px 0;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 16px; /* 16px stops phones zooming into the field */
  outline: none;
  transition: border-color 0.3s ease;
  -webkit-appearance: none;
  appearance: none;
}

.enquiry textarea {
  resize: vertical;
  min-height: 120px;
}

.enquiry input:focus,
.enquiry select:focus,
.enquiry textarea:focus {
  border-bottom-color: var(--ink);
}

.enquiry button[type="submit"] {
  justify-self: start;
  /* Looks like underlined text, but is a full 44px touch target. */
  min-height: 44px;
  min-width: 44px;
  padding: 0 16px 0 0;
  font-size: 16px;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}

.enquiry button[disabled] {
  color: var(--muted);
}

.enquiry .status {
  min-height: 1.4em;
  margin: 0;
  font-size: 14px;
}

/* The bot trap: off-screen for people, still in the form for bots. */
.enquiry .trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.noscript {
  max-width: 40ch;
  margin: 120px auto;
  font-size: 16px;
}

/* Thumbnails: an even grid of equal cells, each photograph centred in its
   cell. Scrolls inside its own layer, scrollbar hidden. ------------------- */

.thumbs {
  position: fixed;
  inset: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(260px, 38vw), 1fr));
  gap: 7vh 5vw;
  align-content: start;
  justify-content: center;
  padding: 84px 10vw 120px;
}

.thumbs::-webkit-scrollbar {
  display: none;
}

.thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 34vh;
  opacity: 0;
  transform: translate3d(0, 14px, 0);
}

.thumb.is-in {
  animation: rise 1s var(--ease) forwards;
}

/* The <picture> wrapper (WebP with JPEG fallback) stays out of the layout. */
.thumb picture {
  display: contents;
}

.thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}


/* Cursor: dot and name in difference blend, light over photographs and dark
   over the page. Invisible until the pointer first moves. */

.cursor {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 999;
  display: none;
  align-items: center;
  gap: 9px;
  color: #fff;
  mix-blend-mode: difference;
  font-size: 15px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  will-change: transform;
}

body.has-cursor .cursor {
  display: flex;
}

.cursor.is-live {
  opacity: 1;
}

.cursor .dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  transform: translateX(-50%);
}

.cursor .label {
  opacity: 0;
  transition: opacity 0.3s ease;
}

.cursor.has-label .label {
  opacity: 1;
}

/* Opening: photographs cut fast in the centre while the name stays at the
   top (the masthead sits above this layer). The frame box matches the stage
   exactly, so the last frame hands over to the slideshow without a seam. */

.intro {
  position: fixed;
  inset: 0;
  z-index: 15;
  background: var(--paper);
  transition: opacity 0.45s ease;
}

.intro.is-out {
  opacity: 0;
  pointer-events: none;
}

.intro-stage {
  position: absolute;
  inset: 88px 6vw 82px;
}

.intro-stage img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* After the cuts: a clean white page with only the name in the centre. */
.intro-name {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  margin: 0;
  color: var(--ink);
  font-size: 24px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.7s ease;
}

.intro.name-in .intro-name {
  opacity: 1;
}

/* Then it dissolves in place: softens and fades slowly, nothing moves. */
.intro.name-in .intro-name.is-dissolving {
  opacity: 0;
  filter: blur(8px);
  transition:
    opacity 1.6s cubic-bezier(0.45, 0, 0.25, 1),
    filter 1.6s cubic-bezier(0.45, 0, 0.25, 1);
}

/* While it plays, only the name and the photographs show. */
body.intro-playing .masthead,
body.intro-playing .switch,
body.intro-playing .arrow,
body.intro-playing .back,
body.intro-playing .touch-name {
  opacity: 0;
  pointer-events: none;
}

/* Thumbnails: a project's name above its photographs. */
.thumbs-title {
  grid-column: 1 / -1;
  margin: 2vh 0 0;
  text-align: center;
  font-size: 15px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.thumbs-title:not(:first-child) {
  margin-top: 8vh;
}

@keyframes viewIn {
  from {
    opacity: 0;
  }
}

@keyframes rise {
  to {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .thumb {
    opacity: 1;
    transform: none;
  }
}

/* Phones: the panel rises from the bottom; the photo glides up above it. */

@media (max-width: 760px) {
  .name {
    font-size: 17px;
  }

  :root {
    --panel-w: 100vw;
  }

  .masthead {
    padding-top: 18px;
  }

  .stage,
  .intro-stage {
    inset: 64px 16px 92px;
  }

  body.panel-open .masthead,
  body.panel-open .switch {
    transform: none;
  }

  .panel {
    top: auto;
    height: 58vh;
    height: 58dvh; /* follows the iPhone toolbar as it shows and hides */
    padding: 20px 16px 70px;
    border-left: 0;
    border-top: 1px solid var(--rule);
    transform: translate3d(0, 100%, 0);
  }

  .tabs {
    margin-bottom: 24px;
  }

  /* The footer rises with the panel and sits just above it, so the photo,
     footer and panel always move together. */
  body.panel-open .switch {
    transform: translate3d(0, -58vh, 0);
  }

  body.panel-open .touch-name {
    opacity: 0;
  }

  .back {
    top: 18px;
    left: 16px;
  }

  .arrow {
    width: 32px;
    font-size: 26px;
  }

  .arrow.prev {
    left: 0;
  }

  .arrow.next {
    right: 0;
  }

  .thumbs {
    grid-template-columns: repeat(2, 1fr);
    gap: 22px 14px;
    padding: 66px 16px 84px;
  }

  .thumb {
    height: 26vh;
  }
}

/* Cookie choice (consent.js): small, bottom left, out of the photograph's way.
   Decline and Accept look alike, so neither is pushed. */
.consent {
  position: fixed;
  left: 24px;
  bottom: 20px;
  z-index: 40;
  max-width: 380px;
  padding: 14px 16px 10px;
  background: var(--paper);
  border: 1px solid #e6e6e6;
  font-size: 14px;
  line-height: 1.45;
  transition: opacity 0.6s ease;
}
.consent[hidden] { display: none; }
.consent p { margin: 0 0 6px; }
.consent a { color: inherit; text-underline-offset: 3px; }
.consent-actions { display: flex; gap: 8px; justify-content: flex-end; }
.consent-actions button {
  min-width: 88px;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.consent-actions button:hover { background: var(--ink); color: var(--paper); }
/* Not during the opening. */
body.intro-playing .consent { opacity: 0; pointer-events: none; }
@media (max-width: 760px) {
  .consent { left: 12px; right: 12px; bottom: 64px; max-width: none; }
}
.small-print { margin-top: 28px; font-size: 14px; color: var(--muted); }
.small-print a { color: inherit; text-underline-offset: 3px; }
