/* ---------------------------------------------
   Obra Studio — V2
   Base styles shared by every page

   Typography/layout constants below are measured
   directly off the reference (christianlopez.xyz)
   at 1920px (desktop) and 390px (mobile) viewports,
   so header/brand/nav sit at identical sizes and
   offsets, just with Obra's own content.
--------------------------------------------- */

:root {
  --color-fg: #fefefe;
  --color-bg: #000000;
  --color-intro-bg: #fefefe;
  --color-intro-fg: #0a0a0a;
  --color-intro-fg-dim: rgba(10, 10, 10, .22);
  --pad: .75rem;       /* header/clock inset, mobile  = 12px  */
  --ease: cubic-bezier(.4, 0, .2, 1);
}

* {
  box-sizing: border-box;
}

/* Visually hidden but readable by search engines / screen readers.
   Used for descriptive copy that would clutter the visual design
   (see SEO notes) without touching what visitors actually see. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

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

html {
  font-size: 100%; /* 16px */
}

@media (min-width: 960px) {
  html {
    font-size: 87.5%; /* 14px — matches reference desktop scale */
  }
  :root {
    --pad: 2rem;        /* = 28px at 14px root, matches reference */
  }
}

body {
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

body.home {
  overflow: hidden;
  height: 100vh;
}

a {
  color: inherit;
  text-decoration: none;
  transition: opacity .3s var(--ease);
}

a:hover,
a:focus-visible {
  opacity: .55;
}

h1 {
  font-weight: 400;
  margin: 0;
}

/* ---------------------------------------------
   Full-bleed background video (home only)
--------------------------------------------- */

.bg-video {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index: 0;
  background: #000;
  cursor: pointer;
}

.bg-video video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* click the video, or hit spacebar, to pause/resume — see
   js/home-video.js. Pausing brings up a dark scrim with the
   studio's statement text over it (matching obra-studio.fr);
   clicking anywhere on it (other than the email link) resumes. */
.pause-overlay {
  position: fixed;
  inset: 0;
  z-index: 6;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(0, 0, 0, .78);
  opacity: 0;
  pointer-events: none;
  transform: scale(.995);
  filter: blur(2px);
  transition: opacity .28s, transform .28s, filter .28s;
  user-select: none;
  cursor: pointer;
}

.pause-overlay.is-visible {
  opacity: 1;
  pointer-events: auto;
  transform: scale(1);
  filter: blur(0);
}

.pause-overlay__copy {
  color: #fff;
  text-transform: uppercase;
  font-size: clamp(14px, 1.6vw, 20px);
  line-height: 1.25;
  letter-spacing: .02em;
  max-width: min(1100px, 92vw);
  text-align: left;
}

.pause-overlay__copy strong {
  font-weight: 700;
  color: #fff;
}

.pause-overlay__email {
  color: #fff;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .14em;
  transition: opacity .15s;
  cursor: pointer;
}

.pause-overlay__email:hover,
.pause-overlay__email:focus {
  opacity: .85;
}

/* ---------------------------------------------
   Header: brand (name + tagline) and nav.
   Mobile: stacked, both left-aligned, in normal flow.
   Desktop (>=960px): nav becomes a full-width flex row
   (first link at the left edge, last at the right edge)
   and brand is absolutely centered — left edge pinned to
   the horizontal center, text itself stays left-aligned.
--------------------------------------------- */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 10;
  padding: var(--pad);
  display: block;
  /* the header/nav boxes span edge-to-edge even where no
     text is visible (e.g. the hidden "current page" link) —
     let clicks/hovers pass through to what's underneath, and
     re-enable them only on the actual links. */
  pointer-events: none;
}

.site-header a {
  pointer-events: auto;
}

.brand {
  font-size: 1rem;
  line-height: 1.4286; /* 20/14 — reference's lead-3 ratio */
  text-align: left;
  color: var(--color-brand, var(--color-fg));
}

/* while a work-page hover preview (image or video) is showing,
   the brand text blends against it instead of sitting flat on
   top — see js/work.js's showMedia()/hideAll(). */
body.is-previewing .brand {
  mix-blend-mode: difference;
}

.brand h1,
.brand .tagline {
  font-size: 1em;
  line-height: inherit;
  margin: 0;
}

.brand h1 {
  font-weight: 500;
}

