/* ============================================================
   Mai Do — Portfolio 2026
   A PDF-portfolio read as a website.
   Type and colour follow the printed document:
   off-white ground, black ink, hairline rules, bottom baseline.
   ============================================================ */

:root{
  --paper:#f2f2f0;
  --ink:#111111;
  --rule:rgba(17,17,17,.42);
  --rule-soft:rgba(17,17,17,.16);

  /* the printed document is set in a neo-grotesk.
     swap --font for a licensed face (e.g. Aeonik, Neue Haas Grotesk)
     by adding an @font-face above and editing this one line. */
  --font:"Helvetica Neue",Helvetica,"Inter",system-ui,-apple-system,Arial,sans-serif;

  --fs-label:clamp(10.5px,.62vw,12.5px);
  --fs-body:clamp(11.5px,.68vw,13.5px);
  --fs-desc:clamp(11.5px,.66vw,13px);

  --pad-x:clamp(16px,2.1vw,40px);
  --pad-y:clamp(16px,1.9vw,34px);
  --plate-gap:22px;      /* picture to caption — matches the home preview */
  --ease:cubic-bezier(.22,.61,.36,1);
}

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

html,body{height:100%}
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font);
  font-size:var(--fs-body);
  line-height:1.42;
  font-weight:500;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow:hidden;
}
body.is-open{overflow:hidden}

figure,blockquote{margin:0}
[hidden]{display:none!important}   /* class display rules must not resurrect it */
a{color:inherit;text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:2px}
img{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}

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

/* ── shared type ─────────────────────────────────────────── */
.label{
  font-size:var(--fs-label);
  font-weight:700;
  letter-spacing:.005em;
  margin:0 0 .45em;
  padding-bottom:.42em;
  border-bottom:1px solid var(--rule);
}
.body{margin:0;font-size:var(--fs-body)}
.linkish{
  font-size:var(--fs-label);
  font-weight:700;
  border-bottom:1px solid var(--rule);
  padding-bottom:.42em;
  display:inline-flex;
  align-items:baseline;
  gap:.6em;
  transition:opacity .18s var(--ease);
}
.linkish:hover{opacity:.55}
/* no rule under these — they read as links, not headings */
.home .linkish,
.reader__close,
.cv [data-close],
.archive [data-close]{border-bottom:0;padding-bottom:0}
.block--links{display:flex;flex-direction:column;align-items:flex-start;gap:.4em}
.kbd{font-weight:500;font-size:.85em}

/* ============================================================
   HOME  — page 1 of the document
   ============================================================ */
.home{
  position:fixed;inset:0;
  display:flex;
  flex-direction:column;
  justify-content:flex-end;
  padding:var(--pad-y) var(--pad-x);
  transition:opacity .45s var(--ease),filter .45s var(--ease);
}
.home.is-behind{opacity:.28;filter:blur(1.5px);pointer-events:none}

.home__grid{
  position:relative;
  z-index:2;
  display:grid;
  /* the contents column takes exactly the width of its longest line, so the
     statement can run right up to it — no dead space in between */
  grid-template-columns:minmax(150px,0.55fr) minmax(0,1fr) max-content;
  gap:clamp(20px,3.4vw,68px);
  align-items:start;   /* the three heading rules sit on one line */
}
.col{display:flex;flex-direction:column;gap:clamp(12px,1.5vw,22px)}
.col--statement{max-width:none}
/* push the contents block right so its longest line ends at the page margin,
   mirroring where the left column begins */
.col--toc .block{width:fit-content;margin-left:auto}
.col--statement p{margin:0}

/* hover preview lives in the empty upper field */
.home__preview{
  position:absolute;
  inset:var(--pad-y) var(--pad-x) auto;
  height:min(70vh,72vw);
  z-index:1;
  pointer-events:none;
  display:flex;
  align-items:flex-start;
  justify-content:flex-start;   /* flush with the left column, not centred */
}
.home__preview img,
.home__preview video{
  position:absolute;
  height:100%;
  width:auto;
  max-width:100%;
  object-fit:contain;
  object-position:left top;
  opacity:0;
  /* a plain, unhurried fade — no zoom, so nothing appears to move */
  transition:opacity .62s cubic-bezier(.33,0,.2,1);
}
.home__preview img.is-on,
.home__preview video.is-on{opacity:1}

