/* Hakim Haiman — portfolio. Design contract: design.md */

/* the plain modes' face (Dark and Light): Switzer by Indian Type Foundry, from Fontshare, free
   for commercial use. LOTR sets its own (see Middle-earth below). */
/* LOTR's face: Rulethem, Hakim's own typeface (drawn in code: tools/font, rebuilt with build.py) */
@font-face {
  font-family: 'Rulethem';
  src: url('../fonts/Rulethem.woff') format('woff');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Switzer';
  src: url('../fonts/Switzer-Medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

/* ---------- tokens ---------- */
:root {
  --bg: #000;
  --fg: #fff;
  --mute: rgba(255, 255, 255, 0.56);   /* ≥ 4.5:1 on black */
  --pill-bg: rgba(44, 44, 47, 0.6);            /* the Close pill on phones */
  --rule: rgba(255, 255, 255, 0.18);

  --cols: 4;
  --gap: 1.4rem;
  --pad: 1.4rem;               /* the reference's side margin: 14px at 1500px, scales with the text */
  --col: calc((var(--vw, 100vw) - 2 * var(--pad) - (var(--cols) - 1) * var(--gap)) / var(--cols));
  --top: 1.55rem;              /* header / panel baseline offset: the reference's baseline on phones */
  --ease: cubic-bezier(0.19, 1, 0.22, 1);   /* expo out */
  --ease-io: cubic-bezier(0.65, 0, 0.35, 1);
  color-scheme: dark;
}

/* 1rem = 10px on a 390px phone and at 1500px on desktop, scaling with the window between
   5px and 12px, as on the reference: from 650px up the desktop design simply scales */
html { font-size: clamp(5px, calc(100vw / 39), 12px); }
@media (min-width: 650px) {
  :root { --cols: 14; --top: 2.05rem; }   /* and on desktop */
  html { font-size: clamp(5px, calc(100vw / 150), 12px); }
}

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: none;
  text-size-adjust: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: var(--bg);
  color: var(--fg);
  font-family: 'Switzer', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 500;
  scrollbar-gutter: stable;
  transition: background-color 0.5s var(--ease-io), color 0.5s var(--ease-io);
}
body {
  margin: 0;
  font-family: 'Switzer', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 500;   /* Switzer Medium (Rulethem has one weight) */
  font-size: 1.7rem;
  line-height: 1.11;
  letter-spacing: -0.02em;   /* Switzer set as tight as the reference's grotesk; LOTR opens it up */
  min-height: 100svh;
  background: var(--bg);
  transition: background-color 0.5s var(--ease-io);
}
h1, h2, h3, h4, p, ul, ol, dl, dd, figure, blockquote { margin: 0; padding: 0; font: inherit; }
ul, ol { list-style: none; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; letter-spacing: inherit; text-align: inherit; }
img, video { display: block; max-width: 100%; }
::selection { background: var(--fg); color: var(--bg); }
:focus-visible { outline: 1px solid var(--fg); outline-offset: 0.4rem; border-radius: 0.2rem; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Lenis */
html.lenis, html.lenis body { height: auto; }
/* only the page itself is locked while paused; the project panel keeps its scroll position */
html.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-smooth iframe { pointer-events: none; }
@media (hover: hover) and (pointer: fine) {
  html.is-scrolling-fast .work { pointer-events: none; }
}

/* ---------- grid ---------- */
.wrap { width: 100%; padding-inline: var(--pad); }
.grid {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  column-gap: var(--gap);
}

/* hover: an underline draws in from the left and, on leaving, carries on out to the right. On
   every link and button (.hp; the tabs keep their star instead) and the address on Contact (.u-line) */
.hp, .u-line { position: relative; display: inline-block; }
.hp::before, .u-line::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.04em;
  height: max(1px, 0.05em);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.6s var(--ease-io);   /* even, so the draw is seen travelling */
  pointer-events: none;
}
.hp:focus-visible, .u-line:focus-visible { outline: none; }
.hp:focus-visible::before, .u-line:focus-visible::after { transform: scaleX(1); transform-origin: left center; }
@media (hover: hover) and (pointer: fine) {
  .hp:hover::before, .u-line:hover::after { transform: scaleX(1); transform-origin: left center; }
}

/* masked line reveal (clip-path, not overflow, so hanging quotes can sit outside) */
.ln { display: block; clip-path: inset(-0.2em -2em 0 -2em); padding-bottom: 0.16em; margin-bottom: -0.16em; }
.ln__i { display: block; transform: translate3d(0, 110%, 0); transition: transform 1s var(--ease); transition-delay: calc(var(--d, 0s) + var(--i, 0) * 0.06s); }
.is-in .ln__i, .ln.is-in .ln__i { transform: none; }
/* the header has no entrance of its own: it is in place under the loader, as on the reference */
.site-header .ln__i { transform: none; }
/* while a project is open the header lines wait below their masks; on close the header is
   back at once and each line slides up in 0.475s, 33ms apart (timed after the reference) */
.hdr-rise .site-header { transition: none; }
.hdr-rise .site-header .ln__i { transition: transform 0.475s cubic-bezier(0.215, 0.61, 0.355, 1); transition-delay: calc(var(--r, 0) * 0.033s); }
.hdr-reset .site-header .ln__i { transform: translate3d(0, 110%, 0); transition: none; }
/* split paragraphs: the indent is measured before the split, then moved to line one */
[data-split] { text-indent: var(--indent, 0); }
[data-split].is-split { text-indent: 0; }
[data-split].is-split .ln:first-child .ln__i { text-indent: var(--indent, 0); }
.fade { opacity: 0; transition: opacity 0.8s var(--ease-io); transition-delay: var(--d, 0s); }
.is-in .fade, .fade.is-in { opacity: 1; }
/* The big text is set tight (0.9), so a line waiting below its mask would show the tops of its
   tall letters in the room kept for descenders. As on the reference, whose mask's lower edge
   follows the rise, that room stays closed until the line comes up, opening in step with it. */
.display .ln, .word__quote .ln {
  clip-path: inset(-0.2em -2em 0.16em -2em);
  transition: clip-path 1s var(--ease);
  transition-delay: calc(var(--d, 0s) + var(--i, 0) * 0.06s);
}
.is-in .display .ln, .display.is-in .ln, .display .ln.is-in,
.is-in .word__quote .ln, .word__quote.is-in .ln, .word__quote .ln.is-in { clip-path: inset(-0.2em -2em 0 -2em); }

/* ---------- canvas, loader, cursor ---------- */
#gl {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  display: none;
}
.has-gl #gl { display: block; }

