/* ============================================================
   Chloe Cheuk — portfolio
   All metrics taken from the Figma source (1440-wide frame).
   Colours:  purple #BF7AB8 · teal #7AB6BF · tan #BF947A
   Plate:    #BF947A at 44% over white, flattened to #E3D0C4
   Type:     Instrument Serif (headings/nav) · Instrument Sans (body)
   ============================================================ */

:root{
  /* 1u = one pixel of the 1440-wide Figma frame. Everything is
     calc(N * var(--u)), so the design scales up on larger screens and
     down on smaller ones with every spacing ratio intact. Pure CSS,
     so it can never fall out of sync with the viewport. */
  --u: calc(100vw / 1440);

  --white:#FFFFFF;
  --ink:#000000;
  --purple:#BF7AB8;
  --teal:#7AB6BF;
  --tan:#BF947A;
  --plate:#E3D0C4;
  --divider:#D9D9D9;

  --serif:"Instrument Serif", Georgia, "Times New Roman", serif;
  --sans:"Instrument Sans", "Helvetica Neue", Arial, sans-serif;

  --radius: calc(17 * var(--u));
  --header-h: calc(173 * var(--u));
}

/* The home screen is one fixed composition, so it scales to FIT the
   window — that keeps the name and the nav fully visible, never scrolled. */
body[data-page="home"]{
  /* COVER, and one scale for the whole composition. The plates and the
     shapes are one drawing — the shapes are placed against the plates —
     so giving them different scales leaves the plates looking small and
     stranded among oversized shapes. Everything rides on --u together.

     The frame is then taller than a wide window, and it is anchored to the
     BOTTOM rather than centred, because the design's content sits low in
     it: the name plate ends 15u above the frame's bottom edge while the
     top of the frame is mostly shapes bleeding off. Centring the overflow
     cropped the name; cropping the empty top instead keeps it. The 48px
     holds it off the window's own edge. */
  --u: max(calc(100vw / 1440), calc(100vh / 1060));
}
/* the vector layer is scaled past the viewport to cover it (see
   coverLayer in main.js); clip that rather than letting it scroll */
@media (min-width:1025px){
  body[data-page="home"]{ overflow:hidden; }
}

/* The about page drives its photograph and starburst off the available
   HEIGHT, so the starburst always fills the bottom-right corner and the
   text column takes whatever width is left over. */
body[data-page="about"]{
  /* Height drives the photo and starburst, but never past the width the
     Figma frame allows — otherwise the photo would squeeze the copy out. */
  --au: min(calc((100vh - var(--header-h)) / 851), calc(100vw / 1440));
}

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

html,body{ margin:0; padding:0; }

body{
  background:var(--white);
  color:var(--ink);
  font-family:var(--sans);
  overflow-x:hidden;          /* vectors bleed past both edges by design */
  -webkit-font-smoothing:antialiased;
}

img{ display:block; max-width:100%; }
a{ color:inherit; }

/* ---------- decorative vector layer ---------- */
.vectors{
  position:absolute;
  inset:0;
  overflow:hidden;
  pointer-events:none;
  z-index:0;
}
.vectors svg{ position:absolute; display:block; }

.page{ position:relative; min-height:100vh; }

/* ---------- header (work / about / contact) ---------- */
.site-header{
  position:relative;
  z-index:2;
  height:var(--header-h);
}
.wordmark{
  position:absolute;
  left: calc(67 * var(--u));
  top:  calc(46 * var(--u));
  margin:0;
  font-family:var(--serif);
  font-weight:400;
  font-size: calc(128 * var(--u));
  line-height: calc(166 / 128);
  white-space:nowrap;
}
.wordmark a{ text-decoration:none; }

.nav-plate{
  position:absolute;
  left: calc(694 * var(--u));
  top:  calc(70 * var(--u));
  width:  calc(678 * var(--u));
  height: calc(103 * var(--u));
  background:var(--plate);
}
.nav{
  position:absolute;
  left: calc(694 * var(--u));
  top:  calc(89 * var(--u));
  width: calc(678 * var(--u));
  display:flex;
  justify-content:center;
  gap: calc(44 * var(--u));
  font-family:var(--serif);
  font-size: calc(48 * var(--u));
  line-height: calc(65 / 48);
}
.nav a{ text-decoration:none; white-space:nowrap; font-style:normal; }
/* feature 1 — the page you are on is italicised */
.nav a[aria-current="page"]{ font-style:italic; }
/* ...and so is whichever one the cursor is over */
@media (hover:hover){
  .nav a:hover, .home-nav a:hover{ font-style:italic; }
}