/* table of contents */
.toc{list-style:none;margin:0;padding:0;counter-reset:toc}
.toc li{counter-increment:toc}
.toc button{
  display:flex;
  flex-wrap:wrap;
  gap:.45em;
  width:fit-content;
  max-width:100%;
  text-align:left;
  padding:.14em 0;
  transform-origin:left center;
  transition:transform .25s var(--ease);
}
.toc button::before{
  content:counter(toc) ".";
  flex:0 0 auto;
  margin-right:.1em;
  font-variant-numeric:tabular-nums;
}
.toc__y{font-variant-numeric:tabular-nums;white-space:nowrap}
/* the whole hover: a barely-there swell, anchored at the left edge so
   nothing shifts sideways and nothing else on the list changes */
.toc button:hover,.toc button:focus-visible{transform:scale(1.012)}
.toc button:focus-visible{outline:none;text-decoration:underline;text-underline-offset:3px}

/* ============================================================
   READER — the project spreads
   ============================================================ */
.reader{
  position:fixed;inset:0;
  z-index:20;
  background:var(--paper);
  display:grid;
  grid-template-rows:auto 1fr auto;
  padding:var(--pad-y) var(--pad-x);
  overflow:hidden;
  opacity:0;
  transform:scale(1.012);
  transition:opacity .4s var(--ease),transform .4s var(--ease);
}
.reader[hidden]{display:none}
.reader:focus,.cv:focus,.archive:focus{outline:none}
.reader.is-on{opacity:1;transform:none}

.reader__bar{
  display:flex;
  align-items:flex-start;
  justify-content:space-between;
  gap:1em;
}
.foot-where{
  display:block;
  font-weight:500;
  margin-top:.35em;
}
.reader__counter{display:flex;align-items:baseline;gap:1.1em}
.work-count{font-weight:500;font-variant-numeric:tabular-nums}
#prevWork,#nextWork{
  font-size:var(--fs-label);
  font-weight:700;
  border-bottom:1px solid var(--rule);
  padding-bottom:.42em;
  transition:opacity .18s var(--ease);
}
#prevWork:hover,#nextWork:hover{opacity:.55}
#prevWork[disabled],#nextWork[disabled]{opacity:.25;cursor:default}
.reader__counter{
  font-size:var(--fs-label);
  font-weight:700;
  font-variant-numeric:tabular-nums;
}

/* ── stage ───────────────────────────────────────────────── */
.reader__stage{
  position:relative;
  /* one continuous band: pictures touch, and the only air is at the two ends,
     where it lines up with the text margins */
  display:flex;
  flex-wrap:nowrap;
  align-items:flex-start;
  justify-content:flex-start;
  gap:0;
  min-height:0;
  min-width:0;
  overflow-x:auto;
  overflow-y:hidden;
  /* a two-finger swipe stays inside the band and scrolls it — it never leaves
     the project or hands the gesture to the browser */
  overscroll-behavior-x:contain;
  /* bleed to both window edges, then pad back to the text margin */
  margin:0 calc(-1 * var(--pad-x));
  padding:0 var(--pad-x) var(--plate-gap,22px);
  scrollbar-width:none;
}
.reader__stage::-webkit-scrollbar{display:none}

/* where a shoot doesn't fill the page, it can hold the left edge, the right,
   or sit in the middle — the white space is part of the layout */
.reader__stage.stage--right{justify-content:flex-end}
.reader__stage.stage--center{justify-content:center}
.reader__stage > *{flex:0 0 auto}

.reader__stage img,
.reader__stage video{
  height:100%;
  width:100%;
  object-fit:cover;      /* the plate is already the picture's own shape;
                            cover leaves no hairline of paper at the edges
                            when the width lands on a fraction of a pixel */
  object-position:center;
  display:block;
}
.plate{position:relative;flex:0 0 auto}
/* Scrolling puts the band on fractional pixel positions, and a fractional edge
   is antialiased — which lets the paper show through as a hairline between two
   pictures. Overlapping each plate by a pixel closes it for good; a pixel of a
   thousand-pixel picture is nothing. */
.reader__stage .plate + .plate,
.reader__stage .plate + .stage-grid,
.reader__stage .stage-grid + .plate{margin-left:-1px}
/* Where a project holds several separate works — Editorials — the pictures
   within one work stay joined, but each new work begins after a clear gap. */