.loader {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  background: var(--bg);
  transition: opacity 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94), visibility 0s 0.6s;   /* the reference's 0.6s ease-out */
}
html:not(.has-gl) .loader { transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94), visibility 0s 0.5s; }   /* no WebGL: lifted in 0.5s */
.is-loaded .loader { opacity: 0; visibility: hidden; }

/* the reference's cursor label: above everything, white in difference mode, shown and hidden
   at once, its first letter a capital (placed and moved by stepCursor) */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 210;
  pointer-events: none;
  color: #fff;
  mix-blend-mode: difference;
  white-space: nowrap;
  visibility: hidden;
  will-change: transform;
}
.cursor::first-letter { text-transform: uppercase; }
.cursor.is-on { visibility: visible; }
@media not ((hover: hover) and (pointer: fine)) { .cursor { display: none; } }

/* ---------- header ---------- */
/* as on the reference, white in difference mode at every size, so it reads over the images */
.site-header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  padding-top: var(--top);
  pointer-events: none;
  color: #fff;
  mix-blend-mode: difference;
  transition: opacity 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.lotr .site-header { color: var(--fg); }
.site-header a, .site-header button { pointer-events: auto; }
.site-header .grid { align-items: start; }
.site-header .grid > * { grid-row: 1; }
.h-name { grid-column: 1 / span 2; justify-self: start; }
.h-tabs { grid-column: 3 / -1; justify-self: end; display: flex; gap: 1rem; }   /* phones: tight, so the name and the tabs never touch */
/* the tabs: the others in muted gold, brightening on hover; the current one in full gold with
   a small four-pointed star under it (the door's star), which grows in as it becomes current */
.h-tabs a { color: var(--mute); white-space: nowrap; transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
/* Case studies is Cases on phones, where the four tabs share the row with the name */
.h-tabs .t-long { display: none; }
@media (min-width: 650px) { .h-tabs .t-long { display: inline; } .h-tabs .t-short { display: none; } }
.h-tabs a.is-active { color: var(--fg); }
@media (hover: hover) and (pointer: fine) { .h-tabs a:hover { color: var(--fg); } }
.h-tabs a::before { display: none; }   /* no pill */
.h-tabs a::after {
  content: '';
  position: absolute;
  left: 50%;
  top: calc(100% + 0.5rem);
  width: 0.8rem;
  height: 0.8rem;
  background: var(--fg);
  clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
  transform: translateX(-50%) scale(0) rotate(-90deg);
  transition: transform 0.5s var(--ease);
  pointer-events: none;
}
.h-tabs a.is-active::after { transform: translateX(-50%) scale(1) rotate(0deg); }
.h-nav { display: none; }
.h-email { display: inline-block; }

@media (max-width: 649px) {
  .site-header {
    position: fixed;
    padding-top: calc(var(--top) + env(safe-area-inset-top, 0px));
  }
}
@media (min-width: 650px) {
  .h-name { grid-column: 1 / span 4; }
  .h-tabs { grid-column: 5 / span 6; justify-self: center; gap: 3.2rem; }   /* the middle of the page */
  .h-nav { grid-column: 11 / -1; justify-self: end; display: flex; align-items: baseline; gap: 2.4rem; }
}

/* ---------- views ---------- */
main { position: relative; z-index: 1; }
.view[hidden] { display: none; }
.view { transition: opacity 0.4s var(--ease-io); }
.is-leaving .view { opacity: 0; }

/* ---------- the stage: everything on one screen ---------- */
/* Everything in the one small size, as on the reference's index (the words about me a little
   larger), the black left to do the rest: no lines. Stacked (phones, small windows): the photo
   and the words, then the work, scrolling. On the stage (a window 900px wide and 560px tall or
   more): a fixed screen in two bands. The top: the photo (80% of columns 1-3), a little about me
   (5-9), the services (11-12), elsewhere (13-14). The bottom, the whole
   width: the strip of covers. */
.stage { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); column-gap: var(--gap); row-gap: 7rem; padding: 12rem var(--pad) 0; transition: opacity 0.75s cubic-bezier(0.455, 0.03, 0.515, 0.955); }
.st { position: relative; min-width: 0; grid-column: 1 / -1; }
.k { color: var(--mute); }
.tabs { display: grid; }
.tabs > * { grid-area: 1 / 1; min-width: 0; }
/* each tab's own blocks: the current tab's come in as the others go */
.stage .t-work, .stage .t-cases, .stage .t-profile, .stage .t-contact { opacity: 0; visibility: hidden; transition: opacity 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94), visibility 0s 0.25s; }
.stage[data-tab='work'] .t-work, .stage[data-tab='cases'] .t-cases, .stage[data-tab='profile'] .t-profile, .stage[data-tab='contact'] .t-contact { opacity: 1; visibility: visible; transition: opacity 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.35s, visibility 0s; }
html:not(.is-stage) .stage:not([data-tab='work']) .t-work,
html:not(.is-stage) .stage:not([data-tab='cases']) .t-cases,
html:not(.is-stage) .stage:not([data-tab='profile']) .t-profile,
html:not(.is-stage) .stage:not([data-tab='contact']) .t-contact { display: none; }
/* behind an open project the stage's words step away, and come back with the covers */
.is-panel .stage { opacity: 0; transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }

/* the top: the photo, and a little about me (short on Work, whole on Profile, the invitation on
   Contact); the services and elsewhere, each list indented under
   its name (the reference's info columns) */