/* ---------- shared type ---------- */
.section-title{
  margin:0;
  font-family:var(--serif);
  font-style:italic;
  font-weight:400;
  font-size: calc(48 * var(--u));
  line-height: calc(62 / 48);
}
.body-copy{
  font-family:var(--sans);
  font-size: calc(24 * var(--u));
  /* Figma's copy block is 522u tall for this text: ~30.7u per line */
  line-height:1.28;
  font-weight:400;
}

/* ============================================================
   HOME  (Figma "Desktop - 1")
   The plates wrap their own text so they size to their contents,
   and sit at the Figma origins with the Figma rotations.
   ============================================================ */
.home{ height:100vh; overflow:hidden; }

.home-stage{
  position:absolute;
  left:50%; bottom: 48px;
  width:  calc(1440 * var(--u));
  height: calc(1060 * var(--u));
  transform: translateX(-50%);
  z-index:1;
}

.home-plate{
  position:absolute;
  left:0; top:0;
  transform-origin:0 0;
  background:var(--plate);
}
.home-name-plate{
  padding: calc(14 * var(--u)) calc(34 * var(--u));
  transform: translate(calc(823.77 * var(--u)), calc(662 * var(--u))) rotate(18.74deg);
}
.home-nav-plate{
  padding: calc(16 * var(--u)) calc(30 * var(--u));
  transform: translate(calc(277.65 * var(--u)), calc(631.37 * var(--u))) rotate(-40.70deg);
}
.home-name{
  margin:0;
  font-family:var(--serif);
  font-weight:400;
  font-size: calc(128 * var(--u));
  line-height: calc(166 / 128);
  white-space:nowrap;
}
.home-nav{
  display:flex;
  flex-direction:column;
  font-family:var(--serif);
  font-size: calc(48 * var(--u));
  line-height: calc(65 / 48);
}
.home-nav a{ text-decoration:none; white-space:nowrap; font-style:normal; }
.home-nav a[aria-current="page"]{ font-style:italic; }

/* ============================================================
   WORK  (Figma "Desktop - 2")
   ============================================================ */
.work-wrap{
  /* The grid is the full-width layout scaled down by --k, which leaves
     the wider left and right margins. Everything inside scales with it,
     --gap included: the split-row column ratios are solved against the
     ratio of gap to row width, so shrinking the row while holding the gap
     fixed would stop the tall tile lining up with the two stacked beside
     it. Scaling both keeps that ratio, and the alignment, exact. */
  --k: 0.758;
  --gap: calc(34 * var(--k) * var(--u));
  position:relative;
  z-index:1;
  width: calc(1372 * var(--k) * var(--u));
  margin: 0 auto var(--gap);
  /* not --gap: the tiles need real air under the header, and 96u is the
     same drop the project pages put below theirs */
  padding-top: calc(96 * var(--u));
  display:flex;
  flex-direction:column;
  gap: var(--gap);
}

.tile{
  position:relative;
  display:block;
  border-radius:var(--radius);
  overflow:hidden;
  background:#f2f2f2;
  transition:transform .28s cubic-bezier(.22,.61,.36,1),
             box-shadow .28s cubic-bezier(.22,.61,.36,1);
  will-change:transform;
}
.tile img{ width:100%; height:100%; object-fit:cover; display:block; }

/* feature 7 — grow slightly on hover, settle back on leave */
@media (hover:hover){
  .tile:hover{
    transform:scale(1.022);
    box-shadow:0 calc(10 * var(--u)) calc(34 * var(--u)) rgba(0,0,0,.16);
    z-index:3;
  }
}
.tile:focus-visible{ outline:calc(3 * var(--u)) solid var(--purple); outline-offset:calc(4 * var(--u)); }

