:root {
  --paper: #eef0ed;
  --ink: #17191b;
  --muted: #656a67;
  --line: rgb(23 25 27 / 16%);
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --font: "Switzer", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --gutter: 4.5vw;
}

* { box-sizing: border-box; }

html, body { min-height: 100%; }

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

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

body {
  min-width: 320px;
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  overflow-x: hidden;
}

button, a { color: inherit; font: inherit; touch-action: manipulation; }
button {
  border: 0;
  background: transparent;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
a { text-decoration: none; }

/* subtle film grain, purely decorative */
.grain {
  position: fixed;
  z-index: 50;
  inset: 0;
  pointer-events: none;
  opacity: 0.025;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

/* ---------- header / footer ---------- */
/* Both are pinned to the viewport edge, so on a notched/Dynamic Island
   phone (in viewport-fit=cover mode, set in index.html) they need the
   safe-area insets added on top of the usual padding, or the header text
   sits under the notch and the footer sits flush against the home
   indicator. env() resolves to 0px on devices without a cutout, so this is
   a no-op everywhere else. */
.site-header,
.site-footer {
  position: fixed;
  z-index: 20;
  right: 0;
  left: 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-right: calc(var(--gutter) + env(safe-area-inset-right, 0px));
  padding-left: calc(var(--gutter) + env(safe-area-inset-left, 0px));
}

.site-header { top: 0; padding-top: calc(27px + env(safe-area-inset-top, 0px)); }
.site-footer { bottom: 0; padding-bottom: calc(23px + env(safe-area-inset-bottom, 0px)); }

.logo {
  font-size: 19px;
  font-weight: 500;
  letter-spacing: -0.02em;
}

.main-nav { display: flex; gap: 24px; }

/* The project/info overlays each have their own "Back to work" / "Close"
   button in the same top-right corner as this nav (same top/right offsets,
   deliberately — it's the site's one consistent "exit" corner). With the
   overlay's z-index above the header, that button rendered directly on top
   of "Work / Info", the two reading as overlapping text. Only one exit
   affordance belongs in that corner at a time, so the nav hides for as
   long as either overlay is open — the logo stays, still linking home. */
body:has(.view-project.is-open) .main-nav,
body:has(.view-info.is-open) .main-nav {
  visibility: hidden;
}

.nav-link,
.site-footer,
.kicker,
.project-index {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* The padding widens the tap area to a comfortable size; the equal negative
   margin keeps the label exactly where it was. */
.nav-link {
  margin: -12px -10px;
  padding: 12px 10px;
  color: var(--muted);
  transition: color 180ms ease, opacity 120ms ease-out;
}
.nav-link:focus-visible, .nav-link.is-active { color: var(--ink); }
.nav-link:active { opacity: 0.6; }

/* Hover only where a real pointer exists: on touch the first tap would leave
   :hover stuck on, which here also hides every other title. */
@media (hover: hover) and (pointer: fine) {
  .nav-link:hover { color: var(--ink); }
}

.site-footer { color: var(--muted); }
.site-footer span:only-child { margin-left: auto; }

/* ---------- layout ---------- */
main {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 100dvh;
  padding: calc(92px + env(safe-area-inset-top, 0px)) var(--gutter) calc(82px + env(safe-area-inset-bottom, 0px));
}

.view { width: 100%; }

/* Every title is the same size. The empty paper above and below the row is
   what lets a full-bleed hover video sit behind the page without fighting
   the text for legibility. */
.work-layout { width: 100%; }

/* A single horizontal row with tight gaps. wrap is only a safety net for
   mid-width windows; each item's margin-top (inline, Fibonacci numbers)
   gives the row a slight uneven rhythm without spreading it out. */
.work-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 18px 88px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.kicker { margin: 0 0 26px 2px; color: var(--muted); }

.work-item { flex: 0 0 auto; }

.work-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 14px 0;
  text-align: left;
  transform-origin: left center;
  transition: opacity 280ms var(--ease), transform 140ms var(--ease);
}

.work-row:active { transform: scale(0.985); }

.work-row:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 5px;
}

/* Stacked, upright digits (2/0/2/6) rather than a horizontal run — the one
   ornamental flourish, borrowed from the same reference. text-orientation
   keeps Latin numerals upright instead of sideways inside vertical text. */
.work-year {
  align-self: center;
  writing-mode: vertical-lr;
  text-orientation: mixed;
  transform: rotate(180deg);
  color: var(--muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.05em;
  line-height: 1;
}

.work-title-wrap {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
  min-width: 0;
}

.work-title {
  font-size: 32px;
  font-weight: 500;
  letter-spacing: -0.04em;
  line-height: 1.06;
}

.work-sub {
  color: var(--muted);
  font-size: 14px;
  font-style: italic;
  line-height: 1.2;
}

.work-row:focus-visible,
.work-row.is-current {
  color: var(--ink);
}

@media (hover: hover) and (pointer: fine) {
  .work-row:hover { color: var(--ink); }
  .work-grid:has(.work-row:hover) .work-row:not(:hover) { opacity: 0; }
}

/* While a preview fills the screen the dark ink would sit on top of the frame,
   so the page chrome and the hovered title switch to light. Overlays are left
   out: they paint their own paper background and keep the normal ink. */
body:has(.preview-media) :is(.site-header, .site-footer, main) {
  --ink: #fff;
  --muted: rgb(255 255 255 / 0.85);
  color: var(--ink);
  text-shadow: 0 1px 14px rgb(0 0 0 / 0.45);
}

/* Legibility without dimming the picture: a soft pool of shade under the
   hovered title (position set from script.js) plus a light fade behind the
   header and footer. The rest of the frame keeps its original grade. */
.fullscreen-stage .preview-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 34vw 17vh at var(--row-x, 50%) var(--row-y, 50%), rgb(0 0 0 / 0.5), transparent 72%),
    linear-gradient(to bottom, rgb(0 0 0 / 0.42), transparent 15vh),
    linear-gradient(to top, rgb(0 0 0 / 0.42), transparent 13vh);
}