.st-top { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); column-gap: var(--gap); row-gap: 3rem; align-items: start; }
.photo { grid-column: 1 / span 2; grid-row: 1; }
.photo__box { display: block; position: relative; aspect-ratio: 1; overflow: hidden; }
.photo__box img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.has-gl .photo__box img { opacity: 0; }   /* drawn on the canvas */
.about { grid-column: 1 / -1; grid-row: 2; }
.say p { font-size: var(--say, 2.6rem); line-height: 1.02; }
.say p.say__mail { font-size: 2rem; }   /* the address whole on a phone's width */
.say p + p { margin-top: 0.8em; }
.say__base { display: inline-block; width: 0; height: 0; }   /* marks the last line's baseline (alignPhoto) */
.info--svc { grid-column: 3 / -1; grid-row: 1; }
.info { grid-column: span 2; }
.info__list { padding-left: 1.4rem; }
.info__list a { display: inline-flex; align-items: center; gap: 0.4rem; }

/* the bottom: the strip of covers (Work), a kind word (Profile), two ways in (Contact) */
.st-bot { display: flex; flex-direction: column; }
/* the kind words (Profile): the names in a list on the left (under the photo), the chosen one
   bright with the tabs' star; their words large on the right (under the words about me) */
.kw { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); column-gap: var(--gap); row-gap: 3rem; align-items: start; }
.kw__fig { grid-column: 1 / -1; grid-row: 2; min-width: 0; transition: opacity 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94), transform 0.6s var(--ease); }
.kw__fig.is-out { opacity: 0; transform: translate3d(0, 0.6rem, 0); transition-duration: 0.2s, 0.2s; }
.kw__quote { font-size: var(--kw, 2.6rem); line-height: 1.02; text-wrap: pretty; }
.kw__who { margin-top: 1.4rem; }
.kw__index { grid-column: 1 / -1; grid-row: 1; }
.kw__list { padding-left: 1.4rem; }
.kw__pick { display: inline-flex; align-items: center; gap: 0.8rem; color: var(--mute); transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.kw__pick[aria-pressed='true'] { color: var(--fg); }
@media (hover: hover) and (pointer: fine) { .kw__pick:hover { color: var(--fg); } }
.kw__n { font-variant-numeric: tabular-nums; }
.kw__pick::after {
  content: '';
  width: 0.7rem;
  height: 0.7rem;
  background: currentColor;
  clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
  transform: scale(0) rotate(-90deg);
  transition: transform 0.5s var(--ease);
}
.kw__pick[aria-pressed='true']::after { transform: scale(1) rotate(0deg); }
/* Contact: two ways in as big lines of text, no boxes; each with its question and a number
   above (like the covers' labels). Hovering one brightens it, with the tabs' star by its
   number, and dims the other; its arrow steps forward. */
.ways { display: flex; flex-direction: column; gap: 4rem; }
.way { display: flex; flex-direction: column; gap: 0.8rem; transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.way__label { display: flex; align-items: baseline; justify-content: space-between; gap: 1.6rem; }
.way__n { display: inline-flex; align-items: center; gap: 0.8rem; color: var(--mute); font-variant-numeric: tabular-nums; }
.way__n::before {
  content: '';
  width: 0.7rem;
  height: 0.7rem;
  background: currentColor;
  clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
  transform: scale(0) rotate(-90deg);
  transition: transform 0.5s var(--ease);
}
.way__big { font-size: 3.6rem; line-height: 0.95; }
.way__arrow { display: inline-block; transition: transform 0.5s var(--ease); }
.way__arrow { vertical-align: -0.08em; }
.way__arrow svg { display: block; width: 0.9em; height: 0.9em; }   /* Phosphor's arrows (js/icons.js) at its Light weight, large */
@media (hover: hover) and (pointer: fine) {
  .ways:hover .way:not(:hover) { color: var(--mute); }
  .way:hover .way__n { color: var(--fg); }
  .way:hover .way__n::before { transform: scale(1) rotate(0deg); }
  .way:hover .way__arrow { transform: translateX(0.25em); }
  .way[target]:hover .way__arrow { transform: translate(0.18em, -0.18em); }
}
.way:focus-visible { outline-offset: 0.8rem; }
/* Case studies: the two side by side across the band, each titled and numbered above its cover
   (as the strip's labels) and what it is under it; drawn on the canvas with WebGL. Hovering one
   dims the other. Stacked (phones, small windows): one under the other at 16:10. */
.cases { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); column-gap: var(--gap); row-gap: 5rem; align-items: start; }
.case { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.case__label { display: flex; align-items: baseline; justify-content: space-between; gap: 0.8rem; white-space: nowrap; }
.case__n { font-variant-numeric: tabular-nums; }
.case__box { display: block; position: relative; aspect-ratio: var(--ar, 16 / 10); overflow: hidden; }   /* stacked: the whole cover */
.case__box img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% var(--focus, 50%); opacity: 0; transition: opacity 0.8s var(--ease-io); }
.case__box img.is-loaded { opacity: 1; }
.has-gl .case__box img { opacity: 0 !important; transition: none !important; }
.case__kind { color: var(--mute); }
@media (hover: hover) and (pointer: fine) { .cases.has-hover .case:not(.is-hover) { color: var(--mute); } }
@media (min-width: 650px) {
  .case { grid-column: span 7; }
  .photo { grid-column: 1 / span 3; width: 80%; }   /* PHOTO in main.js */
  .about { grid-column: 5 / span 5; grid-row: 1; }
  /* 32px on a window 1000px wide or more (the user's size), smaller in proportion below that,
     and never smaller than 3rem (so wider screens keep their larger size) */
  .say p { font-size: var(--say, max(3rem, min(32px, 4.8rem))); }
  .say p.say__mail { font-size: min(var(--say, 2.7rem), 2.7rem); }   /* your address stays within its column */
  .info { grid-row: 1; }
  .info--svc { grid-column: 11 / span 2; }
  .info--out { grid-column: 13 / span 2; }
  .kw__index { grid-column: 1 / span 4; grid-row: 1; }
  .kw__fig { grid-column: 5 / -1; grid-row: 1; }
  .kw__quote { font-size: var(--kw, 3.6rem); }
  .way__big { font-size: 6.4rem; }
}

/* the stage itself */
.is-stage, .is-stage body { height: 100%; overflow: hidden; }
.is-stage .stage {
  position: fixed;
  inset: 0;
  z-index: 1;
  padding: 0 var(--pad);
  grid-template-rows: 8rem var(--band, minmax(0, 1fr)) minmax(0, 1fr);   /* the header's row: HEAD_REM in main.js */
  row-gap: 0;
}
.is-stage .st { padding: var(--gap) 0; min-height: 0; }
.is-stage .st-top { grid-row: 2; grid-template-rows: minmax(0, 1fr); }
.is-stage .st-bot { grid-row: 3; }
.is-stage .photo { width: min(100%, var(--photo, 100%)); }
.is-stage .about { height: 100%; min-height: 0; overflow: hidden; }
.is-stage .say { align-self: start; }   /* each as tall as its words, for fitAbout */
.is-stage .bot { flex: 1; min-height: 0; }
.is-stage .kw { align-self: center; }   /* the kind words sit in the middle of their band, clear of the words above */
.is-stage .ways { align-self: center; }   /* in the middle of the band, as the kind words */
.is-stage .cases { height: 100%; align-items: stretch; }   /* the covers take the band's height */
.is-stage .case { min-height: 0; }
.is-stage .case__box { flex: 1; min-height: 0; aspect-ratio: auto; }
.is-stage .site-footer { display: none; }
/* the strip: covers placed and sized by js (stepStrip), each with its label above; drawn on the
   canvas with WebGL */
.strip { position: relative; }
.frame__idx { display: none; }
.is-stage .strip { height: 100%; overflow: hidden; touch-action: pan-y; user-select: none; -webkit-user-select: none; }
.is-stage .strip a { -webkit-user-drag: none; }
.is-stage .work { position: absolute; inset: 0; display: block; padding: 0; }
.is-stage .work .frame { position: absolute; left: 0; top: 0; width: 20rem; display: flex; flex-direction: column-reverse; gap: 0.6rem; will-change: transform; }
.is-stage .work .frame__label { margin: 0; }
.is-stage .frame__num { display: none; }
.is-stage .frame__idx { display: block; margin-left: auto; font-variant-numeric: tabular-nums; }
.frame__of { display: none; color: var(--mute); }
.is-stage .frame.is-current .frame__of { display: inline; }

/* ---------- work grid ---------- */
/* stacked: a cover per project, two across on phones, four wider */
.work { row-gap: 5rem; align-items: start; }
@media (min-width: 650px) {
  .work { grid-template-columns: repeat(4, minmax(0, 1fr)); row-gap: 7rem; }
}
.frame {
  grid-column: span 2;
  display: block;
  position: relative;
  transition: opacity 0.2s cubic-bezier(0.23, 1, 0.32, 1);   /* the reference's caption fade on hover */
}
@media (min-width: 650px) { .frame { grid-column: span 1; } }
/* its title and year, under it */
.frame__label {
  transition: opacity 0.75s cubic-bezier(0.455, 0.03, 0.515, 0.955);   /* back with the grid on close */
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.8rem;
  margin-top: 0.9rem;
  white-space: nowrap;
}
.frame__title { overflow: hidden; text-overflow: ellipsis; }
.frame__num { margin-left: auto; font-variant-numeric: tabular-nums; }
.frame__box {
  display: block;
  position: relative;
  aspect-ratio: var(--ar);
  overflow: hidden;
}
.frame__box img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% var(--focus, 50%);
  opacity: 0;
  transition: opacity 0.8s var(--ease-io);
}
.frame__box img.is-loaded { opacity: 1; }
/* with WebGL the covers are drawn on the canvas; their images and videos only feed it */
.has-gl .frame__box img, .has-gl .frame__box video { opacity: 0 !important; transition: none !important; }
/* a frame's video plays over its image (without WebGL; with it, the video is the plane's texture) */
.frame__box video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% var(--focus, 50%);
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}
.frame__box video.is-playing { opacity: 1; }