.work-row-split{
  /* 796.1 : 541.9 makes 1.3811a = 1.9663b + gap, i.e. the tall tile is
     exactly as high as the two square ones plus the gap between them */
  display:grid;
  grid-template-columns: 796.1fr 541.9fr;
  column-gap: var(--gap);
  row-gap: var(--gap);
}
.work-row-split .tall{ grid-row:1 / span 2; aspect-ratio: 803 / 1109; }
.work-row-split .sq{ aspect-ratio: 534 / 525; }

.work-row-pair{
  display:grid;
  grid-template-columns: 666fr 669fr;
  column-gap: var(--gap);
}
.work-row-pair .tile{ height:100%; }
.work-row-pair > :nth-child(1){ aspect-ratio: 666 / 862; }
.work-row-pair > :nth-child(2){ aspect-ratio: 669 / 862; }

.tile-full{ width:100%; }
.h-bloodondot  { aspect-ratio: 1372 / 1029; }
.h-sebiowomens { aspect-ratio: 1372 / 771;  }
.h-sebiocube   { aspect-ratio: 1372 / 1060; }
.h-glitched    { aspect-ratio: 1372 / 857;  }

/* feature 6 — cursor-following caption. The outer node carries the
   (untransitioned) translate so it tracks the cursor exactly; the inner
   node carries the fade + scale. */
.hover-tag{
  position:fixed;
  left:0; top:0;
  z-index:60;
  pointer-events:none;
  will-change:transform;
}
.hover-tag__inner{
  padding: calc(10 * var(--u)) calc(18 * var(--u));
  background:var(--plate);
  box-shadow:0 calc(4 * var(--u)) calc(14 * var(--u)) rgba(0,0,0,.18);
  border-radius: calc(14 * var(--u));
  font-family:var(--sans);
  font-size: calc(17 * var(--u));
  line-height:1.35;
  color:var(--ink);
  white-space:nowrap;
  opacity:0;
  transform:scale(.94);
  transform-origin:top left;
  transition:opacity .16s ease, transform .16s ease;
}
.hover-tag.is-visible .hover-tag__inner{ opacity:1; transform:scale(1); }
.hover-tag b{ font-weight:600; }
.hover-tag span{ opacity:.72; }

/* ============================================================
   ABOUT  (Figma "Desktop - 3")
   The photo is sized from the available height so the starburst always
   fills the bottom-right corner; the copy column takes the rest of the
   width, keeping the Figma spacing ratios.
   ============================================================ */
.about-grid{
  position:relative;
  z-index:1;
  height: calc(100vh - var(--header-h));
  overflow:hidden;            /* crops the starburst at the corner, as Figma does */
}

/* The whole about composition runs off --au (driven by the available
   height), so the copy always fits beside the photo and every spacing
   ratio from the Figma frame is preserved. The column takes the width
   left over next to the photo, up to the Figma's own 624 : 851 ratio. */
.about-copy-col{
  /* The column takes the width left beside the photo, capped at the
     Figma's own 624 : 851 ratio. Type and spacing are then a fixed
     fraction of that width, so the block keeps exactly the Figma
     proportions — and therefore always fits the height. */
  --acol: min(calc(100vw - 810 * var(--au)), calc(624 * var(--au)));
  position:absolute;
  left: calc(89 * var(--au));
  /* The column spans exactly the photograph's band and is centred in the
     space under the header with it, so the gap from the header down to
     "Who I am" and to the top of the photo is the same as the gap from
     "Read My CV Here" and the foot of the photo down to the bottom of the
     page. The CV link is pushed to the foot of the column, which keeps it
     level with the bottom of the photograph. */
  top:50%;
  transform:translateY(-50%);
  height: calc(733 * var(--au));
  width: var(--acol);
  display:flex;
  flex-direction:column;
  align-items:flex-start;
}
.about-copy-col .section-title{ font-size: calc(var(--acol) * 48 / 624); }
.about-copy-col .body-copy{ font-size: calc(var(--acol) * 24 / 624); }
.about-title{ margin:0; }
.about-copy{ margin-top: calc(var(--acol) * 26 / 624); }
.about-copy p{ margin:0 0 calc(var(--acol) * 26 / 624); }
.about-copy p:last-child{ margin-bottom:0; }
.about-cv{
  display:inline-block;
  /* to the foot of the column, level with the bottom of the photograph */
  margin-top:auto;
  text-decoration:underline;
  text-underline-offset: calc(var(--acol) * 4 / 624);
}