.reader__stage .plate.work-start{margin-left:clamp(7px,.7vw,13px)}

/* portrait grid (THE VOICES, 65 portraits) */
.stage-grid{
  display:grid;
  grid-template-columns:repeat(var(--cols,11),1fr);
  grid-auto-rows:1fr;
  gap:clamp(2px,.32vw,7px);
}
.stage-grid img{
  width:100%;height:100%;
  aspect-ratio:1;
  object-fit:cover;
  filter:grayscale(0);
  transition:filter .3s var(--ease),transform .3s var(--ease);
}
.stage-grid img:hover{transform:scale(1.06);z-index:2;position:relative}

/* text spread (phantom descriptions) */
.strip-panel{
  padding:0 clamp(20px,2.4vw,44px);
  overflow:hidden;
}
.stage-text{
  columns:clamp(230px,19vw,300px);
  column-gap:clamp(20px,2.4vw,44px);
  width:auto;
}
.note{
  column-width:clamp(220px,17vw,290px);
  column-gap:clamp(20px,2.4vw,44px);
  column-fill:auto;
}
.note .label{margin-bottom:.9em}
.note__h{
  margin:0 0 .5em;
  padding-bottom:.4em;
  font-size:var(--fs-label);
  font-weight:700;
  border-bottom:1px solid var(--rule);
  break-after:avoid;
}
.note__h + p{margin-bottom:1.5em}
.note p:last-child{margin-bottom:0}
.note p{
  margin:0 0 .95em;
  font-size:var(--fs-desc);
  line-height:1.5;
  break-inside:avoid;
}

.strip-panel.steps{
  display:flex;
  flex-direction:column;
  justify-content:flex-end;
  gap:clamp(10px,1.3vw,20px);
  width:clamp(240px,22vw,340px);
}
.phantom{
  display:grid;
  grid-template-columns:clamp(52px,4.6vw,84px) minmax(0,1fr);
  gap:clamp(10px,1.1vw,18px);
  margin-bottom:clamp(12px,1.4vw,22px);
  align-items:start;
}
.phantom img{
  width:100%;
  aspect-ratio:1;
  object-fit:cover;
}
.stage-text p{
  margin:0;
  font-size:clamp(9.5px,.58vw,11.5px);
  line-height:1.5;
  text-align:justify;
  hyphens:auto;
}

.steps .step h4{margin:0 0 .35em;font-size:var(--fs-label);font-weight:700;padding-bottom:.35em;border-bottom:1px solid var(--rule)}
.steps .step p{margin:0;font-size:var(--fs-desc)}
.phantom{break-inside:avoid}

/* ── footer meta, exactly the printed footer ─────────────── */
.reader__foot{font-size:var(--fs-body)}

.foot-line{
  display:grid;
  grid-template-columns:minmax(0,2.6fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1fr);
  gap:clamp(12px,2vw,40px);
  align-items:start;
}
.foot-line span:nth-child(n+2){text-align:left}
.foot-line .t{font-weight:700}
/* one line, spread across the page: title … year … place … medium */
.foot-line--spread{
  display:flex;
  justify-content:space-between;
  align-items:baseline;
  gap:clamp(16px,2.4vw,48px);
  font-weight:700;
}
.foot-line--spread span:last-child{text-align:right}

/* intro spread footer: description + dimensions */
.foot-intro{
  display:grid;
  grid-template-columns:minmax(0,3fr) minmax(190px,1fr);
  gap:clamp(20px,3.4vw,68px);
  align-items:start;
}
.foot-intro .desc{margin:0;font-size:var(--fs-desc);max-width:none;white-space:pre-line}
.foot-intro .material{margin:0;font-size:var(--fs-desc);white-space:pre-line}
.foot-intro .label{margin-bottom:.5em}
.foot-link{margin:.7em 0 0;font-size:var(--fs-desc)}
.foot-link a{border-bottom:1px solid var(--rule)}

/* ============================================================
   ARCHIVE — loose images, justified into flush rows
   ============================================================ */
.archive{
  position:fixed;inset:0;z-index:25;
  background:var(--paper);
  display:grid;
  grid-template-rows:auto 1fr;
  padding:var(--pad-y) var(--pad-x);
  opacity:0;
  transition:opacity .4s var(--ease);
}
.archive[hidden]{display:none}
.archive.is-on{opacity:1}
.archive__scroll{overflow:auto;padding:clamp(16px,2vw,34px) 0 10vh;min-width:0}