/* intro: labels rise with their frame */
.frame__label .ln__i { transition-delay: calc(var(--d, 0s)); }
/* the grid's entrance (WebGL): the whole label travels up with its image, already set */
.frame.is-rise .frame__label .ln__i { transform: none; transition: none; }
.frame.is-rise .frame__label { will-change: transform; }

/* hover focus: everything but the hovered project steps back */
.work.has-hover .frame:not(.is-hover) { opacity: 0.2; }
.is-panel .work .frame { opacity: 1; }
.is-panel .frame__label { opacity: 0 !important; transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }   /* the reference's 0.3s ease-out */
/* the header fades as the reference's does when a project opens (its own 0.25s fade trailing a
   0.3s scripted one, measured) and is gone at 0.29s, as the panel's title rises in its place */
@keyframes hdr-out {
  0% { opacity: 1; } 11.5% { opacity: 0.987; } 23% { opacity: 0.929; } 34.5% { opacity: 0.843; } 46% { opacity: 0.741; }
  57.5% { opacity: 0.633; } 69% { opacity: 0.525; } 80.5% { opacity: 0.424; } 92% { opacity: 0.334; } 100% { opacity: 0.28; }
}
.is-panel .site-header { opacity: 0; transition: none; animation: hdr-out 0.29s linear; }
.is-panel .work .frame:not(.is-active) .frame__box img.is-loaded,
.is-panel .work .frame:not(.is-active) .frame__box video.is-playing { opacity: 0.15; }
.has-gl.is-panel .work .frame .frame__box img, .has-gl.is-panel .work .frame .frame__box video { opacity: 0; }