.about-photo{
  /* centred in the space under the header, so the gap above it matches the
     gap below it; the starburst hanging off its corner still runs past the
     bottom-right corner of the page */
  position:absolute;
  right:  calc(68 * var(--au));
  top:50%;
  transform:translateY(-50%);
  width:  calc(544 * var(--au));
  height: calc(733 * var(--au));
}
.about-photo__frame{ width:100%; height:100%; overflow:hidden; }
.about-photo img{ width:100%; height:100%; object-fit:cover; }

/* the page's only vector — pinned past the photo's bottom-right corner,
   so it always runs into the corner of the page */
.about-star{
  position:absolute;
  width: calc(599 * var(--au));
  height:calc(599 * var(--au));
  right:  calc(-225 * var(--au));
  bottom: calc(-268 * var(--au));
  z-index:2;
  pointer-events:none;
}

/* ============================================================
   CONTACT  (Figma "Desktop - 4")
   ============================================================ */
.contact-block{ position:relative; z-index:1; padding-bottom: calc(120 * var(--u)); }
.contact-title{ margin-left: calc(67 * var(--u)); padding-top: calc(119 * var(--u)); }
.contact-list{
  margin: calc(26 * var(--u)) 0 0 calc(73 * var(--u));
  width: calc(624 * var(--u));
}
.contact-list h3{
  margin:0 0 calc(4 * var(--u));
  font-family:var(--sans);
  font-weight:400;
  font-size: calc(24 * var(--u));
}
.contact-list ul{ margin:0 0 calc(26 * var(--u)); padding-left: calc(26 * var(--u)); }
.contact-list li{ margin-bottom: calc(2 * var(--u)); }
.contact-list a{ text-decoration:none; }
.contact-list a:hover{ text-decoration:underline; }

/* ============================================================
   HOME INTRO
   Each vector travels in, overshoots its mark, bounces back and
   settles. On desktop they fall from above; on a phone each one
   slides in from whichever side it is nearer to.
   The animation rides on the `translate` property, so the element's
   own `transform` (its Figma position + rotation) is left intact.
   ============================================================ */
.js .home .vectors svg{ opacity:0; }

.home.is-ready .vectors svg{
  animation-name: vec-fall;
  animation-duration:1.05s;
  animation-timing-function: cubic-bezier(.32,0,.24,1);
  animation-fill-mode: both;
  animation-delay: var(--d, 0s);
}
.home.is-ready .vectors svg[data-from="left"] { animation-name: vec-from-left; }
.home.is-ready .vectors svg[data-from="right"]{ animation-name: vec-from-right; }

/* once it has played, a repaint (resize) must not replay it */
.home.intro-done .vectors svg{ animation:none; opacity:1; translate:0 0; }

@keyframes vec-fall{
  0%   { opacity:0; translate:0 -110vh; }
  55%  { opacity:1; translate:0 calc(30 * var(--u)); }   /* past the mark */
  72%  { translate:0 calc(-15 * var(--u)); }             /* bounce back  */
  86%  { translate:0 calc(6 * var(--u));  }
  95%  { translate:0 calc(-2 * var(--u)); }
  100% { opacity:1; translate:0 0; }                     /* settle       */
}
@keyframes vec-from-left{
  0%   { opacity:0; translate:-110vw 0; }
  55%  { opacity:1; translate:calc(30 * var(--u)) 0; }
  72%  { translate:calc(-15 * var(--u)) 0; }
  86%  { translate:calc(6 * var(--u)) 0; }
  95%  { translate:calc(-2 * var(--u)) 0; }
  100% { opacity:1; translate:0 0; }
}
@keyframes vec-from-right{
  0%   { opacity:0; translate:110vw 0; }
  55%  { opacity:1; translate:calc(-30 * var(--u)) 0; }
  72%  { translate:calc(15 * var(--u)) 0; }
  86%  { translate:calc(-6 * var(--u)) 0; }
  95%  { translate:calc(2 * var(--u)) 0; }
  100% { opacity:1; translate:0 0; }
}

