/* ═══════════════════════════════════════════════════════════
   QUIN — portfolio
   Vanilla CSS. No build step, no dependencies.
   Colours live in :root — change them there, not further down.
   ═══════════════════════════════════════════════════════════ */

:root{
  --bg:      #f8d3dc;   /* pink */
  --ink:     #1a1114;   /* plum-black, warmer than true black */
  --muted:   #8f6b77;
  --line:    #eebbc8;
  --overlay: #f8d3dc;

  /* The wordmark blends with mix-blend-mode: difference, which computes
     |backdrop - source| — so its own colour is never the colour you see.
     syncWordmarkInk() in app.js derives this from --bg and --ink so the letters
     land exactly on --ink over the background, whatever you set --bg to.
     The value here is only the fallback if that script doesn't run. */
  --wordmark-ink: #dec2c8;

  --pad: clamp(16px, 3vw, 34px);
  --font: "Helvetica Neue", Helvetica, Arial, "Segoe UI", sans-serif;

  --micro: 10px;
  --track-letter: .16em;

  /* the giant name — this is the whole design */
  --wordmark: clamp(72px, 26vw, 520px);
}

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

html, body{ height: 100%; }

body{
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  overflow: hidden;              /* the page never scrolls; the views do */
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

a{ color: inherit; text-decoration: none; }
button{ font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }

/* small caps label — every piece of UI text on the site */
.bar a, .bar button, .counter, .views button, .info__close, .lightbox__close, .stats dt{
  font-size: var(--micro);
  text-transform: uppercase;
  letter-spacing: var(--track-letter);
  font-weight: 500;
}

/* ── Loader ─────────────────────────────────────────────── */
#loader{
  position: fixed; inset: 0; z-index: 100;
  background: var(--bg);
  transition: opacity .7s ease, visibility .7s;
}
body.ready #loader{ opacity: 0; visibility: hidden; }

/* ── The giant name ─────────────────────────────────────── */
.wordmark{
  position: fixed;
  top: 66%; left: 50%;
  z-index: 3;                    /* above the photos, but blended into them */
  mix-blend-mode: difference;
  font-size: var(--wordmark);
  font-weight: 700;
  letter-spacing: -.035em;
  line-height: .78;
  white-space: nowrap;

  /* Outlined, not filled. Filled letters invert whole slabs of the photo — over
     warm skin that comes out teal, and it buries the model's face. A stroke
     stays legible on any photo, keeps the difference-blend inversion so it
     never disappears, and leaves the image intact.
     For solid letters instead: color: var(--wordmark-ink) and drop the stroke. */
  color: transparent;
  -webkit-text-stroke: 0.02em var(--wordmark-ink);

  pointer-events: none;
  user-select: none;
  transform: translate(-50%, -50%);
  opacity: 0;
  transition: opacity .5s ease;
}
body.ready .wordmark{
  opacity: 1;
  animation: wordmark-in 1.1s cubic-bezier(.16,.8,.24,1) both;
}
@keyframes wordmark-in{
  from{ opacity: 0; letter-spacing: .06em; }
  to  { opacity: 1; letter-spacing: -.035em; }
}
/* the vertical views carry their own inline title — a fixed layer behind a full
   sheet of photos would never show through */
body[data-view="editorial"] .wordmark,
body[data-view="index"] .wordmark{ display: none; }

/* in orbit the name is the axle the photos turn around, so it sits dead centre */
body[data-view="orbit"] .wordmark{ top: 50%; }

/* ── Top / bottom bars ──────────────────────────────────── */
.bar{
  position: fixed; left: 0; right: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--pad);
  pointer-events: none;                 /* let scroll pass through the gaps */
  transition: opacity .3s ease, color .3s ease;
}
.bar a, .bar button{ pointer-events: auto; }
.bar--top{ top: 0; }
.bar--bottom{ bottom: 0; gap: var(--pad); }
body.lb-open .bar{ opacity: 0; pointer-events: none; }

/* the bars float over photos and giant type, so they need a ground */
.bar::before{
  content: ""; position: absolute; left: 0; right: 0;
  height: calc(100% + 34px);
  pointer-events: none; z-index: -1;
}
.bar--top::before{
  top: 0;
  background: linear-gradient(to bottom, var(--bg) 28%, transparent);
}
.bar--bottom::before{
  bottom: 0;
  background: linear-gradient(to top, var(--bg) 28%, transparent);
}