/* ---------- footer ---------- */
.site-footer {
  position: relative;
  z-index: 1;
  margin-top: 15rem;
  padding-bottom: 1.2rem;
  transition: opacity 0.75s cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
/* (a project's page is see-through over the canvas: the footer's words step away too) */
.has-gl.is-panel .site-footer { opacity: 0; transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
@media (min-width: 650px) { .site-footer { margin-top: 35rem; } }
html:not(.is-profile) .site-footer { margin-top: 10rem; }
.site-footer .grid { align-items: end; row-gap: 3rem; }
.f-a { grid-column: 1 / span 2; }
.f-b { grid-column: 3 / span 2; }
.f-c { grid-column: 1 / span 2; }
.f-d { grid-column: 3 / span 2; }
.f-e { grid-column: 3 / span 2; justify-self: end; }
.site-footer li { display: block; }
@media (min-width: 650px) {
  .f-a { grid-column: 1 / span 4; }
  .f-b { grid-column: 5 / span 3; }
  .f-c { grid-column: 8 / span 3; }
  .f-d { grid-column: 11 / span 2; }
  .f-e { grid-column: 13 / span 2; }
}

/* ---------- a project: a page over the index (words left, frames right; stacked on phones) ---------- */
.panel {
  position: fixed;
  inset: 0;
  z-index: 30;
  visibility: hidden;
  pointer-events: none;
}
.panel.is-open { visibility: visible; }
.ps {
  position: absolute;
  inset: 0;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  pointer-events: none;
}
.ps::-webkit-scrollbar { display: none; }
.panel.is-open .ps { pointer-events: auto; }
.ps__inner { display: flex; flex-direction: column; align-items: center; padding-top: 12rem; padding-bottom: 12rem; }
/* its words: the title at the display size, what it is, the credits and the Dribbble shots */
.pi { width: 100%; max-width: 80rem; display: flex; flex-direction: column; align-items: center; text-align: center; }
.pi__title { font-size: 3.2rem; line-height: 0.9; letter-spacing: 0; }
@media (min-width: 650px) { .pi__title { font-size: 4.4rem; } }
.pi__title:focus { outline: none; }
.pi__desc { margin-top: 3.5rem; max-width: 56rem; text-wrap: balance; }
.pi__meta { margin-top: 3.5rem; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 3.2rem; }
.pi__meta .k { color: var(--mute); margin-right: 0.5rem; }
.pi__shots { margin-top: 1.8rem; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 3.2rem; }
.pi__shots a .ln__i, .pi__shots .ln__i { display: inline-flex; align-items: center; gap: 0.4rem; }
.pi__shots a { display: inline-flex; align-items: center; gap: 0.4rem; }
/* its lines rise in 0.5s (cubic out), the first about 0.27s after the click, 33ms apart */
.pi .ln__i { transition: transform 0.5s cubic-bezier(0.215, 0.61, 0.355, 1); transition-delay: calc(0.19s + var(--i, 0) * 0.033s); }
.panel.is-closing .pi { opacity: 0; transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
/* its frames, one under another in the middle ten of the fourteen columns */
.pf-list { width: 100%; margin-top: 6rem; display: flex; flex-direction: column; gap: 1.5rem; }
/* from 650px, left and right: the words in columns 1-4, held in place while the frames
   (columns 6-13, clear of Close and Next in the corners) scroll past; the description a
   little larger than the rest */
@media (min-width: 650px) {
  .ps__inner { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); column-gap: var(--gap); align-items: start; padding-top: 8rem; }
  .pi { grid-column: 1 / span 4; position: sticky; top: 8rem; max-width: none; align-items: flex-start; text-align: left; }
  .pi__desc { margin-top: 2.4rem; max-width: none; font-size: 2.2rem; line-height: 1.05; text-wrap: pretty; }
  .pi__meta { margin-top: 3rem; flex-direction: column; justify-content: flex-start; gap: 0.2rem; }
  .pi__shots { margin-top: 2.4rem; flex-direction: column; justify-content: flex-start; gap: 0.2rem; }
  .pf-list { grid-column: 6 / span 8; width: auto; margin-top: 0; }
  .pnav { grid-column: 1 / -1; margin-top: 0; }
}
.pf { position: relative; }
.pf__box { position: relative; aspect-ratio: var(--ar); overflow: hidden; background: transparent; }
.pf__box img, .pf__box video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.pf { opacity: 0; transition: opacity 0.35s var(--ease-io); cursor: zoom-in; }
.pf:focus-visible { outline-offset: 0.3rem; }
.pf.is-shown { opacity: 1; }
.pf__box video { opacity: 0; transition: opacity 0.4s ease; }
.pf__box video.is-playing { opacity: 1; }
/* Close sits where the header's links were (Esc, a click beside the page and ←/→ work too) */
.panel__close {
  position: absolute;
  top: var(--top);
  right: var(--pad);
  z-index: 2;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.panel.is-open:not(.is-closing) .panel__close { opacity: 1; pointer-events: auto; transition-delay: 0.25s; }
.is-focus .panel__close { opacity: 0 !important; pointer-events: none; }
/* its number, and the previous and next projects: in the page's corners on desktop, at its end on phones */
.pnav { width: 100%; margin-top: 5rem; display: grid; grid-template-columns: 1fr auto 1fr; align-items: baseline; gap: 1rem; }
.pnav__count { grid-column: 2; grid-row: 1; color: var(--mute); font-variant-numeric: tabular-nums; }
.pnav__step { display: inline-flex; align-items: center; gap: 0.5rem; }
.pnav__step svg { width: 1em; height: 1em; flex: none; }
.pnav__prev { grid-column: 1; grid-row: 1; justify-self: start; }
.pnav__next { grid-column: 3; grid-row: 1; justify-self: end; }
@media (min-width: 650px) {
  .pnav__count, .pnav__step { position: fixed; z-index: 2; transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
  .pnav__count { top: var(--top); left: var(--pad); }
  .pnav__prev { left: var(--pad); bottom: 1.2rem; }
  .pnav__next { right: var(--pad); bottom: 1.2rem; }
  .pnav > * { opacity: 0; pointer-events: none; }
  .panel.is-open:not(.is-closing) .pnav > * { opacity: 1; pointer-events: auto; transition-delay: 0.25s; }
  .is-focus .pnav > * { opacity: 0 !important; pointer-events: none !important; }
}

/* ---------- a case study: the project's page, with a story (see fillCase in main.js) ---------- */
/* on the left, held in place: what it is, its title and line, its sections (the one being read
   carries the tabs' star; a click scrolls to it) and the links to the live prototype */
.pi__kicker { color: var(--mute); margin-bottom: 1.4rem; }
.panel.is-case .pi { align-items: flex-start; text-align: left; }
.pi__toc { margin-top: 3rem; display: flex; flex-direction: column; gap: 0.2rem; }
.panel.is-case .pi__shots { justify-content: flex-start; }
.pi__sec { display: inline-flex; align-items: center; gap: 0.8rem; color: var(--mute); transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.pi__sec[aria-current] { color: var(--fg); }
@media (hover: hover) and (pointer: fine) { .pi__sec:hover { color: var(--fg); } }
.pi__sec::after {
  content: '';
  width: 0.7rem;
  height: 0.7rem;
  background: currentColor;
  clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
  transform: scale(0) rotate(-90deg);
  transition: transform 0.5s var(--ease);
}
.pi__sec[aria-current]::after { transform: scale(1) rotate(0deg); }
/* on the right, the story: what it is and the facts, the cover it flew from, then its sections;
   its words fade in once the page is there (the frames come as a project's do) */
.panel.is-case .pf-list { gap: 0; }
.cs { opacity: 0; transition: opacity 0.6s var(--ease-io); }
.panel.is-read .cs { opacity: 1; }
.panel.is-closing .cs { opacity: 0; transition-duration: 0.3s; }
.cs__lead { font-size: 2.2rem; line-height: 1.05; text-wrap: pretty; }
.cs__facts { margin-top: 3.2rem; display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.4rem var(--gap); }
.pf--cover { margin-top: 4rem; }
.cs--sec { margin-top: 14rem; }
.cs__label { display: flex; align-items: baseline; gap: 0.8rem; color: var(--mute); }
.cs__n { color: var(--mute); font-variant-numeric: tabular-nums; }
.cs__title { margin-top: 1.4rem; font-size: 3.2rem; line-height: 1; text-wrap: balance; }
.cs__p { margin-top: 2.8rem; font-size: 2.2rem; line-height: 1.1; text-wrap: pretty; }
.cs .pf { margin-top: 3.2rem; }
.pf__cap { margin-top: 1rem; color: var(--mute); }
.cs__d { color: var(--mute); }
/* then and now, row by row: the old way muted, the new one bright */
.cs__vs { margin-top: 3.2rem; display: flex; flex-direction: column; gap: 2.4rem; }
.cs__vs-row { display: grid; grid-template-columns: 3rem minmax(0, 1fr) minmax(0, 1fr); gap: 1.2rem 2.4rem; }
.cs__vs-head { color: var(--mute); }
.cs__cl { display: none; color: var(--mute); }
.cs__cell--was .cs__t { color: var(--mute); }
/* who it's for: a photo (when there is one) beside their words */
.cs__people { margin-top: 3.2rem; display: flex; flex-direction: column; gap: 4.8rem; }
.cs__person { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); column-gap: var(--gap); align-items: start; }
.cs .cs__person .pf { grid-column: 1 / span 2; margin-top: 0; }
.cs__who { grid-column: 1 / -1; }
.cs__person.has-photo .cs__who { grid-column: 3 / -1; }
.cs__quote { margin-top: 1.6rem; font-size: 2.2rem; line-height: 1.05; }
.cs__rows { margin-top: 1.6rem; display: grid; gap: 1rem; }
.cs__rows ul { display: flex; flex-direction: column; }
/* numbered points and steps, two across */
.cs__points { margin-top: 3.2rem; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.8rem var(--gap); }
.cs__point { display: flex; flex-direction: column; gap: 0.3rem; }
/* a section's parts (the screens, the decisions) */
.cs__sub { margin-top: 9rem; }
.cs__subtitle { margin-top: 1.2rem; font-size: 2.6rem; line-height: 1.02; text-wrap: balance; }
.cs__sub .cs__p { margin-top: 1.8rem; }
.cs__bullets { margin-top: 2.4rem; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.4rem var(--gap); }
.cs__try { margin-top: 1.6rem; display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 1.6rem; }
.cs__try a, .cs__links a { display: inline-flex; align-items: center; gap: 0.4rem; }
.cs__tags { margin-top: 2rem; color: var(--mute); }
.cs__list { margin-top: 3.2rem; }
.cs__list ul { margin-top: 0.6rem; padding-left: 1.4rem; columns: 2; column-gap: var(--gap); }   /* as the lists at the top: indented under their name */
.cs__links { margin-top: 3rem; display: flex; flex-wrap: wrap; gap: 0.4rem 3.2rem; }
@media (min-width: 650px) {
  .cs__facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 649px) {
  .cs--sec { margin-top: 9rem; }
  .cs__sub { margin-top: 6rem; }
  .cs__vs-head { display: none; }
  .cs__vs-row { grid-template-columns: 2.6rem minmax(0, 1fr); }
  .cs__cell--now { grid-column: 2; }
  .cs__cl { display: block; }
  .cs__points, .cs__bullets { grid-template-columns: minmax(0, 1fr); }
  .cs__list ul { columns: 1; }
  .cs__person { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .cs__person.has-photo .cs__who { grid-column: 1 / -1; margin-top: 1.4rem; }
}

/* without WebGL (touch, reduced motion): a sheet over the page */
.panel.is-sheet { background: var(--bg); pointer-events: auto; }
.panel.is-sheet .ps { pointer-events: auto; }
.panel.is-sheet .pf { opacity: 1; }
/* phones: the project reads as a page under the site header, which stays in place and usable;
   one Close pill sits centred at the bottom */
@media (max-width: 649px) {
  .ps__inner { padding-top: 12rem; padding-bottom: 8rem; }
  .pi__desc, .pi__meta { margin-top: 2.4rem; }
  .pi__meta, .pi__shots { gap: 0.3rem 2rem; }
  .pf-list { margin-top: 4rem; }
  .panel__close {
    top: auto;
    right: auto;
    left: 50%;
    bottom: calc(1.2rem + env(safe-area-inset-bottom, 0px));
    display: inline-flex;
    align-items: center;
    height: 2.5rem;
    padding: 0.075em 0.5rem 0;
    border-radius: 0.5rem;
    background: var(--pill-bg);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    transform: translateX(-50%);
  }
  .panel__close::before { display: none; }
  .is-panel .site-header { opacity: 1 !important; z-index: 31; animation: none; }
  .panel.is-case.is-sheet::before { content: ''; position: fixed; z-index: 1; top: 0; left: 0; right: 0; height: calc(var(--top) + 4.4rem); background: var(--bg); pointer-events: none; }
}
/* a fade to the page colour and back: the reference's page change on phones */
.veil {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: var(--bg);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), visibility 0s 0.3s;
}
.veil.is-on { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), visibility 0s; }

/* in-place zoom (WebGL desktop): dim layer, top canvas, caption bar */
.focus-dim {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--bg);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.55s var(--ease-io), visibility 0s 0.55s;
}
.focus-dim.is-on { opacity: 0.9; visibility: visible; transition: opacity 0.55s var(--ease-io), visibility 0s; }
#gl-top {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 41;
  pointer-events: none;
  display: none;
}
.has-gl #gl-top { display: block; }
.focus-bar {
  position: fixed;
  left: var(--pad);
  right: var(--pad);
  bottom: calc(1.2rem + env(safe-area-inset-bottom, 0px));
  z-index: 42;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s var(--ease-io), visibility 0s 0.4s;
}
.focus-bar.is-on { opacity: 1; visibility: visible; transition: opacity 0.4s var(--ease-io) 0.2s, visibility 0s; }
.focus-bar button { pointer-events: auto; }
.pf.is-focused { opacity: 0 !important; transition: none !important; }

/* lightbox */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: calc(var(--pad) + env(safe-area-inset-top, 0px)) var(--pad) calc(var(--pad) + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s var(--ease-io), visibility 0s 0.35s;
}
.lightbox.is-open { opacity: 1; visibility: visible; transition: opacity 0.35s var(--ease-io), visibility 0s; }
.lightbox__stage { position: relative; min-height: 0; }
.lightbox__stage img, .lightbox__stage video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.lightbox__bar { display: flex; justify-content: space-between; padding-top: 1.2rem; }

/* the display size (7rem, 3.2rem on phones, 0.9 leading), set without tightening (see .lotr).
   Kind words quotes use it too. */
.display, .word__quote {
  font-size: 3.2rem;
  line-height: 0.9;
  letter-spacing: 0;
}
@media (min-width: 650px) { .display, .word__quote { font-size: 7rem; } }
/* footer on phones: links first, credits last */
@media (max-width: 649px) {
  .f-b { grid-column: 1 / span 2; grid-row: 1; }
  .f-c { grid-column: 3 / span 2; grid-row: 1; }
  .f-d { grid-column: 1 / span 2; grid-row: 2; }
  .f-e { grid-column: 3 / span 2; grid-row: 2; justify-self: end; }
  .f-a { grid-column: 1 / -1; grid-row: 3; }
}

/* outbound arrow: one copy leaves to the top-right, the next arrives from the bottom-left */
.ext { display: inline-grid; overflow: hidden; flex-shrink: 0; }
.ext svg { display: block; width: 1em; height: 1em; }   /* Phosphor icons at the text size */
.ext > span { grid-area: 1 / 1; transition: transform 0.5s var(--ease); }
.ext > span:first-child { transition-delay: 0.2s; }   /* as on the reference: one arrow leaves, 0.2s later the other arrives */
.ext > span:last-child { transform: translate(-105%, 105%); }
@media (hover: hover) and (pointer: fine) {
  a:hover .ext > span:first-child { transform: translate(105%, -105%); transition-delay: 0s; }
  a:hover .ext > span:last-child { transform: none; transition-delay: 0.2s; }
}

/* Shift+G: the columns, as on the reference (white at 10%) */
.columns { position: fixed; inset: 0; z-index: 999; pointer-events: none; }
.columns .grid { height: 100%; }
.columns .grid > div { height: 100%; background: rgba(255, 255, 255, 0.1); }
@media (max-width: 649px) { .columns .grid > div:nth-child(n + 5) { display: none; } }

/* ---------- Middle-earth: the site's one look ---------- */
/* every word in Rulethem, Hakim's own uncial (tools/font), in old gold: one alphabet written
   with a broad pen, large for the capitals and small for the lower case. It is spaced for
   text, so no tracking, and the big text is not tightened. */
.lotr { --fg: #d8b76a; --mute: rgba(216, 183, 106, 0.62); --rule: rgba(216, 183, 106, 0.3); color-scheme: dark; }
.lotr, .lotr body { font-family: 'Rulethem', Georgia, serif; }

/* ---------- the plain modes (Dark, the default, and Light), after the reference ---------- */
/* Dark is the tokens at the top: white Switzer on black. Light turns them over. Big text sets
   a little tighter; the tabs' and lists' star (LOTR's, from the door) is a plain dot. */
.light {
  --bg: #ffffff;
  --fg: #000000;
  --mute: rgba(0, 0, 0, 0.5);
  --rule: rgba(0, 0, 0, 0.12);
  --pill-bg: rgba(255, 255, 255, 0.72);
  color-scheme: light;
}
.light .site-header { mix-blend-mode: normal; color: var(--fg); }
html:not(.lotr) .say p, html:not(.lotr) .kw__quote, html:not(.lotr) .way__big, html:not(.lotr) .pi__title { letter-spacing: -0.035em; }
html:not(.lotr) .h-tabs a::after, html:not(.lotr) .kw__pick::after, html:not(.lotr) .way__n::before, html:not(.lotr) .pi__sec::after { clip-path: circle(50%); width: 0.5rem; height: 0.5rem; }
/* the toggle: the three words, the current one bright (the reference's Normal · Star Wars), on
   their own in the bottom-right corner (empty on the stage, under the small covers), on the
   page's margin; like the header in difference mode (normal in Light) so they read over the
   images when the page scrolls under them. They step away behind the door and while a project
   is open (Next lives in that corner). Phones: the footer. */
.theme-pill {
  position: fixed;
  right: var(--pad);
  bottom: 1.5rem;
  z-index: 25;
  display: none;
  mix-blend-mode: difference;
  transition: opacity 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.light .theme-pill { mix-blend-mode: normal; }
@media (min-width: 650px) {
  .theme-pill { display: block; }
  .theme-toggle.f-theme, .f-theme-br { display: none; }
}
.is-panel .theme-pill, .themed:not(.is-ready) .theme-pill { opacity: 0; pointer-events: none; }
.theme-toggle { display: inline-flex; align-items: baseline; gap: 0.7rem; padding: 0; }
.theme-toggle__o { color: var(--mute); transition: color 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.theme-toggle__o[aria-pressed='true'] { color: var(--fg); }
@media (hover: hover) and (pointer: fine) { .theme-toggle__o:hover { color: var(--fg); } }
.f-theme { margin-top: 0.4rem; }
.lotr .display, .lotr .word__quote { letter-spacing: 0; }
.lotr .cursor { color: var(--fg); mix-blend-mode: normal; }

/* with Sting drawn (.themed): a project's frames are drawn in WebGL too (swShow), so the blade
   passes over them and marks them; the page keeps their boxes, and its images and videos only
   feed the planes */
.themed.has-gl .panel:not(.is-sheet) .pf__box img,
.themed.has-gl .panel:not(.is-sheet) .pf__box video { opacity: 0 !important; transition: none !important; }
/* the zoom stays on that canvas too: no dark layer; the page's words fade out instead */
.themed.has-gl .focus-dim { background: transparent; }
.themed.has-gl .panel:not(.is-closing) .pi { transition: opacity 0.55s var(--ease-io); }
.themed.has-gl.is-focus .panel .pi { opacity: 0; }
/* the way in: the black lifts in 0.8s; a doorway is drawn in moonlit lines (js/door.js) and
   waits for a click; the lines flare, the leaves slide apart into the columns and the door
   fades (gone at 1.6s); only then does the page come up (DOOR in main.js) */
.themed .loader { transition: opacity 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94), visibility 0s 0.8s; }
/* with the door (.themed, set before the first paint), the page stays hidden from the start,
   under the loader too, and only comes up once the door opens (opacity on the containers: a
   tab's own blocks set their visibility, so hiding by visibility would leak through) */
.themed:not(.is-ready) .site-header, .themed:not(.is-ready) .stage { opacity: 0; transition: none; pointer-events: none; }
.mode-waiting .site-header { visibility: hidden; }
.mode-entering .site-header, .mode-entering .stage { transition: opacity 1s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.crawl__catch { position: fixed; inset: 0; z-index: 55; padding: 0; border: 0; background: transparent; }
.door {
  position: fixed;
  inset: 0;
  z-index: 55;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: #e3ebf6;
  --glow: rgba(140, 185, 255, 0.45);
}
.door:focus { outline: none; }
.door__svg {
  display: block;
  height: min(74vh, 64rem);
  width: auto;
  max-width: 86vw;
  overflow: visible;
  filter: drop-shadow(0 0 0.35rem var(--glow));
  transition: filter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94), color 0.6s;
}
@media (hover: hover) and (pointer: fine) { .door:hover { --glow: rgba(160, 200, 255, 0.7); } }
/* each line is drawn in, the frame first, then the trees and the stars (--k orders them) */
.dl { fill: none; stroke: currentColor; stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 1; animation: door-draw 1.5s cubic-bezier(0.65, 0, 0.35, 1) calc(0.3s + var(--k, 0) * 0.1s) forwards; }
.dl--fill { fill: currentColor; fill-opacity: 0; animation-name: door-draw, door-fill; animation-duration: 1.5s, 0.8s; animation-delay: calc(0.3s + var(--k, 0) * 0.1s), calc(1.2s + var(--k, 0) * 0.1s); }
.dl--faint { stroke-opacity: 0.35; }
@keyframes door-draw { to { stroke-dashoffset: 0; } }
@keyframes door-fill { to { fill-opacity: 1; } }
.door__words, .door__mark { fill: currentColor; font-family: 'Rulethem', Georgia, serif; opacity: 0; animation: door-fade 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; }
.door__words { font-size: 16px; letter-spacing: 0.06em; animation-delay: 0.9s; }
.door__mark { font-size: 20px; letter-spacing: 0.04em; animation-delay: 1.8s; }
@keyframes door-fade { to { opacity: 1; } }
/* opening */
.door__leaf { transition: transform 1.3s cubic-bezier(0.65, 0, 0.35, 1) 0.15s; }
.door.is-open { --glow: rgba(200, 225, 255, 0.95); color: #fff; pointer-events: none; }
.door.is-open .door__leaf--l { transform: translateX(-104px); }
.door.is-open .door__leaf--r { transform: translateX(104px); }
.door.is-open .door__svg { opacity: 0; transition: filter 0.3s, color 0.3s, opacity 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.8s; }

/* hold F on the Profile: the intro and approach come up written in fire, word after word */
.inscription {
  position: fixed;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  padding: 0 14vw;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94), visibility 0s 0.45s;
}
.inscription.is-on { opacity: 1; visibility: visible; transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); }
.inscription__text { font-size: 2.3vw; line-height: 1.32; text-align: center; letter-spacing: 0.02em; color: #ffd27a; }
.inscription__text p + p { margin-top: 0.9em; }
.inscription .w { opacity: 0; }
.inscription.is-lit .w {
  animation: kindle 1.1s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards, flicker 2.6s ease-in-out infinite;
  animation-delay: calc(var(--k) * 45ms), calc(var(--k) * 45ms + 1.1s);
}
@keyframes kindle {
  0% { opacity: 0; color: #3a0600; text-shadow: none; }
  35% { opacity: 1; color: #b3260a; text-shadow: 0 0 0.4em rgba(255, 60, 0, 0.5); }
  100% { opacity: 1; color: #ffd27a; text-shadow: 0 0 0.12em #ffae3d, 0 0 0.45em #ff6a00, 0 0 1.1em rgba(255, 64, 0, 0.55); }
}
@keyframes flicker {
  0%, 100% { text-shadow: 0 0 0.12em #ffae3d, 0 0 0.45em #ff6a00, 0 0 1.1em rgba(255, 64, 0, 0.55); }
  50% { text-shadow: 0 0 0.1em #ffb347, 0 0 0.36em #ff7a10, 0 0 0.85em rgba(255, 80, 0, 0.42); }
}
.is-crawling main, .is-crawling .site-header { opacity: 0; transition: opacity 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); }

/* reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; transition-delay: 0s !important; animation-duration: 0.01ms !important; }
}
