/* Fonts are loaded from the fonts installed on this computer.
   To publish the site, replace these with licensed web fonts. */
@font-face {
  font-family: "P22 Mackinac Pro";
  font-weight: 400;
  src: local("P22 Mackinac Pro Book"), local("P22MackinacPro-Book");
}

@font-face {
  font-family: "P22 Mackinac Pro";
  font-weight: 500;
  src: local("P22 Mackinac Pro Medium"), local("P22MackinacPro-Medium");
}

@font-face {
  font-family: "P22 Mackinac Pro";
  font-weight: 700;
  src: local("P22 Mackinac Pro Bold"), local("P22MackinacPro-Bold");
}

@font-face {
  font-family: "Anthropic Sans Display";
  font-weight: 300;
  src: local("Anthropic Sans Display Light"), local("AnthropicSans-Display-Light");
}

@font-face {
  font-family: "Anthropic Sans Display";
  font-weight: 400;
  src: local("Anthropic Sans Display Regular"), local("AnthropicSans-Display-Regular");
}

@font-face {
  font-family: "Anthropic Sans Display";
  font-weight: 500;
  src: local("Anthropic Sans Display Medium"), local("AnthropicSans-Display-Medium");
}

@font-face {
  font-family: "Anthropic Sans Display";
  font-weight: 600;
  src: local("Anthropic Sans Display Semibold"), local("AnthropicSansDisplay-Semibold");
}

:root {
  /* Colors */
  /* The palette. One cream, one black (the plum-black ink), and one blue for each ground: --blue on
     the cream, --blue-light on the ink. Use these rather than writing colours out. */
  --cream: #f3f4ec;
  /* A shade under the cream, for squares pressed into the page */
  --cream-deep: #e3e4d8;
  /* A shade over the cream, only for the light edge of things pressed into or raised from the
     page, which needs to be lighter than the page to read (never pure white) */
  --highlight: #fdfff2;
  --ink: #211b24;
  --blue: #3a678e;
  /* The blue for the ink ground, where --blue is too dim to read: [Selected work], the footer,
     numbers and stars on the dark sections */
  --blue-light: #6a90b0;
  --chartreuse: #e1e8bc;

  /* Typography */
  --serif: "P22 Mackinac Pro", Georgia, "Times New Roman", serif;
  --sans: "Anthropic Sans Display", "Helvetica Neue", Arial, sans-serif;

  --weight-light: 300;
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  --text-xs: 13px;
  --text-s: 14px;
  --text-base: 16px;
  --text-m: 20px;
  --text-l: 24px;
  --text-xl: 36px;
  --text-2xl: 40px;
  --text-display: 64px;

  /* Figma sets -7% letter spacing on all text. Large type keeps most of that pull;
     small text is eased off so letters stop touching. Bracketed labels open up to 30%. */
  --tracking-display: -0.055em;
  --tracking-tight: -0.02em;
  --tracking-label: 0.3em;
  --leading-list: 1.479;

  /* Spacing */
  --space-xs: 10px;
  --space-s: 15px;
  --space-m: 30px;
  --space-l: 60px;
  --space-xl: 90px;
  --space-2xl: 120px;

  /* Layout */
  --page-width: 1280px;
  --content-width: 1000px;

  /* Borders and shadows */
  --line: 1px;
  --line-bold: 2px;
  --inset-shadow: inset 4px 4px 4px 0 rgba(33, 27, 36, 0.25);
  --raised-shadow: 4px 4px 4px 0 var(--highlight);

  /* Motion */
  --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
}

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

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  line-height: normal;
  overflow-wrap: break-word;
}

h1,
h2,
h3,
p {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
}

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

img {
  display: block;
  max-width: none;
}

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

/* Paper texture over the whole page. assets/paper-texture.png is the surface detail of ambientCG's
   Paper002 (its colour map, desaturated, with the sheet's own tone removed): only the grain and
   fine flecks remain, as faint dark and light specks, so it reads on the cream and the plum
   sections without changing their colour. The tile repeats seamlessly. The layer covers the whole
   document, not the screen, so the paper scrolls with the page as if printed on it; the tile starts
   at the document's top-left corner, so it runs on unbroken from section to section. It sits above
   everything, including the pictures, and never takes clicks.
   --texture-opacity sets the strength; 0 turns it off. */
:root {
  --texture-opacity: 0.85;
}

/* The page itself is the box the texture layer covers */
html {
  position: relative;
}

body::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 50;
  background: url("assets/paper-texture.png") 0 0 / 900px auto repeat;
  opacity: var(--texture-opacity);
  pointer-events: none;
}

/* Small bracketed label and note above a section, sitting on a hairline */
.section-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-m);
  margin-bottom: var(--space-l);
  padding-bottom: var(--space-xs);
  border-bottom: var(--line) solid currentColor;
  font-family: var(--serif);
  font-size: var(--text-base);
  letter-spacing: var(--tracking-tight);
}

.section-label a {
  transition: color 0.3s;
}

.section-label a:hover,
.section-label a:focus-visible {
  color: var(--blue);
}

/* Header / hero (Figma "Header" frame)

   A contained board of square cells with negative space around it. The navigation runs along the top
   of the page over the name, which is set huge and pressed into the paper; the headline, specialties
   and welcome note sit over the board and "this way" below it. --cell is one square. The board is --cols × --rows cells and its
   black lines are drawn on the board itself, so they always land on the cell edges. Pictures are placed
   with --c / --r (column and row). Desktop and phone each get their own arrangement below. */

.site-header {
  min-height: 100vh;
  min-height: 100svh;
  padding: 0 0 28px;
  overflow: clip;
  background: var(--cream);
  container-type: inline-size;
}

/* Composition after the Figma "Home hero" frame (1408 wide): the board is centred and takes two
   thirds of the width; the headline starts one cell out to its left and the welcome note ends on its
   right edge. Distances in the header are set in cells, so the whole composition scales together.
   The cell is the largest that fits that width and keeps the board, "this way" and the arrow on the
   first screen. Cells are whole pixels. */
.hero {
  --cols: 9;
  --rows: 5;
  --at: 3;
  --screen: 100vh;
  --line-w: var(--line);
  --cell-fit: min(calc(100cqw * 0.075), calc((var(--screen) - 88px) / 7.34), 140px);
  --cell: max(40px, var(--cell-fit));
  --cell: max(40px, round(down, var(--cell-fit), 1px));
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: calc(var(--cell) * 0.73);
}

@supports (height: 100svh) {
  .hero {
    --screen: 100svh;
  }
}

/* Navigation along the top of the page: home on the left, works in the middle, contact on the right */
.hero-nav {
  position: absolute;
  z-index: 2;
  top: 0;
  right: 0;
  left: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  padding: 14px max(16px, 6.46cqw) 0;
}

.nav-link {
  justify-self: start;
  color: var(--blue);
  font-family: var(--serif);
  font-size: 12px;
  font-weight: var(--weight-regular);
  white-space: nowrap;
  text-underline-offset: 3px;
}

.nav-link:nth-child(2) {
  justify-self: center;
}

.nav-link:last-child {
  justify-self: end;
}

.nav-link:hover,
.nav-link:focus-visible {
  text-decoration: underline;
  outline: none;
}

/* The name across the top of the page, wider than the screen and cut off by its top edge.
   assets/hero-name-emboss.svg is the Figma "Embossed Text": the letters are near-paper coloured,
   with a soft light edge and a faint inner shadow, so they read as pressed into the paper. */
.hero-name {
  position: absolute;
  z-index: 0;
  top: -2.06cqw;
  left: 0;
  width: 100.57cqw;
  margin: 0;
  pointer-events: none;
  user-select: none;
}

.hero-name img {
  display: block;
  width: 100%;
  height: auto;
}

/* Headline, specialties and welcome note: one row the width of the board, sitting just above it */
.hero-intro {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr auto;
  width: calc(var(--cols) * var(--cell) + var(--line-w));
  /* As tall as the headline's two lines; the welcome note may hang a little below */
  height: calc(var(--cell) * 0.967);
  margin-bottom: calc(var(--cell) * 0.36);
  color: var(--ink);
}

/* The board. One line at the start of every cell, plus the closing line on the right and bottom. */
.hero-board {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--cols), var(--cell));
  grid-template-rows: repeat(var(--rows), var(--cell));
  padding: 0 var(--line-w) var(--line-w) 0;
  background-image:
    linear-gradient(to right, var(--ink) var(--line-w), transparent var(--line-w)),
    linear-gradient(to bottom, var(--ink) var(--line-w), transparent var(--line-w));
  background-size: var(--cell) var(--cell);
}

.hero-item {
  position: relative;
  z-index: 1;
  grid-column: var(--c, 1) / span var(--w, 1);
  grid-row: var(--r, 1) / span var(--h, 1);
  min-width: 0;
  margin: 0;
}

/* Project squares fill the inside of their cell, between the lines */
.hero-img {
  margin: var(--line-w) 0 0 var(--line-w);
  overflow: hidden;
  background: var(--cream-deep);
}

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

/* Logos and cut-out products keep their whole artwork */
.hero-img--contain img {
  padding: 12%;
  object-fit: contain;
}

.hero-img--burger img {
  padding: 7%;
}

.hero-img--laptop img {
  object-position: 64% 50%;
}

.hero-img--ground-floor img {
  object-position: 9% 50%;
}

/* Cell numbers, added by home.js: (1) to the last column in every row, skipping picture cells */
.hero-label {
  grid-column: var(--c);
  grid-row: var(--r);
  padding: 5px 0 0 calc(var(--line-w) + 5px);
  color: rgba(33, 27, 36, 0.5);
  font-size: 10px;
  line-height: 1;
  letter-spacing: 0.02em;
  pointer-events: none;
  user-select: none;
}

/* Under a picture: hidden until the picture moves away */
.hero-label.is-covered {
  visibility: hidden;
}

/* The brackets are drawn here, a little apart from the number */
.hero-label::before {
  content: "(";
  margin-right: 0.3em;
}

.hero-label::after {
  content: ")";
  margin-left: 0.3em;
}

/* The headline, two lines, starting one cell out to the left of the board.
   --w: 2 makes its ink block build in two steps. */
.hero-headline {
  --w: 2;
  grid-area: 1 / 1;
  align-self: start;
  justify-self: start;
  margin-left: calc(var(--cell) * -1);
  font-size: calc(var(--cell) * 0.455);
  font-weight: var(--weight-semibold);
  line-height: 1.0625;
  white-space: nowrap;
}

.hero-headline span,
.hero-specialties span,
.hero-welcome span {
  display: block;
}

/* Specialties beside the end of the headline, and the welcome note over the board's right edge.
   Both are small and set justified, so every line but the last runs the full width. */
.hero-specialties,
.hero-welcome {
  align-self: start;
  margin-top: calc(var(--cell) * 0.664);
  font-size: 12px;
  text-align: justify;
  text-align-last: justify;
}

.hero-specialties span:last-child,
.hero-welcome span:last-child {
  text-align-last: start;
}

.hero-specialties {
  grid-area: 1 / 1;
  justify-self: start;
  width: 106px;
  margin-left: calc(var(--cell) * 2.626);
  line-height: 12px;
}

.hero-welcome {
  grid-area: 1 / 2;
  width: 102px;
  line-height: 1.25;
}

/* Marquee: runs the full width of the screen just under the headline, behind the pictures */
/* Runs through row --at (set on .hero); its text sits on that row's centre line */
.hero-marquee {
  position: absolute;
  z-index: 0;
  top: calc(var(--cell) * (var(--at) - 0.5));
  left: calc(50% - 50cqw);
  display: flex;
  width: 100cqw;
  overflow: clip;
  font-size: var(--text-base);
  font-weight: var(--weight-light);
  line-height: 1.25;
  letter-spacing: -0.07em;
  white-space: pre;
  transform: translateY(-50%);
}

.hero-marquee-track {
  display: flex;
  flex-shrink: 0;
  animation: hero-marquee 60s linear infinite;
}

@keyframes hero-marquee {
  to {
    transform: translateX(-50%);
  }
}

/* "this way" and the arrow, centred under the board */
.hero-cue {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.this-way {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  margin-top: calc(var(--cell) * 0.284);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 12px;
  font-weight: var(--weight-regular);
}

.this-way-arrow {
  /* Set against the cell so it keeps the same rendered size as the board changes rows */
  width: calc(var(--cell) * 0.285);
  height: auto;
  transform: rotate(90deg);
  transition: transform 0.5s var(--ease-out);
}

.this-way:hover .this-way-arrow,
.this-way:focus-visible .this-way-arrow {
  transform: translateY(6px) rotate(90deg);
}

.this-way:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 6px;
}

/* Arrangement: 9 × 5. The pictures fall from the top left to the lower right, one in every column,
   with Wild Nest as a 2 × 2 square on the right. The marquee runs through the middle row. */