::selection { background: var(--ink); color: var(--paper); }

/* On wide desktop windows every title sits on one row: no wrapping, with the
   column gap and type size scaling with the viewport so all of them fit. */
@media (min-width: 1100px) {
  /* The gap is only the minimum: the titles spread across the whole width so
     the composition isn't left-heavy with an empty right side. */
  .work-grid { flex-wrap: nowrap; justify-content: space-between; column-gap: clamp(16px, calc(5vw - 22px), 88px); }
  /* script.js adds this only when the titles genuinely don't fit on one row
     (more projects, longer names), so nothing is ever clipped. */
  .work-grid.is-wrapping { flex-wrap: wrap; justify-content: flex-start; }

  /* Type grows with the screen instead of stopping at its laptop size. */
  .work-title { font-size: clamp(24px, 2.3vw, 38px); }
  .work-sub { font-size: clamp(14px, 1vw, 17px); }
  .work-year { font-size: clamp(11px, 0.8vw, 13px); }
  .nav-link, .site-footer, .kicker, .project-index { font-size: clamp(11px, 0.85vw, 13px); }
  .info-tagline { font-size: clamp(44px, 4.2vw, 76px); }
}

/* ---------- fullscreen stage ----------
   No boxed frame: on hover/focus the current project's clip fills the
   entire viewport behind the page (fixed, out of flow, z-index below the
   content), then clears back to plain paper on leave — the page itself
   becomes the preview instead of a small panel beside the list. */
.fullscreen-stage {
  position: fixed;
  z-index: 1;
  inset: 0;
  pointer-events: none;
}

.fullscreen-stage .preview-media {
  position: absolute;
  inset: 0;
}

.fullscreen-stage video,
.fullscreen-stage img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* main is the only unpositioned sibling of the fixed fullscreen-stage —
   without a stacking context of its own it would paint (and sit) below it
   regardless of z-index. Header/footer/overlays are already position:fixed
   elsewhere in this file, so they stack above it correctly on their own. */
main { position: relative; z-index: 2; }

/* ---------- info / project overlays ---------- */
.view-info,
.view-project {
  position: fixed;
  z-index: 30;
  inset: 0;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  /* Leaving is quicker than arriving: the system responds fast, the
     entrance can take its time. */
  transition: opacity 200ms ease-out, visibility 0s linear 200ms;
}

.view-info.is-open,
.view-project.is-open {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
  transition: opacity 280ms var(--ease), visibility 0s;
}

.view-info {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(280px, 0.8fr);
  gap: 64px;
  align-content: center;
  align-items: center;
  padding: 92px 12vw 82px;
  background: var(--paper);
}

.info-tagline {
  margin: 0;
  font-size: 44px;
  font-weight: 400;
  letter-spacing: -0.05em;
  line-height: 0.99;
}