/* the two plates arrive the same way — name from below, nav from above */
.js .home .home-plate{ opacity:0; }
.home.is-ready .home-plate{
  animation-duration:.9s;
  animation-timing-function: cubic-bezier(.22,.61,.36,1);   /* ease-out, no overshoot */
  animation-fill-mode: both;
}
.home.is-ready .home-name-plate{ animation-name: rise; animation-delay:.62s; }
.home.is-ready .home-nav-plate { animation-name: drop; animation-delay:.72s; }
.home.intro-done .home-plate{ animation:none; opacity:1; translate:0 0; }

/* the plates simply shift into place — no bounce */
@keyframes rise{
  from{ opacity:0; translate:0 calc(170 * var(--u)); }
  to  { opacity:1; translate:0 0; }
}
@keyframes drop{
  from{ opacity:0; translate:0 calc(-170 * var(--u)); }
  to  { opacity:1; translate:0 0; }
}

@media (prefers-reduced-motion:reduce){
  .js .home .vectors svg,
  .js .home .home-plate{ opacity:1; }
  .home.is-ready .vectors svg,
  .home.is-ready .home-plate{ animation:none; translate:0 0; }
  .tile{ transition:none; }
}

/* ============================================================
   MOBILE
   ============================================================ */
/* ============================================================
   PROJECT PAGES  (a work tile opens one of these)
   ============================================================ */
.project{
  position:relative;
  z-index:1;
  /* 96u below the header puts the title on the frame's own 269 line.
     The side inset is wider than the frame's own 89u so the images run
     narrower and leave air down both edges, matching the work grid. */
  padding: calc(96 * var(--u)) calc(200 * var(--u)) calc(150 * var(--u));
  /* One step for every gap in the title -> copy -> image rhythm. Each of
     those gaps is a margin on the element above it and nothing else, so
     they all resolve to exactly this and stay even. */
  --vstep: calc(32 * var(--u));
}
.project-title{ margin:0 0 var(--vstep); }

/* Reset first: this and .project-copy figure have the same specificity, so
   whichever comes last wins — with the reset below, the copy column's
   figure lost its top margin and sat flush against the text. */
.project figure{ margin:0; }
.project img{
  display:block;
  /* Sized so a whole image fits on screen: the box takes its column but is
     never taller than --imgmax of the window, and object-fit letterboxes
     the picture inside it, left-aligned, at its own aspect ratio.

     The box has to keep width:100%. With width:auto a lazy image that has
     not loaded yet has no definite width to size from, so all of them
     collapsed to 0x0 — no space reserved, and the page had no height. Only
     the horizontal remainder is dead space and it is white, so this looks
     the same as sizing the box to the picture. */
  width:100%;
  height:auto;
  max-height: var(--imgmax, 80vh);
  object-fit: contain;
  object-position: left top;
}
/* The hero beside the copy is capped like every other image. It used to
   be exempt, to hold its frame's column exactly, but that let it run past
   the bottom of the window — opencall's was 885px tall in an 845px one. */

/* Two intro shapes from the Figma frames: copy beside a portrait hero, or
   copy above a full-width one. --split carries each page's own column
   ratio, taken from its frame.

   Only the split intro is a grid, and only for its columns: when the
   stacked intro was one too, its row gap landed on top of the title's own
   margin and the title -> copy gap came out at 70u against 30u elsewhere.
   Stacked simply flows, so every gap in it is a single margin. */
.project-intro--split{
  display:grid;
  grid-template-columns: var(--split, 1fr 1.6fr);
  column-gap: calc(40 * var(--u));
  row-gap: var(--vstep);
  /* stretch, not start: the copy column has to take the row's full height
     for anything in it to be able to sit on the row's bottom edge */
  align-items:stretch;
}
/* The copy column is a flex column so a figure in it (mfah's square post)
   can be pushed to the foot and line up with the bottom of the hero
   beside it, the way the frame has them — 269+1109 and 928+450 both land
   on 1378. The body copy keeps a --vstep bottom margin so the gap can
   never close up completely if the column runs out of slack. */