.hero-img--nova { --c: 2; --r: 1; }
.hero-img--dragonfly { --c: 7; --r: 1; }
.hero-img--takt { --c: 9; --r: 1; }
.hero-img--cans { --c: 4; --r: 2; }
.hero-img--laptop { --c: 6; --r: 2; }
.hero-img--crazy-creek { --c: 1; --r: 3; }
.hero-img--burger { --c: 5; --r: 3; }
.hero-img--wild-nest { --c: 8; --r: 3; --w: 2; --h: 2; }
.hero-img--eko { --c: 3; --r: 4; }
.hero-img--ground-floor { --c: 6; --r: 5; }

/* Narrower screens: the board takes nearly the full width, so there is no margin for the headline to
   start in. It starts on the board's left edge instead, under the name, with the specialties and the
   welcome note on a line of their own between it and the board. Landscape tablets keep the desktop
   layout, which leaves them bigger cells. */
@media (max-width: 899px), (max-width: 1099px) and (orientation: portrait) {
  /* Tall screens: the composition sits in the middle of the first screen, while the navigation
     and the name stay at the top */
  .site-header {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  .hero {
    --cell-fit: min(calc((100cqw - 48px) / var(--cols)), 112px);
    position: static;
    padding-top: max(64px, 8cqw);
  }

  .hero-intro {
    height: auto;
    row-gap: 16px;
    margin-bottom: 24px;
  }

  .hero-headline {
    grid-area: 1 / 1 / 2 / 3;
    margin-left: 0;
    font-size: max(26px, calc(var(--cell) * 0.455));
  }

  .hero-specialties,
  .hero-welcome {
    grid-row: 2;
    margin-top: 0;
  }

  .hero-specialties {
    margin-left: 0;
  }
}

/* Phone: nine columns would make the cells too small, so the board drops back to seven */
@media (max-width: 699px) {
  .hero {
    --cols: 7;
    --cell-fit: min(calc((100cqw - 32px) / var(--cols)), 112px);
  }

  .hero-img--nova { --c: 2; --r: 1; }
  .hero-img--dragonfly { --c: 5; --r: 1; }
  .hero-img--takt { --c: 7; --r: 1; }
  .hero-img--cans { --c: 1; --r: 2; }
  .hero-img--laptop { --c: 3; --r: 2; }
  .hero-img--burger { --c: 4; --r: 3; }
  .hero-img--wild-nest { --c: 6; --r: 3; }
  .hero-img--crazy-creek { --c: 1; --r: 4; }
  .hero-img--eko { --c: 3; --r: 4; }
  .hero-img--ground-floor { --c: 5; --r: 5; }

  .this-way-arrow {
    width: calc(var(--cell) * 0.36);
  }
}

/* Load sequence, after the grid in the video reference: every piece first appears as a solid ink block
   that builds across the grid one cell at a time, holds, then steps away to leave the piece behind.
   The grid lines never move. */
.hero-item {
  --build: 1.15s;
  --delay: calc(var(--i, 0) * 70ms + 150ms);
  /* The piece itself appears while its block fully covers it */
  animation: hero-show 0s both;
  animation-delay: calc(var(--delay) + var(--build) * 0.42);
}

.hero-item::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  background: var(--ink);
  visibility: visible;
  clip-path: inset(0 0 0 100%);
  animation: hero-build-x var(--build) var(--delay) both;
  /* One whole cell per step, so the block snaps along the grid */
  animation-timing-function: steps(var(--w, 1), end);
  pointer-events: none;
}

/* The marquee and the text under the board settle in once the board has built */
.hero-marquee,
.hero-name,
.hero-specialties,
.hero-welcome,
.hero-cue {
  animation: hero-settle 0.8s var(--ease-out) 1.3s both;
}

@keyframes hero-show {
  from {
    visibility: hidden;
  }

  to {
    visibility: visible;
  }
}

@keyframes hero-build-x {
  0% {
    clip-path: inset(0 100% 0 0);
  }

  38%,
  55% {
    clip-path: inset(0);
  }

  100% {
    clip-path: inset(0 0 0 100%);
  }
}

@keyframes hero-settle {
  from {
    opacity: 0;
  }
}

/* Coming back to the page in the same tab: skip the build and settle in */
.hero-seen .hero-item,
.hero-seen .hero-item::after {
  animation: none;
}

.hero-seen .hero-board > .hero-item,
.hero-seen .hero-headline,
.hero-seen .hero-marquee,
.hero-seen .hero-name,
.hero-seen .hero-specialties,
.hero-seen .hero-welcome,
.hero-seen .hero-cue {
  animation: hero-settle 0.5s var(--ease-out) both;
}

/* The pictures make their own entrance in home.js, flowing into their cells, so they skip the ink
   build and the fade; until then they stay hidden */
.hero-board > .hero-img.hero-item {
  animation: none;
}

.hero-board > .hero-img::after {
  content: none;
}

.has-js .hero-board:not(.is-entered) > .hero-img {
  visibility: hidden;
}

/* Projects: numbered cards with a caption on a hairline, shared by both pages */

.projects-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: var(--space-xl) var(--space-l);
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-l) var(--space-m);
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.card-image {
  position: relative;
  overflow: hidden;
  aspect-ratio: 9 / 8;
  background: var(--cream);
}

.card-image img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.9s var(--ease-out);
}

.card:hover .card-image img {
  transform: scale(1.04);
}

/* (01)  Name ........ Category */
.card-bar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: var(--space-s);
  padding-top: var(--space-xs);
  border-top: var(--line) solid var(--ink);
  color: var(--ink);
}

.card-number {
  font-size: 12px;
  letter-spacing: var(--tracking-label);
}

.card-title {
  font-family: var(--serif);
  font-size: var(--text-l);
  letter-spacing: var(--tracking-display);
  transition: color 0.3s;
}

/* The title link covers the whole card */
.card-title a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.card:hover .card-title,
.card:focus-within .card-title {
  color: var(--blue);
}

.card-title a:focus-visible {
  outline: none;
}

.card:has(a:focus-visible) {
  outline: 2px solid var(--blue);
  outline-offset: 6px;
}

.card-tags {
  font-size: var(--text-s);
  letter-spacing: var(--tracking-tight);
  text-align: right;
}

.card {
  container-type: inline-size;
}

/* In a narrow card the category moves under the name instead of squeezing it */
@container (max-width: 340px) {
  .card-bar {
    grid-template-columns: auto 1fr;
    row-gap: 2px;
  }

  .card-tags {
    grid-column: 2;
    text-align: left;
  }
}

/* Footer */

.site-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: var(--page-width);
  margin: 0 auto;
  padding: var(--space-l);
}

.wordmark {
  position: relative;
  display: block;
  flex-shrink: 0;
  width: 100%;
  aspect-ratio: 1160 / 127.71522521972656;
}

.wordmark-art {
  position: absolute;
  inset: -0.78% 0;
}

.wordmark-art img {
  width: 100%;
  height: 100%;
}

/* Home page: hover gallery */

.gallery {
  --gallery-ease: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  height: clamp(420px, 100vh, 910px);
  height: clamp(420px, 100svh, 910px);
  /* The strip sits in the lower third, clear of the middle of each photograph */
  padding: 0 16px clamp(24px, 9vh, 90px);
  overflow: hidden;
  background: var(--ink);
}

/* Names the project on screen, set like the caption bar under each card */
.gallery-caption {
  position: absolute;
  top: var(--space-m);
  left: var(--space-l);
  z-index: 2;
  display: flex;
  align-items: baseline;
  gap: var(--space-s);
  max-width: calc(100% - 2 * var(--space-l));
  padding: 8px 14px;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--serif);
  font-size: var(--text-base);
  letter-spacing: var(--tracking-tight);
  opacity: 0;
  transition: opacity 0.5s ease 0.6s;
  pointer-events: none;
}

.gallery.is-started .gallery-caption {
  opacity: 1;
}

.gallery-caption > span {
  display: block;
  overflow: hidden;
  white-space: nowrap;
}

.gallery-caption > span > span {
  display: block;
}

.gallery-caption-count {
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: var(--tracking-label);
}

.gallery-caption-category {
  color: var(--chartreuse);
  font-family: var(--sans);
  font-size: var(--text-s);
}

.gallery-caption.is-rolling > span > span {
  animation: reel-roll 0.55s var(--ease-out) both;
}

.gallery-caption.is-rolling .gallery-caption-name > span {
  animation-delay: 0.05s;
}

.gallery-caption.is-rolling .gallery-caption-category > span {
  animation-delay: 0.1s;
}

/* Main display. It waits off to the right until the section is first seen. */
.gallery-stage {
  position: absolute;
  inset: 0;
  transform: translateX(100%);
  transition: transform 0.95s var(--gallery-ease);
}

.gallery.is-started .gallery-stage {
  transform: none;
}

/* One full-size panel per project; gallery.js slides them sideways */
.gallery-panel {
  position: absolute;
  inset: 0;
  background: var(--panel-color, var(--ink));
  will-change: transform;
}

.gallery-panel img,
.gallery-square img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.gallery-strip {
  position: relative;
  display: flex;
  width: min(911px, 100%);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: var(--line) solid var(--cream);
  list-style: none;
}

.gallery-strip li {
  flex: 1 0 0;
  min-width: 1px;
}

.gallery-square {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-width: 0;
  aspect-ratio: 1;
  padding: 0;
  border: var(--line) solid var(--cream);
  background: var(--blue);
  color: var(--cream);
  font-family: var(--sans);
  font-size: var(--text-s);
  font-weight: var(--weight-regular);
  line-height: normal;
  letter-spacing: var(--tracking-label);
  cursor: default;
  touch-action: manipulation;
}

/* Dark wash over every square except the active one */
.gallery-square::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: rgba(33, 27, 36, 0.2);
  transition: opacity 0.4s ease;
}

.gallery-square-number {
  position: relative;
  z-index: 2;
  /* letter-spacing adds a gap after the last character; this re-centres the label */
  margin-right: calc(-1 * var(--tracking-label));
  transition: opacity 0.4s ease;
}

.gallery-square.is-active::before,
.gallery-square.is-active .gallery-square-number {
  opacity: 0;
}

.gallery-square:focus-visible {
  outline: 2px solid var(--cream);
  outline-offset: 3px;
}

/* Home page: project reel */

.reel {
  --reel-unit: 1vmin;
  height: calc(100vh + 380vh);
  height: calc(100svh + 380vh);
  background: var(--cream);
}

.reel-pin {
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  height: 100svh;
  overflow: clip;
  color: var(--cream);
}

/* One more hero square. home.js grows it until it fills the screen and becomes the dark background. */
.reel-seed {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 90.551px;
  height: 82.167px;
  margin: -41.08px 0 0 -45.28px;
  background: var(--ink);
  transform: scale(var(--seed, 1));
  will-change: transform;
}

.reel-label,
.reel-all {
  position: absolute;
  top: var(--space-m);
  z-index: 4;
  /* Plum backing knocks out any tile that drifts underneath; the padding sits outside the text column */
  margin: -6px -10px;
  padding: 6px 10px;
  background: var(--ink);
  font-family: var(--serif);
  font-size: var(--text-base);
  letter-spacing: var(--tracking-tight);
}

.reel-label {
  left: var(--space-l);
}

.reel-all {
  right: var(--space-l);
  transition: color 0.3s;
}

.reel-all:hover,
.reel-all:focus-visible {
  color: var(--chartreuse);
}

.reel-field {
  position: absolute;
  inset: 0;
  max-width: 1440px;
  margin: 0 auto;
}

.has-js .reel-label,
.has-js .reel-all,
.has-js .reel-field,
.has-js .reel-caption {
  opacity: 0;
  transition: opacity 0.5s ease;
}

.has-js .reel.is-open .reel-label,
.has-js .reel.is-open .reel-all,
.has-js .reel.is-open .reel-field,
.has-js .reel.is-open .reel-caption {
  opacity: 1;
}

.reel-tile {
  position: absolute;
  top: var(--y);
  left: var(--x);
  width: calc(var(--s) * var(--reel-unit));
  aspect-ratio: 1;
  transform: translate3d(0, var(--shift, 0px), 0);
  will-change: transform;
}

.reel-tile.is-active {
  z-index: 2;
}

.reel-tile-inner {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--cream);
  opacity: 0.3;
  transition: opacity 0.45s ease, transform 0.6s var(--ease-out);
}

.reel-tile.is-active .reel-tile-inner {
  opacity: 1;
  transform: scale(1.3);
}

.reel-tile-inner img {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
}

.reel-tile-number {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  font-size: 12px;
  letter-spacing: var(--tracking-label);
  opacity: 0.6;
  transition: opacity 0.45s ease, transform 0.6s var(--ease-out);
}

.reel-tile.is-active .reel-tile-number {
  opacity: 1;
  transform: translate(-15%, 15%);
}