.main-nav {
  margin-top: .2em;
  display: block;
  font-size: 2rem;           /* 32px mobile */
  line-height: .875;         /* reference's lead-1 ratio */
  text-transform: uppercase;
  letter-spacing: normal;
  white-space: nowrap;
}

/* Home is the only page that shows all three nav links at once
   (Work/Archive/About each hide their own "current" link) — at
   32px uppercase that doesn't fit a phone width on one line, so
   it wraps ("About" dropping below). Shrink just this case so it
   stays on a single row instead of wrapping. */
@media (max-width: 959px) {
  /* Home and project-detail pages are the only ones that show
     all three nav links at once (every other page hides its own
     "current" link) -- at 32px uppercase that does not fit a
     phone width on one line, so it wraps or gets clipped ("About"
     dropping below, or cut off at the edge). Shrink just these
     cases so the row stays on one line and fully on-screen. */
  body.home .main-nav,
  body.project-page .main-nav {
    font-size: clamp(1.1rem, 7.2vw, 2rem);
  }
}

/* :not(.current) guards -- on mobile the current item is
   display:none (see below), which removes it from layout
   entirely; without the guard this margin would still apply
   using its DOM position, leaving a phantom gap where the
   hidden link used to be (e.g. "Index" sitting indented on
   work.html once "Work" is hidden). */
.main-nav > a:not(.current) + a,
.main-nav > a:not(.current) + .nav-group {
  margin-left: 1rem;
}

.nav-group > a:not(.current) + a {
  margin-left: .5em;
}

/* current page's own nav link is redundant — on mobile it's
   removed outright (display:none); desktop (below, >=960px)
   instead keeps its layout space via visibility:hidden so the
   remaining two links stay correctly spread by
   justify-content:space-between. */
.main-nav a.current {
  display: none;
}

/* ---------------------------------------------
   Live clock / location
--------------------------------------------- */

.clock {
  position: fixed;
  right: var(--pad);
  bottom: var(--pad);
  z-index: 10;
  display: flex;
  gap: .5em;
  align-items: baseline;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: .75rem;
}

#clock-time {
  font-variant-numeric: tabular-nums;
  opacity: .85;
}

.clock-place {
  font-weight: 700;
}

.clock-temp {
  font-variant-numeric: tabular-nums;
  opacity: .55;
}

.clock-temp:empty {
  display: none;
}

/* About page — Instagram link, mirrored bottom-left of the clock */
.social-link {
  position: fixed;
  left: var(--pad);
  bottom: var(--pad);
  z-index: 10;
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: .75rem;
  font-weight: 700;
  color: inherit;
}

.social-link:hover,
.social-link:focus {
  opacity: .7;
}

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

@media (min-width: 960px) {
  /* raised well clear of the standard --pad inset — closes up
     the big empty band above the header/work list — while the
     work list's own top padding (below) is nudged down to the
     same reduced height, so its first item stays level with the
     header instead of drifting apart. */
  .site-header {
    padding-top: calc(var(--pad) - 1rem);
  }

  .brand {
    position: absolute;
    top: calc(var(--pad) - 1rem);
    left: 50%;
    /* no transform: left edge sits at the horizontal
       center, text itself stays left-aligned — same as
       the reference, not centered on the midpoint. */
  }

  .main-nav {
    margin-top: 0;
    display: flex;
    justify-content: space-between;
    font-size: 4rem;         /* 56px desktop */
  }

  .main-nav > a + a,
  .main-nav > .nav-group {
    margin-left: 0;
  }

  .nav-group {
    display: flex;
  }

  /* Index/About sit right next to Work's own big gap
     (justify-content:space-between) — the base mobile spacing
     (.5em, tiny at this 56px font size) reads as cramped between
     them, so desktop gets its own wider, non-proportional gap. */
  .nav-group > a + a {
    margin-left: 2.5rem;
  }

  .clock {
    font-size: .85rem;
  }

  .social-link {
    font-size: .85rem;
  }

  /* current page's own nav link is redundant on desktop —
     visibility:hidden (not display:none) keeps its layout
     space, so with 3 links the remaining two (e.g. Archive/
     About) stay correctly spread by justify-content:space-between
     instead of the row collapsing */
  .main-nav a.current {
    display: inline;
    visibility: hidden;
  }
}

/* ---------------------------------------------
   Simple inner pages (About / Archive placeholders)
--------------------------------------------- */

.page-content {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
  text-align: center;
}