.mark{ color: var(--muted); transition: color .25s ease; }
.mark:hover{ color: var(--ink); }

.bar--top nav{ display: flex; gap: clamp(14px, 2.4vw, 30px); }
.bar--top nav a, .bar--top nav button{ position: relative; padding-bottom: 2px; }
.bar--top nav a::after, .bar--top nav button::after{
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: right;
  transition: transform .35s cubic-bezier(.2,.7,.2,1);
}
.bar--top nav a:hover::after, .bar--top nav button:hover::after{
  transform: scaleX(1); transform-origin: left;
}

/* ── Progress + counter ─────────────────────────────────── */
.progress{
  flex: 1; height: 1px; background: var(--line); position: relative;
  max-width: 46vw;
  transition: opacity .3s ease;
}
.progress__fill{
  position: absolute; inset: 0 auto 0 0;
  width: 0%; background: var(--ink);
  transition: width .12s linear;
}
.credit{
  flex: 0 1 auto; min-width: 0;
  color: var(--muted);
  font-size: var(--micro);
  text-transform: uppercase;
  letter-spacing: var(--track-letter);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: opacity .3s ease;
}
body[data-view="editorial"] .credit{ opacity: 0; }

.counter{ color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.counter #counterNow{ color: var(--ink); }

/* ── View switcher ──────────────────────────────────────── */
.views{ display: flex; gap: 16px; white-space: nowrap; }
.views button{ color: var(--muted); transition: color .25s ease; }
.views button:hover{ color: var(--ink); }
.views button[aria-selected="true"]{
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}

/* progress means distance along the runway, or round the orbit — nothing else */
body[data-view="editorial"] .progress,
body[data-view="index"] .progress,
body[data-view="stack"] .progress{ opacity: 0; }

/* ── The runway ─────────────────────────────────────────── */
main{ height: 100%; display: flex; align-items: center; position: relative; z-index: 2; }

.track{
  display: flex;
  align-items: center;
  gap: clamp(28px, 6vw, 120px);
  width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  outline: none;
  cursor: grab;
  /* fallback only — app.js measures the exact value once images have loaded */
  padding: 0 30vw;
  scrollbar-width: none;
  /* entrance */
  opacity: 0;
  transform: translateY(34px);
  transition: opacity .9s ease .25s, transform 1s cubic-bezier(.16,.8,.24,1) .25s;
}
.track::-webkit-scrollbar{ display: none; }
.track[hidden]{ display: none; }
.track.dragging{ cursor: grabbing; }
body.ready .track{ opacity: 1; transform: none; }

figure{
  margin: 0;
  flex: 0 0 auto;
  position: relative;
  /* --d is set by JS: 0 when the photo is dead centre, 1 when it's at the edge */
  --d: 1;
  transform: scale(calc(1 - var(--d) * .07));
  transition: transform .45s cubic-bezier(.2,.7,.2,1);
  will-change: transform;
}

figure img{
  display: block;
  height: min(64vh, 740px);
  width: auto;
  max-width: 88vw;
  object-fit: cover;
  background: var(--line);
  /* off-centre photos fall back into the dark; the centred one is lit */
  filter: grayscale(calc(var(--d) * 80%)) opacity(calc(1 - var(--d) * .5));
  transition: filter .45s cubic-bezier(.2,.7,.2,1), opacity .5s ease;
  user-select: none; -webkit-user-drag: none;
}

figure[data-ratio="portrait"]  img{ aspect-ratio: 2/3; }
figure[data-ratio="tall"]      img{ aspect-ratio: 4/5; }
figure[data-ratio="square"]    img{ aspect-ratio: 1/1; }
figure[data-ratio="landscape"] img{ aspect-ratio: 3/2; height: min(48vh, 560px); }
/* set from the file's own dimensions, for photos with no forced ratio */
.track figure[data-orient="landscape"] img{ height: min(48vh, 560px); }

figcaption{
  display: none;
  position: absolute; left: 0; bottom: 6px;
  width: 100%;
  font-size: var(--micro);
  text-transform: uppercase;
  letter-spacing: var(--track-letter);
  color: var(--muted);
  /* only the centred photo shows its credit */
  opacity: calc(1 - var(--d) * 3);
  transition: opacity .35s ease;
  pointer-events: none;
}

figure img:not(.loaded){ opacity: 0; }

/* ── Shared shell for the three vertical views ──────────── */
.sheet{
  width: 100%; height: 100%;
  overflow-y: auto; overflow-x: hidden;
  padding: calc(var(--pad) * 2.6) var(--pad) calc(var(--pad) * 3.4);
  scrollbar-width: none;
}
.sheet::-webkit-scrollbar{ display: none; }
.sheet[hidden]{ display: none; }

.sheet-title{
  color: var(--ink);
  margin: 0 0 clamp(14px, 2.2vw, 34px);
  font-size: var(--wordmark);
  font-weight: 700;
  letter-spacing: -.035em;
  line-height: .8;
}
.sheet-title--sm{ font-size: clamp(38px, 7vw, 96px); }

.sheet figure{
  margin: 0; padding: 0;
  transform: none;
  cursor: pointer;
}
.sheet figure img{
  height: auto; width: 100%; max-width: none;
  aspect-ratio: auto;
  filter: grayscale(40%) opacity(.88);
  transition: filter .4s ease, opacity .5s ease;
}
.sheet figure:hover img{ filter: none; }
.sheet figcaption{
  display: block;
  position: static;
  opacity: 0;
  padding-top: 8px;
  transition: opacity .3s ease;
}
.sheet figure:hover figcaption{ opacity: 1; }

/* ── Editorial: the broken magazine grid ────────────────── */
/* Explicit column spans with auto row flow: two photos share a row whenever
   their columns don't collide, which is what produces the uneven rhythm.
   align-items:start stops them stretching to match each other's height. */
.editorial__inner{
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  align-items: start;
  gap: clamp(18px, 3vw, 60px) clamp(12px, 2vw, 34px);
}
.editorial__inner figure:nth-child(5n+1){ grid-column: 1 / 7;  }
.editorial__inner figure:nth-child(5n+2){ grid-column: 9 / 13; margin-top: 14vh; }
.editorial__inner figure:nth-child(5n+3){ grid-column: 2 / 6;  }
.editorial__inner figure:nth-child(5n+4){ grid-column: 7 / 13; margin-top: 6vh;  }
.editorial__inner figure:nth-child(5n+5){ grid-column: 3 / 10; margin-top: 4vh;  }

/* ── Index: the type list ───────────────────────────────── */
.index__list{
  list-style: none;
  margin: 0; padding: 0;
  border-top: 1px solid var(--line);
}
.index__row{
  display: flex; align-items: baseline; gap: clamp(12px, 2vw, 32px);
  padding: clamp(8px, 1.1vw, 16px) 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  color: var(--muted);
  transition: color .25s ease, padding-left .35s cubic-bezier(.2,.7,.2,1);
}
.index__row:hover{ color: var(--ink); padding-left: clamp(8px, 1.4vw, 24px); }

.index__num{
  font-size: var(--micro);
  letter-spacing: var(--track-letter);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}
.index__label{
  flex: 1 1 auto;
  font-size: clamp(17px, 2.3vw, 34px);
  font-weight: 500;
  letter-spacing: -.02em;
  line-height: 1.05;
}
.index__credit{
  flex: 0 0 auto;
  font-size: var(--micro);
  text-transform: uppercase;
  letter-spacing: var(--track-letter);
}

/* fine pointers get the floating preview; touch gets an inline thumbnail */
.index__thumb{ display: none; flex: 0 0 auto; width: 54px; }
.index__thumb img{ width: 100%; display: block; }

.index__preview{
  position: fixed; top: 0; left: 0; z-index: 4;
  width: clamp(200px, 21vw, 340px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease;
  will-change: transform;
}
.index__preview.on{ opacity: 1; }
.index__preview img{ width: 100%; display: block; }

@media (pointer: coarse){
  .index__preview{ display: none; }
  .index__thumb{ display: block; }
  .index__credit{ display: none; }
  /* baseline alignment leaves the thumbnail floating above the text */
  .index__row{ align-items: center; }
}

/* ── Orbit: photos on an elliptical track ───────────────── */
/* A sticky stage over a tall spacer: scrolling the spacer spins the track
   without the stage ever leaving the screen. Every figure's position, scale,
   opacity and stacking is written by orbitFrame() in app.js. */
.orbit{
  width: 100%; height: 100%;
  overflow-y: auto; overflow-x: hidden;
  scrollbar-width: none;
}
.orbit::-webkit-scrollbar{ display: none; }
.orbit[hidden]{ display: none; }

.orbit__stage{
  position: sticky; top: 0;
  height: 100vh; width: 100%;
}
.orbit__runway{ height: 320vh; }     /* scroll distance for one full turn */

.orbit figure{
  position: absolute;
  top: 0; left: 0;
  width: clamp(120px, 16vw, 260px);
  margin: 0; padding: 0;
  cursor: pointer;
  will-change: transform, opacity;
}
.orbit figure img{
  display: block;
  width: 100%; height: auto;
  max-width: none;
  aspect-ratio: auto;
  filter: none;
}
.orbit figcaption{ display: none; }

@media (max-width: 760px){
  .orbit figure{ width: clamp(104px, 34vw, 170px); }
  .orbit__runway{ height: 260vh; }
}

/* ── Full view: one photo per screen ────────────────────── */
.stack{
  width: 100%; height: 100%;
  overflow-y: auto; overflow-x: hidden;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
}
.stack::-webkit-scrollbar{ display: none; }
.stack[hidden]{ display: none; }

.stack figure{
  position: relative;
  height: 100%; width: 100%;
  margin: 0; padding: 0;
  transform: none;
  scroll-snap-align: start;
  cursor: pointer;
  overflow: hidden;
}
.stack figure img{
  height: 100%; width: 100%; max-width: none;
  object-fit: cover;
  aspect-ratio: auto !important;
  filter: none;
}


/* the name sits over the opening photo and clears as you scroll */
body[data-view="stack"] .wordmark{
  opacity: var(--wm-opacity, 1);
  transition: none;
}

/* ── Lightbox ───────────────────────────────────────────── */
.lightbox{
  position: fixed; inset: 0; z-index: 60;
  /* grid auto-sizes its row, so the image's max-height:100% would have nothing
     to resolve against; flex gives a definite height */
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--overlay) 96%, transparent);
  padding: clamp(40px, 7vw, 90px);
  opacity: 0;
  transition: opacity .3s ease;
}
.lightbox[hidden]{ display: none; }
.lightbox.open{ opacity: 1; }
.lightbox img{
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
}
.lightbox__close{
  position: absolute; top: var(--pad); right: var(--pad);
  color: var(--ink); z-index: 2;
}
.lightbox__caption{
  position: absolute; left: var(--pad); bottom: var(--pad);
  color: var(--muted);
  font-size: var(--micro);
  text-transform: uppercase;
  letter-spacing: var(--track-letter);
}
.lightbox__nav{
  position: absolute; top: 0; bottom: 0; width: clamp(50px, 9vw, 110px);
  display: grid; place-items: center;
  color: var(--ink); font-size: 20px;
  opacity: .4; transition: opacity .25s ease;
}
.lightbox__nav:hover{ opacity: 1; }
.lightbox__nav--prev{ left: 0; }
.lightbox__nav--next{ right: 0; }