.reel-tile:focus-visible {
  outline: 2px solid var(--chartreuse);
  outline-offset: 4px;
}

.reel-caption {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  max-width: min(760px, 86vw);
  text-align: center;
  text-shadow: 0 0 24px var(--ink), 0 0 6px var(--ink);
  pointer-events: none;
}

.reel-count,
.reel-name,
.reel-category {
  display: block;
  overflow: hidden;
}

.reel-count {
  font-size: var(--text-s);
  letter-spacing: var(--tracking-label);
}

.reel-name {
  padding: 0 0.08em 0.12em;
  font-family: var(--serif);
  font-size: clamp(36px, 6.4vw, 88px);
  line-height: 1.02;
  letter-spacing: var(--tracking-display);
}

.reel-category {
  color: var(--chartreuse);
  font-size: var(--text-base);
  letter-spacing: var(--tracking-tight);
}

.reel-caption span > span {
  display: block;
}

/* Each caption line rolls up into place when the project changes */
.reel-caption.is-rolling span > span {
  animation: reel-roll 0.55s var(--ease-out) both;
}

.reel-caption.is-rolling .reel-name > span {
  animation-delay: 0.05s;
}

.reel-caption.is-rolling .reel-category > span {
  animation-delay: 0.1s;
}

@keyframes reel-roll {
  from {
    transform: translateY(110%);
  }
}

/* Home page: project index */

.index {
  background: var(--cream);
  color: var(--ink);
}

.index-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: var(--space-xl) var(--space-l) var(--space-2xl);
}

.index-list {
  font-family: var(--serif);
  font-size: clamp(32px, 5.4vw, 76px);
  line-height: 1.14;
  letter-spacing: var(--tracking-display);
}

.index-item {
  transition: color 0.3s ease;
}

.has-js .index-item {
  opacity: 0;
  transition: opacity 0.7s ease calc(var(--i) * 80ms), color 0.3s ease;
}

.has-js .index-list.is-visible .index-item {
  opacity: 1;
}

.index-item:hover,
.index-item:focus-visible {
  color: var(--blue);
}

.index-item:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 4px;
}

.index-thumb {
  margin-left: 0.14em;
  font-family: var(--sans);
  font-weight: var(--weight-light);
  white-space: nowrap;
}

.index-thumb-image {
  position: relative;
  display: inline-block;
  width: 0.92em;
  height: 0.7em;
  margin: 0 0.05em;
  overflow: hidden;
  background: var(--cream);
  transform-origin: 50% 100%;
  transition: transform 0.45s var(--ease-out);
}

.index-thumb-image img {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
}

.index-item:hover .index-thumb-image,
.index-item:focus-visible .index-thumb-image {
  z-index: 2;
  transform: scale(2.6) rotate(-3deg);
}

/* Home page: about tabs */

.info {
  /* Type rises into place fast and settles slowly; it leaves the opposite way */
  --ease-type-in: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-type-out: cubic-bezier(0.5, 0, 0.75, 0);
  /* Blocks move along the grid: a quick start and a firm stop */
  --ease-grid: cubic-bezier(0.65, 0, 0.25, 1);
  /* Gap between the last panel leaving and the next one arriving */
  --enter: 0.4s;
  background: var(--ink);
}

.info-inner {
  display: flex;
  flex-direction: column;
  gap: 32px;
  max-width: var(--page-width);
  margin: 0 auto;
  padding: var(--space-xl) var(--space-l);
}

.info-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-m);
  margin: 0;
  padding: 0;
  color: var(--cream);
  font-family: var(--serif);
  font-size: 19px;
  letter-spacing: var(--tracking-tight);
  list-style: none;
}

/* Sizes inside the box are in cqw (1% of the box width) so the Figma frame, 1488 wide, scales as one piece */
.info-box {
  container-type: inline-size;
  position: relative;
  display: grid;
  aspect-ratio: 1488 / 760;
  background: var(--cream);
  filter: drop-shadow(4px 4px 2px rgba(253, 255, 242, 0.25));
}

.info-box::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 3px 4px 4px 0 rgba(33, 27, 36, 0.25);
  pointer-events: none;
}

/* Nothing inside the box, including closed bag notes, may widen the page */
.info-box-inner {
  display: flex;
  flex-direction: column;
  gap: 2.55cqw;
  min-width: 0;
  overflow: clip;
  padding: 2.02cqw 4.03cqw;
  color: var(--ink);
}

.info-stage {
  display: grid;
  flex: 1;
  min-width: 0;
}

.info-panel {
  display: flex;
  flex-direction: column;
  grid-area: 1 / 1;
  align-items: center;
  justify-content: flex-end;
  gap: 2.55cqw;
  min-width: 0;
}

/* Hidden panels keep their space, so the box holds one height whichever tab is open */
.info-panel[hidden] {
  visibility: hidden;
}

.info-panel.is-leaving {
  pointer-events: none;
}

.has-js .info-box:not(.is-inview) .info-panel {
  visibility: hidden;
}

.info-text {
  width: 100%;
  margin: 0;
  font-size: var(--text-base);
}

/* Type in and out: each line sits in a mask and rises out of it, then sinks back, last line first */

.info-mask {
  display: block;
  clip-path: inset(-0.5em -0.6em 0 -0.6em);
}

.info-mask-inner {
  display: block;
}

@keyframes info-type-in {
  from {
    translate: 0 110%;
  }
}

.info-box.is-inview .info-panel.is-active .info-mask-inner {
  animation: info-type-in 0.65s var(--ease-type-in) calc(var(--enter) + var(--delay, 0s) + var(--line, 0) * 70ms) both;
}

/* Lines rebuilt after a resize appear in place */
.info-box.is-inview .info-panel.is-active.is-played .info-who-text .info-mask-inner {
  animation: none;
}

.info-panel.is-leaving .info-mask-inner {
  translate: 0 110%;
  transition: translate 0.32s var(--ease-type-out) calc((var(--lines, 3) - 1 - var(--line, 0)) * 30ms);
}

/* Blocks that are not type fade out as the panel leaves */
.info-panel.is-leaving .info-who-photo,
.info-panel.is-leaving .info-skill-image,
.info-panel.is-leaving .info-card,
.info-panel.is-leaving .info-bag-bag,
.info-panel.is-leaving .info-bag-button {
  opacity: 0;
  transition: opacity 0.3s ease-in;
}

/* A faint grid that the experience cards and bag items move along while they arrive */
@keyframes info-grid {
  0%,
  100% {
    opacity: 0;
  }

  12%,
  70% {
    opacity: 1;
  }
}

.info-cards::before,
.info-bag::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(33, 27, 36, 0.14) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(33, 27, 36, 0.14) 1px, transparent 1px);
  opacity: 0;
  pointer-events: none;
}

.info-box.is-inview .info-panel.is-active .info-cards::before,
.info-box.is-inview .info-panel.is-active .info-bag::before {
  animation: info-grid 2.4s linear var(--enter) both;
}

/* Who is Madeline? */

.info-panel--who {
  gap: 2.4cqw;
}

/* Figma frame: photo 864 x 539 over a 1368 wide column, name 1160 wide laid across it */
.info-who-art {
  position: relative;
  width: 100%;
  aspect-ratio: 1368 / 539;
}

.info-who-photo {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 32.57%;
  width: 42.59%;
  overflow: hidden;
}

/* The cut-out is anchored by its bottom edge, cropped just below the dress as in Figma */
.info-who-photo img {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: auto;
  translate: 0 2.96%;
}

.info-name {
  position: absolute;
  top: 26.16%;
  left: 8.41%;
  width: 84.8%;
  aspect-ratio: 1160 / 134;
  margin: 0;
  clip-path: inset(-60% -3% -4% -3%);
}

.info-name-letter {
  position: absolute;
  display: block;
}

.info-name-letter img {
  display: block;
  width: 100%;
  height: auto;
}

@keyframes info-rise-photo {
  from {
    clip-path: inset(100% 0 0 0);
  }

  to {
    clip-path: inset(0);
  }
}

.info-box.is-inview .info-panel.is-active .info-who-photo {
  animation: info-rise-photo 1s var(--ease-type-in) var(--enter) both;
}

.info-box.is-inview .info-panel.is-active .info-name-letter {
  animation: info-type-in 0.7s var(--ease-type-in) calc(var(--enter) + 0.25s + var(--i) * 35ms) both;
}

.info-panel.is-leaving .info-name-letter {
  translate: 0 110%;
  transition: translate 0.3s var(--ease-type-out) calc((14 - var(--i)) * 15ms);
}

/* Skills */

.info-panel--skills {
  align-items: flex-start;
  justify-content: center;
}

.info-skills {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(24px, 4.1cqw, 64px);
  line-height: 1.2;
  letter-spacing: var(--tracking-display);
}

.info-skill {
  display: inline-block;
  transition: color 0.3s ease;
}

.info-skill:hover {
  color: var(--blue);
}

.info-skill > .info-mask-inner {
  display: inline-block;
}

.info-skill-thumb {
  margin-left: 0.14em;
  font-family: var(--sans);
  font-weight: var(--weight-light);
  white-space: nowrap;
}

.info-skill-image {
  position: relative;
  display: inline-block;
  width: 1.05em;
  height: 0.8em;
  margin: 0 0.05em;
  vertical-align: -0.08em;
}

.info-skill-image img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Experience */

.info-cards {
  position: relative;
  flex: none;
  width: min(52cqw, 100%);
  margin: 0;
  padding: 0;
  font-family: var(--serif);
  letter-spacing: var(--tracking-tight);
  list-style: none;
}

/* Grid cells match the step between the cards: one eighth of the width across, two rows down */
.info-cards::before {
  background-size: 12.5% calc(100% / 7);
}

.info-card {
  --depth: 0px;
  /* Where the card starts, measured in its own size: on top of the first card */
  --from-x: 0%;
  --from-y: 0%;
  position: relative;
  display: flex;
  flex-direction: column;
  width: 72.2%;
  aspect-ratio: 593 / 234;
  padding: 2.02cqw;
  border: var(--line-bold) solid var(--ink);
  background: var(--ink);
  color: var(--chartreuse);
  transform: translate(calc(var(--px, 0) * var(--depth)), calc(var(--py, 0) * var(--depth) + var(--lift, 0px)));
  transition: transform 0.6s var(--ease-out);
}

.info-card + .info-card {
  --depth: 5px;
  --from-x: -17.9%;
  --from-y: -68.4%;
  margin-top: -9%;
  margin-left: 12.9%;
  background: var(--blue);
  color: var(--cream);
}

.info-card + .info-card + .info-card {
  --depth: 10px;
  --from-x: -38.6%;
  --from-y: -138%;
  gap: 1.28cqw;
  margin-left: 27.8%;
  background: var(--cream);
  color: var(--ink);
}

.info-card:hover {
  --lift: -8px;
}

.info-card h3,
.info-card p {
  margin: 0;
  font-weight: var(--weight-regular);
}

.info-card h3 {
  font-size: clamp(18px, 2.15cqw, 32px);
}

.info-card p {
  font-size: clamp(13px, 1.34cqw, 20px);
}

.info-card p + p {
  font-size: clamp(13px, 1.28cqw, 19px);
}

/* Each card starts as one grid cell on top of the first card, fills out along its row, then down,
   then steps across and down the grid to its place in the stack */
@keyframes info-card-build {
  0% {
    clip-path: inset(0 100% 100% 0);
    translate: var(--from-x) var(--from-y);
    animation-timing-function: var(--ease-grid);
  }

  10% {
    clip-path: inset(0 82.7% 66% 0);
    translate: var(--from-x) var(--from-y);
    animation-timing-function: var(--ease-grid);
  }

  32% {
    clip-path: inset(0 0 66% 0);
    translate: var(--from-x) var(--from-y);
    animation-timing-function: var(--ease-grid);
  }

  52% {
    clip-path: inset(0);
    translate: var(--from-x) var(--from-y);
    animation-timing-function: var(--ease-grid);
  }

  76% {
    clip-path: inset(0);
    translate: 0 var(--from-y);
    animation-timing-function: var(--ease-grid);
  }

  100% {
    clip-path: inset(0);
    translate: 0 0;
  }
}

.info-box.is-inview .info-panel.is-active .info-card {
  animation: info-card-build 1.4s linear calc(var(--enter) + var(--i) * 0.16s) both;
}

.info-card .info-mask-inner {
  --delay: calc(1.1s + var(--i) * 0.16s);
}

.info-resume {
  color: var(--ink);
  font-size: 17px;
  font-weight: var(--weight-light);
}

.info-resume .info-mask-inner {
  --delay: 1.6s;
  display: flex;
  flex-wrap: wrap;
  gap: 0 15px;
}

.info-resume a {
  font-weight: var(--weight-regular);
  text-decoration: none;
  transition: color 0.3s ease;
}