.page-content p {
  opacity: .7;
  max-width: 40ch;
  margin: 0 auto;
}

/* About page — long-form flowing layout: intro copy, two large
   statement headlines, three always-visible service territories,
   then a contact block. Unlike the other inner pages this one
   scrolls with the page instead of sitting fixed/centered.
   Selectors are scoped under .page-content--about throughout so
   they reliably beat the generic ".page-content p" reset above
   (centered, dimmed, 40ch-capped) instead of being fought by it.
   Rounded/casual display face per the latest reference — scoped
   here only, the rest of the site keeps Helvetica. */
.page-content--about {
  display: block;
  min-height: 0;
  align-items: initial;
  justify-content: initial;
  /* top padding must clear the fixed .site-header (~104px on
     phone at 32px nav) -- 7.25rem/116px leaves a small margin
     under it instead of overlapping. */
  padding: 7.25rem var(--pad) 4rem;
  text-align: left;
}

.page-content--about .about-wrap {
  max-width: 1400px;
  margin: 0;
}

.page-content--about .about-intro {
  max-width: 30em;
  margin: 0 0 1.2em;
  color: #fff;
  opacity: 1;
  font-size: 1rem;
  line-height: 1.4286;
  text-align: left;
}

.page-content--about .about-statement {
  margin: 1.75rem 0;
  color: #fff;
  font-size: 2rem;
  line-height: .875;
  font-weight: normal;
  text-transform: uppercase;
}

.page-content--about .about-territories {
  display: grid;
  grid-template-columns: repeat(3, max-content);
  column-gap: 3.5rem;
  row-gap: 2.5rem;
  margin: 1.75rem 0;
}

.page-content--about .territory-col h3 {
  margin: 0 0 1.1em;
  color: #fff;
  font-size: clamp(1.15rem, 1.8vw, 1.6rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .01em;
}

.page-content--about .territory-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  color: #fff;
  opacity: 1;
  max-width: none;
  font-size: 1rem;
  line-height: 2;
  text-align: left;
}

.page-content--about .about-contact {
  margin-top: 1.75rem;
}

.page-content--about .about-contact p {
  margin: 0;
  max-width: 26em;
  color: #fff;
  opacity: 1;
  font-size: 1rem;
  line-height: 1.4286;
  text-align: left;
}

.page-content--about .about-contact a {
  color: #fff;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .14em;
}

.page-content--about .about-contact a:hover,
.page-content--about .about-contact a:focus {
  opacity: .85;
}

@media (min-width: 960px) {
  /* Wide left gutter (~1/3 of the viewport) so the headlines and
     territory columns start well clear of the edge — matches the
     latest reference layout, desktop only. */
  .page-content--about {
    padding: 6.5rem var(--pad) 5rem clamp(4rem, 34vw, 40rem);
  }

  /* Intro/contact copy sit one notch further in than the
     headlines and lists, roughly under the header's tagline. */
  .page-content--about .about-intro,
  .page-content--about .about-contact p {
    margin-left: clamp(0px, 16vw, 18rem);
  }

  /* Statement headlines match the nav's own desktop size exactly
     (same typeface treatment as WORK / ARCHIVE / ABOUT). */
  .page-content--about .about-statement {
    font-size: 4rem;
  }
}

@media (max-width: 959px) {
  .page-content--about .about-territories {
    grid-template-columns: 1fr;
    column-gap: 0;
    row-gap: 2rem;
  }
  .page-content--about .about-statement {
    margin: 1.5rem 0;
  }
  /* the hard line breaks below are tuned for the wide desktop
     layout; on phone/tablet let the copy wrap naturally instead */
  .page-content--about br.about-break {
    display: none;
  }
}




/* ---------------------------------------------
   Work page — light theme + project list
--------------------------------------------- */

body.theme-light {
  --color-fg: #0a0a0a;
  --color-bg: #ffffff;
  --color-brand: #0000ff;   /* light theme = blue RGB — desktop only, see below */
}

/* on phone/tablet the brand (wordmark + tagline) reads in plain
   black instead of blue — blue is a desktop-only accent. */
@media (max-width: 959px) {
  body.theme-light {
    --color-brand: var(--color-fg);
  }
}

.work-list {
  position: relative;
  z-index: 1;
  list-style: none;
  margin: 9rem 0 0;
  padding: 0 var(--pad) var(--pad);
  font-size: 2rem;
  line-height: .875;
  text-transform: uppercase;
}

