/* ==========================================================================
   Katie Murday — styles
   Spec: Web Design – Katie Murday (04-30). Two text colours on white,
   one monospace family. Interstate Mono is the spec's face; Overpass Mono
   stands in (both descend from the US highway signage alphabet). To switch
   to licensed Interstate Mono, add its @font-face and change --font below.
   ========================================================================== */

@font-face {
  font-family: "Overpass Mono";
  font-weight: 400;
  font-display: swap;
  src: url("fonts/overpass-mono-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Overpass Mono";
  font-weight: 600;
  font-display: swap;
  src: url("fonts/overpass-mono-latin-600-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Overpass Mono";
  font-weight: 700;
  font-display: swap;
  src: url("fonts/overpass-mono-latin-700-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Overpass Mono";
  font-weight: 400;
  font-display: swap;
  src: url("fonts/overpass-mono-latin-ext-400-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Overpass Mono";
  font-weight: 600;
  font-display: swap;
  src: url("fonts/overpass-mono-latin-ext-600-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Overpass Mono";
  font-weight: 700;
  font-display: swap;
  src: url("fonts/overpass-mono-latin-ext-700-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --ink: #1f1f1f;        /* off-black text */
  --lav: #b279e7;        /* lavender: active + hover */
  --lav-pale: #dbc0f6;   /* email/phone hover */
  --paper: #ffffff;
  --font: "Overpass Mono", "Interstate Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --gutter: clamp(1.25rem, 3.6vw, 3.25rem);
  --header-h: 6.5rem;    /* kept in sync by app.js; used to fit layouts to one screen */
  --fade: 0.3s ease;
}

*,
*::before,
*::after { box-sizing: border-box; }

html {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: 16px;
  font-variant-ligatures: none; /* a monospace shouldn't join "fi" */
  -webkit-text-size-adjust: 100%;
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; transition: color var(--fade); }
button { font: inherit; color: inherit; }

/* The spec: Interstate Mono has no italic, so italics are a synthetic slant. */
em, i, .italic { font-style: italic; font-synthesis: style; }

:focus-visible { outline: 2px solid var(--lav); outline-offset: 3px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}

/* ---------- header ---------- */

.site-header {
  position: relative;
  z-index: 30;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 2rem;
  padding: clamp(1.5rem, 3.4vw, 2.75rem) var(--gutter) 1rem;
  background: var(--paper);
}

.wordmark {
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.wordmark:hover { color: var(--lav); }

.nav { display: flex; gap: clamp(1.5rem, 2.6vw, 2.5rem); }
.nav a {
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.nav a:hover { color: var(--lav); }
.nav a[aria-current="page"] { color: var(--lav); font-weight: 700; }

.menu-toggle {
  display: none;
  width: 2.25rem;
  height: 2.25rem;
  margin: -0.5rem -0.4rem -0.5rem 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  align-self: center;
  -webkit-tap-highlight-color: transparent;
}
.menu-toggle svg { width: 1.4rem; height: 1.4rem; margin: auto; display: block; }
.menu-toggle line {
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  transform-origin: 12px 12px; /* centre of the 24×24 icon */
  transition: transform 0.25s ease, opacity 0.2s ease;
}
/* open: the outer lines slide to the middle and turn into an ×; the middle one fades */
.menu-toggle[aria-expanded="true"] line:nth-child(1) { transform: rotate(45deg) translateY(6px) scaleX(1.2); }
.menu-toggle[aria-expanded="true"] line:nth-child(2) { opacity: 0; }
.menu-toggle[aria-expanded="true"] line:nth-child(3) { transform: rotate(-45deg) translateY(-6px) scaleX(1.2); }
/* (scaleX lengthens the diagonals so the × is about as wide as the ≡) */
/* lavender while the menu is open, as in the spec */
.menu-toggle[aria-expanded="true"] { color: var(--lav); }

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 20;
  padding: calc(var(--header-h) + 2rem) var(--gutter) 2.5rem;
  background: var(--paper);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3.5rem;
}
.mobile-menu[hidden] { display: none; }
.mobile-menu nav { display: flex; flex-direction: column; align-items: center; gap: 2.2rem; }
.mobile-menu a { font-size: 1.1rem; letter-spacing: 0.04em; text-transform: uppercase; }
.mobile-menu a[aria-current="page"] { color: var(--lav); font-weight: 700; }
.mobile-menu img { width: 9.5rem; align-self: center; margin-right: 4rem; }

main {
  flex: 1;
  width: 100%;
  padding: 0 var(--gutter);
}

/* ---------- shared: coming-soon watercolour ---------- */

.watercolour {
  background: #c9a6ec url("images/watercolor.jpg") center / cover no-repeat;
  color: var(--paper);
}

/* ---------- home: collage ---------- */

/* One 2:1 "screen", scaled to fit the viewport under the header.
   Slot positions are measured off the spec's home page (slide 1). */
.collage {
  position: relative;
  width: min(100%, calc((100svh - var(--header-h) - 3rem) * 2));
  aspect-ratio: 2 / 1;
  margin: clamp(4rem, 9vh, 6rem) auto 0;
}
.collage + .collage { margin-top: 8rem; }
.collage:last-child { margin-bottom: 3rem; }

/* Each still is hung like the works on the Directing and Producing walls:
   a thin off-black frame, a white mat, a hairline around the picture, a soft
   shadow, and a string up to a nail. The slot below sets the size of the
   picture itself; the mat and frame are drawn outside it (--m is the mat, --bw
   the frame, --hs the height of the string). */
.still {
  --m: clamp(10px, 3.4vw, 34px);
  --bw: clamp(2px, 0.28vw, 4.5px);
  --hs: clamp(1rem, 2.6vw, 2.2rem);
  --f: calc(var(--m) + var(--bw)); /* how far the frame reaches beyond the picture */
  position: absolute;
  display: block;
  background: #eee;
  box-shadow:
    0 0 0 var(--m) var(--paper),
    0 0 0 calc(var(--m) + var(--bw)) var(--ink),
    2px 5px 12px calc(var(--m) + var(--bw) - 2px) rgba(31, 31, 31, 0.22);
  outline: 1px solid rgba(31, 31, 31, 0.22);
  outline-offset: clamp(3px, 0.5vw, 7px);
}
.still:focus-visible { outline: 2px solid var(--lav); outline-offset: calc(var(--m) + var(--bw) + 3px); }
.cord {
  position: absolute;
  left: calc(-1 * (var(--m) + var(--bw)));
  width: calc(100% + 2 * (var(--m) + var(--bw)));
  bottom: calc(100% + var(--m) + var(--bw) / 2); /* ends in the middle of the frame's outline */
  height: var(--hs);
}
.cord svg { display: block; width: 100%; height: 100%; overflow: visible; }
.cord polyline {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.2;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  opacity: 0.75;
}
.cord .nail { position: absolute; left: 50%; top: 0; }
.still img { width: 100%; height: 100%; object-fit: cover; }
/* Hover: each still tilts like a painting knocked askew on its nail. It
   pivots on a point just above its top edge, swings past, settles at a
   slight angle, and casts a soft shadow as if lifted off the wall. Leaving
   swings it back to straight. --tilt alternates direction by spot. */
@media (hover: hover) {
  .still {
    transform-origin: 50% calc(-1 * (var(--m) + var(--bw) / 2 + var(--hs))); /* the nail */
    /* browsers without linear() fall back to a single overshoot */
    transition: transform 0.9s cubic-bezier(0.34, 1.7, 0.5, 1), box-shadow 0.45s ease;
    transition: transform 1.1s linear(0, 0.133, 0.306, 0.498, 0.692, 0.872, 1.028, 1.152, 1.24, 1.294, 1.315, 1.309, 1.28, 1.236, 1.182, 1.125, 1.069, 1.019, 0.977, 0.944, 0.922, 0.91, 0.907, 0.912, 0.922, 0.937, 0.953, 0.97, 0.986, 1, 1.011, 1.019, 1.025, 1.027, 1.027, 1.025, 1), box-shadow 0.45s ease;
  }
  .still:hover,
  .still:focus-visible {
    transform: rotate(var(--tilt, 2deg));
    box-shadow:
      0 0 0 var(--m) var(--paper),
      0 0 0 calc(var(--m) + var(--bw)) var(--ink),
      4px 16px 28px calc(var(--m) + var(--bw) - 4px) rgba(31, 31, 31, 0.3);
    z-index: 2;
  }
}
.slot-1, .slot-3, .slot-5 { --tilt: -2deg; }
.slot-2, .slot-4 { --tilt: 2deg; }

/* Each slot is the whole frame's place on the page; the picture sits inside
   it (inset by --f), keeping the slot's shape. */
.slot-1 { left: calc(3% + var(--f));    top: calc(3.7% + var(--f));  width: calc(22.8% - 2 * var(--f)); aspect-ratio: 1.75; }
.slot-2 { left: calc(55.7% + var(--f)); top: calc(0% + var(--f));    width: calc(30.9% - 2 * var(--f)); aspect-ratio: 2.4; }
.slot-3 { left: calc(36.9% + var(--f)); top: calc(41% + var(--f));   width: calc(36.7% - 2 * var(--f)); aspect-ratio: 1.77; }
.slot-4 { left: calc(0% + var(--f));    top: calc(63.3% + var(--f)); width: calc(17.8% - 2 * var(--f)); aspect-ratio: 1.77; }
.slot-5 { left: calc(80.2% + var(--f)); top: calc(73.3% + var(--f)); width: calc(19.4% - 2 * var(--f)); aspect-ratio: 1.8; }

.collage .cat { position: absolute; pointer-events: none; height: auto; }
.cat-ladder { left: 26.2%; top: 57.4%; width: 11%; }
.cat-papers { left: 80.6%; top: 36.5%; width: 16.4%; }
.cat-reach  { display: none; }
.cat-dust   { display: none; }

@media (max-width: 760px) {
  .collage {
    box-sizing: border-box;
    padding-inline: calc(clamp(10px, 3.4vw, 34px) + 3px); /* room for each frame */
    width: 100%;
    aspect-ratio: auto;
    margin-top: 4.5rem;
    display: flex;
    flex-direction: column;
    gap: 4.25rem;
  }
  .collage + .collage { margin-top: 4.5rem; }
  .still { position: relative; left: auto !important; top: auto !important; }
  /* stacked, alternating, as on slide 7 */
  .slot-1 { width: 58%; align-self: flex-start; }
  .slot-2 { width: 100%; align-self: flex-end; }
  .slot-3 { width: 66%; align-self: flex-end; margin-right: 6%; }
  .slot-4 { width: 74%; align-self: flex-start; }
  .slot-5 { width: 86%; align-self: flex-end; }
  .cat-ladder, .cat-papers { display: none; }
  /* the ladder cat stands in the gap left of the centre frame, dusting it */
  .cat-reach { display: none; }
  .still .cat-dust {
    display: block;
    position: absolute;
    width: 34%;
    height: auto;
    right: calc(100% + var(--f) + 4px);
    bottom: calc(-1 * var(--f));
    object-fit: contain;
  }
}

/* ---------- directing / producing: poster grid ---------- */

/* The wall: one continuous scattered layout on 12 columns. app.js deals the
   projects into blocks and gives each piece its row and columns (--rd, --cd,
   --sd on desktop and tablet; --rm, --cm, --sm on phones), how far to push
   it down (--dd, --dm, in rem), a slightly different width (--pw) and a nudge
   across (--jx, 0-1) within its own columns. */
.wall {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(0.6rem, 1.6vw, 1.5rem);
  row-gap: clamp(4rem, 9vh, 6rem); /* room for the strings */
  align-items: start;
  width: min(100%, 72rem);
  margin: clamp(3.5rem, 8vh, 5.5rem) auto 5rem;
}

.piece {
  display: block;
  grid-row: var(--rd);
  grid-column: var(--cd) / span var(--sd);
}
.slot {
  position: relative;
  display: block;
  width: var(--pw, 80%);
  margin-left: calc((100% - var(--pw, 80%)) * var(--jx, 0.5));
  margin-top: calc(var(--dd, 0) * 1rem);
}

.wall-cat {
  grid-row: var(--rd);
  grid-column: var(--cd) / span var(--sd);
  width: 100%;
  max-width: 7.5rem;
  height: auto;
  align-self: end;
  pointer-events: none;
}
.wall-cat.painter { justify-self: start; }
.wall-cat.sitter { justify-self: end; }
@media (min-width: 761px) { .wall-cat.fin.few { display: none; } }
.wall-cat.fin { grid-column: 6 / span 2; justify-self: center; max-width: 5.5rem; margin-top: 1rem; }

/* The frame, its mat and the picture; this part tilts. A thin off-black
   frame around a white mat, like a gallery print. The mat is a percentage
   of the frame's width (10% each side, 11% below), so it scales with it.
   --k turns "14% of the width" into a share of the frame's height, which is
   what the nail's position needs. */
.hang {
  --mat-x: 10%;
  --mat-b: 11%;
  --bw: clamp(2px, 0.28vw, 4.5px); /* frame thickness */
  --k: calc(1 / (0.8 / var(--ar, 0.647) + 0.21));
  position: relative;
  display: block;
  box-sizing: border-box;
  border: var(--bw) solid var(--ink);
  background: var(--paper);
  /* soft shadow so the frame sits off the wall */
  box-shadow: 2px 5px 12px -2px rgba(31, 31, 31, 0.22);
  padding: var(--mat-x) var(--mat-x) var(--mat-b);
  transform-origin: 50% calc(var(--k) * -14% - var(--bw) / 2); /* the nail */
}
/* The string runs from the top corners of the frame up to the nail. Its height is
   14% of the poster's width (set with aspect-ratio, so it scales with it). */
.wire {
  position: absolute;
  left: 0;
  bottom: calc(100% + var(--bw) / 2); /* the string ends in the middle of the frame's outline */
  width: 100%;
  height: auto;
  aspect-ratio: 100 / 14;
  overflow: visible;
}
.wire polyline {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.2;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  opacity: 0.75;
}
.nail {
  position: absolute;
  left: 50%;
  top: calc(var(--k) * -14% - var(--bw) / 2);
  width: 6px;
  height: 6px;
  translate: -50% -50%;
  border-radius: 50%;
  background: var(--ink);
}
.nail:not(.stub) { cursor: pointer; }
.nail:not(.stub)::after { content: ""; position: absolute; inset: -14px; border-radius: 50%; } /* a bigger target for fingers */
.nail.stub { position: absolute; z-index: 0; translate: -50% -50%; width: 6px; height: 6px; border-radius: 50%; background: var(--ink); }
/* a frame that has been cut loose: the original goes invisible and a copy
   (.fall-body) falls to the bottom of the page; see the easter egg in app.js */
.hang.fallen, .still.fallen { visibility: hidden; }
.still.fallen .cat { visibility: visible; } /* the cat beside a cut frame stays */
.fall-body {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 25;
  display: block;
  transform-origin: 50% 50%;
  will-change: transform;
}
.fall-body .hang {
  width: 100%;
  margin: 0;
  transition: none !important;
  transform: none !important;
  box-shadow: 1px 3px 9px rgba(31, 31, 31, 0.32);
}
.fall-body .still {
  transform: none !important;
  transition: none !important;
  margin: 0;
  z-index: auto;
}

/* Hover: the poster swings as if knocked on its hook (same motion as the
   home page) and lifts off the wall; the label underneath stays put. */
@media (hover: hover) {
  .hang {
    transition: transform 0.9s cubic-bezier(0.34, 1.7, 0.5, 1);
    transition: transform 1.1s linear(0, 0.133, 0.306, 0.498, 0.692, 0.872, 1.028, 1.152, 1.24, 1.294, 1.315, 1.309, 1.28, 1.236, 1.182, 1.125, 1.069, 1.019, 0.977, 0.944, 0.922, 0.91, 0.907, 0.912, 0.922, 0.937, 0.953, 0.97, 0.986, 1, 1.011, 1.019, 1.025, 1.027, 1.027, 1.025, 1);
  }
  .hang { transition-property: transform, box-shadow; }
  .piece:hover,
  .piece:focus-visible { position: relative; z-index: 2; }
  .piece:hover .hang,
  .piece:focus-visible .hang { transform: rotate(var(--tilt, 2deg)); }
  .piece:hover .hang,
  .piece:focus-visible .hang { box-shadow: 4px 16px 28px -4px rgba(31, 31, 31, 0.3); }
}

.poster {
  position: relative;
  aspect-ratio: var(--ar, 0.647);  /* the poster's own shape (see app.js), 11:17 until it loads */
  overflow: hidden;
  /* a hairline just around the picture, like the cut edge of a mat */
  outline: 1px solid rgba(31, 31, 31, 0.22);
  outline-offset: clamp(3px, 0.5vw, 7px);
}
.poster:not(.watercolour) { background: #f3eefa; }
.poster img { width: 100%; height: 100%; object-fit: cover; }
.poster.watercolour {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.poster.watercolour .poster-label {
  font-size: clamp(0.95rem, 1.9vw, 1.5rem);
  font-weight: 700;
  line-height: 1.15;
  max-width: 78%;
  text-align: center;
  text-wrap: balance;
  overflow-wrap: anywhere;
  text-transform: uppercase;
  transition: opacity var(--fade);
}
/* spec: hover fades a white overlay over the poster, coming-soon ones too */
.poster::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--paper);
  opacity: 0;
  transition: opacity var(--fade);
}
.piece:hover .poster::after,
.piece:focus-visible .poster::after { opacity: 0.7; }

/* The title sits over the picture (the frame's padding matches the mat),
   centred, and fades in with the white overlay. (Touchscreens can't hover:
   see below.) */
.poster-title {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--mat-x) calc(var(--mat-x) + 9%) var(--mat-b);
  pointer-events: none;
  text-align: center;
  overflow-wrap: anywhere;
  text-wrap: balance;
  font-size: clamp(0.95rem, 1.45vw, 1.2rem);
  font-style: italic;
  font-synthesis: style;
  line-height: 1.3;
  opacity: 0;
  transition: opacity var(--fade);
}
.piece:hover .poster-title,
.piece:focus-visible .poster-title { opacity: 1; }
.piece:hover .poster-label,
.piece:focus-visible .poster-label { opacity: 0; }
.hang:has(.watercolour) .poster-title { display: none; }
.piece:hover .poster.watercolour .poster-label { opacity: 1; }

.poster-meta {
  display: block;
  width: max-content;
  max-width: 100%;
  margin: 0.6rem 0 0;
  position: relative;
  left: 50%;
  translate: -50% 0;
  text-align: center;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* Touchscreens have no hover, so the title shows under the poster instead
   (as in the mobile mockup, slide 8). */
.poster-caption { display: none; }
@media (hover: none) {
  .poster-title { display: none; }
  .poster-caption {
    display: block;
    margin: 0.55rem 0 0;
    text-align: center;
    font-size: 0.85rem;
    font-style: italic;
    font-synthesis: style;
    line-height: 1.25;
  }
  .poster-meta { margin-top: 0.2rem; }
}

.empty-note { text-align: center; margin: 6rem auto; font-size: 0.9rem; }

/* Phones: the same idea on 4 columns, in rows of one or two; most cats step aside. */
@media (max-width: 760px) {
  .wall { grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: 1rem; row-gap: 3.5rem; margin-top: 3.5rem; }
  .piece { grid-row: var(--rm); grid-column: var(--cm) / span var(--sm); }
  .slot { width: calc(var(--pw, 80%) + 10%); margin-left: calc((90% - var(--pw, 80%)) * var(--jx, 0.5)); margin-top: calc(var(--dm, 0) * 1rem); }
  .wall-cat { display: none; }
  .wall-cat.fin { display: block; grid-row: var(--rm); grid-column: 2 / span 2; }
  .poster.watercolour .poster-label { font-size: 1.05rem; }
  .poster-meta { font-size: 0.58rem; }
}

/* ---------- project pop-up ---------- */

.popup {
  --popup-gap: clamp(1rem, 3.5vw, 3rem);   /* space around the panel */
  --panel-pad: clamp(2.75rem, 4.5vw, 4rem); /* space inside it */
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  padding: var(--popup-gap);
}
.popup-veil {
  position: absolute;
  inset: 0;
  background: rgba(31, 31, 31, 0.5);
  opacity: 0;
  transition: opacity 0.3s ease;
}
.popup-panel {
  position: relative;
  width: min(100%, 82rem);
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--panel-pad);
  background: var(--paper);
  border: clamp(2px, 0.28vw, 4.5px) solid var(--ink); /* the same frame edge as the posters */
  box-shadow: 0 30px 60px -20px rgba(31, 31, 31, 0.45);
  opacity: 0;
  translate: 0 14px;
  scale: 0.985;
  transition: opacity 0.3s ease, translate 0.35s cubic-bezier(0.2, 0.8, 0.2, 1), scale 0.35s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.popup.show .popup-veil { opacity: 1; }
.popup.show .popup-panel { opacity: 1; translate: 0 0; scale: 1; }

.popup-close {
  position: absolute;
  top: 0.85rem;
  right: 0.85rem;
  z-index: 2;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  display: grid;
  place-items: center;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--fade);
}
.popup-close svg { width: 1.15rem; height: 1.15rem; transition: rotate 0.3s ease; }
.popup-close line { stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }
.popup-close:hover { color: var(--lav); }
.popup-close:hover svg { rotate: 90deg; }

.project {
  display: grid;
  grid-template-columns: minmax(0, 65fr) minmax(15rem, 27fr);
  gap: clamp(1.25rem, 2.3vw, 2rem);
  align-items: start;
}

/* the media is as large as the panel allows: limited by the width available,
   or by the window's height for wide shapes on short screens */
.frame-wrap {
  position: relative;
  width: min(100%, calc((100svh - 2 * var(--popup-gap) - 2 * var(--panel-pad)) * var(--ratio, 1.7778)));
  justify-self: center;
}

.frame {
  position: relative;
  aspect-ratio: var(--ratio, 1.7778);
  width: 100%;
}
.frame:not(.watercolour) { background: #111; }

.slides {
  position: absolute;
  inset: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.slides::-webkit-scrollbar { display: none; }
.slide {
  flex: 0 0 100%;
  height: 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.slide img { width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.slide iframe { width: 100%; height: 100%; border: 0; display: block; }
.slide video { width: 100%; height: 100%; display: block; object-fit: contain; background: #000; }

.arrow {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  width: 1.9rem;
  height: 1.9rem;
  border: 0;
  border-radius: 50%;
  padding: 0;
  cursor: pointer;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.72);
  color: #555;
  transition: background var(--fade), color var(--fade);
}
.arrow:hover { background: var(--lav); color: var(--paper); }
.arrow svg { width: 0.8rem; height: 0.8rem; }
.arrow.prev { left: 1.1rem; }
.arrow.next { right: 1.1rem; }

.frame:has(.slide video) .dots { bottom: auto; top: 0.9rem; }
.dots {
  position: absolute;
  left: 50%;
  bottom: 0.9rem;
  translate: -50% 0;
  display: flex;
  gap: 0.55rem;
}
.dot {
  width: 0.45rem;
  height: 0.45rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
  cursor: pointer;
  transition: background var(--fade);
}
.dot[aria-current="true"] { background: #fff; }

/* coming-soon cover, with the painter cat hanging off the bottom-left */
.cover {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}
.cover span {
  font-size: clamp(1.6rem, 3.4vw, 3rem);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.cover-cat {
  position: absolute;
  left: 6%;
  bottom: -3%;
  width: 13.5%;
  min-width: 4.5rem;
  pointer-events: none;
}

/* side info — starts level with the top of the frame; if it's taller than
   the frame it's clipped there until "See More" opens it up */
.info {
  height: var(--frame-h, auto);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  font-size: 0.8rem;
  line-height: 1.45;
}
.info-body { min-height: 0; overflow: hidden; }
.info.clipped { justify-content: flex-start; }
.info.clipped .info-body {
  flex: 1;
  -webkit-mask-image: linear-gradient(#000 calc(100% - 3rem), transparent);
          mask-image: linear-gradient(#000 calc(100% - 3rem), transparent);
}
/* after See More: same height as the frame, the text scrolls inside it */
.info.open .info-body {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: 0.75rem;
  scrollbar-width: thin;
  scrollbar-color: #cfcfcf transparent;
}
.info.open .info-body:focus-visible { outline-offset: 0; }

.info .project-title {
  margin: -0.1em 0 0; /* cancels the line's built-in top space so the letters sit level with the frame */
  font-size: 1.3rem;
  font-weight: 700;
  font-style: italic;
  font-synthesis: style;
  line-height: 1.2;
}
.info .meta,
.info .role { margin: 0; font-size: 0.85rem; }
.info .role { text-transform: uppercase; }
.info .description { margin: 1.7rem 0 0; }
.info .description p { margin: 0 0 0.9em; }
.info .description p:last-child { margin-bottom: 0; }
.info .divider { margin: 1.5rem 0; text-align: center; font-weight: 700; }
.credits { margin: 0; }
.credits h3 { margin: 0 0 0.9rem; font-size: inherit; font-weight: 700; }
.credits dl { margin: 0; }
.credits dt { font-weight: 700; }
.credits dd { margin: 0; }
.credits dd + dt { margin-top: 0.85rem; }

.see-more {
  align-self: flex-start;
  margin-top: 0.6rem;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: 0.8rem;
  font-style: italic;
  font-synthesis: style;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  transition: color var(--fade);
}
.see-more:hover { color: var(--lav); }
.see-more[hidden] { display: none; }

.not-found { text-align: center; margin: 8rem auto; font-size: 0.9rem; }
.not-found a { color: var(--lav); text-decoration: underline; }

@media (max-width: 900px) {
  /* phones and small tablets: the pop-up fills the screen and scrolls */
  .popup { padding: 0; }
  .popup-panel {
    width: 100%;
    height: 100%;
    max-height: none;
    padding: 3.75rem var(--gutter) 3rem;
    box-shadow: none;
    translate: 0 24px;
    scale: 1;
  }
  .popup-close { position: fixed; top: 0.6rem; right: 0.6rem; background: rgba(255, 255, 255, 0.9); }
  .project { grid-template-columns: minmax(0, 1fr); gap: 1.6rem; }
  .frame-wrap { width: 100%; }
  /* no arrows on mobile — swipe; no See More — everything is in the scroll */
  .arrow { display: none; }
  .info { height: auto; }
  .info.clipped .info-body { -webkit-mask-image: none; mask-image: none; }
  .see-more { display: none; }
}

/* fullscreen viewer (tap a still) */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(15, 12, 18, 0.92);
  display: flex;
  align-items: center;
  opacity: 0;
  transition: opacity 0.25s ease;
}
.lightbox.show { opacity: 1; }
.lightbox .slides { position: static; width: 100%; height: 100%; }
.lightbox .slide { display: grid; place-items: center; padding: 3.5rem 1rem; }
.lightbox .slide img { width: auto; height: auto; max-width: 100%; max-height: 100%; object-fit: contain; cursor: default; }
.lightbox-close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  width: 2.5rem;
  height: 2.5rem;
  border: 0;
  background: none;
  color: #fff;
  cursor: pointer;
  font-size: 1.6rem;
  line-height: 1;
}
.lightbox-close:hover { color: var(--lav-pale); }
.lightbox .arrow { display: grid; }
@media (hover: none) { .lightbox .arrow { display: none; } }

/* ---------- contact ---------- */

/* A stage whose proportions match the spec's contact layout (slides 5–6);
   cat slots are percentages of it. */
.contact-stage {
  position: relative;
  width: min(100%, calc((100svh - var(--header-h) - 1.5rem) * 1.855));
  aspect-ratio: 1.855;
  margin: 0 auto 2rem;
}

.about {
  position: absolute;
  left: 50%;
  top: 29.5%;
  translate: -50% 0;
  width: clamp(18.5rem, 29%, 25rem);
}
/* On smaller desktop screens the text column wraps into more lines, so it
   sits a little higher to stay clear of the cats and the social icons. */
@media (max-width: 1365px) {
  .about { top: 26%; }
}
@media (max-width: 1199px) {
  .about { top: 21%; }
}
.about h1 { margin: 0 0 0.9rem; font-size: 1.3rem; font-weight: 700; }
.about h1 span { color: var(--lav); }
.bio { margin: 0 0 1.4rem; font-size: 0.8rem; line-height: 1.75; letter-spacing: 0.01em; }
.bio p { margin: 0 0 0.9em; }
.contact-lines { margin: 0; font-size: 0.8rem; line-height: 1.6; }
.contact-lines a {
  color: var(--lav);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}
.contact-lines a:hover { color: var(--lav-pale); }

.socials {
  display: flex;
  justify-content: center;
  gap: 0.6rem;
}
.contact-stage .socials {
  position: absolute;
  left: 0;
  right: 0;
  top: 88%;
}
.socials a { position: relative; display: block; width: 2.1rem; height: 2.1rem; }
.socials img { position: absolute; inset: 0; width: 100%; height: 100%; transition: opacity var(--fade); }
.socials img.hover { opacity: 0; }
.socials a:hover img.hover,
.socials a:focus-visible img.hover { opacity: 1; }

.contact-cat,
.contact-ghost {
  position: absolute;
  height: auto;
  pointer-events: none;
}

/* Spec slide 6: hovering the email or phone reveals pale silhouettes of the
   cats at every other spot, one after another (--i is each one's place in
   the sweep, set by app.js). Leaving runs the same sweep after a short
   pause, so crossing the gap between the two lines doesn't flicker. */
.contact-stage {
  --ghost-fade: 0.22s;   /* how long each silhouette takes to fade */
  --ghost-step: 60ms;    /* gap between one silhouette and the next */
  --ghost-linger: 0.12s; /* pause before they start leaving */
}
.contact-ghost {
  opacity: 0;
  transition: opacity var(--ghost-fade) ease;
  transition-delay: calc(var(--ghost-linger) + var(--i, 0) * var(--ghost-step));
}
.contact-stage:has(.contact-lines a:hover, .contact-lines a:focus-visible) .contact-ghost {
  opacity: 1;
  transition-delay: calc(var(--i, 0) * var(--ghost-step));
}

/* mobile: one cat, locked above the heading; tapping it changes the pose */
.pose-button {
  display: none;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

@media (max-width: 900px) {
  .contact-stage {
    width: min(100%, 24rem);
    aspect-ratio: auto;
    margin: 0.5rem auto 3rem;
    display: flex;
    flex-direction: column;
  }
  .contact-cat,
  .contact-ghost { display: none; }
  .pose-button { display: block; align-self: flex-end; margin: 0 1rem -0.6rem 0; }
  .pose-button img { width: 6.5rem; height: 6.5rem; object-fit: contain; object-position: bottom; }
  .about { position: static; translate: none; width: 100%; }
  .contact-stage .socials { position: static; margin-top: 3.5rem; gap: 0.75rem; }
  .socials a { width: 2.6rem; height: 2.6rem; }
}

/* ---------- 404 ---------- */

.lost { text-align: center; margin: 12vh auto 4rem; font-size: 0.9rem; }
.lost img { width: 9rem; margin: 0 auto 1.5rem; }
.lost h1 { margin: 0 0 0.4rem; font-size: 1.3rem; }
.lost a { color: var(--lav); text-decoration: underline; }
.lost a:hover { color: var(--lav-pale); }

@media (max-width: 760px) {
  .nav { display: none; }
  .menu-toggle { display: block; }
  .site-header { align-items: center; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
  .contact-ghost { transition-delay: 0s !important; }
}