/* Contact is the whole point of this page, so it gets real weight. */
.info-body { display: flex; flex-direction: column; gap: 12px; max-width: 420px; font-size: clamp(18px, 1.7vw, 27px); line-height: 1.3; letter-spacing: -0.02em; }
.info-body a { text-decoration: underline; text-underline-offset: 4px; }

/* Padding extends the tap target to near the 44px touch-target minimum
   without moving the visible text — top/right are pulled in by the same
   amount the padding pushes the text back out, so the on-screen position
   is unchanged. The 11px uppercase label alone was only ~53x17px, easy to
   miss on a real phone. */
.dismiss-btn {
  position: absolute;
  top: calc(27px + env(safe-area-inset-top, 0px) - 14px);
  right: calc(var(--gutter) + env(safe-area-inset-right, 0px) - 14px);
  padding: 14px;
  color: var(--muted);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition: opacity 120ms ease-out;
}
.dismiss-btn:active { opacity: 0.6; }

.view-project {
  display: grid;
  grid-template-columns: minmax(0, 1.75fr) minmax(210px, 0.55fr);
  gap: 52px;
  align-items: center;
  padding: 92px 11vw 82px;
  background: var(--paper);
}

.project-player {
  position: relative;
  width: 100%;
  overflow: hidden;
  background: #0d0f10;
  animation: project-media-in 520ms var(--ease) both;
}

/* If the film can't be loaded the poster stays up and says so, instead of a
   dead black box. */
.project-player[data-failed]::after {
  content: "video unavailable at the moment";
  position: absolute;
  inset: auto 0 22% 0;
  text-align: center;
  color: #fff;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-shadow: 0 1px 12px rgb(0 0 0 / 0.7);
  pointer-events: none;
}

/* Lost in an earlier refactor (the selector used to be shared with the old
   .preview-stage box, which no longer exists) — without it the video falls
   back to its native pixel size, gets clipped by .project-player's own
   overflow:hidden, and shows a zoomed-in fragment of the frame instead of
   the whole picture scaled to fit. */
.project-player video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@keyframes project-media-in {
  from { opacity: 0; transform: scale(0.94); }
  to { opacity: 1; transform: scale(1); }
}

.project-meta { animation: project-copy-in 440ms 130ms var(--ease) both; }
@keyframes project-copy-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

.project-title {
  margin: 16px 0 12px;
  font-size: 29px;
  font-weight: 400;
  letter-spacing: -0.045em;
  line-height: 1.08;
}

.project-brand,
.project-campaign { display: block; }

.project-controls {
  position: absolute;
  right: calc(var(--gutter) + env(safe-area-inset-right, 0px));
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  display: flex;
  gap: 18px;
}

.project-arrow {
  width: 36px;
  height: 36px;
  color: var(--muted);
  font-size: 20px;
  transition: color 180ms ease, transform 180ms var(--ease);
}
.project-arrow:active { transform: scale(0.92); }

@media (hover: hover) and (pointer: fine) {
  .project-arrow:hover { color: var(--ink); }
}

/* ---------- responsive ---------- */
@media (max-width: 900px) {
  .site-header,
  .site-footer {
    padding-right: calc(24px + env(safe-area-inset-right, 0px));
    padding-left: calc(24px + env(safe-area-inset-left, 0px));
  }
  .site-header { padding-top: calc(20px + env(safe-area-inset-top, 0px)); }
  /* The footer only holds the year, which reads as clutter at the bottom
     of a mobile list — desktop keeps it. */
  .site-footer { display: none; }
  main { padding: calc(92px + env(safe-area-inset-top, 0px)) 24px 34px; }

  /* On narrow screens the row stacks into a plain list. The per-item
     margin-top is inline, so it needs !important to be cleared here. */
  main { min-height: 0; justify-content: flex-start; }
  .work-grid { flex-direction: column; gap: 4px; }
  .work-item { margin-top: 0 !important; }
  .work-title { font-size: 25px; }

  .view-info { grid-template-columns: 1fr; gap: 30px; padding: 92px 24px 40px; }
  .info-tagline { font-size: 37px; }
  .view-project { grid-template-columns: 1fr; align-content: center; gap: 25px; padding: 92px 24px 72px; overflow-y: auto; overscroll-behavior: contain; }
  .project-controls { right: 24px; bottom: 15px; }
}

@media (max-width: 540px) {
  .site-header { align-items: center; }
  .logo { font-size: 17px; }
  .main-nav { gap: 17px; }
  .work-row { padding: 11px 0; }
  .work-title { font-size: 22px; }
  .work-sub { width: 100%; font-size: 13px; }
  .project-title { font-size: 26px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
  }
}