/* ── Info panel ─────────────────────────────────────────── */
.info{
  position: fixed; inset: 0; z-index: 50;
  background: var(--bg);
  padding: calc(var(--pad) * 2.6) var(--pad) var(--pad);
  overflow-y: auto;
  transform: translateY(-100%);
  transition: transform .6s cubic-bezier(.6,0,.2,1);
}
.info[hidden]{ display: none; }
.info.open{ transform: translateY(0); }
.info__close{ position: absolute; top: var(--pad); right: var(--pad); }

.info__inner{
  max-width: 1100px; margin: 0 auto;
  display: grid; gap: clamp(30px, 6vw, 90px);
  grid-template-columns: 1.25fr 1fr;
  align-items: start;
}
.info__name{
  margin: 0 0 .3em;
  font-size: clamp(56px, 13vw, 200px);
  font-weight: 700;
  letter-spacing: -.035em;
  line-height: .82;
}
.info__bio{ margin: 0 0 2em; max-width: 46ch; font-size: clamp(15px, 1.5vw, 18px); }
.info__links{ margin: 0 0 2.4em; line-height: 2; }
.info__links a{ border-bottom: 1px solid var(--line); }
.info__links a:hover{ border-color: var(--ink); }
.muted{ color: var(--muted); }

.btn{
  border: 1px solid var(--ink);
  padding: 12px 22px;
  font-size: var(--micro);
  text-transform: uppercase;
  letter-spacing: var(--track-letter);
  transition: background .3s ease, color .3s ease;
}
.btn:hover{ background: var(--ink); color: var(--bg); }