.project-copy{ display:flex; flex-direction:column; }
.project-lede{ max-width: calc(1150 * var(--u)); margin: 0 0 var(--vstep); }
.project-copy .body-copy p{ margin: 0 0 calc(22 * var(--u)); }
.project-copy .body-copy p:last-child{ margin-bottom:0; }
.project-copy .body-copy{ margin-bottom: var(--vstep); }
.project-copy > figure{ margin: auto 0 0; }


/* three steps, so a new section still reads as a break in the same rhythm */
.project-section{ margin-top: calc(var(--vstep) * 3); }
.project-section > .section-title{ margin: 0 0 var(--vstep); }
.project-caption{ margin: var(--vstep) 0 0; }

/* A row of images. Every cell is one equal fraction and every image fills
   its cell, so the images share a width whatever their proportions are,
   and height:auto keeps each one's aspect ratio. */
.img-row{
  display:grid;
  gap: var(--vstep);
  grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));
}
.img-row.cols-2{ --cols:2; }
.img-row.cols-3{ --cols:3; }
.img-row + .img-row{ margin-top: var(--vstep); }

.figma-link{
  margin: var(--vstep) 0 0;
  padding: calc(18 * var(--u)) calc(22 * var(--u));
  background:#EFEFEF;
  overflow-wrap:anywhere;
}
.project-back{
  display:inline-block;
  margin-top: calc(var(--vstep) * 3);
  font-family:var(--sans);
  font-size: calc(20 * var(--u));
  text-decoration:underline;
  text-underline-offset: calc(4 * var(--u));
}

/* Stacking happens only at the site's own 1024 breakpoint, where the mobile
   block puts every row into one column. A three-up row used to collapse at
   1200 as well, but the only one is the JSA Instagram stories, which are
   meant to read as a set across the row on any desktop width. */