.info-resume a:hover,
.info-resume a:focus-visible {
  color: var(--blue);
}

/* Whats in My Bag? */

.info-panel--bag {
  align-items: stretch;
  justify-content: stretch;
}

/* Bag in the middle columns, items set around it on a 12 x 6 grid, as in the flat-lay reference */
.info-bag {
  position: relative;
  display: grid;
  flex: 1;
  grid-template-columns: repeat(12, 1fr);
  grid-template-rows: repeat(6, 1fr);
  gap: 1cqw;
  min-height: 30cqw;
}

.info-bag::before {
  background-size: calc(100% / 12) calc(100% / 6);
}

.info-bag-bag {
  position: relative;
  z-index: 1;
  grid-area: 1 / 5 / 7 / 9;
  margin: 0;
  transition: opacity 0.4s ease;
}

.info-bag-bag img,
.info-bag-button img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.info-bag-items {
  display: contents;
}

.info-bag-item {
  --dx: 0px;
  --dy: 0px;
  position: relative;
  list-style: none;
}

.info-bag-item--airpods { grid-area: 1 / 1 / 3 / 3; }
.info-bag-item--sketchbook { grid-area: 1 / 3 / 4 / 5; }
.info-bag-item--energy-drink { grid-area: 4 / 1 / 7 / 3; }
.info-bag-item--lipstick { grid-area: 5 / 3 / 7 / 4; }
.info-bag-item--pen { grid-area: 1 / 9 / 4 / 10; }
.info-bag-item--laptop { grid-area: 1 / 10 / 4 / 13; }
.info-bag-item--planner { grid-area: 4 / 10 / 7 / 12; }

.info-bag-button {
  position: absolute;
  inset: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.info-bag-button img {
  transition: opacity 0.4s ease, translate 0.5s var(--ease-out);
}

.info-bag-button:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 4px;
}

.info-bag-number {
  position: absolute;
  top: -2px;
  left: -2px;
  z-index: 1;
  font-size: var(--text-xs);
}

/* One item at a time: the rest step back so its note reads cleanly */
.info-bag.has-open .info-bag-bag,
.info-bag.has-open .info-bag-item:not(.is-open) .info-bag-button {
  opacity: 0.25;
}

.info-bag-item.is-open {
  z-index: 3;
}

.info-bag-item.is-open .info-bag-button img {
  translate: 0 -4px;
}

.info-bag-note {
  position: absolute;
  top: 0;
  left: calc(100% + 1.4cqw);
  z-index: 3;
  width: var(--note-width, max(190px, 15cqw));
  translate: 0 var(--note-y, 0px);
  pointer-events: none;
}

.info-bag-item.is-flipped .info-bag-note {
  right: calc(100% + 1.4cqw);
  left: auto;
  text-align: right;
}

.info-bag-note h3,
.info-bag-note p {
  margin: 0;
}

.info-bag-note-number {
  display: block;
  margin-bottom: 8px;
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.info-bag-note h3 {
  font-family: var(--serif);
  font-size: clamp(20px, 2cqw, 30px);
  font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-tight);
}

.info-bag-rule {
  display: block;
  width: 48px;
  height: var(--line);
  margin: 10px 0 12px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 0.25s var(--ease-type-out);
}

.info-bag-item.is-flipped .info-bag-rule {
  margin-left: auto;
  transform-origin: 100% 50%;
}

.info-bag-note p {
  font-size: 15px;
  font-weight: var(--weight-light);
  line-height: 1.35;
}

/* A note's lines stay below their masks until the item opens */
.info-panel .info-bag-note .info-mask-inner,
.info-box.is-inview .info-panel.is-active .info-bag-note .info-mask-inner {
  translate: 0 110%;
  animation: none;
  transition: translate 0.25s var(--ease-type-out) calc((2 - var(--line)) * 30ms);
}

.info-box.is-inview .info-panel.is-active .info-bag-item.is-open .info-mask-inner {
  translate: 0 0;
  transition: translate 0.55s var(--ease-type-in) calc(0.05s + var(--line) * 70ms);
}

.info-bag-item.is-open .info-bag-rule {
  transform: scaleX(1);
  transition: transform 0.5s var(--ease-type-in) 0.12s;
}

@keyframes info-bag-open {
  from {
    clip-path: inset(50% 50% 50% 50%);
  }

  to {
    clip-path: inset(0);
  }
}

/* Each item comes out from behind the bag, across the columns first and then along the rows */
@keyframes info-bag-out {
  0% {
    opacity: 0;
    translate: var(--dx) var(--dy);
    scale: 0.3;
    animation-timing-function: var(--ease-grid);
  }

  15% {
    opacity: 1;
  }

  55% {
    translate: 0 var(--dy);
    scale: 1;
    animation-timing-function: var(--ease-grid);
  }

  100% {
    opacity: 1;
    translate: 0 0;
    scale: 1;
  }
}

.info-box.is-inview .info-panel.is-active .info-bag-bag {
  animation: info-bag-open 0.8s var(--ease-grid) var(--enter) both;
}

.info-box.is-inview .info-panel.is-active .info-bag-item {
  animation: info-bag-out 1.2s linear calc(var(--enter) + 0.45s + var(--i) * 90ms) both;
}

/* Tabs */

.info-tabs {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-s);
  padding-top: 14px;
  border-top: var(--line) solid var(--ink);
}

.info-tab {
  padding: 5px;
  border: var(--line) solid transparent;
  background: none;
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--text-s);
  font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-tight);
  cursor: pointer;
  transition: color 0.3s;
}

.info-tab:hover {
  color: var(--blue);
}

.info-tab.is-active {
  border-color: var(--ink);
}

.info-tab:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 4px;
}

/* With the script running, one frame slides between the tabs in place of each tab's own border */
.has-js .info-tab.is-active {
  border-color: transparent;
}

.info-tab-frame {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
  border: var(--line) solid var(--ink);
  pointer-events: none;
}

.has-js .info-tab-frame {
  display: block;
}

.info-tab-frame.is-ready {
  transition: transform 0.6s var(--ease-out), width 0.6s var(--ease-out), height 0.6s var(--ease-out);
}

/* Home page: contact */

.contact {
  color: var(--ink);
}

.contact-inner {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: var(--space-xl) var(--space-l) var(--space-l);
}

.contact-body {
  display: flex;
  align-items: center;
  gap: var(--space-l);
}

/* Turns a quarter step at a time; the slight overshoot is on purpose */
.contact-square {
  position: relative;
  flex: none;
  width: clamp(150px, 18vw, 220px);
  aspect-ratio: 1;
  border: var(--line) solid var(--ink);
  background: var(--chartreuse);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-tight);
  container-type: inline-size;
  transform: rotate(var(--turn, 0deg));
  transition: transform 0.9s cubic-bezier(0.5, 1.5, 0.4, 1);
}

.contact-square span {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  text-align: center;
  white-space: nowrap;
  transform: translate(-50%, -50%) rotate(var(--side)) translateY(calc(-50cqw + 14px));
}

.contact-square span:nth-child(1) { --side: 0deg; }
.contact-square span:nth-child(2) { --side: 90deg; }
.contact-square span:nth-child(3) { --side: 180deg; }
.contact-square span:nth-child(4) { --side: 270deg; }

.contact-email {
  min-width: 0;
  padding: 0 0.08em;
  background: linear-gradient(var(--chartreuse), var(--chartreuse)) no-repeat 0 100% / 0% 100%;
  color: var(--blue);
  font-family: var(--serif);
  font-size: clamp(24px, 4.4vw, 60px);
  line-height: 1.1;
  letter-spacing: var(--tracking-display);
  transition: background-size 0.5s var(--ease-out), color 0.3s;
}

.contact-email:hover,
.contact-email:focus-visible {
  background-size: 100% 100%;
  color: var(--ink);
}

.contact-email:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

/* Smaller screens (the Figma frame only covers the 1280px desktop layout) */

@media (max-width: 900px) {
  .reel {
    --reel-unit: 1.6vmin;
  }

  .reel-label {
    left: 24px;
  }

  .reel-all {
    right: 24px;
  }

  .index-inner,
  .info-inner,
  .contact-inner {
    padding: var(--space-l) 24px;
  }

  .section-label {
    margin-bottom: var(--space-m);
  }

  .info-tools {
    justify-content: flex-start;
    font-size: var(--text-base);
  }

  .contact-body {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-m);
  }

  .projects-inner {
    padding: var(--space-l) 24px;
  }

  .gallery-caption {
    left: 24px;
    max-width: calc(100% - 48px);
  }

  .site-footer {
    padding: var(--space-m) 24px;
  }

  .gallery-square {
    font-size: 11px;
    letter-spacing: 2px;
  }

  .gallery-square-number {
    margin-right: -2px;
  }
}

/* About tabs: below this width the box stops scaling as one piece and stacks */
@media (max-width: 760px) {
  .info-box {
    aspect-ratio: auto;
  }

  .info-box-inner {
    gap: var(--space-m);
    padding: var(--space-m) 24px 24px;
  }

  .info-stage {
    min-height: 420px;
  }

  /* Stacked, the panels differ too much in height to share one */
  .info-panel[hidden] {
    display: none;
  }

  .info-panel {
    gap: var(--space-m);
  }

  .info-who-art {
    aspect-ratio: 1 / 1;
  }

  .info-who-photo {
    left: 15%;
    width: 70%;
  }

  .info-name {
    top: 47%;
    left: 0;
    width: 100%;
  }

  .info-cards {
    width: 100%;
  }

  .info-card {
    padding: var(--space-s);
  }

  .info-bag {
    min-height: 56cqw;
  }

  .info-tabs {
    justify-content: flex-start;
    gap: var(--space-xs) 20px;
  }
}

/* Phones. About tabs: a taller portrait, and the bag on top with its items in two columns below */
@media (max-width: 560px) {
  .info-who-art {
    aspect-ratio: 4 / 5;
  }

  .info-who-photo {
    left: 6%;
    width: 88%;
  }

  .info-bag {
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: 200px;
    grid-auto-rows: 150px;
    gap: var(--space-s);
    min-height: 0;
  }

  .info-bag::before {
    background-size: 25% 100%;
  }

  .info-bag-bag {
    grid-area: 1 / 1 / 2 / 5;
  }

  .info-bag-items .info-bag-item {
    grid-area: auto / span 2;
  }

  .reel {
    --reel-unit: 1.7vmin;
  }

  .gallery-caption-category {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  /* The hero pictures swap places with a short fade instead of travelling */
  .hero-img {
    transition: opacity 0.2s;
  }

  .hero-board.is-swapping .hero-img {
    opacity: 0;
  }

  .hero-marquee-track {
    animation: none;
  }

  .gallery-stage,
  .gallery-square::before,
  .gallery-square-number {
    transition: none;
  }

  .hero-item,
  .hero-item::after,
  .hero-marquee,
  .hero-name,
  .hero-specialties,
  .hero-welcome,
  .hero-cue,
  .info-box.is-inview .info-panel.is-active .info-mask-inner,
  .info-box.is-inview .info-panel.is-active .info-who-photo,
  .info-box.is-inview .info-panel.is-active .info-name-letter,
  .info-box.is-inview .info-panel.is-active .info-cards::before,
  .info-box.is-inview .info-panel.is-active .info-card,
  .info-box.is-inview .info-panel.is-active .info-bag::before,
  .info-box.is-inview .info-panel.is-active .info-bag-bag,
  .info-box.is-inview .info-panel.is-active .info-bag-item,
  .reel-caption.is-rolling span > span,
  .gallery-caption.is-rolling > span > span {
    animation: none;
  }

  .reel-tile-inner,
  .reel-tile-number,
  .has-js .index-item,
  .index-thumb-image,
  .info-panel.is-leaving *,
  .info-bag-note *,
  .info-bag-button img,
  .info-bag-bag,
  .info-card,
  .info-tab-frame.is-ready,
  .contact-square,
  .contact-email,
  .card-image img,
  .this-way-arrow {
    transition: none;
  }

  .card:hover .card-image img {
    transform: none;
  }
}

/* ==========================================================================
   Case study page (dragonfly-desserts.html), after the Figma "Items" board.
   Its own type: Epilogue for text, Instrument Serif for headings.
   --u is one pixel of the 1728-wide Figma frame, so the hero scales with the screen.
   ========================================================================== */

.page-case,
.page-landing {
  --cs-sans: "Epilogue", "Helvetica Neue", Arial, sans-serif;
  --cs-display: "Instrument Serif", Georgia, "Times New Roman", serif;
  --cs-grey: #d9d9d9;
  --cs-gutter: max(16px, 6.42vw);
  font-family: var(--cs-sans);
}

/* Section 1: project hero */
/* The hero fills the first screen: the navigation and the project info take the room they need and
   the picture band takes what is left (never taller than in Figma, never too short to read as the
   lead image), so the whole description is on screen at load. Vertical spacing tightens on shorter
   screens. */
.cs-hero {
  --u: calc(100cqw / 1728);
  --screen: 100vh;
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: var(--screen);
  overflow: clip;
  padding-bottom: clamp(24px, 4vh, 56px);
  container-type: inline-size;
}

@supports (height: 100svh) {
  .cs-hero {
    --screen: 100svh;
    padding-bottom: clamp(24px, 4svh, 56px);
  }
}

/* The name pressed into the paper, letter by letter, cut off by the top of the page */
.cs-emboss {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  pointer-events: none;
  user-select: none;
}

.cs-emboss img {
  position: absolute;
  top: calc(var(--y) * var(--u));
  left: calc(var(--x) / 1728 * 100%);
  width: calc(var(--w) / 1728 * 100%);
  height: auto;
}

/* home on the left, works in the middle, contact on the right */
.cs-nav {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  padding: max(20px, min(calc(112 * var(--u)), var(--screen) * 0.09)) max(16px, calc(111 * var(--u))) 0;
  color: var(--blue);
  font-size: var(--text-base);
}

.cs-nav a {
  justify-self: start;
  padding: 2px 0;
  text-underline-offset: 3px;
}

.cs-nav a:nth-child(2) {
  justify-self: center;
}

.cs-nav a:last-child {
  justify-self: end;
}

/* No underline: the cursor shows these are links. Keyboard focus keeps a quiet outline. */
.cs-nav a:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

/* Full-width band ruled top and bottom; the picture sits in the middle, ruled at its sides */
.cs-hero-band {
  position: relative;
  flex: 1 1 0;
  min-height: max(240px, var(--screen) * 0.34);
  max-height: calc(592 * var(--u));
  margin-top: max(24px, min(calc(77 * var(--u)), var(--screen) * 0.06));
  border-block: var(--line) solid var(--ink);
}

/* Grey stand-in for a project's hero picture until it has one */
.cs-placeholder {
  background: var(--cs-grey);
}

/* Once a picture has loaded, its grey stand-in goes, so pictures with transparent edges (phone
   and packaging mockups) sit on the section's own background */
[data-image][data-image-loaded] {
  background: none;
}

/* Project pictures from site-images.js, filling their grey boxes (hero, process, gallery) */
.cs-hero-image > .site-image,
/* A video over its picture (site-images.js marks the box data-video), wherever the picture appears:
   the picture shows until the video is playing, then the video fades in over it */
[data-video] {
  position: relative;
}

[data-video] > .site-video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.3s;
  pointer-events: none;
}