.work-list li {
  margin-top: .75rem;
}

.work-list li:first-child {
  margin-top: 0;
}

.work-list a {
  position: relative;
  cursor: default;
}

.mobile-media {
  margin-top: .5rem;
}

.mobile-media img,
.mobile-media video {
  display: block;
  width: 100%;
  height: auto;
}

/* ---------------------------------------------
   Hover preview — desktop only, single media item
   (image or video), reproducing christianlopez.xyz's
   ".floating" mechanism: fixed full-viewport box,
   padded inset, object-fit:contain so the media sits
   centered without cropping, no pointer events, no
   fade — an instant show/hide exactly like the reference.
--------------------------------------------- */

.work-preview {
  display: none;
}

@media (min-width: 960px) {
  .mobile-media {
    display: none;
  }

  .work-list {
    margin-top: 0;
    padding: var(--pad);
    /* the list's own type (4rem / line-height .875) sits ~8.5px
       higher than its padding-top implies, unlike the nav's flex
       layout — compensated here so the first item's glyph still
       lands exactly level with the header text above. */
    padding-top: calc(var(--pad) - 5.5px);
    /* same bottom offset as the fixed clock/temperature, so once
       the list is scrolled all the way down the last project's
       name sits right at the same baseline as the clock instead
       of leaving a gap above it. */
    padding-bottom: var(--pad);
    width: 50%;
    font-size: 4rem;
  }

  .work-list li {
    margin-top: 0;
  }

  /* these two titles are long enough to wrap onto a second line
     inside the 50%-width column — keep them on one line instead */
  .work-list a[href="/work/unemployed-boys-club"],
  .work-list a[href="/work/unemployed-girls-club"] {
    white-space: nowrap;
  }

  .work-preview {
    display: block;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
    z-index: 5;
  }

  /* position/size are set inline per hover by js/work.js — every
     appearance gets a fresh random spot and size, never the same
     placement twice, reproducing createdbyus.org's effect */
  .work-preview img,
  .work-preview video {
    display: none;
    position: fixed;
    /* contain, never cover — the box only ever scales the media,
       it never crops it — full frame, original aspect ratio kept */
    object-fit: contain;
    background: transparent;
  }

  /* images: one random position + size per page load (js/work.js
     sets top/left/width/height inline), reproducing the scale of
     createdbyus.org's mosaic — always a large, legible size, up to
     nearly the full viewport */
  .work-preview img[data-active] {
    display: block;
  }

  /* videos: always the same fixed, centered format as
     christianlopez.xyz/work's floating box — never randomized */
  .work-preview video {
    inset: 0;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    padding: 4rem;
    box-sizing: border-box;
  }

  @media (min-height: 60em) {
    .work-preview video {
      padding: 9rem;
    }
  }

  .work-preview video[data-active] {
    display: block;
  }
}


/* ---------------------------------------------
   Archive — Obra Playground
   A bounded, dark "viewport" panel holding an
   infinite panning mosaic of packshots (drag or
   two-finger trackpad scroll in any direction —
   tiles recycle so there is always more to see).
   Clicking a tile zooms it into a full-screen
   viewer; arrow keys then slide a filmstrip left/
   right so the *camera* feels like it is moving,
   not the picture just changing in place.
--------------------------------------------- */

.playground-page {
  position: relative;
  z-index: 1;
}

.playground-frame {
  position: fixed;
  inset: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  background: var(--color-bg);
  overflow: hidden;
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.playground-frame.is-grabbing {
  cursor: grabbing;
}

.playground-canvas {
  position: absolute;
  inset: 0;
}

.tile {
  position: absolute;
  box-sizing: border-box;
  padding: .15rem;
  will-change: transform;
}

.tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity .4s var(--ease), transform .2s var(--ease);
  pointer-events: none;
}

.tile img.is-loaded {
  opacity: 1;
}

.tile:hover img.is-loaded {
  opacity: 1;
  transform: scale(1.04);
}

.playground-label {
  position: absolute;
  bottom: var(--pad);
  left: var(--pad);
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: rgba(0, 0, 0, .45);
  pointer-events: none;
}

/* ---------------------------------------------
   Viewer — full-screen filmstrip
--------------------------------------------- */

.viewer {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s var(--ease);
  overflow: hidden;
}

.viewer.is-open {
  opacity: 1;
  pointer-events: auto;
}