@media (max-width:1024px){
  /* 1u tracks a 390-wide phone frame, capped so tablet-width windows
     don't balloon the type. Widths stay fluid (100%/auto). */
  :root{ --u: min(calc(100vw / 390), 1.25px); --radius: calc(10 * var(--u)); --header-h:auto; }
  /* the phone home screen is its own fixed composition, 390 x 780 */
  body[data-page="home"]{ --u: min(calc(100vw / 390), calc(100vh / 806)); }
  body[data-page="about"]{ --au: var(--u); }

  /* --- header: nav bar first, name directly beneath it --- */
  .site-header{
    height:auto;
    padding-bottom:0;
    display:flex;
    flex-direction:column;
  }
  .site-header .nav{ order:1; }        /* nav to the very top */
  .site-header .wordmark{ order:2; }   /* name directly below it */
  .nav-plate{ display:none; }          /* the bar becomes the nav's own background */
  .nav{
    position:static;
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap: calc(8 * var(--u));
    width:100%;
    padding: calc(13 * var(--u)) calc(16 * var(--u));
    background:var(--plate);
    font-size: calc(20 * var(--u));
    line-height:1.3;
  }
  .wordmark{
    position:static;
    margin: calc(16 * var(--u)) 0 0 calc(16 * var(--u));
    font-size: calc(54 * var(--u));
    line-height:1.1;
  }

  /* --- home: the same rotated plates as the desktop composition --- */
  .home{ height:100vh; overflow:hidden; }
  .home-stage{
    width:  calc(390 * var(--u));
    height: calc(806 * var(--u));
    /* the phone frame fits the screen, so centre it again */
    top:50%; bottom:auto;
    transform: translate(-50%, -50%);
  }
  /* the phone composition from the mockup: a wide name plate tilted
     across the upper third, and a tilted nav square below and right */
  .home-name-plate{
    padding: calc(14 * var(--u)) calc(26 * var(--u));
    transform: translate(calc(14 * var(--u)), calc(274 * var(--u))) rotate(-24deg);
  }
  .home-nav-plate{
    padding: calc(20 * var(--u)) calc(26 * var(--u));
    transform: translate(calc(93 * var(--u)), calc(545 * var(--u))) rotate(-20deg);
  }
  .home-name{ font-size: calc(56 * var(--u)); }
  .home-nav{ font-size: calc(34 * var(--u)); line-height:1.35; }

  /* --- work: one column --- */
  .work-wrap{
    --gap: calc(16 * var(--u));
    width:auto;
    margin: 0 var(--gap) var(--gap);
    padding-top: var(--gap);
    gap: var(--gap);
  }
  .work-row-split, .work-row-pair{
    display:flex; flex-direction:column; gap: var(--gap);
  }
  .work-row-split .tall, .work-row-split .sq,
  .work-row-pair .tile, .work-row-pair > *,
  .h-bloodondot,.h-sebiowomens,.h-sebiocube,.h-glitched{ height:auto; aspect-ratio:auto; }
  .tile img{ height:auto; }
  .hover-tag{ display:none; }              /* no pointer on touch devices */

  /* --- about: one column, and the gap under the name matches the gap
         between the CV link and the photograph --- */
  .about-grid{ height:auto; overflow:visible; padding: 0 calc(16 * var(--u)) calc(46 * var(--u)); }
  .about-copy-col{
    position:static; top:auto; bottom:auto; transform:none;
    height:auto; display:block; width:auto;
    --acol: calc(624 * var(--u));
  }
  .about-copy-col .section-title{ font-size: calc(34 * var(--u)); }
  .about-copy-col .body-copy{ font-size: calc(16 * var(--u)); }
  .about-title{
    font-size: calc(34 * var(--u));
    margin-top: calc(28 * var(--u));      /* name  ->  "Who I am"      */
  }
  .about-copy{ margin-top: calc(14 * var(--u)); }
  .about-copy p{ margin-bottom: calc(16 * var(--u)); }
  .body-copy{ font-size: calc(16 * var(--u)); }
  .about-cv{ margin-top: calc(14 * var(--u)); }
  .about-photo{
    position:relative;
    right:auto; top:auto; bottom:auto; transform:none;
    width:100%; height:auto;
    aspect-ratio: 544 / 733;
    margin-top: calc(28 * var(--u));      /* "Read My CV Here" -> photo */
    border-radius: calc(8 * var(--u));
  }
  .about-star{
    width: calc(260 * var(--u));
    height:calc(260 * var(--u));
    right:  calc(-98 * var(--u));
    bottom: calc(-116 * var(--u));
  }

  /* --- project pages: one column, everything stacked --- */
  /* the same even rhythm, one smaller step; every gap still comes from
     --vstep alone, so nothing here has to restate a spacing */
  .project{ padding: calc(26 * var(--u)) calc(16 * var(--u)) calc(56 * var(--u)); --vstep: calc(18 * var(--u)); }
  .project-title{ font-size: calc(26 * var(--u)); }
  .project-copy .body-copy, .project-lede, .project-caption, .figma-link{ font-size: calc(15 * var(--u)); }
  .project-copy .body-copy p{ margin-bottom: calc(14 * var(--u)); }
  .project-intro--split{ grid-template-columns: minmax(0, 1fr); column-gap:0; }
  /* stacked on a phone, so nothing to bottom-align against */
  .project-copy{ display:block; }
  .project-copy .body-copy{ margin-bottom:0; }
  .project-copy > figure{ margin: var(--vstep) 0 0; }
  .project-section > .section-title{ font-size: calc(22 * var(--u)); }
  /* every row stacks, each image the same width, aspect ratio kept */
  .img-row, .img-row.cols-2, .img-row.cols-3{ --cols:1; }
  .project-back{ font-size: calc(16 * var(--u)); }

  /* --- contact --- */
  .contact-block{ padding: 0 calc(16 * var(--u)) calc(56 * var(--u)); }
  .contact-title{ margin-left:0; padding-top: calc(26 * var(--u)); font-size: calc(34 * var(--u)); }
  .contact-list{ margin: calc(16 * var(--u)) 0 0; width:auto; }
  .contact-list h3{ font-size: calc(16 * var(--u)); }
  .contact-list ul{ margin-bottom: calc(18 * var(--u)); }
}