[data-video] > .site-video.is-playing {
  opacity: 1;
}

.cs-shot-frame > .site-image,
.cs-gallery-image > .site-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

.cs-hero-image {
  position: absolute;
  top: 0;
  left: 50%;
  width: calc(1200 * var(--u));
  height: 100%;
  border-inline: var(--line) solid var(--ink);
  object-fit: cover;
  transform: translateX(-50%);
}

/* Title and discipline on the left, client and work done on the right, summary under the title */
.cs-info {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto 1fr;
  column-gap: 48px;
  width: min(1028px, 100% - 2 * var(--cs-gutter));
  margin: max(24px, min(calc(49 * var(--u)), var(--screen) * 0.045)) auto 0;
  color: var(--ink);
}

.cs-title-block {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.cs-title {
  display: flex;
  align-items: center;
  gap: 22px;
  color: var(--ink);
  font-family: var(--cs-display);
  font-size: 46px;
  line-height: 28px;
  white-space: nowrap;
}

.cs-star {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  animation: cs-spin 24s linear infinite;
}

@keyframes cs-spin {
  to {
    transform: rotate(360deg);
  }
}

.cs-discipline {
  font-size: var(--text-base);
  font-weight: var(--weight-light);
}

.cs-facts {
  display: flex;
  grid-row: 1 / 3;
  grid-column: 2;
  gap: 30px;
  margin: 0;
}

.cs-fact {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.cs-fact dt {
  font-family: var(--cs-display);
  font-size: var(--text-l);
}

.cs-fact dd {
  margin: 0;
  font-size: var(--text-s);
  line-height: 18px;
}

.cs-fact--client dd {
  width: 211px;
}

.cs-fact ul {
  margin: 0;
  padding: 0;
  line-height: 20px;
  white-space: nowrap;
}

.cs-fact li {
  margin-left: 21px;
}

/* Directly under the title, in the left column, so the info block is no taller than the list */
.cs-summary {
  grid-column: 1;
  max-width: 560px;
  margin-top: 28px;
  font-size: var(--text-base);
  line-height: 1.4;
}

/* Section 2: the process. Without script it is a plain column; case-study.js adds .is-driven,
   which pins the stage for the length of the section and moves the images from scroll. */
.cs-process {
  --side: clamp(260px, 17.36vw, 300px);
  /* Left edge of the image column, and the width of the large image */
  --col: calc(var(--side) + 7.18vw);
  /* The large image's height stays inside the screen below its Figma top (170 of 1117) */
  --big-w: min(54vw, (100vh * 947 / 1117 - 48px) * 1.6587, 100vw - var(--col) - 32px);
  position: relative;
}

@supports (height: 100svh) {
  .cs-process {
    --big-w: min(54vw, (100svh * 947 / 1117 - 48px) * 1.6587, 100vw - var(--col) - 32px);
  }
}

.cs-process-pin {
  position: relative;
  min-height: 100vh;
}

/* Left panel: the four steps on the dark strip, pinned to the top left */
.cs-steps {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  display: flex;
  flex-direction: column;
  gap: 15px;
  width: var(--side);
  margin: 0;
  padding: 15px 15px 20px;
  overflow: hidden;
  background: var(--ink);
  color: var(--cream);
  list-style: none;
}

.cs-step {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 10px;
  /* The step whose image is large stays full strength; the others step back */
  opacity: 0.4;
  transition: opacity 0.4s var(--ease-out);
}

.cs-step.is-current {
  opacity: 1;
}

.cs-step h2 {
  font-family: var(--cs-display);
  font-size: var(--text-l);
}

.cs-step h2 span {
  margin-right: 0.55em;
}

.cs-step p {
  font-size: 12px;
  font-weight: var(--weight-light);
  letter-spacing: -0.01em;
}

/* Thin rule between the panel and the images */
/* Figma "STROKE": 2px black, centred between the panel and the images, and centred top to bottom
   with an even gap above and below (37 of the frame's 1117 pixels at each end) */
.cs-rule {
  position: absolute;
  top: calc(37 / 1117 * 100%);
  bottom: calc(37 / 1117 * 100%);
  left: calc(var(--side) + 3.59vw - 1px);
  width: 2px;
  background: var(--ink);
}

.cs-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 80px;
  margin-left: var(--col);
  padding: 170px 0 120px;
}

.cs-shot {
  position: relative;
  width: var(--big-w);
  aspect-ratio: 831 / 501;
  margin: 0;
}

.cs-shot-frame {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: var(--cs-grey);
}

/* Notes laid over the large image, at their Figma places: they fade in (rising a few pixels) once
   the image has settled large, and out as soon as it moves on, so only one image's notes ever show.
   They never take the pointer, so they cannot get in the way of scrolling. */
/* Both notes on an image share one box: the same 1px stroke, padding and left-aligned text */
.cs-note {
  position: absolute;
  z-index: 1;
  padding: 13px 14px;
  border: var(--line) solid var(--ink);
  background: var(--cream);
  color: var(--ink);
  font-size: var(--text-s);
  line-height: 1.35;
  letter-spacing: -0.01em;
  text-align: left;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(6px);
  transition: opacity 0.18s, visibility 0.18s, transform 0.18s;
}

/* The two notes stack, staggered as in Figma (the upper one 91px further right), so however long
   their text, they never run into each other */
.cs-notes {
  position: absolute;
  z-index: 1;
  top: 21.96%;
  left: min(94.5%, 100vw - var(--col) - 346px);
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 330px;
  pointer-events: none;
}

.cs-notes .cs-note {
  position: relative;
}

.cs-note--a {
  align-self: flex-end;
  width: 239px;
}

.cs-note--b {
  align-self: flex-start;
  width: 243px;
}

.cs-note-title {
  margin-bottom: 6px;
  font-family: var(--cs-display);
  font-size: 23px;
  line-height: 1.1;
}

.cs-note-list {
  margin: 0;
  padding-left: 1.05em;
}

.cs-shot.is-active .cs-note {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 0.4s var(--ease-out) 0.05s, visibility 0.4s 0.05s, transform 0.5s var(--ease-out) 0.05s;
}

.cs-shot.is-active .cs-note--b {
  transition-delay: 0.12s;
}

@media (prefers-reduced-motion: reduce) {
  .cs-note,
  .cs-shot.is-active .cs-note {
    transform: none;
    transition: opacity 0.2s, visibility 0.2s;
  }
}

/* Pinned and scroll-driven */
.cs-process.is-driven .cs-process-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  min-height: 0;
  overflow: hidden;
}

.cs-process.is-driven .cs-stage {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: var(--col);
  display: block;
  margin: 0;
  padding: 0;
  overflow: clip;
}