.arow{display:flex;min-width:0}
.arow figure{flex:0 0 auto;overflow:hidden}
.arow img{width:100%;height:100%;object-fit:cover;display:block}

.archive__empty{
  font-size:var(--fs-body);
  max-width:44ch;
  padding-top:2em;
}

/* ============================================================
   CV
   ============================================================ */
.cv{
  position:fixed;inset:0;z-index:30;
  background:var(--paper);
  display:grid;
  grid-template-rows:auto 1fr;
  padding:var(--pad-y) var(--pad-x);
  opacity:0;
  transition:opacity .4s var(--ease);
}
.cv[hidden]{display:none}
.cv.is-on{opacity:1}
.cv__scroll{overflow:auto;padding:clamp(20px,3vw,44px) 0 8vh}

.cv__head{
  display:grid;
  grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
  gap:clamp(20px,3vw,56px);
  margin-bottom:clamp(28px,4vw,60px);
  padding-left:0;
}
/* the name sits in the label column, flush with Biography, Education and the rest */
.cv__head{grid-template-columns:150px minmax(0,1fr)}
.cv__head > div:first-child{grid-column:1}
.cv__headside{grid-column:2}
.cv__head h2{margin:0;font-size:var(--fs-body);font-style:normal;font-weight:700}
.cv__head .born{margin:.3em 0 0;font-size:var(--fs-body)}
/* same split as every row below, so the contact lands on the second column */
.cv__headside{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:clamp(20px,3vw,56px)}

.cv__row{
  display:grid;
  grid-template-columns:150px minmax(0,1fr);
  gap:clamp(20px,3vw,56px);
  padding:clamp(12px,1.3vw,20px) 0;
  border-top:1px solid var(--rule-soft);
}
.cv__row > h3{margin:0;font-size:var(--fs-label);font-weight:700}
.cv__bio{margin:0;max-width:none}

/* the one split every section shares */
.cv__pair{
  display:grid;
  /* the left side needs room for the longest award name to stay on one line */
  grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
  gap:clamp(20px,3vw,56px);
  margin-bottom:.4em;
}
.cv__pair:last-child{margin-bottom:0}
.cv__pair em{font-style:normal;color:var(--ink)}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:900px){
  :root{--fs-label:12px;--fs-body:13px;--fs-desc:12.5px}
  body{overflow:auto}

  .home{position:static;min-height:100svh;justify-content:flex-start;padding-top:clamp(26px,7vw,50px)}
  .home__preview{display:none}
  .home__grid{grid-template-columns:1fr;gap:30px;align-items:start}
  .col--statement{order:-1}
  /* title on its own line, year and medium tucked underneath */
  .toc button{padding:.32em 0;font-size:14px}
  .col--toc .block{width:auto;margin-left:0}

  .reader{position:fixed;grid-template-rows:auto 1fr auto;padding-bottom:calc(var(--pad-y) + env(safe-area-inset-bottom))}
  .reader__stage{
    -webkit-overflow-scrolling:touch;
    padding-bottom:var(--plate-gap,22px);
  }
  /* the band behaves the same here as on a wide screen — one height, no gaps.
     The picture is only ever cut off if the caption starves it, so that is
     capped below and scrolls instead. */
  .stage-grid{height:100%}
  .strip-panel{width:82vw;height:100%;overflow-y:auto}
  .stage-text{columns:1}
  .reader__foot{max-height:34vh;overflow-y:auto}

  .foot-line{grid-template-columns:1fr;gap:.15em}
  .foot-line .t{margin-bottom:.2em}
  .foot-intro{grid-template-columns:1fr;gap:18px}
  .cv__head,.cv__row,.cv__pair{grid-template-columns:1fr;gap:8px}
  .cv__head{padding-left:0}
  .cv__row{gap:6px}
  .cv__pair{margin-bottom:.8em}
}

@media (prefers-reduced-motion:reduce){
  *{transition-duration:.01ms!important;animation-duration:.01ms!important}
}

/* print: hand back a clean document */
@media print{
  body{overflow:visible}
  .nav,.reader__bar,.home__preview{display:none}
  .reader,.cv{position:static;opacity:1}
}