.stats{ margin: 0; border-top: 1px solid var(--line); }
.stats div{
  display: flex; justify-content: space-between; gap: 20px;
  padding: 11px 0; border-bottom: 1px solid var(--line);
}
.stats dt{ color: var(--muted); }
.stats dd{ margin: 0; font-variant-numeric: tabular-nums; }

/* ── Responsive ─────────────────────────────────────────── */
@media (max-width: 1000px){
  /* the broken grid needs width to read as broken; below this, two even columns */
  .editorial__inner figure:nth-child(5n+1),
  .editorial__inner figure:nth-child(5n+2),
  .editorial__inner figure:nth-child(5n+3),
  .editorial__inner figure:nth-child(5n+4),
  .editorial__inner figure:nth-child(5n+5){
    grid-column: span 6; margin-top: 0;
  }
}

@media (max-width: 760px){
  .info__inner{ grid-template-columns: 1fr; }
  .bar--top nav{ gap: 14px; }
  .views{ gap: 11px; }
  figure img{ height: 58vh; max-width: 76vw; }
  figure[data-ratio="landscape"] img{ height: 42vh; }
  .track figure[data-orient="landscape"] img{ height: 42vh; }
  .lightbox__nav{ width: 44px; }
  .progress{ display: none; }      /* five view buttons need the whole bar */
  .credit{ display: none; }
  .bar--bottom{ gap: 10px; }
  :root{ --wordmark: clamp(52px, 32vw, 220px); }
}