.cs-process.is-driven .cs-shot {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.cs-process.is-driven .cs-shot:not(.is-active) {
  pointer-events: none;
}

/* Section 3: full project gallery, two rows of four on the dark ground */
.cs-gallery {
  padding: max(48px, 6.08vw) 0 max(48px, 6.02vw);
  background: var(--ink);
}

.cs-gallery-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 37px 12px;
  width: min(80.1%, 100% - 32px);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.cs-gallery-grid li {
  display: flex;
  align-items: flex-start;
}

/* Under the gallery: (back to top) at its left edge, (next project) at its right, in the homepage's
   "( Back to top )" type, light on the dark ground */
.cs-page-nav {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  width: min(80.1%, 100% - 32px);
  margin: clamp(48px, 5vw, 80px) auto 0;
}

.cs-page-nav-link {
  color: var(--cream);
  font-family: var(--serif);
  font-size: 12px;
  white-space: nowrap;
  text-underline-offset: 3px;
}

.cs-page-nav-link:hover,
.cs-page-nav-link:focus-visible {
  text-decoration: underline;
}

.cs-page-nav-link:focus-visible {
  outline: 1px solid var(--cream);
  outline-offset: 6px;
}

.cs-gallery-image {
  flex: 1;
  aspect-ratio: 313 / 435.5;
  background: var(--cs-grey);
}

/* Tablet */
@media (max-width: 1099px) {
  .cs-gallery-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Phone: the hero picture gets its own proportion, the info stacks, and the process panel becomes
   a dark band above the images showing the current step */
@media (max-width: 699px) {
  .cs-hero {
    min-height: 0;
  }

  .cs-hero-band {
    display: flex;
    justify-content: center;
    flex: none;
    min-height: 0;
    max-height: none;
  }

  .cs-hero-image {
    position: static;
    width: calc(100% - 32px);
    height: auto;
    aspect-ratio: 16 / 10;
    transform: none;
  }

  .cs-info {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    row-gap: 32px;
  }

  .cs-title {
    font-size: 38px;
    white-space: normal;
  }

  .cs-facts {
    flex-wrap: wrap;
    grid-row: auto;
    grid-column: auto;
  }

  .cs-fact--client dd {
    width: auto;
  }

  .cs-fact ul {
    white-space: normal;
  }

  .cs-summary {
    margin-top: 0;
  }

  .cs-process {
    --big-w: min(100vw - 32px, (100vh - 300px) * 1.6587);
  }

  @supports (height: 100svh) {
    .cs-process {
      --big-w: min(100vw - 32px, (100svh - 300px) * 1.6587);
    }
  }

  .cs-process-pin {
    display: flex;
    flex-direction: column;
  }

  .cs-steps {
    position: relative;
    display: grid;
    width: auto;
    padding: 12px 16px;
  }

  /* All steps share one place so the band keeps the height of the longest; only the current shows */
  .cs-step {
    grid-area: 1 / 1;
    padding: 0;
    opacity: 1;
    visibility: hidden;
    transition: none;
  }

  .cs-step.is-current {
    visibility: visible;
  }

  .cs-step h2 {
    font-size: var(--text-m);
  }

  .cs-rule {
    display: none;
  }

  .cs-stage {
    margin-left: 16px;
    padding-top: 40px;
  }

  .cs-process.is-driven .cs-stage {
    position: relative;
    inset: auto;
    flex: 1;
    margin: 0 16px;
  }

  /* Every image centred across the stage, small or large and on the way between: each is laid at
     the large width in the middle, and scales about its top centre (the column's spacing, read
     from the top edges, is unchanged) */
  .cs-process.is-driven .cs-shot {
    left: calc((100% - var(--big-w)) / 2);
    transform-origin: 50% 0;
  }

  /* Phone: no notes over the process pictures; the step text above them gives the context */
  .cs-notes {
    display: none;
  }

  .cs-note {
    padding: 9px 11px;
    font-size: 12px;
  }

  .cs-note-title {
    font-size: 18px;
  }

  .cs-note--a,
  .cs-note--b {
    width: auto;
    max-width: 82%;
  }

  .cs-gallery-grid {
    gap: 24px 8px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cs-star {
    animation: none;
  }

  .cs-step,
  .cs-note {
    transition: none;
  }
}

/* ==========================================================================
   Homepage (index.html), after the Figma "Home" frame and its "Part 1" to "Part 3" animation
   frames. Shares the case study type and footer. --k is one pixel of the 1728 × 1117 Figma frame,
   scaled so the frame fits the screen; --u is the width-only version the embossed name uses.
   ========================================================================== */

/* Section 1: hero */
.hm-hero {
  --screen: 100vh;
  --u: calc(100cqw / 1728);
  --k: min(100cqw / 1728, var(--screen) / 1117);
  position: relative;
  height: var(--screen);
  min-height: 560px;
  overflow: clip;
  color: var(--ink);
  container-type: inline-size;
}

@supports (height: 100svh) {
  .hm-hero,
  .hm-work {
    --screen: 100svh;
  }
}

/* The specialties pressed into the paper under the name: outlined artwork from Figma, 282px apart */
.hm-specialties {
  position: absolute;
  top: calc(208.23 * var(--k));
  left: 50%;
  display: flex;
  align-items: center;
  gap: calc(282 * var(--k));
  margin: 0;
  padding: 0;
  list-style: none;
  /* index.html defines the filter */
  filter: url(#hm-press);
  transform: translateX(-50%);
  pointer-events: none;
  user-select: none;
}

.hm-specialties img {
  display: block;
  width: calc(var(--w) * var(--k));
  height: auto;
}

.hm-specialties .hm-inner {
  filter: url(#hm-press-inner);
}

/* Headline, marquee and navigation, a little below the middle of the screen as in Figma */
.hm-centre {
  position: absolute;
  top: calc(50% - 77.5 * var(--k));
  right: 0;
  left: 0;
  display: flex;
  justify-content: center;
}

.hm-headline {
  margin-top: calc(-1 * max(14px, calc(30 * var(--k))));
  font-family: var(--cs-display);
  font-size: max(40px, calc(86 * var(--k)));
  line-height: max(42px, calc(88 * var(--k)));
  text-align: center;
  white-space: nowrap;
}

/* Each line in its own mask (extended above and below, so ascenders and descenders are not cut,
   without changing the line spacing). On load the words rise into place one after another in
   reading order, fast then settling; home.js slides the whole lines back up out of their masks with
   the scroll (bottom line first) through the "translate" property, so the two never fight. */
.hm-line {
  display: block;
  margin: -0.14em 0 -0.2em;
  padding: 0.14em 0 0.2em;
  overflow: hidden;
}

.hm-line-in {
  display: block;
}

.hm-word {
  display: inline-block;
  animation: hm-line-rise 0.95s cubic-bezier(0.16, 1, 0.3, 1) calc(0.25s + var(--i) * 0.055s) both;
}

@keyframes hm-line-rise {
  from {
    transform: translateY(118%);
  }
}

/* Marquee separators: the portfolio's star, in the marquee's ink */
.hm-marquee-star {
  display: inline-block;
  width: 1.05em;
  height: 1.05em;
  margin: 0 0.3em;
  vertical-align: -0.2em;
}

.hm-marquee {
  position: absolute;
  top: max(100px, calc(193 * var(--k)));
  left: 0;
  display: flex;
  width: 100%;
  overflow: hidden;
  font-size: max(13px, calc(16 * var(--k)));
  white-space: pre;
}

.hm-marquee-track {
  display: flex;
  flex-shrink: 0;
  animation: hm-marquee 90s linear infinite;
}

@keyframes hm-marquee {
  to {
    transform: translateX(-50%);
  }
}

.hm-nav {
  position: absolute;
  top: max(124px, calc(239 * var(--k)));
  left: calc(50% - 23.5 * var(--k));
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  width: max(240px, calc(285 * var(--k)));
  color: var(--blue);
  font-size: max(13px, calc(16 * var(--k)));
  transform: translateX(-50%);
}

.hm-nav a {
  justify-self: start;
  padding: 2px 0;
  text-underline-offset: 3px;
}

.hm-nav a:nth-child(2) {
  justify-self: center;
}

.hm-nav a:last-child {
  justify-self: end;
}

/* No underline: the cursor shows these are links. Keyboard focus keeps a quiet outline. */
.hm-nav a:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

.hm-this-way {
  position: absolute;
  bottom: calc(116 * var(--k));
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: var(--serif);
  font-size: 12px;
  transform: translateX(-50%);
}

/* The arrow artwork points right; turned to point down, it hangs 16px under the words */
.hm-this-way img {
  width: 30px;
  height: auto;
  margin-top: 16px;
  transform: rotate(90deg);
  transition: transform 0.5s var(--ease-out);
}

.hm-this-way:hover img,
.hm-this-way:focus-visible img {
  transform: translateX(0) translateY(5px) rotate(90deg);
}

.hm-this-way:focus-visible {
  outline: 1px solid var(--blue);
  outline-offset: 6px;
}

/* Hero to selected work: the arrow carried into the middle of the screen, then grown until it is the
   dark section. home.js clips this layer and the section itself to the same arrow shape, so the
   arrow and the section are one surface; the layer covers the part of the arrow still over the
   hero. It sits under the section's content and over the hero. */
.hm-burst {
  position: fixed;
  z-index: 1;
  inset: 0;
  background: var(--ink);
  visibility: hidden;
  pointer-events: none;
}

.hm-burst.is-on {
  visibility: visible;
}

.hm-work {
  z-index: 2;
}

/* While the drawn arrow stands in for it */
.hm-this-way.is-carried img {
  visibility: hidden;
}

.hm-this-way > span {
  transform-origin: 50% 100%;
}

/* Section 2: selected work. The frame keeps Figma's proportions and is centred on the screen. */
.hm-work {
  --screen: 100vh;
  position: relative;
  height: var(--screen);
  min-height: 560px;
  overflow: clip;
  background: var(--ink);
  color: var(--cream);
  container-type: inline-size;
}

.hm-work-frame {
  --k: min(100cqw / 1200, var(--screen) / 1117);
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(1728 * var(--k));
  height: calc(1117 * var(--k));
  transform: translate(-50%, -50%);
}

/* The tag and the line beside it, 20px apart, on one row */
.hm-intro {
  position: absolute;
  top: calc(75 * var(--k));
  left: calc(323 * var(--k));
  display: flex;
  align-items: center;
  gap: max(12px, calc(20 * var(--k)));
  width: calc(1081 * var(--k));
}

.hm-tag {
  flex-shrink: 0;
  padding: 1px 8px;
  border: var(--line) solid var(--blue-light);
  color: var(--blue-light);
  font-size: max(11px, calc(14 * var(--k)));
  line-height: max(20px, calc(28 * var(--k)));
  white-space: nowrap;
}

.hm-tagline {
  font-size: max(12px, calc(16 * var(--k)));
  line-height: max(20px, calc(28 * var(--k)));
}

/* 11 × 6 cells ruled in the paper colour: a line at the start of every cell, closed on the right and bottom */
.hm-grid {
  --cols: 11;
  --rows: 6;
  position: absolute;
  top: calc(151 * var(--k));
  left: calc(323 * var(--k));
  width: calc(1081.18 * var(--k));
  height: calc(603 * var(--k));
  border-right: var(--line) solid var(--cream);
  border-bottom: var(--line) solid var(--cream);
  background-image:
    linear-gradient(to right, var(--cream) 1px, transparent 1px),
    linear-gradient(to bottom, var(--cream) 1px, transparent 1px);
  background-size: calc(100% / var(--cols)) calc(100% / var(--rows));
}

/* Numbers: the embossed Figma artwork while resting; the lit artwork fades in over it while the
   project is current. Each artwork is 4px larger than its number for the shadow, offset so the
   number itself sits in the middle of the cell. --nk scales them with the frame, never below
   the 12px the numbers had as text. */
.hm-number {
  --nk: max(12px / 21, var(--k));
  position: absolute;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.hm-number-emboss,
.hm-number-lit {
  position: absolute;
  top: 50%;
  left: 50%;
  transition: opacity 0.3s var(--ease-out);
}

img.hm-number-emboss,
img.hm-number-lit {
  height: calc(26.1235 * var(--nk));
  width: auto;
}

.hm-number-emboss {
  transform: translate(calc(-50% + 1.5 * var(--nk)), calc(-50% + 1.5 * var(--nk)));
}

img.hm-number-lit {
  transform: translate(calc(-50% + 0.5 * var(--nk)), calc(-50% + 0.5 * var(--nk)));
}

.hm-number-lit {
  opacity: 0;
}

.hm-number.is-active .hm-number-emboss {
  opacity: 0;
}

.hm-number.is-active .hm-number-lit {
  opacity: 1;
}

/* Lit number as text, for numbers without lit artwork: Figma's original type for the lit state */
.hm-number-text {
  padding-left: 0.31em;
  color: var(--cream);
  font-family: var(--cs-sans);
  font-size: calc(21 * var(--nk));
  font-weight: var(--weight-medium);
  line-height: 1.479;
  letter-spacing: 0.31em;
  white-space: nowrap;
  text-shadow: 0.5px 0.5px 2px rgba(243, 244, 236, 0.24);
  transform: translate(-50%, -50%);
}

.hm-number:focus-visible {
  outline: 1px solid var(--cream);
  outline-offset: -6px;
}

/* The current project's picture: four cells by three (home.js), opening out of a corner of its
   number's cell; it may lie over other numbers, which stay hoverable through it */
.hm-shot {
  position: absolute;
  z-index: 2;
  display: block;
  overflow: hidden;
  pointer-events: none;
}

/* Grey until the project's picture has loaded, or if it is still missing */
.hm-shot {
  background: #d9d9d9;
}

.hm-shot.is-shown {
  pointer-events: auto;
}

.hm-shot img,
.hm-shot-placeholder {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hm-shot-placeholder {
  background: #d9d9d9;
}

.hm-shot:focus-visible {
  outline: 2px solid var(--cream);
  outline-offset: 3px;
}

/* Title and category, boxed, beside the number */
.hm-title {
  position: absolute;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(8 * var(--k));
  pointer-events: none;
}

.hm-box {
  padding: 1px 8px;
  border: var(--line) solid var(--cream);
  background: var(--ink);
  font-size: max(11px, calc(14 * var(--k)));
  line-height: max(20px, calc(28 * var(--k)));
  white-space: nowrap;
}

/* Under the steps: "( there's more )" and the hero's arrow, centred on the grid and set like the
   hero's cue (12px serif, arrow 30px wide, 16px below the words), in the section's off-white */
.hm-more {
  position: absolute;
  top: calc(960 * var(--k));
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: inherit;
  font-family: var(--serif);
  font-size: 12px;
  white-space: nowrap;
  transform: translateX(-50%);
}

.hm-more svg {
  width: 30px;
  height: auto;
  margin-top: 16px;
  transform: rotate(90deg);
}

.hm-more:focus-visible {
  outline: 1px solid var(--cream);
  outline-offset: 6px;
}

/* The four steps under the grid, with a rule between each */
.hm-labels {
  position: absolute;
  top: calc(805 * var(--k));
  left: calc(310 * var(--k));
  display: flex;
  align-items: flex-start;
  gap: calc(20 * var(--k));
  width: calc(1108 * var(--k));
  color: var(--cream);
  text-align: center;
}

.hm-label {
  flex: 1;
  overflow: hidden;
  padding-bottom: 4px;
}

.hm-label-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(20 * var(--k));
  transform: translateY(110%);
}

.hm-label-title {
  font-family: var(--cs-display);
  font-size: max(20px, calc(32 * var(--k)));
  line-height: max(22px, calc(28 * var(--k)));
  white-space: nowrap;
}

.hm-label-text {
  /* 201px at Figma's 14px, growing with the type when the type is held at its smallest size */
  max-width: calc(201em / 14);
  white-space: pre-line;
  font-size: max(12px, calc(14 * var(--k)));
  line-height: 1.21;
}

.hm-label-rule {
  flex-shrink: 0;
  width: 1px;
  align-self: center;
  height: calc(58 * var(--k));
  background: var(--cream);
  transform: scaleY(0);
}

/* Phone: the hero type sets its own sizes, and the work section becomes a column with a 6 × 9 grid */
@media (max-width: 699px) {
  .hm-hero {
    --k: calc(100cqw / 760);
  }

  .hm-specialties {
    display: grid;
    grid-template-columns: 1fr 1fr;
    justify-items: center;
    gap: 12px 0;
    width: calc(100% - 32px);
  }

  .hm-work {
    height: auto;
    min-height: var(--screen);
    padding: 56px 16px;
  }

  .hm-work-frame {
    position: relative;
    top: auto;
    left: auto;
    display: flex;
    flex-direction: column;
    gap: 20px;
    width: auto;
    height: auto;
    transform: none;
  }

  .hm-intro,
  .hm-grid,
  .hm-labels {
    position: relative;
    top: auto;
    left: auto;
  }

  .hm-more {
    position: relative;
    top: auto;
    left: auto;
    align-self: center;
    margin-top: 40px;
    transform: none;
  }

  .hm-intro {
    flex-direction: column;
    align-items: flex-start;
    width: auto;
  }

  .hm-grid {
    --cols: 6;
    --rows: 9;
    width: 100%;
    height: auto;
    aspect-ratio: 587 / 900;
    margin-top: 12px;
  }

  .hm-number {
    --nk: calc(13px / 21);
  }

  .hm-labels {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px 16px;
    width: auto;
    margin-top: 16px;
  }

  .hm-label-rule {
    display: none;
  }

  .hm-label-inner {
    gap: 10px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hm-marquee-track,
  .hm-word {
    animation: none;
  }

  .hm-number,
  .hm-this-way img {
    transition: none;
  }
}

/* Section 3: selected projects (Figma "Projects"). Five cards in a row with the quote pressed into the
   paper below. It scrolls like any other section. Hovering a card changes no sizes: the other four
   recede into the paper and the hovered card's title is underlined. --k is one pixel of the
   1728 × 1117 frame. */
.pg {
  --screen: 100vh;
  position: relative;
  container-type: inline-size;
}

@supports (height: 100svh) {
  .pg {
    --screen: 100svh;
  }
}

/* One screen tall, holding the Figma frame */
/* Ends a little under the quote (Figma "Projects": cards, then the quote 72px below them), so the
   tops of the gallery's first row show at the foot of the screen while this section is in view */
.pg-inner {
  --k: min(100cqw / 1728, var(--screen) / 1117);
  position: relative;
  height: calc(951 * var(--k));
  min-height: 560px;
  overflow: hidden;
}

.pg-frame {
  --k: min(100cqw / 1728, var(--screen) / 1117);
  position: absolute;
  top: 0;
  left: 50%;
  width: calc(1728 * var(--k));
  height: calc(1117 * var(--k));
  transform: translateX(-50%);
}

/* "[Full Gallery]" in a blue box, with its line beside it */
.pg-intro {
  position: absolute;
  top: calc(67 * var(--k));
  left: calc(79 * var(--k));
  width: calc(1589 * var(--k));
  display: flex;
  align-items: center;
  gap: max(10px, calc(15 * var(--k)));
}

.pg-tag {
  flex-shrink: 0;
  padding: 1px 8px;
  border: var(--line) solid var(--blue);
  color: var(--blue);
  font-size: max(11px, calc(14 * var(--k)));
  line-height: max(20px, calc(28 * var(--k)));
  white-space: nowrap;
}

.pg-tagline,
.pg-aside {
  color: var(--ink);
  font-size: max(12px, calc(16 * var(--k)));
  line-height: max(20px, calc(28 * var(--k)));
  white-space: nowrap;
}

/* Figma: set right against the right edge of the cards and gallery */
.pg-aside {
  margin-left: auto;
  text-align: right;
}

.pg-track {
  position: absolute;
  top: calc(128 * var(--k));
  left: calc(60 * var(--k));
  display: flex;
  align-items: flex-start;
  gap: calc(12 * var(--k));
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Number beside the picture, text under it */
.pg-card {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: calc(5 * var(--k));
  row-gap: calc(10 * var(--k));
  width: calc(312 * var(--k));
  color: var(--ink);
  transition: opacity 0.45s cubic-bezier(0.65, 0, 0.25, 1);
}

/* While one card is open the others step back */
.pg-track:has(.pg-card.is-open) .pg-card:not(.is-open) {
  opacity: 0.4;
}

.pg-number {
  color: var(--blue);
  font-family: var(--serif);
  font-size: max(14px, calc(23 * var(--k)));
  letter-spacing: -0.01em;
  line-height: 1.17;
}

/* Pressed into the paper: a light edge outside, a soft shadow inside */
.pg-image {
  position: relative;
  display: block;
  height: calc(379 * var(--k));
  overflow: hidden;
  background: #d9d9d9;
  box-shadow: 4px 4px 4px 0 var(--highlight);
}

.pg-image::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 3px 3px 4px 0 rgba(33, 27, 36, 0.15);
  pointer-events: none;
}

/* The picture fills the frame, centred, at its own proportions, trimmed evenly top and bottom */
.pg-image img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
}

.pg-title {
  text-decoration: underline 1px transparent;
  text-underline-offset: 0.18em;
  transition: text-decoration-color 0.45s cubic-bezier(0.65, 0, 0.25, 1);
}

.pg-card.is-open .pg-title {
  text-decoration-color: currentColor;
}

.pg-text {
  display: flex;
  flex-direction: column;
  grid-column: 1 / -1;
  padding-left: calc(20 * var(--k));
}

.pg-title {
  font-family: var(--cs-display);
  font-size: max(22px, calc(32 * var(--k)));
  letter-spacing: -0.01em;
  line-height: 1.31;
}

.pg-role {
  font-family: var(--serif);
  font-size: max(12px, calc(13 * var(--k)));
  letter-spacing: -0.01em;
}

.pg-did {
  display: flex;
  flex-direction: column;
  margin-top: calc(5 * var(--k));
  font-family: var(--sans);
  font-size: max(12px, calc(14 * var(--k)));
  letter-spacing: -0.01em;
}

a.pg-card[href]:focus-visible {
  outline: 1px solid var(--blue);
  outline-offset: 6px;
}

/* The quote: each letter at its Figma position (--x, --y from the quote's top-left, --w its width) */
.pg-quote {
  position: absolute;
  top: calc(741 * var(--k));
  left: 0;
  width: 100%;
  margin: 0;
}

.pg-quote-art {
  position: relative;
  height: calc(162.13 * var(--k));
}

.pg-quote-art img {
  position: absolute;
  top: calc(var(--y) * var(--k));
  left: calc(var(--x) * var(--k));
  width: calc(var(--w) * var(--k));
  height: auto;
}



/* Tablets and small laptops: the card text is held at a readable size, so it outgrows the scaled
   frame; the section follows its content instead, the quote 72 Figma pixels under the cards */
@media (min-width: 700px) and (max-width: 1099px) {
  .pg-inner {
    height: auto;
    min-height: 0;
  }

  .pg-frame {
    position: relative;
    left: auto;
    width: auto;
    height: auto;
    padding-bottom: calc(48 * var(--k));
    transform: none;
  }

  .pg-intro {
    position: relative;
    top: auto;
    margin-top: calc(67 * var(--k));
  }

  .pg-track {
    position: relative;
    top: auto;
    margin-top: 24px;
  }

  .pg-quote {
    position: relative;
    top: auto;
    margin-top: calc(72 * var(--k));
  }
}

/* Phone: the cards wrap into two columns with the quote under them, and the section grows
   to its own height */
@media (max-width: 699px) {
  .pg-inner {
    height: auto;
    min-height: 0;
    overflow: visible;
  }

  .pg-frame {
    --k: calc((100cqw - 44px) / 2 / 312);
    position: relative;
    left: auto;
    width: auto;
    height: auto;
    padding: 48px 16px;
    transform: none;
  }

  .pg-intro {
    position: relative;
    top: auto;
    left: auto;
    width: auto;
    flex-wrap: wrap;
    row-gap: 4px;
    margin-bottom: 24px;
  }

  .pg-track {
    position: relative;
    top: auto;
    left: auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
    gap: 28px 12px;
  }

  .pg-card {
    width: auto;
  }

  .pg-quote {
    --k: calc((100cqw - 32px) / 650);
    position: relative;
    top: auto;
    margin-top: 48px;
  }

  .pg-quote-art {
    width: calc(650 * var(--k));
  }

  .pg-quote-art img {
    left: calc((var(--x) - 78.8) * var(--k));
  }

}

@media (prefers-reduced-motion: reduce) {
  .pg-card,
  .pg-title {
    transition: none;
  }
}

/* ==========================================================================
   Site footer (footer.js), shared by every page: Figma "Footer". The name between two stars and the
   links under it, centred on the dark ground, with the name pressed in again along the bottom edge.
   --u is one pixel of the 1728-wide frame; --s scales the name and stars down on narrow screens.
   The stars open out from the middle of the name when the footer first comes into view (footer.js
   removes .is-closed), and turn the whole time like the star beside a project's title.
   ========================================================================== */
.sf {
  --u: calc(100cqw / 1728);
  --s: min(1, (100cqw - 32px) / 472px);
  --open: cubic-bezier(0.65, 0, 0.25, 1);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: max(280px, 462 * var(--u));
  padding-top: max(48px, 69 * var(--u));
  overflow: hidden;
  background: var(--ink);
  container-type: inline-size;
}

/* The name and its stars: 472 wide in Figma, the stars at its two ends */
.sf-mark {
  position: relative;
  width: calc(472px * var(--s));
  height: calc(38px * var(--s));
}

.sf-name {
  position: absolute;
  top: 0;
  left: 50%;
  margin: 0;
  transform: translateX(-50%);
  clip-path: inset(0 0 0 0);
  transition: clip-path 1.3s var(--open);
}

.sf-name img {
  display: block;
  width: calc(346.291px * var(--s));
  height: auto;
}

.sf-star {
  --to: calc(217px * var(--s));
  position: absolute;
  top: 0;
  left: 50%;
  width: calc(38px * var(--s));
  height: calc(38px * var(--s));
  margin-left: calc(-19px * var(--s));
  transition: transform 1.3s var(--open);
}

.sf-star--left {
  transform: translateX(calc(var(--to) * -1));
}

.sf-star--right {
  transform: translateX(var(--to));
}

.sf-star img {
  display: block;
  width: 100%;
  height: 100%;
  animation: cs-spin 24s linear infinite;
}

.sf-star--left img {
  animation-direction: reverse;
}

/* Start frame: both stars together over the middle, the name not yet open */
.sf.is-closed .sf-star {
  transform: none;
}

.sf.is-closed .sf-name {
  clip-path: inset(0 50% 0 50%);
}

.sf-nav {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  width: 285px;
  margin-top: calc(29px * var(--s));
  color: var(--blue-light);
  font-family: var(--cs-sans, "Epilogue", "Helvetica Neue", Arial, sans-serif);
  font-size: var(--text-base);
}

.sf-nav a {
  justify-self: start;
  padding: 2px 0;
  text-underline-offset: 3px;
}

.sf-nav a:nth-child(2) {
  justify-self: center;
}

.sf-nav a:last-child {
  justify-self: end;
}

/* No underline: the cursor shows these are links. Keyboard focus keeps a quiet outline. */
.sf-nav a:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
}

/* The name pressed into the dark ground, its lower edge running off the bottom */
.sf-emboss {
  position: absolute;
  bottom: calc(-21.34 * var(--u));
  left: calc(0.5 * var(--u));
  width: calc(1732.2 * var(--u));
  height: auto;
  pointer-events: none;
  user-select: none;
}

@media (prefers-reduced-motion: reduce) {
  .sf-star img {
    animation: none;
  }

  .sf-star,
  .sf-name {
    transition: none;
  }
}

/* ==========================================================================
   Contact page (contact.html). The content of the old About page, set in the portfolio's own
   language: the pressed name across the top, Instrument Serif headings, Epilogue text, the cream
   paper and the ink sections, and the turning blue star as the one accent. Sizes follow the
   homepage and project pages: 16px text, headings in the 30–90px range.
   ========================================================================== */
.page-contact {
  --ct-gutter: max(20px, 6.42vw);
  --ct-section: clamp(64px, 7.5vw, 120px);
  --ct-band: clamp(34px, 2.5vw, 44px);
}

.ct-wrap {
  width: min(1180px, 100% - 2 * var(--ct-gutter));
  margin: 0 auto;
}

.ct-star {
  display: block;
  width: 40px;
  height: 40px;
  animation: cs-spin 24s linear infinite;
}

/* Hero: three quarters of the screen, so the greeting below shows on load. The name pressed into
   the paper runs across the top, as on every page; the portrait stands on the descriptor band, and
   contact.js lets it lag behind the scroll so it slides down under the band. */
.ct-hero {
  --u: calc(100cqw / 1728);
  --screen: 100vh;
  --band: var(--ct-band);
  position: relative;
  display: flex;
  flex-direction: column;
  height: max(560px, calc(var(--screen) * 0.76));
  overflow: clip;
  container-type: inline-size;
}

@supports (height: 100svh) {
  .ct-hero {
    --screen: 100svh;
  }
}

.ct-hero .cs-nav {
  z-index: 3;
}

.ct-hero-stage {
  flex: 1;
}

.ct-portrait {
  position: absolute;
  z-index: 1;
  bottom: var(--band);
  left: 50%;
  /* The size she has on the published About page (madelinekendallart.com/about): 606 x 627, in
     its 980-wide layout, which narrower screens show scaled down, as here. contact.js sets --drift
     and --grow together, as the published page moves her: she shrinks about her centre and sinks
     as the hero scrolls away. */
  width: min(606px, 61.84vw);
  height: auto;
  transform: translate3d(-50%, var(--drift, 0px), 0) scale(var(--grow, 1));
  transform-origin: 50% 50%;
  will-change: transform;
}

/* The greeting, in the italic of the page's heading face, tilted as the old lettering was; its
   lines and words use the homepage headline's masks and entrance (.hm-line, .hm-word) */
.ct-hello {
  position: absolute;
  z-index: 2;
  top: 27%;
  left: 61%;
  margin: 0;
  color: var(--ink);
  font-family: var(--cs-display);
  font-size: clamp(30px, 3.3vw, 60px);
  font-style: italic;
  line-height: 0.92;
  white-space: nowrap;
  transform: rotate(-6deg);
  transform-origin: 0 0;
}

.ct-hello .hm-line:nth-child(2) {
  padding-left: 0.45em;
}

.ct-hello .hm-line:nth-child(3) {
  padding-left: 0.2em;
}

/* The three descriptors, running on an ink band with the star between them */
.ct-band {
  position: relative;
  z-index: 2;
  display: flex;
  height: var(--ct-band);
  overflow: hidden;
  background: var(--ink);
  color: var(--cream);
}

.ct-band-track {
  display: flex;
  flex-shrink: 0;
  animation: hm-marquee 80s linear infinite;
}

/* Both bands: text and stars styled as the homepage hero marquee (.hm-marquee): Epilogue at its
   size, and its star (.hm-marquee-star), here in the site's blue, spaced a word space plus 0.3em
   either side */
.ct-band-set {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: 0.55em;
  padding-right: 0.55em;
  font-family: var(--cs-sans);
  font-size: max(13px, min(16 * 100vw / 1728, 16 * 100vh / 1117));
}

/* The small text needs twice as many words per set to span wide screens; a longer loop keeps it
   moving at the bands' earlier pace */
.ct-band .ct-band-track {
  animation-duration: 100s;
}

.ct-band span {
  white-space: nowrap;
}

.ct-band .hm-marquee-star {
  flex-shrink: 0;
  margin: 0 0.3em;
  color: var(--blue-light);
}

/* Two columns: a heading on the left that stays in view while the right column scrolls past (as
   the side panel does on the project pages), released at the end of its section */
.ct-split {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: clamp(32px, 4vw, 64px);
  align-items: start;
}

.ct-split > :first-child {
  position: sticky;
  top: 48px;
}

/* Introduction */
.ct-intro {
  padding: clamp(56px, 6vw, 96px) 0 var(--ct-section);
}

.ct-hey {
  color: var(--ink);
  font-family: var(--cs-display);
  font-size: clamp(48px, 5vw, 86px);
  line-height: 0.9;
}

.ct-intro-copy {
  display: flex;
  flex-direction: column;
  gap: 1.1em;
  max-width: 540px;
  padding-top: 6px;
  color: var(--ink);
  font-size: 16px;
  line-height: 1.6;
}

.ct-intro-copy strong {
  font-weight: var(--weight-medium);
}

/* Experience: an index, one line per skill on a hairline, on the ink ground. The heading stays in
   its place and scrolls with the page; the lines come and go as contact.js moves them. */
.ct-experience .ct-split > :first-child {
  position: static;
}

.ct-experience {
  padding: var(--ct-section) 0;
  background: var(--ink);
  color: var(--cream);
}

.ct-exp-title {
  color: var(--cream);
  font-family: var(--cs-display);
  font-size: clamp(32px, 3vw, 46px);
  line-height: 1;
}

.ct-exp-list {
  margin: 0;
  padding: 0;
  border-bottom: var(--line) solid rgba(243, 244, 236, 0.4);
  list-style: none;
}

.ct-exp-list li {
  display: flex;
  align-items: baseline;
  gap: 20px;
  padding: 10px 0 8px;
  border-top: var(--line) solid rgba(243, 244, 236, 0.4);
  color: var(--cream);
  font-family: var(--cs-display);
  font-size: clamp(22px, 2vw, 32px);
  line-height: 1.15;
}

/* Each line in its own mask (after Inspiration/Motion/Text in and out.mov): contact.js slides the
   words up into it as the line comes into view and back up out of it once the line has passed well
   above the middle of the screen; scrolling back brings them down into it again. The hairlines stay. */
.ct-exp-list.is-masked li {
  overflow: hidden;
}

.ct-exp-in {
  display: flex;
  align-items: baseline;
  gap: inherit;
  transition: transform 0.95s cubic-bezier(0.16, 1, 0.3, 1);
}

.ct-exp-list li.is-below .ct-exp-in {
  transform: translateY(140%);
}

.ct-exp-list li.is-above .ct-exp-in {
  transform: translateY(-140%);
}

.ct-exp-num {
  flex-shrink: 0;
  width: 2.6em;
  color: var(--blue-light);
  font-family: var(--cs-sans);
  font-size: 13px;
  letter-spacing: 0.04em;
}

/* Contact form beside the full-length cutout, which stands on the descriptor band below */
.ct-contact {
  padding-top: var(--ct-section);
}

.ct-contact-grid {
  display: grid;
  /* minmax(0, …): the title, set on one line, may run past its column without widening it */
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: 48px;
  align-items: end;
}

/* The star to the left of the title, spaced and sized as on the project pages (.cs-title) */
.ct-touch {
  display: flex;
  align-items: center;
  gap: 22px;
  white-space: nowrap;
}

.ct-touch .ct-star {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
}

.ct-touch {
  margin-bottom: clamp(28px, 3vw, 44px);
  color: var(--ink);
  font-family: var(--cs-display);
  font-size: clamp(40px, 4vw, 64px);
  line-height: 0.95;
}

.ct-form {
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: 480px;
}

.ct-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ct-field label {
  color: var(--ink);
  font-size: 13px;
  font-weight: var(--weight-medium);
}

.ct-field input,
.ct-field textarea {
  width: 100%;
  padding: 8px 0;
  border: 0;
  border-bottom: var(--line) solid var(--ink);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 16px;
  transition: box-shadow 0.2s;
}

.ct-field textarea {
  min-height: 130px;
  resize: vertical;
}

.ct-field input::placeholder,
.ct-field textarea::placeholder {
  color: rgba(33, 27, 36, 0.45);
}

.ct-field input:focus,
.ct-field textarea:focus {
  border-bottom-color: var(--blue);
  outline: none;
  box-shadow: inset 0 -1px 0 var(--blue);
}

.ct-field [aria-invalid="true"] {
  border-bottom-color: var(--ink);
  box-shadow: inset 0 -1px 0 var(--ink);
}

.ct-error {
  min-height: 1.2em;
  color: var(--ink);
  font-family: var(--cs-display);
  font-size: 16px;
  font-style: italic;
}

.ct-submit {
  align-self: flex-start;
  padding: 10px 32px;
  border: var(--line) solid var(--ink);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  transition: background-color 0.2s, color 0.2s;
}

.ct-submit:hover {
  background: var(--ink);
  color: var(--cream);
}

.ct-submit:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 4px;
}

.ct-submit:disabled {
  opacity: 0.5;
  cursor: progress;
}

.ct-status {
  max-width: 480px;
  color: var(--ink);
  font-size: 14px;
  line-height: 1.5;
}

.ct-status:not(:empty) {
  padding-left: 14px;
  border-left: 2px solid var(--blue);
}

/* The cutout bleeds off the right edge so the pavement runs out of the page; the band below hides
   its lower edge. */
.ct-contact {
  position: relative;
  z-index: 1;
  /* the cutout starts partly past the right edge; nothing scrolls sideways */
  overflow-x: clip;
}

.ct-band--foot {
  z-index: 3;
}

/* The size it has on the published About page: 846 x 795, in its 980-wide layout (scaled down on
   narrower screens), flush with the right edge. contact.js slides it in from the right as on the
   published page (--slide). */
.ct-full {
  position: relative;
  transform: translate3d(var(--slide, 0px), 0, 0);
  will-change: transform;
  display: block;
  justify-self: end;
  width: min(846px, 86.33vw);
  max-width: none;
  height: auto;
  /* out to the edge of the screen, past the side margin of .ct-wrap */
  margin-right: calc((min(1180px, 100vw - 2 * var(--ct-gutter)) - 100vw) / 2);
}

/* "(see my work)": the homepage's back-to-top graphic (.fg-top). On desktop it sits centred at the
   foot of the form section, in the room under the form, clear of the cutout and the band below. */
.ct-contact-main {
  padding-bottom: clamp(132px, 11vw, 180px);
}

.ct-see-work {
  position: absolute;
  z-index: 2;
  bottom: clamp(40px, 3.6vw, 60px);
  left: 50%;
  margin: 0;
  transform: translateX(-50%);
}

@media (max-width: 1099px) {
  .ct-split,
  .ct-contact-grid {
    grid-template-columns: 1fr;
  }

  /* One column: nothing to stay beside, so nothing sticks */
  .ct-split > :first-child {
    position: static;
  }

  .ct-contact-main {
    padding-bottom: 0;
  }

  /* One column: under the form, centred, before the cutout */
  .ct-see-work {
    position: static;
    margin: clamp(56px, 8vw, 80px) auto clamp(40px, 6vw, 56px);
    transform: none;
  }

}

@media (max-width: 699px) {
  /* Only as tall as its content: the greeting (its lines end about 190px down), then the portrait
     (her phone height below, as contact.js seats her) standing on the band, so her head comes up
     just under the greeting instead of leaving a gap of empty paper between them */
  .ct-hero {
    height: calc(196px + min(var(--screen) * 0.41, 87vw) * 0.92 + var(--ct-band));
  }

  .ct-intro {
    padding-top: 40px;
  }

  /* Phone: the published page has no phone layout (it shows its 980-wide desktop page shrunk to
     fit), so its sizes would leave her small at the foot of this taller hero. She keeps the size
     her figure had here; she moves as on the published page, from the first scroll (contact.js). */
  .ct-portrait {
    width: auto;
    height: min(calc(var(--screen) * 0.41), 87cqw);
  }

  .ct-hello {
    top: 92px;
    right: 24px;
    left: auto;
    font-size: 30px;
  }

  .ct-split {
    gap: 24px;
  }

  .ct-exp-list li {
    gap: 12px;
  }

  .ct-touch {
    white-space: normal;
  }

  .ct-exp-num {
    width: 2em;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ct-star,
  .ct-band-track {
    animation: none;
  }

  .ct-portrait,
  .ct-full {
    will-change: auto;
  }
}

/* Section 4: full gallery (Figma "Group 26"). Four columns of tall pictures at the Figma proportion
   (369.5 × 503 in a 1589-wide grid with 37px gaps), on the paper, no frames. Rows are centred, so a
   short last row sits in the middle. Below, "( Back to top )" with the hero's arrow pointing up. */
.fg {
  padding: min(48 * 100vw / 1728, 48 * 100vh / 1117) 0 clamp(48px, 4vw, 72px);
}

.fg-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(16px, 2.14vw, 40px);
  width: min(91.96vw, 1680px);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.fg-grid li {
  flex: 0 0 calc((100% - 3 * clamp(16px, 2.14vw, 40px)) / 4);
}

.fg-item {
  display: block;
}

.fg-image {
  display: block;
  aspect-ratio: 369.5 / 503;
  overflow: hidden;
  background: #d9d9d9;
}

.fg-image > .site-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.fg-item:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 4px;
}

.fg-top {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: max-content;
  margin: clamp(56px, 6.6vw, 114px) auto 0;
  color: var(--ink);
  font-family: var(--serif);
  font-size: 12px;
}

.fg-top svg {
  width: 30px;
  height: auto;
  margin-bottom: 10px;
  transform: rotate(-90deg);
}

.fg-top:focus-visible {
  outline: 1px solid var(--blue);
  outline-offset: 6px;
}

/* Four across down to small laptops, then two, so with 24 pictures every row is full */
@media (max-width: 899px) {
  .fg-grid li {
    flex-basis: calc((100% - clamp(16px, 2.14vw, 40px)) / 2);
  }
}

@media (max-width: 699px) {
  .fg-grid {
    width: calc(100% - 32px);
    gap: 12px;
  }

  .fg-grid li {
    flex-basis: calc((100% - 12px) / 2);
  }
}