.viewer-track {
  position: absolute;
  top: 0;
  left: 0;
  width: 300vw;
  height: 300vh;
  display: grid;
  grid-template-columns: repeat(3, 100vw);
  grid-template-rows: repeat(3, 100vh);
  transform: translate(-100vw, -100vh);
}

.viewer-track.no-transition {
  transition: none !important;
}

.viewer.is-animating .viewer-track {
  transition: transform .6s cubic-bezier(.65, 0, .35, 1);
}

.viewer-panel {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.viewer-panel img {
  max-width: min(82vw, 82vh);
  max-height: min(82vw, 82vh);
  object-fit: contain;
  transform: scale(.82);
  transition: transform .45s var(--ease);
}

.viewer.is-open .viewer-panel img {
  transform: scale(1);
}

.viewer-close {
  position: absolute;
  top: var(--pad);
  right: var(--pad);
  z-index: 2;
  font-size: 2rem;
  line-height: 1;
  background: none;
  border: 0;
  color: #0a0a0a;
  cursor: pointer;
  opacity: 0;
  transition: opacity .3s var(--ease) .15s;
}

.viewer.is-open .viewer-close {
  opacity: .7;
}

.viewer-close:hover {
  opacity: 1 !important;
}

.viewer-hint {
  position: absolute;
  right: var(--pad);
  bottom: var(--pad);
  z-index: 2;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #0a0a0a;
  opacity: 0;
  transition: opacity .3s var(--ease) .15s;
}

.viewer.is-open .viewer-hint {
  opacity: .5;
}

@media (max-width: 599px) {
  .viewer-hint {
    display: none;
  }
}

/* ---------------------------------------------
   Project page — Work detail
   One single full-bleed horizontal carousel:
   the first slide is the "related" row (links
   toward other projects), every slide after it
   is one of the project's own photos/videos at
   near-full viewport height — exactly the same
   sequence, so paging past the related row with
   an arrow or a swipe is what reveals the shoot,
   each frame filling almost the whole screen.
--------------------------------------------- */

.project-page main {
  position: relative;
  z-index: 1;
}

/* the header sits on its own solid strip above the gray
   gallery stage, like a printed masthead — everything
   below it goes gray, the header never does. */
.project-page .site-header {
  background: var(--color-bg);
  padding-bottom: 1.25rem;
}

.project-gallery {
  position: fixed;
  inset: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: var(--color-bg);
  cursor: pointer;
}

.project-gallery[data-arrow="left"] {
  cursor: w-resize;
}

.project-gallery[data-arrow="right"] {
  cursor: e-resize;
}


.gallery-track {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  display: flex;
  transform: translateX(-100%);
}

.gallery-track.no-transition {
  transition: none !important;
}

.project-gallery.is-animating .gallery-track {
  transition: transform .6s cubic-bezier(.65, 0, .35, 1);
}

.gallery-panel {
  width: 100vw;
  height: 100%;
  flex: 0 0 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6.5rem 3vw 3rem;
}

.gallery-panel img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.gallery-panel video {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: 100%;
  object-fit: contain;
}

/* slide 0 — the related row: the project you're
   leaving stays large and centered behind it, like a
   held frame, while the other projects float in front
   as a staggered shelf of thumbnails — tall / short /
   short / short / tall — held well clear of the screen
   edges, near the bottom of the frame. */
.gallery-panel--related {
  position: relative;
  display: block;
  padding: 0;
}

.related-anchor {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6.5rem 3vw 3rem;
}

.related-anchor img,
.related-anchor video {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  /* this is the project you're leaving — it fades back to
     make room for what's next, which is why only this image
     (never the related thumbnails) loses opacity here. */
  opacity: .4;
}

.related-rail {
  /* sized to its own content (capped at max-width) rather than
     stretched to fill it — a stretched box needs
     justify-content to visually center a shorter row, and that
     breaks the scroll starting position the moment the row is
     wider than the box (the browser starts scrolled to the
     middle of the overflow instead of at the first item). */
  position: absolute;
  left: 50%;
  bottom: 30vh;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  align-items: flex-end;
  gap: .6rem;
  /* 5 fixed-width columns + 4 gaps add up to a known total,
     so this only needs to be a safety ceiling, never the
     thing actually shaping the row. */
  max-width: 92vw;
  /* the row's own height comes from its tallest card, not a
     forced rail height — each card keeps its own picture's
     real proportions instead of being cropped to fit one. */
  height: auto;
  max-height: 46vh;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.related-rail::-webkit-scrollbar {
  display: none;
}

.related-item {
  flex: 0 0 auto;
  display: block;
  cursor: pointer !important;
  /* held back until JS reveals it — each one arrives in turn
     rather than the whole row popping in at once. */
  opacity: 0;
  transform: translateY(18px);
}

.related-item.is-in {
  opacity: 1;
  transform: translateY(0);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}

.related-item:nth-child(1).is-in { transition-delay: 0ms; }
.related-item:nth-child(2).is-in { transition-delay: 70ms; }
.related-item:nth-child(3).is-in { transition-delay: 140ms; }
.related-item:nth-child(4).is-in { transition-delay: 210ms; }
.related-item:nth-child(5).is-in { transition-delay: 280ms; }

.related-thumb {
  /* fixed, even column width — like the reference, height is
     whatever each picture's own aspect ratio gives at that
     width, which is what actually produces the taller/shorter
     rhythm across the row (not a forced crop). */
  width: 16vw;
  max-height: 46vh;
  overflow: hidden;
  background: #ececec;
}

.related-thumb img,
.related-thumb video {
  display: block;
  width: 100%;
  height: auto;
  max-height: 46vh;
  object-fit: cover;
  opacity: 1;
}

.related-label {
  display: inline-block;
  margin-top: .6rem;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--color-fg);
  background: var(--color-bg);
  padding: .2em .4em;
  white-space: nowrap;
}

.related-label .n {
  font-variant-numeric: tabular-nums;
  margin-right: .5em;
}

/* the project you're on: title, position, and the
   placeholder credits, all held in one vertical column
   pinned to the right edge — same type and color as the
   rest of the page, just stacked instead of run in a row. */
.project-info-rail {
  position: fixed;
  top: 8.5rem;
  right: var(--pad);
  bottom: 4.5rem;
  z-index: 6;
  width: min(19vw, 220px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-end;
  gap: 2rem;
  text-align: right;
  pointer-events: none;
  opacity: 1;
  transition: opacity .35s var(--ease);
}

.project-info-rail.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.project-info-rail > * {
  pointer-events: auto;
}

.project-info-head {
  display: flex;
  flex-direction: column;
  gap: .35em;
}

.project-info-title {
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: .02em;
}

.project-info-count {
  font-variant-numeric: tabular-nums;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  opacity: .55;
}

.project-info-credits {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  margin: 0;
}

.project-info-credits dt {
  font-size: .65rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  opacity: .5;
}

.project-info-credits dd {
  margin: .3em 0 0;
  font-size: .9rem;
  text-transform: uppercase;
}

/* exception: a project's own flowing bio/description text (used in
   place of a normal short credit value, e.g. instead of Client) stays
   in normal case for readability instead of shouting a whole paragraph */
.project-info-credits dd.credit-freetext {
  text-transform: none;
}

@media (max-width: 599px) {
  .gallery-panel {
    padding: 5.5rem 4vw 4.5rem;
  }

  .related-anchor {
    padding: 5.5rem 4vw 4.5rem;
  }

  .related-rail {
    bottom: 17vh;
    max-height: 28vh;
    max-width: 94vw;
    gap: .5rem;
  }

  .related-thumb {
    width: 38vw;
    max-height: 28vh;
  }

  .related-thumb img,
  .related-thumb video {
    max-height: 28vh;
  }

  .project-info-rail {
    top: auto;
    bottom: 4.5rem;
    right: var(--pad);
    width: 48vw;
    justify-content: flex-end;
    gap: 1.25rem;
  }

  .project-info-title {
    font-size: .85rem;
  }
}

.project-next {
  display: none;
}

/* Phone/tablet (<960px): the related/"other projects" row never
   appears in the filmstrip (see js/project.js), so the gallery no
   longer needs to be a fixed full-screen overlay -- it becomes a
   normal in-flow section, and the credits (previously a floating
   right-hand rail on top of the photo) become a plain stacked block
   that follows it in the page, left-aligned, ending with a
   "Next project" link -- same idea as the credits-below-the-photo
   layout on suzieandleo.com plus the Next project link/click-through
   on christianlopez.xyz. */
@media (max-width: 959px) {
  .project-gallery {
    position: relative;
    inset: auto;
    width: 100%;
    height: 100vh;
    height: 100svh;
  }

  .gallery-panel {
    /* .site-header stays position:fixed at the very top even
       in this in-flow mobile layout -- keep the same top
       clearance the photo already had so it isn't covered. */
    padding: 5.5rem 4vw 1.5rem;
  }

  .project-info-rail {
    position: static;
    top: auto;
    right: auto;
    bottom: auto;
    width: auto;
    max-width: none;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: flex-start;
    text-align: left;
    gap: 1.5rem;
    padding: 1.5rem var(--pad) 3.5rem;
    opacity: 1;
    pointer-events: auto;
  }

  .project-info-rail.is-hidden {
    /* the related row never shows on mobile, so the rail never
       needs to step aside for it */
    opacity: 1;
    pointer-events: auto;
  }

  .project-info-head,
  .project-info-credits {
    align-items: flex-start;
  }

  .project-next {
    display: block;
    width: 100%;
    margin-top: .5rem;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(254, 254, 254, .2);
    font-size: 1.6rem;
    text-transform: uppercase;
    letter-spacing: .01em;
  }
}

/* ---------------------------------------------
   Home intro — same loader as the previous site:
   the wordmark "OBRA STUDIO™" sits centered over a
   solid background, holds for a couple seconds, then
   scales down slightly and blurs out while it fades
   away, revealing the background video already
   playing underneath. Same motion/timing as the old
   site's loader — only the color scheme changed
   (white here, black there). See js/intro.js.
   Stays in the DOM afterwards (inert, opacity 0)
   rather than being removed. Once per browser
   session — see js/intro.js.
--------------------------------------------- */

.intro-loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-intro-bg);
  opacity: 1;
  /* plain opacity fade only -- no scale/blur. Combined with the
     white loader's opacity easing down from 1, a scale+blur here
     made the white background visibly shrink and soften right
     before it vanished (looked like it was "retracting"), and let
     the video/header show through blurred and misaligned for a
     moment. A pure fade has neither problem. */
  transition: opacity 1.4s var(--ease);
  user-select: none;
}