/* snap only for touch — it fights trackpad inertia on desktop */
@media (pointer: coarse){
  .track{ scroll-snap-type: x mandatory; }
  .track figure{ scroll-snap-align: center; }
}

@media (prefers-reduced-motion: reduce){
  *{ animation-duration: .001ms !important; transition-duration: .001ms !important; }
  figure{ transform: none; }
  .track{ opacity: 1; transform: none; }
  figure img{ filter: none; }
  figcaption{ opacity: 1; }
}

/* ── Print: the comp card ───────────────────────────────── */
.compcard{ display: none; }

@media print{
  @page{ size: A4 portrait; margin: 12mm; }
  /* the site is dark; the comp card is not */
  body{ overflow: visible; background: #fff; color: #000; }
  #loader, .bar, main, .wordmark, .sheet, .stack, .index__preview,
  .lightbox, .info{ display: none !important; }

  .compcard{ display: block; color: #000; }
  .compcard__head{ text-align: center; margin-bottom: 8mm; }
  .compcard__head h2{
    margin: 0; font-size: 30pt; font-weight: 500; letter-spacing: .22em;
  }
  .compcard__head p{ margin: 3mm 0 0; font-size: 8pt; letter-spacing: .1em; }

  .compcard__grid{
    display: grid; grid-template-columns: repeat(2, 1fr);
    gap: 5mm; margin-bottom: 8mm;
  }
  .compcard__grid img{
    width: 100%; height: 95mm; object-fit: cover; display: block;
  }
  .compcard__stats{
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: 3mm 6mm; margin: 0; font-size: 8.5pt;
  }
  .compcard__stats div{
    display: flex; justify-content: space-between;
    border-bottom: .3pt solid #999; padding-bottom: 1.5mm;
  }
  .compcard__stats dt{ text-transform: uppercase; letter-spacing: .1em; font-size: 7pt; }
  .compcard__stats dd{ margin: 0; }
}
