/* ============ tokens ============ */
:root{
  --ground:#262626;
  --panel:#ffffff;
  --ink:#0f0f0f;
  --body:#313336;
  --muted:rgba(0,0,0,.6);
  --accent:#06bcc1;
  --accent-mid:#009aa6;
  --accent-ink:#00767f;
  --line:#ededed;
  --radius-panel:24px;
  --radius-card:14px;
  --gap:20px;
  --pad:36px;
  --maxw:1384px;
  --serif:"Fraunces",ui-serif,Georgia,serif;
  --sans:"Geist",ui-sans-serif,-apple-system,"Segoe UI",sans-serif;
  --mono:"Geist Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;
  background:var(--ground);
  font-family:var(--sans);
  color:var(--body);
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:inherit}

/* ============ panels ============ */
.panel{
  position:relative;
  margin:8px;
  border-radius:var(--radius-panel);
  background:var(--panel);
  background-image:linear-gradient(rgba(0,0,0,.038) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(0,0,0,.038) 1px,transparent 1px);
  background-size:88px 88px;
  overflow:hidden;
}
.panel + .panel{margin-top:8px}
.inner{
  max-width:var(--maxw);
  margin:0 auto;
  padding:var(--pad);
}

/* ============ nav ============ */
.nav{
  position:fixed;
  top:22px;left:0;right:0;
  z-index:50;
  width:min(780px,calc(100% - 48px));
  margin:0 auto;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:16px;
}
.logo{
  font-family:var(--serif);
  font-weight:600;
  font-size:25px;
  letter-spacing:-.06em;
  color:var(--accent);
  text-decoration:none;
  line-height:1;
}
.logo.sm{font-size:20px}
.navpill{
  display:flex;
  gap:6px;
  padding:6px;
  border-radius:999px;
  background:rgba(255,255,255,.82);
  backdrop-filter:blur(12px);
  -webkit-backdrop-filter:blur(12px);
  box-shadow:0 1px 2px rgba(0,0,0,.05),0 6px 20px rgba(0,0,0,.07);
}
.navpill a{
  padding:7px 14px;
  border-radius:999px;
  font-size:14px;
  color:var(--muted);
  text-decoration:none;
  transition:background .18s ease,color .18s ease;
}
.navpill a:hover{background:rgba(0,0,0,.05);color:var(--ink)}
.btn-dark{
  padding:11px 20px;
  border-radius:12px;
  background:#2f2f2f;
  color:#fff;
  font-size:14px;
  text-decoration:none;
  box-shadow:0 1px 2px rgba(0,0,0,.18),0 6px 18px rgba(0,0,0,.16);
  transition:transform .18s ease,background .18s ease;
}
.btn-dark:hover{background:#141414;transform:translateY(-1px)}

/* ============ hero ============ */
.hero{
  min-height:calc(100svh - 16px);
  display:flex;
  align-items:center;
}
.hero-inner{
  width:min(800px,100%);
  margin:0 auto;
  padding:120px 24px;
}
.eyebrow{
  margin:0 0 6px;
  font-size:22px;
  font-weight:500;
  letter-spacing:-.03em;
  line-height:1.4;
  color:var(--accent-mid);
}
.diamond{font-size:.85em;vertical-align:1px;margin-right:2px}

.display{
  font-family:var(--serif);
  font-weight:300;
  color:var(--ink);
  letter-spacing:-.06em;
  line-height:1.2;
  margin:0;
}
h1.display{font-size:clamp(40px,6vw,48px)}
h2.display{font-size:clamp(30px,4.6vw,38px)}

/* figma-style selection box on the name */
/* selection square on the name */
.selectbox{
  position:relative;
  display:inline-block;
  padding:2px 6px;
  margin-left:-6px;
  outline:1.4px solid var(--accent);
}
.selectbox .h{
  position:absolute;
  width:7px;height:7px;
  background:#fff;
  border:1.4px solid var(--accent);
}
.selectbox .tl{top:-4px;left:-4px}
.selectbox .tr{top:-4px;right:-4px}
.selectbox .bl{bottom:-4px;left:-4px}
.selectbox .br{bottom:-4px;right:-4px}

.lead{margin-top:34px}
.lead p{
  margin:0 0 2px;
  font-size:18px;
  line-height:1.5;
  color:var(--body);
}
.lead a{color:var(--muted);text-decoration:underline;text-underline-offset:3px;transition:color .18s ease}
.lead a:hover{color:var(--accent-ink)}
.lead strong{color:var(--ink);font-weight:600}

/* ============ section head ============ */
.sechead{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:20px;
  margin-bottom:26px;
}
.sub{
  margin:10px 0 26px;
  font-size:15px;
  line-height:1.5;
  color:var(--body);
  max-width:1000px;
}
.toggle{
  display:flex;gap:4px;padding:4px;
  border-radius:12px;
  background:rgba(0,0,0,.04);
}
.tg{
  display:grid;place-items:center;
  width:36px;height:32px;
  border:0;border-radius:9px;
  background:transparent;color:rgba(0,0,0,.4);
  cursor:pointer;transition:all .18s ease;
}
.tg:hover{color:var(--ink)}
.tg.is-on{background:#fff;color:var(--ink);box-shadow:0 1px 3px rgba(0,0,0,.12)}

/* ============ grids ============ */
.grid{
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--gap);
}

/* ============ work cards ============ */
.card{
  border:1px solid var(--line);
  border-radius:var(--radius-card);
  background:#fff;
  padding:14px;
  transition:box-shadow .22s ease,transform .22s ease;
}
.card:hover{box-shadow:0 4px 14px rgba(0,0,0,.06),0 18px 44px rgba(0,0,0,.06);transform:translateY(-2px)}
.thumb{
  border-radius:10px;
  background:var(--tint,#eee);
  aspect-ratio:4/3;
  display:grid;place-items:center;
  padding:34px;
  overflow:hidden;
}
.shot{
  width:100%;height:100%;
  border-radius:6px;
  overflow:hidden;
  background:#fff;
  box-shadow:0 2px 6px rgba(0,0,0,.10),0 14px 34px rgba(0,0,0,.12);
  display:grid;place-items:center;
}
.shot img{width:100%;height:100%;object-fit:cover;object-position:top center}
.shot .ph{display:none}
.shot.empty img{display:none}
.shot.empty .ph{
  display:block;
  font-family:var(--serif);
  font-weight:300;
  font-size:26px;
  letter-spacing:-.05em;
  color:rgba(0,0,0,.26);
}
.card-head{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:14px;
  flex-wrap:wrap;
  margin:16px 2px 0;
}
.card-head h3{
  margin:0;
  font-size:20px;
  font-weight:600;
  letter-spacing:-.02em;
  color:var(--ink);
}
.card-desc{
  margin:10px 2px 2px;
  font-size:16px;
  line-height:1.35;
  color:var(--muted);
}

/* ============ tags ============ */
.tags{display:flex;gap:8px;flex-wrap:wrap}
.tag{
  display:inline-flex;
  align-items:center;
  gap:6px;
  padding:6px 10px;
  border:1px solid var(--line);
  border-radius:8px;
  font-size:12px;
  line-height:1.4;
  color:var(--body);
  white-space:nowrap;
}
.tag svg{opacity:.5;flex:none}
.tags.on-img .tag{
  border-color:rgba(255,255,255,.28);
  background:rgba(255,255,255,.08);
  backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);
  color:rgba(255,255,255,.85);
}
.tags.on-img .tag svg{opacity:.7}

/* ============ project cards ============ */
.pcard{
  border:1px solid var(--line);
  border-radius:var(--radius-card);
  background:#fff;
  padding:7px 7px 0;
  transition:box-shadow .22s ease,transform .22s ease;
}
.pcard:hover{box-shadow:0 4px 14px rgba(0,0,0,.06),0 18px 44px rgba(0,0,0,.06);transform:translateY(-2px)}
.pimg{
  position:relative;
  border-radius:10px;
  overflow:hidden;
  aspect-ratio:4/3;
  background:var(--tint,#ddd);
  display:grid;place-items:center;
}
.pimg img{width:100%;height:100%;object-fit:cover}
.pimg .ph{display:none}
.pimg.empty img{display:none}
.pimg.empty .ph{
  display:block;
  font-family:var(--serif);
  font-weight:300;
  font-size:34px;
  letter-spacing:-.05em;
  color:rgba(0,0,0,.22);
}
.scrim{
  position:absolute;inset:auto 0 0 0;
  height:56%;
  background:linear-gradient(to top,rgba(0,0,0,.82),rgba(0,0,0,.45) 42%,transparent);
  pointer-events:none;
}
.pimg .tags{position:absolute;left:14px;right:14px;bottom:14px}
.pdesc{
  margin:14px 8px 16px;
  font-size:15px;
  line-height:1.45;
  color:var(--body);
}
.pdesc em{font-style:italic;font-weight:600;color:var(--ink)}

/* ============ about ============ */
.about-inner{padding-top:70px;padding-bottom:80px}
.about-body{max-width:680px;margin-top:22px}
.about-body p{margin:0 0 16px;font-size:18px;line-height:1.55;color:var(--body)}
.about-meta{font-size:15px !important;color:var(--muted) !important}
.about-cta{display:flex;gap:12px;flex-wrap:wrap;margin-top:26px}
.btn-dark.lg,.btn-ghost.lg{padding:14px 24px;font-size:15px;border-radius:12px}
.btn-ghost{
  border:1px solid var(--line);
  background:#fff;
  color:var(--ink);
  text-decoration:none;
  transition:background .18s ease,transform .18s ease;
}
.btn-ghost:hover{background:#f4f4f4;transform:translateY(-1px)}

/* ============ footer ============ */
.foot{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:20px;
  flex-wrap:wrap;
  max-width:var(--maxw);
  margin:0 auto;
  padding:34px 44px 46px;
}
.foot-link,.foot-nav a{
  font-size:14px;
  color:rgba(255,255,255,.6);
  text-decoration:none;
  transition:color .18s ease;
}
.foot-link:hover,.foot-nav a:hover{color:#fff}
.foot-nav{display:flex;gap:22px;flex-wrap:wrap}

/* ============ list view ============ */
.work-grid.is-list{grid-template-columns:1fr;gap:0}
.work-grid.is-list .card{
  display:grid;
  grid-template-columns:132px 1fr;
  align-items:center;
  gap:22px;
  border:0;
  border-bottom:1px solid var(--line);
  border-radius:0;
  padding:18px 6px;
}
.work-grid.is-list .card:hover{transform:none;box-shadow:none;background:rgba(0,0,0,.015)}
.work-grid.is-list .thumb{aspect-ratio:16/10;padding:12px}
.work-grid.is-list .shot.empty .ph{font-size:14px}
.work-grid.is-list .card-head{margin:0}
.work-grid.is-list .card-desc{grid-column:2;margin:8px 2px 0;font-size:15px}

/* ============ responsive ============ */
@media (max-width:1000px){
  :root{--pad:24px;--gap:16px}
  .grid{grid-template-columns:1fr}
  .nav{width:calc(100% - 28px);top:14px}
  .btn-dark{padding:10px 16px}
}
@media (max-width:640px){
  .navpill{display:none}
  .nav{justify-content:space-between}
  .hero-inner{padding:100px 4px}
  .eyebrow{font-size:18px}
  .lead p{font-size:16px}
  .card-head{align-items:flex-start;flex-direction:column;gap:10px}
  .work-grid.is-list .card{grid-template-columns:1fr}
  .work-grid.is-list .card-desc{grid-column:1}
  .foot{padding:28px 20px 36px}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{transition:none !important}
}

/* active nav link */
.navpill a.is-active{background:rgba(0,0,0,.06);color:var(--ink)}

/* cards are links */
a.card,a.pcard{display:block;text-decoration:none;color:inherit}

/* ============ motion ============ */
@keyframes riseIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
@keyframes popIn{from{opacity:0;transform:scale(.4)}to{opacity:1;transform:scale(1)}}

/* hero load sequence */
.hero-inner .eyebrow,
.hero-inner .selectbox,
.hero-inner .lead p{opacity:0;animation:riseIn .7s cubic-bezier(.2,.7,.3,1) forwards}
.hero-inner .eyebrow{animation-delay:.10s}
.hero-inner .selectbox{animation-delay:.20s}
.hero-inner .lead p:nth-child(1){animation-delay:.34s}
.hero-inner .lead p:nth-child(2){animation-delay:.42s}
.hero-inner .lead p:nth-child(3){animation-delay:.50s}

.selectbox .h{opacity:0;animation:popIn .3s cubic-bezier(.3,1.4,.5,1) forwards}
.selectbox .tl{animation-delay:.62s}
.selectbox .tr{animation-delay:.68s}
.selectbox .bl{animation-delay:.74s}
.selectbox .br{animation-delay:.80s}

.nav{opacity:0;animation:riseIn .6s ease .05s forwards}

/* scroll reveal */
[data-reveal]{opacity:0;transform:translateY(50px);transition:opacity .4s ease-out,transform .55s cubic-bezier(.2,.7,.3,1)}
[data-reveal].in{opacity:1;transform:none}
[data-reveal].d1{transition-delay:.08s}
[data-reveal].d2{transition-delay:.16s}
[data-reveal].d3{transition-delay:.24s}

/* richer hovers */
.thumb,.pimg{overflow:hidden}
.shot img,.pimg img{transition:transform .6s cubic-bezier(.2,.7,.3,1)}
a.card:hover .shot img,a.pcard:hover .pimg img{transform:scale(1.04)}
a.card:hover h3{color:var(--accent-ink)}
.card-head h3{transition:color .25s ease}
.tg svg{transition:transform .3s ease}
.tg:hover svg{transform:scale(1.12)}
.btn-dark,.btn-ghost{position:relative;overflow:hidden}
.logo{transition:transform .3s cubic-bezier(.3,1.4,.5,1)}
.logo:hover{transform:translateY(-2px) rotate(-4deg)}

@media (prefers-reduced-motion:reduce){
  .hero-inner .eyebrow,.hero-inner .selectbox,.hero-inner .lead p,.nav,.selectbox .h{animation:none !important;opacity:1 !important;transform:none !important}
  [data-reveal]{opacity:1 !important;transform:none !important;transition:none !important}
}

/* ============ identity details ============ */

/* section index numerals */
.secidx{
  font-family:var(--mono);
  font-size:13px;
  letter-spacing:.08em;
  color:var(--accent-mid);
  margin:0 0 8px;
}
.sechead-l{min-width:0}

@media (prefers-reduced-motion:reduce){
}

/* ============ stack section (skills + tools) ============ */
.stackwrap{display:grid;grid-template-columns:1fr 1fr;gap:44px;margin-top:26px}
.stackwrap h3{
  font-family:var(--mono);
  font-size:13px;font-weight:400;
  letter-spacing:.02em;
  color:rgba(0,0,0,.4);
  margin:0 0 16px;
}
.stack{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.sitem{
  display:flex;flex-direction:column;gap:2px;
  padding:16px 18px;
  border:1px solid var(--line);
  border-radius:var(--radius-card);
  background:#fff;
  transition:border-color .18s ease,transform .18s ease;
}
.sitem:hover{border-color:rgba(6,188,193,.5);transform:translateY(-1px)}
.sitem .n{font-size:15px;font-weight:500;color:var(--ink)}
.sitem .d{font-size:13px;color:var(--muted)}
@media (max-width:900px){
  .stackwrap{grid-template-columns:1fr;gap:34px}
  .stack{grid-template-columns:1fr 1fr}
}

/* ============ about portrait ============ */
.about-grid{display:grid;grid-template-columns:1fr 300px;gap:56px;align-items:start}
.portrait{
  border-radius:var(--radius-card);
  overflow:hidden;
  background:#f0f0f0;
  aspect-ratio:4/5;
}
.portrait img{width:100%;height:100%;object-fit:cover}
@media (max-width:900px){
  .about-grid{grid-template-columns:1fr;gap:30px}
  .portrait{max-width:240px;aspect-ratio:1/1}
}

/* ============ big media cards ============ */
.grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:24px}

a.card{display:block;text-decoration:none;color:inherit}
.card{padding:0;border:0;background:transparent;border-radius:0}
.card:hover{transform:none;box-shadow:none}

.media{
  position:relative;
  aspect-ratio:1.84;
  border-radius:44px;
  overflow:hidden;
  background:var(--tint,#eee);
  isolation:isolate;
}
.media img,.media video{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  transition:transform .9s cubic-bezier(.2,.7,.3,1);
}
.media img{z-index:1}
.media video{z-index:2}
a.card:hover .media img,a.card:hover .media video{transform:scale(1.035)}

.media .ph{
  position:absolute;inset:0;z-index:0;
  display:grid;place-items:center;
  font-family:var(--serif);font-weight:300;
  font-size:34px;letter-spacing:-.04em;
  color:rgba(0,0,0,.24);
}

.pill{
  position:absolute;left:18px;bottom:18px;z-index:4;
  display:inline-flex;align-items:baseline;gap:8px;
  background:#fff;
  border-radius:999px;
  padding:7px 15px;
  font-size:15px;line-height:1.3;
  box-shadow:0 1px 3px rgba(0,0,0,.12),0 4px 14px rgba(0,0,0,.08);
}
.pill b{font-weight:500;color:var(--ink)}
.pill i{font-style:normal;color:var(--muted)}

.cdesc{
  margin:16px 6px 0;
  font-size:16px;line-height:1.45;
  color:var(--muted);
  max-width:58ch;
}

/* list view: one big card per row */
.work-grid.is-list{grid-template-columns:1fr;gap:28px}
.work-grid.is-list .card{display:block;border:0;border-radius:0;padding:0;background:transparent}
.work-grid.is-list .card:hover{background:transparent}
.work-grid.is-list .media{aspect-ratio:2.4}

@media (max-width:1000px){
  .grid{grid-template-columns:1fr;gap:20px}
  .media{border-radius:32px}
  .pill{left:14px;bottom:14px;font-size:14px;padding:6px 13px}
}
@media (max-width:640px){
  .media{border-radius:24px;aspect-ratio:1.6}
  .cdesc{font-size:15px;margin-top:13px}
}

/* description reveals on hover, like the reference. space is reserved so
   nothing reflows when it appears. */
.cdesc{
  opacity:0;
  transform:translateY(-4px);
  transition:opacity .32s ease,transform .32s ease;
  min-height:2.9em;
}
a.card:hover .cdesc,
a.card:focus-visible .cdesc{opacity:1;transform:none}

/* touch devices have no hover, so always show it */
@media (hover:none){
  .cdesc{opacity:1;transform:none;min-height:0}
}
@media (prefers-reduced-motion:reduce){
  .cdesc{transition:none}
}

/* cover at rest, video on hover.
   the covers are the clean, consistent set; the clips are the life. */
.media img{z-index:2;opacity:1}
.media video{
  z-index:3;
  opacity:0;
  transition:opacity .45s ease,transform .9s cubic-bezier(.2,.7,.3,1);
}
a.card:hover .media video,
a.card:focus-visible .media video{opacity:1}

@media (hover:none){
  .media video{display:none}
}

/* reference geometry: 644x349 cards, 24px column gap, 399px row pitch */
.grid{gap:14px 24px}
.cdesc{margin:12px 6px 0;min-height:2.3em;font-size:15px}

/* ============ project cards ============
   format taken from the reference she sent: two per row, media at 1.50 on top,
   then a solid footer on the same surface carrying the name, the year tag, the
   description and a corner arrow. the description no longer hides until hover,
   because in the reference it is always on. the scene art was re-rendered at
   1600x1067 to match 1.50, so nothing crops into the wordmark. */
.grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:44px 42px}

a.card{
  display:block;
  background:#fff;
  border:1px solid var(--line);
  border-radius:24px;
  overflow:hidden;
  transition:transform .24s ease,box-shadow .24s ease,border-color .24s ease;
}
a.card:hover{
  transform:translateY(-3px);
  border-color:#e4e4e4;
  box-shadow:0 6px 18px rgba(0,0,0,.05),0 22px 52px rgba(0,0,0,.07);
}

.media{aspect-ratio:1.5;border-radius:0}
.media .ph{font-size:40px}

.cmeta{padding:26px 28px 30px}
.chead{display:flex;align-items:center;gap:14px}
.chead h3{
  margin:0;
  font-family:var(--serif);font-weight:300;
  font-size:38px;line-height:1.06;letter-spacing:-.05em;
  color:var(--ink);
  transition:color .25s ease;
}
a.card:hover .chead h3{color:var(--accent-ink)}
.tag{
  font-family:var(--mono);
  font-size:12px;line-height:1;letter-spacing:.06em;
  color:var(--muted);
  border:1px solid var(--line);
  border-radius:999px;
  padding:7px 12px 6px;
  white-space:nowrap;
}
.chead .go{
  margin-left:auto;
  flex:none;
  color:rgba(0,0,0,.32);
  transition:color .25s ease,transform .25s ease;
}
a.card:hover .go{color:var(--accent-ink);transform:translate(2px,-2px)}

.cdesc{
  margin:14px 0 0;
  font-size:16px;line-height:1.5;
  color:var(--muted);
  max-width:52ch;
  opacity:1;transform:none;min-height:0;
}

/* list view: one per row, media beside the text instead of above it.
   aspect stays 1.50 so the same art is used uncropped. */
.work-grid.is-list{grid-template-columns:1fr;gap:24px}
.work-grid.is-list .card{
  display:grid;grid-template-columns:1.32fr 1fr;align-items:center;
  background:#fff;border:1px solid var(--line);border-radius:24px;
}
.work-grid.is-list .card:hover{background:#fff;border-color:#e4e4e4}
.work-grid.is-list .media{aspect-ratio:1.5}
.work-grid.is-list .cmeta{padding:36px 44px}
.work-grid.is-list .chead h3{font-size:44px}
.work-grid.is-list .cdesc{font-size:18px;max-width:42ch;margin-top:18px}

@media (max-width:1000px){
  .grid{grid-template-columns:1fr;gap:28px}
  .work-grid.is-list .card{grid-template-columns:1fr}
  .work-grid.is-list .cmeta{padding:26px 28px 30px}
  .work-grid.is-list .chead h3{font-size:38px}
  .work-grid.is-list .cdesc{font-size:16px;margin-top:14px}
  a.card,.work-grid.is-list .card{border-radius:20px}
}
@media (max-width:640px){
  .cmeta{padding:22px 22px 26px}
  .chead h3,.work-grid.is-list .chead h3{font-size:32px}
  .cdesc,.work-grid.is-list .cdesc{font-size:15px}
}

/* ============ motion layer ============
   additive. every rule here is new or a deliberate override of one above it.
   transform and opacity only, no layout-animating properties, no libraries,
   and all of it is switched off under prefers-reduced-motion at the bottom. */

/* --- cross-document transitions, home to case page and back --- */
@view-transition{navigation:auto}
@keyframes vtOut{to{opacity:0;transform:translateY(-10px)}}
@keyframes vtIn{from{opacity:0;transform:translateY(18px)}}
::view-transition-old(root){animation:vtOut .26s cubic-bezier(.4,0,1,1) both}
::view-transition-new(root){animation:vtIn .46s cubic-bezier(.2,.75,.25,1) both}

/* --- scroll progress hairline --- */
.sprog{
  position:fixed;top:0;left:0;right:0;height:2px;z-index:60;
  pointer-events:none;
  background:linear-gradient(90deg,var(--accent-ink),var(--accent));
  transform:scaleX(0);transform-origin:0 50%;
}
@keyframes sprogGrow{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@supports (animation-timeline:scroll()){
  .sprog{animation:sprogGrow linear both;animation-timeline:scroll(root block)}
}

/* --- hero: the name rises word by word out of a mask --- */
.hero-inner .selectbox{opacity:1;animation:none}
.selectbox .wm{
  display:inline-block;overflow:hidden;vertical-align:bottom;
  padding-bottom:.14em;margin-bottom:-.14em;
}
.selectbox .wm>span{display:inline-block;transform:translateY(112%);animation:wordRise 1s cubic-bezier(.2,.75,.25,1) both}
.selectbox .wm:nth-of-type(1)>span{animation-delay:.16s}
.selectbox .wm:nth-of-type(2)>span{animation-delay:.28s}
@keyframes wordRise{to{transform:none}}
.selectbox .tl{animation-delay:.92s}
.selectbox .tr{animation-delay:.98s}
.selectbox .bl{animation-delay:1.04s}
.selectbox .br{animation-delay:1.10s}

/* --- lead link: the rule thickens and takes the accent --- */
.lead a{
  text-decoration:none;
  background-image:linear-gradient(currentColor,currentColor);
  background-size:100% 1px;background-position:0 100%;background-repeat:no-repeat;
  transition:color .22s ease,background-size .35s cubic-bezier(.2,.75,.25,1);
}
.lead a:hover{background-size:100% 2px}

/* --- section headings rise out of a mask instead of sliding as a block --- */
[data-reveal].mrev{opacity:1;transform:none;transition:none}
.mask{display:block;overflow:hidden;padding-bottom:.1em;margin-bottom:-.1em}
.mask>span{display:block;transform:translateY(112%);transition:transform .9s cubic-bezier(.2,.75,.25,1)}
.mrev.in .mask>span{transform:none}

/* --- work cards: tilt toward the pointer, art counter-moves --- */
.cardwrap{display:grid}
.cardwrap[data-reveal]{transform:translateY(46px) scale(.985)}
.cardwrap[data-reveal].in{transform:none}
a.card{
  height:100%;
  --ty:0px;
  transform:perspective(1100px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translateY(var(--ty));
  transition:transform .5s cubic-bezier(.2,.75,.25,1),box-shadow .24s ease,border-color .24s ease;
}
a.card:hover{--ty:-4px}
a.card.tilt{transition:transform .14s ease-out,box-shadow .24s ease,border-color .24s ease}
.media img,.media video{transform:translate3d(var(--px,0px),var(--py,0px),0)}
a.card:hover .media img,
a.card:hover .media video{transform:scale(1.05) translate3d(var(--px,0px),var(--py,0px),0)}

/* --- a label that rides the pointer while it is over a card --- */
.viewbub{
  position:fixed;top:0;left:0;z-index:65;pointer-events:none;
  transform:translate3d(var(--bx,0px),var(--by,0px),0);
  opacity:0;transition:opacity .22s ease;
}
.viewbub.on{opacity:1}
.viewbub span{
  display:flex;align-items:center;gap:8px;
  transform:translate(-50%,-50%) scale(var(--bs,.55));
  transition:transform .34s cubic-bezier(.3,1.2,.4,1);
  background:var(--accent-ink);color:#fff;
  font-family:var(--mono);font-size:11px;letter-spacing:.1em;
  padding:13px 17px;border-radius:999px;white-space:nowrap;
  box-shadow:0 6px 22px rgba(0,0,0,.2);
}
.viewbub.on span{--bs:1}
@media (pointer:coarse){.viewbub{display:none}}

/* the wand. It flicks up as the label lands, then the sparkles keep a slow
   twinkle going while you are over a card. Both are decorative, and the whole
   bubble is already hidden under prefers-reduced-motion. */
.viewbub svg{display:block;flex:0 0 auto;overflow:visible;transform-origin:60% 60%}
.viewbub.on svg{animation:wandFlick .55s cubic-bezier(.3,1.35,.4,1) both}
@keyframes wandFlick{
  0%{transform:rotate(-22deg) scale(.65)}
  58%{transform:rotate(7deg) scale(1.08)}
  100%{transform:rotate(0) scale(1)}
}
.viewbub svg .sp{transform-box:fill-box;transform-origin:center}
.viewbub.on svg .sp{animation:sparkle 1.9s ease-in-out .5s infinite}
.viewbub.on svg .sp2{animation-delay:.85s}
.viewbub.on svg .sp3{animation-delay:1.3s}
@keyframes sparkle{
  0%,100%{opacity:1;transform:scale(1)}
  45%{opacity:.45;transform:scale(.78)}
}

/* --- magnetic controls --- */
[data-mag]{transform:translate3d(var(--mx,0px),var(--my,0px),0)}
.btn-dark[data-mag]:hover{transform:translate3d(var(--mx,0px),calc(var(--my,0px) - 1px),0)}

/* --- nav: one highlight that slides between links --- */
.navpill{position:relative}
.navpill::before{
  content:"";position:absolute;top:6px;left:0;z-index:0;
  height:calc(100% - 12px);width:var(--nw,0px);
  transform:translate3d(var(--nx,0px),0,0);
  background:rgba(0,0,0,.06);border-radius:999px;
  opacity:var(--no,0);
  transition:transform .42s cubic-bezier(.2,.75,.25,1),width .42s cubic-bezier(.2,.75,.25,1),opacity .25s ease;
}
.navpill a{position:relative;z-index:1}
.navpill a:hover,.navpill a.is-active{background:transparent}

/* --- the grid/list swap animates instead of cutting --- */
@keyframes swapIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
.work-grid.swapping .cardwrap{animation:swapIn .5s cubic-bezier(.2,.75,.25,1) both}
.work-grid.swapping .cardwrap:nth-child(2){animation-delay:.05s}
.work-grid.swapping .cardwrap:nth-child(3){animation-delay:.1s}
.work-grid.swapping .cardwrap:nth-child(4){animation-delay:.15s}
.tg svg{transition:transform .3s cubic-bezier(.3,1.3,.4,1)}
.tg.is-on svg{transform:scale(1.08)}

/* --- tag, about, footer details --- */
.tag{transition:border-color .25s ease,color .25s ease,background .25s ease}
a.card:hover .tag{border-color:var(--accent);color:var(--accent-ink)}
.portrait{overflow:hidden}
.portrait img{transition:transform 1s cubic-bezier(.2,.7,.3,1)}
.portrait:hover img{transform:scale(1.05)}
.foot-link,.foot-nav a{position:relative}
.foot-link::after,.foot-nav a::after{
  content:"";position:absolute;left:0;right:0;bottom:-3px;height:1px;
  background:currentColor;transform:scaleX(0);transform-origin:100% 50%;
  transition:transform .38s cubic-bezier(.2,.75,.25,1);
}
.foot-link:hover::after,.foot-nav a:hover::after{transform:scaleX(1);transform-origin:0 50%}

@media (prefers-reduced-motion:reduce){
  .selectbox .wm>span,
  .work-grid.swapping .cardwrap{animation:none !important;transform:none !important;opacity:1 !important}
  .mask>span,.cardwrap[data-reveal]{transform:none !important;transition:none !important}
  a.card,a.card:hover,.media img,.media video,[data-mag]{transform:none !important}
  .sprog,.viewbub,.navpill::before{display:none !important}
  ::view-transition-old(root),::view-transition-new(root){animation:none !important}
}

/* Chrome drops a parent's rounded-corner clip on a composited child once the
   parent carries a 3D transform, so the tilt above cost the cards their top
   corners. The media now carries its own radius instead of leaning on the
   card's overflow. Inner radius is the card's 24 minus its 1px border. */
a.card .media{border-radius:23px 23px 0 0}
.work-grid.is-list .card .media{border-radius:23px 0 0 23px}
@media (max-width:1000px){
  a.card .media,
  .work-grid.is-list .card .media{border-radius:19px 19px 0 0}
}

/* ============ hero: name without the selection box ============
   she asked for the outline off. the word masks that drive the entrance move
   with it, so they are re-scoped here rather than left on the dead class. */
.heroname{position:relative;display:inline-block}
.hero-inner .heroname{opacity:1;animation:none}
.heroname .wm{
  display:inline-block;overflow:hidden;vertical-align:bottom;
  padding-bottom:.14em;margin-bottom:-.14em;
}
.heroname .wm>span{display:inline-block;transform:translateY(112%);animation:wordRise 1s cubic-bezier(.2,.75,.25,1) both}
.heroname .wm:nth-of-type(1)>span{animation-delay:.16s}
.heroname .wm:nth-of-type(2)>span{animation-delay:.28s}


/* ============ first-open loader ============
   Shown once per session, on the first page opened. It is gated by a class on
   <html> that an inline script in the head sets, so with JS off or with
   reduced motion asked for, the markup below never displays at all and the
   site paints normally. While it is up, the hero entrance is held paused so
   the name and the glass do not play to an empty room. */
.loader{display:none}
html.is-loading{overflow:hidden}
html.is-loading .loader{display:grid}
html.is-loading .hero-inner .eyebrow,
html.is-loading .heroname .wm>span,
html.is-loading .hero-inner .lead p,
html.is-loading .nav{animation-play-state:paused}

.loader{
  position:fixed;inset:0;z-index:999;
  place-items:center;
  background:var(--panel);
  background-image:linear-gradient(rgba(0,0,0,.038) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(0,0,0,.038) 1px,transparent 1px);
  background-size:88px 88px;
  will-change:transform;
}
.loader.out{
  transform:translateY(-101%);
  transition:transform .52s cubic-bezier(.76,0,.24,1);   /* was .82s */
}
.loader-in{
  display:grid;
  grid-template-columns:1fr auto;
  align-items:end;
  gap:20px 14px;
  width:min(340px,72vw);
}
.loader-mark{
  grid-column:1 / -1;
  font-family:var(--serif);font-weight:600;
  font-size:46px;line-height:1;letter-spacing:-.06em;
  color:var(--accent);
  animation:markIn .7s cubic-bezier(.2,.75,.25,1) both;
}
@keyframes markIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.loader-meter{
  position:relative;height:1px;
  background:var(--line);
  overflow:hidden;
}
.loader-meter i{
  position:absolute;inset:0;
  transform-origin:0 50%;
  transform:scaleX(var(--p,0));
  background:linear-gradient(90deg,var(--accent-ink),var(--accent));
}
.loader-num{
  font-family:var(--mono);font-size:11px;line-height:1;
  letter-spacing:.1em;color:var(--muted);
  font-variant-numeric:tabular-nums;
}

/* ═══════════════════════════════════════════════════════════════════════════
   lalau.ca pass — 2026-08-31
   Her call: keep the light identity and Fraunces, take the interactions.
   Four things the reference had that this site did not. The hover scale she
   admired was already here (a.card:hover .media img), so it is untouched.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. hero film — REMOVED 2026-09-01 on her note ("get rid of the killshot
   demo you put next to my name"). The hero is type on the ground again, so the
   left-shift that existed only to clear the film went with it. */

/* ── 2. the job title sweeps, as the reference's does ────────────────────────
   Their ramp is a rainbow. This one is built from her own aqua ramp so it
   stays her brand. The lightest stop is brief and the sweep sits mostly on
   --accent-mid / --accent-ink, which keeps 22px/500 legible. */
.eyebrow .grad {
  background-image: linear-gradient(90deg,
    #00767f 0%, #009aa6 18%, #06bcc1 34%, #009aa6 52%, #00767f 70%, #009aa6 86%, #06bcc1 100%);
  background-size: 300% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: eyebrowSweep 6s linear infinite;
}
@keyframes eyebrowSweep { from { background-position: 0 0; } to { background-position: 100% 0; } }

/* ── 3. work grid: alternating two-thirds cover, one-third text ──────────────
   The reference's signature layout. Card chrome comes off in this mode: there
   the cover IS the object and the text sits beside it on the page ground.
   List view is untouched, so the toggle still works. */
.work-grid:not(.is-list) { grid-template-columns: 1fr; gap: 132px; }
.work-grid:not(.is-list) .cardwrap { display: block; }

.work-grid:not(.is-list) a.card {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 52px;
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
  transform: none;                 /* no pointer-tilt without a card to tilt */
}
.work-grid:not(.is-list) a.card:hover { transform: none; box-shadow: none; }

.work-grid:not(.is-list) .cardwrap:nth-child(odd)  .media { grid-column: 1 / 3; }
.work-grid:not(.is-list) .cardwrap:nth-child(odd)  .cmeta { grid-column: 3 / 4; grid-row: 1; }
.work-grid:not(.is-list) .cardwrap:nth-child(even) .media { grid-column: 2 / 4; }
.work-grid:not(.is-list) .cardwrap:nth-child(even) .cmeta { grid-column: 1 / 2; grid-row: 1; }

.work-grid:not(.is-list) .media {
  aspect-ratio: 3 / 2;
  border-radius: 24px;
  border: 1px solid var(--line);
}
.work-grid:not(.is-list) .cmeta { padding: 0; }
.work-grid:not(.is-list) .chead h3 { font-size: 38px; }
.work-grid:not(.is-list) .cdesc { font-size: 17px; line-height: 1.5; margin-top: 14px; }

/* ── 4. the header gets out of the way going down, returns coming up ─────────
   Moves the nav's children, not .nav itself: .nav carries the riseIn entrance
   animation and an animation's fill would win over a transform set here. */
.nav > * { transition: transform .38s cubic-bezier(.2,.75,.25,1), opacity .26s ease; }
.nav.is-hidden > * { transform: translateY(-190%); opacity: 0; }

@media (max-width: 900px) {
  .work-grid:not(.is-list) { gap: 76px; }
  .work-grid:not(.is-list) a.card { grid-template-columns: 1fr; gap: 22px; }
  .work-grid:not(.is-list) .cardwrap:nth-child(n) .media,
  .work-grid:not(.is-list) .cardwrap:nth-child(n) .cmeta { grid-column: 1 / -1; grid-row: auto; }
  .work-grid:not(.is-list) .chead h3 { font-size: 30px; }
}

@media (prefers-reduced-motion: reduce) {
  .eyebrow .grad { animation: none; background-position: 0 0; }
  .nav > * { transition: none; }
  .nav.is-hidden > * { transform: none; opacity: 1; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   DARK THEME — 2026-09-01, her call: "make my portfolio black like lalau.ca,
   but keep my design."

   Nothing structural moves. This only re-points the tokens and overrides the
   places where a light value was hardcoded rather than tokenised.

   The accent ramp has to INVERT. Hers was built for contrast on white, so the
   darkest step (#00767f) was the readable one. On black that flips: the light
   steps carry text and the dark step becomes decoration.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{
  --ground:#000000;          /* page behind the panels */
  --panel:#0c0c0e;           /* the panels themselves */
  --ink:#ffffff;
  --body:#c9c9cd;
  --muted:rgba(255,255,255,.60);
  --line:rgba(255,255,255,.11);   /* lalau's tonal border value */
  --accent:#06bcc1;               /* decorative only */
  --accent-mid:#3ddfe2;           /* large accent text */
  --accent-ink:#83eff0;           /* links and small accent text */
}

/* graph paper: the light grid is invisible on black, so it flips to a white one */
.panel,
.loader{
  background-image:linear-gradient(rgba(255,255,255,.045) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(255,255,255,.045) 1px,transparent 1px);
}

/* nav */
.navpill{
  background:rgba(255,255,255,.07);
  box-shadow:0 1px 2px rgba(0,0,0,.5),0 6px 20px rgba(0,0,0,.45);
}
.navpill a:hover{background:rgba(255,255,255,.09);color:var(--ink)}
.navpill a.is-active{background:rgba(255,255,255,.11);color:var(--ink)}
.navpill::before{background:rgba(255,255,255,.10)}

/* the dark pill has to become a light one to stay a button on black */
.btn-dark{
  background:#f2f2f4;color:#0b0b0d;
  box-shadow:0 1px 2px rgba(0,0,0,.5),0 6px 18px rgba(0,0,0,.4);
}
.btn-dark:hover{background:#ffffff}

/* work cards */
a.card{
  background:#111114;
  border:1px solid var(--line);
  box-shadow:none;
}
a.card:hover{
  border-color:rgba(255,255,255,.20);
  box-shadow:0 6px 18px rgba(0,0,0,.55),0 22px 52px rgba(0,0,0,.5);
}
/* in the alternating grid there is no card chrome, so keep it off */
.work-grid:not(.is-list) a.card{background:transparent;border:0;box-shadow:none}
.work-grid:not(.is-list) .media{border:1px solid var(--line)}
.work-grid.is-list .card{background:#111114;border-color:var(--line)}
.work-grid.is-list .card:hover{background:#141418;border-color:rgba(255,255,255,.2)}
a.card:hover .tag{border-color:var(--accent-mid);color:var(--accent-ink)}

/* view toggle */
.toggle{background:rgba(255,255,255,.06)}
.tg{color:rgba(255,255,255,.45)}
.tg.is-on{background:rgba(255,255,255,.14);color:var(--ink);box-shadow:none}

/* the selection-box handles around her name are white squares on white; invert */
.selectbox .h{background:#0c0c0e;border-color:var(--accent)}

/* pointer bubble */
.viewbub span{background:var(--accent-ink);color:#08181a}

/* loader */
.loader{background:var(--panel)}
.loader-meter{background:rgba(255,255,255,.14)}
.loader-meter i{background:var(--accent)}

/* the media placeholder letterform sat at 24% black */
.media .ph{color:rgba(255,255,255,.28)}

/* scroll progress bar */
.sprog{background:var(--accent)}

::selection{background:var(--accent);color:#04191a}

/* ── covers play when the project scrolls into view ──────────────────────────
   Was hover-only, which meant the clips never ran on a touch screen and never
   ran at all unless you happened to point at a card. `.playing` is set by the
   observer in script.js and cleared when the card leaves, so at most one or
   two decode at a time. Hover still reveals it early on a pointer device. */
.media video{opacity:0;transition:opacity .5s ease}
.media.playing video{opacity:1}
@media (hover:none){
  .media video{display:block}          /* was display:none, which killed it on mobile */
}
@media (prefers-reduced-motion: reduce){
  .media video{display:none}           /* respect the OS setting instead */
}

/* ── EXPERIMENTS ─────────────────────────────────────────────────────────────
   Geometry measured off ericwangdesign.com, not approximated: a 639px centred
   container, two 315px columns, 9px gutter, and tiles of uneven height so the
   two columns stagger. Each tile runs the real sketch at tile size; clicking it
   opens the same sketch wired to the pointer. */
.xwrap{ width:min(639px,calc(100% - 48px)); margin:0 auto; padding:84px 0 96px; }
.xlabel{
  margin:0 0 18px;
  font-size:16px; font-weight:400; letter-spacing:-.01em;
  color:var(--body);
}
.xgrid{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:9px;
  grid-auto-flow:dense;
}
.xtile{
  position:relative; display:block; overflow:hidden;
  border-radius:12px;
  background:#0b0b0d;
  border:1px solid var(--line);
  text-decoration:none;
  transition:border-color .3s ease, transform .4s cubic-bezier(.2,.75,.25,1);
}
.xtile canvas{ display:block; width:100%; height:100%; }
.xtile.paper{ background:#f4f1e8; }

/* the uneven heights are what make the two columns stagger, as on the reference */
.t-ink   { grid-column:1; height:324px; }   /* Type   */
.t-line  { grid-column:1; height:315px; }   /* Reaction */
.t-half  { grid-column:2; height:239px; }
.t-squish{ grid-column:2; height:400px; }   /* Flock  */

.xcap{
  position:absolute; left:12px; bottom:10px;
  font-family:var(--mono); font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  color:rgba(255,255,255,.5);
  opacity:0; transform:translateY(4px);
  transition:opacity .28s ease, transform .28s ease;
  pointer-events:none;
}
.xtile.paper .xcap{ color:rgba(0,0,0,.45); }
.xtile:hover{ border-color:rgba(255,255,255,.26); transform:translateY(-2px); }
.xtile:hover .xcap, .xtile:focus-visible .xcap{ opacity:1; transform:none; }

@media (max-width:640px){
  .xgrid{ grid-template-columns:1fr; }
  .t-ink,.t-line,.t-half,.t-squish{ grid-column:1; height:280px; }
}

/* ── dark theme, round two ───────────────────────────────────────────────────
   Same diff on this file. Two live rules still carried a light value. */
.btn-ghost{
  background:transparent;
  border:1px solid rgba(255,255,255,.22);
  color:var(--ink);
}
.btn-ghost:hover{ background:rgba(255,255,255,.07); border-color:rgba(255,255,255,.34); }

/* the arrow on each work card was a 32%-black glyph */
.chead .go{ color:rgba(255,255,255,.34); }

/* legacy empty-state letterforms, in case a cover ever fails to load */
.shot.empty .ph,
.pimg.empty .ph{ color:rgba(255,255,255,.26); }

/* ── pure black, her call ────────────────────────────────────────────────────
   The panels were #0c0c0e, a hair off black. Everything that reads as
   "background" is now #000; component surfaces keep only enough lift to stay
   legible as objects. */
:root{ --panel:#000000; }
a.card{ background:#08080a; }
.work-grid.is-list .card{ background:#08080a; }
.work-grid.is-list .card:hover{ background:#0d0d10; }
.xtile{ background:#000; }
.band-inner{ background:#000; }

/* ── hero flow field ─────────────────────────────────────────────────────────
   Sits behind the type, above the graph paper. Faded at the left so it never
   fights the name, and masked at the edges so it has no hard boundary. */
.heroflow{
  position:absolute; inset:0;
  width:100%; height:100%;
  z-index:1;
  pointer-events:none;
  opacity:.85;
  -webkit-mask-image:radial-gradient(120% 100% at 62% 50%, #000 30%, rgba(0,0,0,.35) 68%, transparent 100%);
          mask-image:radial-gradient(120% 100% at 62% 50%, #000 30%, rgba(0,0,0,.35) 68%, transparent 100%);
}
@media (prefers-reduced-motion: reduce){ .heroflow{ display:none; } }
@media (max-width:700px){ .heroflow{ opacity:.5; } }

/* "move to stir" — from the pasted spec: bottom 18px, centred, z-2,
   pointer-events-none, 12px, medium, uppercase, .08em tracking, white/80. */
.stirlabel{
  position:absolute; bottom:18px; left:50%; transform:translateX(-50%);
  z-index:2; pointer-events:none;
  font-size:12px; font-weight:500;
  text-transform:uppercase; letter-spacing:.08em;
  color:rgba(255,255,255,.8);
}
@media (prefers-reduced-motion: reduce){ .stirlabel{ display:none; } }

/* the hero mask was hiding most of the fluid; keep only a gentle left fade so
   the name stays readable and the field is actually visible */
.heroflow{
  opacity:1;
  -webkit-mask-image:linear-gradient(90deg, transparent 0%, rgba(0,0,0,.55) 26%, #000 58%);
          mask-image:linear-gradient(90deg, transparent 0%, rgba(0,0,0,.55) 26%, #000 58%);
}

/* ── experiments + about, corrections 2026-09-01 ──────────────────────────────
   Three fixes, all from her review:
     - the section is numbered 02 now, About moved to 03
     - the grid was two equal-height rows, not masonry, so the short Halftone
       tile left an 85px hole under itself. Real column masonry removes it.
     - it was 639px wide, matched off the reference; she wants it wider here. */
.xwrap{ width:min(1000px,calc(100% - 48px)); padding:78px 0 96px; }
.xhead{ margin:0 0 26px; }
.xlabel{ display:none; }

.xgrid{
  display:block;
  columns:2;
  column-gap:12px;
}
.xtile{
  break-inside:avoid;
  -webkit-column-break-inside:avoid;
  display:block;
  width:100%;
  margin:0 0 12px;
}
/* heights now only set the shape of each tile; masonry handles the packing */
.t-ink   { height:360px; }   /* Type     */
.t-half  { height:300px; }   /* Halftone */
.t-line  { height:392px; }   /* Reaction */
.t-squish{ height:452px; }   /* Flock    */

@media (max-width:640px){
  .xgrid{ columns:1; }
  .t-ink,.t-line,.t-half,.t-squish{ height:280px; }
}

/* ── about: the portrait is 1536x1024, a landscape frame. It was being forced
   into a 4/5 box and hard-cropped, and its top did not line up with the body
   copy because only the copy carried a 22px offset. */
.about-grid{ grid-template-columns:1fr 420px; gap:64px; align-items:start; }
.portrait{
  aspect-ratio:3/2;              /* the image's own ratio, so nothing is cut */
  background:#0d0d10;
  margin-top:22px;               /* matches .about-body, so the tops align */
}
.portrait img{ object-position:center }
@media (max-width:900px){
  .about-grid{ grid-template-columns:1fr; gap:30px; }
  .portrait{ max-width:420px; aspect-ratio:3/2; margin-top:4px; }
}