.intro-word {
  color: var(--color-intro-fg); /* fallback: plain text when JS
     doesn't run, or the reduced-motion path that skips the
     per-character reveal entirely */
  font-family: Arial, Helvetica, sans-serif;
  font-size: clamp(.85rem, 1.9vw, 1.35rem);
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  white-space: pre;
}

/* Each character starts dim/unrevealed and switches to the solid
   color on its own short delay -- js/intro.js splits the word into
   one span per character and adds "is-revealing" a frame later, a
   small, fast version of the type-on reveal effect. */
.intro-char {
  display: inline-block;
  color: var(--color-intro-fg-dim);
  transition: color .35s ease;
  transition-delay: calc(var(--i, 0) * 65ms);
}

.intro-word.is-revealing .intro-char {
  color: var(--color-intro-fg);
}

/* returning visitor this session — data-intro="skip" is set on
   <html> synchronously before paint (see index.html), so the
   loader is hidden from the very first frame, no flash. */
html[data-intro="skip"] .intro-loader {
  display: none;
}

.intro-loader.hidden {
  opacity: 0;
  pointer-events: none;
}

/* Homepage video/header/clock fade in together right as the loader
   finishes fading out (js/intro.js adds "intro-done" to <body> at
   the same moment it adds "hidden" to the loader) -- a soft, timed
   arrival instead of the video/nav just popping in behind the
   loader the instant it becomes transparent. Scoped to body.home
   since #intro-loader only exists on index.html. */
body.home .bg-video,
body.home .site-header,
body.home .clock {
  opacity: 0;
  transition: opacity 1.8s var(--ease);
}

body.home .site-header,
body.home .clock {
  transition-delay: .4s;
}

body.home.intro-done .bg-video,
body.home.intro-done .site-header,
body.home.intro-done .clock {
  opacity: 1;
}

/* returning visitor this session: data-intro="skip" hides the
   loader before first paint (see index.html) -- the video/header
   should just be there already, not fade in from nothing. */
html[data-intro="skip"] body.home .bg-video,
html[data-intro="skip"] body.home .site-header,
html[data-intro="skip"] body.home .clock {
  opacity: 1;
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .intro-loader {
    transition: opacity .3s linear;
  }

  .intro-char {
    transition: none;
  }

  body.home .bg-video,
  body.home .site-header,
  body.home .clock {
    transition-duration: .2s;
    transition-delay: 0s;
  }
}
