/* Static illustrated woodland, independent rabbit; product images have no overlays. */
:root { --bg:#080e12; --ink:#eeeae1; --accent:#9faeb8; }
body { background:var(--bg); font-weight:400; }
::selection { background:#d5c4a2; color:#080e12; }
:focus-visible { outline:2px solid #d5c4a2; outline-offset:6px; }
.site-header { height:88px; padding-inline:max(5.2vw,calc((100vw - 1320px)/2)); gap:64px; }
.monogram { font-family:var(--sans); font-weight:300; letter-spacing:-4px; font-size:31px; padding-right:4px; }
.site-header nav { gap:30px; font-size:12px; }
.hero { height:680px; min-height:0; max-height:none; padding:132px max(5.2vw,calc((100vw - 1320px)/2)) 88px; background:#080e12; isolation:isolate; }
.hero-copy { width:58%; max-width:700px; }
.hero h1 { font-size:clamp(46px,4.8vw,72px); font-weight:600; line-height:1.1;
  letter-spacing:-.015em; margin-bottom:24px; text-wrap:balance; }
.hero h1 em { font-style:normal; font-weight:600; color:#b3c1cb; }
.eyebrow { font-size:10px; letter-spacing:.2em; }
.hero .eyebrow { font-size:11px; }
.hero .eyebrow b { font-size:19px; font-weight:500; letter-spacing:.05em; color:#eef2f4; }
.hero .eyebrow .zh { margin-left:11px; font-size:17px; letter-spacing:.04em; color:#c4ced5; }
.lead { max-width:480px; font-size:16px; line-height:1.75; color:#adb7bf; }
.actions { margin-top:29px; gap:12px; }
.button { border-radius:3px; min-width:166px; padding:13px 17px; font-size:12px; background:#080e1270; }
.button-light { background:#ece8de; }
.terminal { margin-top:29px; font-size:11px; color:#82949f; }
/* The resume download is the one clickable thing in the terminal line, so it
   carries a warm highlight instead of inheriting the muted grey. */
.hero .terminal a { color:#f2c552; font-weight:500; text-decoration:underline;
  text-decoration-color:#f2c55266; text-underline-offset:3px; transition:color .2s,text-decoration-color .2s; }
.hero .terminal a:hover { color:#ffd970; text-decoration-color:#ffd970; }
.wonderland { inset:auto max(5.2vw,calc((100vw - 1320px)/2)) 150px auto; width:38%; max-width:653px; height:54.7%; overflow:hidden; z-index:-1; opacity:1; }
/* <picture> is an inline box; display:contents removes it so the img keeps sizing
   against .wonderland / .tea-art rather than an auto-height wrapper. */
.wonderland picture,.tea-art picture { display:contents; }
.hero-artwork { display:block; width:100%; height:100%; object-fit:cover; object-position:center; }
.wonderland::after { content:""; position:absolute; inset:0; background:linear-gradient(90deg,#080e12,transparent 26%,transparent 84%,#080e12),linear-gradient(0deg,#080e12,transparent 17%,transparent 82%,#080e12); pointer-events:none; }
.section { padding-inline:0; }
.featured,.other-projects,.two-column { width:min(89.6%,1320px); margin-inline:auto; }
/* Featured project: the index rule spans both tracks so the copy and the
   screenshot read as one block instead of two left-flush slabs. */
.featured { padding-block:60px 58px; background:none; border:0;
  grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);
  grid-template-areas:"idx idx" "copy shot";
  column-gap:clamp(28px,3.2vw,52px); row-gap:26px;
  align-items:center; justify-content:stretch; }
.section-index { grid-area:idx; align-self:center; display:flex; align-items:center; gap:18px;
  margin:0; padding:0; font:400 13px/1 var(--mono); letter-spacing:.2em; color:#8ea1b1; }
.section-index::after { content:""; flex:1; height:1px; background:#26323a; }
.project-copy { grid-area:copy; }
.project-copy h2 { font-size:68px; font-weight:600; letter-spacing:-.015em; margin:0 0 14px; }
.project-copy>p { font-size:14px; line-height:1.8; max-width:52ch; }
.project-copy .subtitle { font-size:15px!important; line-height:1.55; padding-bottom:18px; margin-bottom:18px; }
.project-copy .terminal { font-size:11px; line-height:1.9; letter-spacing:.02em;
  color:#8b99a4; max-width:none; margin:20px 0 18px; padding:9px 12px;
  border-left:1px solid #39424a; }
.project-copy p b { color:#d5dbdf; font-weight:500; }
.tags { gap:7px; margin:24px 0 0; max-width:none; }
.tags li,.vertical-tags li { border:1px solid #253038; background:#111b21; border-radius:4px; padding:5px 9px; font-size:11px; color:#b8c2c8; }
.project-shot { grid-area:shot; margin:0; border-color:#3c4a54; border-radius:7px;
  box-shadow:0 22px 65px #0005; width:100%; max-width:none; justify-self:stretch; }
.window-bar { height:31px; background:#10171c; font-size:8px; letter-spacing:.12em; }
.window-bar b { font-weight:400; }
.project-shot .deck-card img { width:100%; height:auto; max-height:800px; object-fit:cover; object-position:top center; opacity:1; filter:none; }
/* ---- Image deck -------------------------------------------------------
   Cards share one grid cell, so the deck is as tall as its tallest card and
   nothing is absolutely positioned. --i is the card's depth in the stack (0 =
   front); script.js rewrites it on click to rotate the deck. With a single
   card every offset below evaluates to zero, so a one-image group renders
   exactly like a plain screenshot. */
.deck { --peek:32px; --count:1; position:relative; display:grid;
  padding-right:calc((var(--count) - 1) * var(--peek)); }
.deck-card { grid-area:1/1; display:block; position:relative;
  z-index:calc(20 - var(--i,0));
  transition:transform .5s cubic-bezier(.2,.72,.18,1); }
/* The fan-out is gated on .is-deck (set by script.js): without JS there is no
   --count to pad for, so an un-padded fan would just spill past the frame. */
.deck.is-deck .deck-card {
  transform:translateX(calc(var(--i,0) * var(--peek))) scale(calc(1 - var(--i,0) * .018)); }
/* Only a real stack gets an edge and a shadow; a lone card must stay pixel-for-pixel
   what a plain screenshot looked like before decks existed. */
.deck.is-deck .deck-card { border-radius:3px; overflow:hidden;
  outline:1px solid rgb(206 228 242 / calc(.22 + var(--i,0) * .15)); outline-offset:-1px;
  box-shadow:16px 0 30px -6px #000000ad; }
.deck-card:nth-child(1) { --i:0 } .deck-card:nth-child(2) { --i:1 }
.deck-card:nth-child(3) { --i:2 } .deck-card:nth-child(4) { --i:3 }
.deck-card:nth-child(5) { --i:4 } .deck-card:nth-child(6) { --i:5 }
/* Cards behind the front one darken with depth and take a pointer cue. */
.deck-card::after { content:""; position:absolute; inset:0; background:#070d11;
  opacity:calc(var(--i,0) * .3); transition:opacity .45s; pointer-events:none; }
.deck.is-deck .deck-card:not([data-front]) { cursor:pointer; }
.deck.is-deck .deck-card:not([data-front]):hover { transform:translateX(calc(var(--i,0) * var(--peek) + 9px)) scale(calc(1 - var(--i,0) * .018));
  outline-color:rgb(226 242 252 / .72);
  box-shadow:16px 0 30px -6px #000000ad, 0 0 22px -3px rgb(170 202 224 / .45); }
.deck.is-deck .deck-card:not([data-front]):hover::after { opacity:calc(var(--i,0) * .3 - .14); }
.deck-card img { display:block; }
/* Counter only appears once a group actually has more than one image. */
.deck-counter { position:absolute; z-index:30; left:10px; bottom:10px;
  padding:4px 8px; border-radius:3px; background:#070d11e0; border:1px solid #ffffff1f;
  color:#c3ced6; font:9px/1 var(--mono); letter-spacing:.14em; font-variant-numeric:tabular-nums;
  pointer-events:none; }
/* The deck's own rim outline outranks the global focus ring, so restore it here. */
.deck.is-deck .deck-card:focus-visible { outline:2px solid #d5c4a2; outline-offset:3px; }
.other-projects { padding-block:16px 48px; background:none; border:0; }
.section-heading { gap:25px; border:0; margin-bottom:21px; padding:0; }
.section-heading::after { content:""; height:1px; background:#26323a; flex:1; order:1; }
.project-grid { gap:27px; grid-template-columns:repeat(3,minmax(0,1fr)); }
.project-card { padding:19px; background:linear-gradient(135deg,#111b21,#0b1318); border-color:#28343c; border-radius:6px; display:flex; flex-direction:column; }
.card-heading { gap:17px; align-items:center; min-height:80px; }
.card-heading>span { font-size:33px; font-weight:500; color:#8fa6b6; border-right:1px solid #29363e; padding-right:16px; }
.card-heading h3 { font-family:var(--sans); font-size:19px; font-weight:500; }
/* Role chip: whether a project is mine or one I contribute to, said once,
   in the heading, instead of splitting the page into two sections. */
.card-heading h3 .role { display:inline-block; vertical-align:2px; margin-left:9px;
  padding:3px 7px; border:1px solid #4a4331; border-radius:3px; background:#161a17;
  font:400 9px/1 var(--mono); letter-spacing:.14em; text-transform:uppercase; color:#cbb98f; }
.card-heading h3 .role-own { border-color:#2b3740; background:#111b21; color:#9dacb6; }
.card-heading p { font-size:12px; color:#b5bec4; }
.card-media { display:flex; flex-direction:column; gap:12px; }
.card-media .deck-card img { width:100%; height:auto; max-height:none; object-fit:contain; border-radius:3px; opacity:1; filter:none; }
[data-deck="avatar"] .deck-card img { aspect-ratio:1429/795; }
.vertical-tags { flex-direction:row; flex-wrap:wrap; gap:6px; margin:0; }
/* Closed-source work still needs a resting line where a repo link would be. */
.project-copy .band-label { margin-top:22px; }
/* Every card ends with the same band: a micro-label, a short list, then either
   links or the repository's status. One auto margin pins the whole thing. */
.card-footnote { margin-top:auto; padding-top:14px; }
.band-label { display:flex; align-items:center; gap:9px; margin:0;
  font:400 9px/1 var(--mono); letter-spacing:.16em; text-transform:uppercase; color:#7f8d97; }
.band-label::before { content:""; width:5px; height:5px; border-radius:50%; border:1px solid #5d6b75; }
.contrib-list + .band-label { margin-top:13px; }
/* What I actually shipped inside someone else's repository, and what a role
   actually consisted of: same dotted list, two places. */
.contrib-list,.role-points { list-style:none; margin:9px 0 0; padding:0; display:flex; flex-direction:column;
  gap:6px; font-size:12px; line-height:1.4; color:#aab5bc; }
.contrib-list li,.role-points li { display:flex; gap:9px; }
.contrib-list li::before,.role-points li::before { content:""; flex:none; width:4px; height:4px;
  margin-top:6px; border-radius:50%; background:#8b7c58; }
.role-points { margin-top:7px; line-height:1.6; }
.role-points li::before { background:#66757f; margin-top:8px; }
.feature-list li::before { background:#66757f; }
.card-links { display:flex; justify-content:flex-end; gap:20px; margin:14px 0 0;
  font-size:11px; color:#c6d0d7; }
.card-links a { padding-bottom:3px; border-bottom:1px solid transparent; transition:border-color .2s; }
.card-links a:hover { border-color:#7f8d97; }
.two-column { gap:72px; padding-bottom:56px; }
.two-column .section:first-child { border-right:0; }
.compact { padding:12px 0 0; border:0; }
.list-title { margin-bottom:8px; padding-bottom:15px; }
.list-title h2 { font-size:26px; font-weight:600; letter-spacing:-.01em; }
.list-title small { font-size:10px; }
.list-title h2 span { font-size:15px; }
.list-row { min-height:76px; }
.list-row>i { width:1px; height:38px; }
.list-row b,.timeline b { font-size:13px; }
.list-row strong { display:flex; align-items:center; gap:8px; white-space:nowrap;
  font-size:11px; color:#c6d0d7; }
.list-row:hover strong { color:#eaf1f6; }
.list-row small,.timeline small { font-size:12px; }
.timeline { grid-template-columns:66px 9px 1fr; min-height:0; padding-top:17px; padding-bottom:6px; gap:14px; }
.timeline time { line-height:1.5; }
.timeline.study>i::before { background:#0b1318; border:1px solid #8fa0ae; }
.timeline.study { min-height:74px; }
.contact { height:355px; border-top:1px solid #29353c; padding:50px max(9vw,calc((100vw - 1110px)/2)); background:radial-gradient(ellipse at 76% 48%,#243037 0,transparent 36%),#070d11; isolation:isolate; }
.contact::before { content:""; position:absolute; inset:62% -40% -90%; background:repeating-linear-gradient(90deg,transparent 0 149px,#53606a20 150px 151px),repeating-linear-gradient(0deg,transparent 0 89px,#53606a20 90px 91px); transform:perspective(180px) rotateX(45deg); z-index:-1; }
.contact h2 { font-size:57px; font-weight:600; letter-spacing:-.015em; }
.contact-copy>p:not(.eyebrow) { font-size:18px; color:#a9b8c3; }
/* Square icon buttons: every link is a glyph now, so they share one size. */
.socials { gap:12px; }
.socials a { width:36px; height:36px; min-width:36px; padding:0; border-radius:8px;
  border-color:#40505b; color:#c3ced6; background:#0d161c;
  transition:color .2s, border-color .2s, background .2s, transform .2s; }
.socials a svg { width:17px; height:17px; display:block; }
.socials a:hover { background:#e6e6df; color:#071016; border-color:#e6e6df;
  transform:translateY(-2px); }
.socials a:focus-visible { outline:2px solid #a8b9c8; outline-offset:2px; }
@media(prefers-reduced-motion:reduce) { .socials a:hover { transform:none; } }
footer { height:65px; padding-inline:5.2%; background:#070d11; border-top:1px solid #ffffff08; }
/* Project images fade in cleanly, then gain restrained pointer depth. */
.project-shot,.project-card { --tilt-x:0deg; --tilt-y:0deg; --pointer-x:50%; --pointer-y:50%; transform-style:preserve-3d; }
.js .project-shot.reveal,.js .project-card.reveal { opacity:0; transform:perspective(1100px) translateY(34px) scale(.975); transition:opacity .85s ease var(--reveal-delay,0ms),transform .9s cubic-bezier(.2,.72,.18,1) var(--reveal-delay,0ms),border-color .25s,box-shadow .3s; }
.js .project-shot.reveal.visible,.js .project-card.reveal.visible { opacity:1; transform:perspective(1100px) rotateX(var(--tilt-x)) rotateY(var(--tilt-y)) translateY(0) scale(1); }
.project-shot::after,.project-card::after { content:""; position:absolute; inset:0; z-index:3; border-radius:inherit; pointer-events:none; opacity:0; background:radial-gradient(320px circle at var(--pointer-x) var(--pointer-y),rgba(197,215,222,.1),transparent 60%); transition:opacity .3s; }
.project-shot:hover::after,.project-card:hover::after { opacity:1; }
.project-shot { position:relative; }
.mobile-nav { visibility:hidden; pointer-events:none; }
.mobile-nav.open { visibility:visible; pointer-events:auto; }
@media(max-width:1000px) {
  .hero { height:610px; }
  .hero-copy { width:64%; }
  .hero h1 { font-size:54px; }
  .project-copy h2 { font-size:54px; }
  .two-column { gap:35px; }
  .project-grid { grid-template-columns:1fr 1fr; }
}
@media(max-width:900px) {
  .site-header { display:flex; height:76px; padding-inline:5.2%; }
  .mobile-nav { inset:76px 0 auto; }
  .hero { align-items:center; padding-top:105px; padding-bottom:65px; }
  .wonderland { inset:auto 5.2% 65px auto; width:41.3%; height:56%; }
  .project-grid,.two-column { grid-template-columns:1fr 1fr; }
  .featured { grid-template-columns:minmax(0,1fr); grid-template-areas:"idx" "copy" "shot";
    padding-block:48px 46px; row-gap:20px; }
  .project-copy>p,.project-shot { max-width:560px; }
  .project-shot { justify-self:start; }
  .project-shot .deck-card img { max-height:660px; }
  .contact { padding-inline:7%; }
}
@media(max-width:650px) {
  /* The bottom padding reserves exactly the height the artwork takes at this
     width, so the hero grows with the picture instead of pinning it to a
     fixed 385px band that cropped most of the frame away. */
  .hero { height:auto; align-items:flex-start; padding:113px 6% calc(100% * 897 / 1672); }
  .hero-copy { width:100%; }
  .hero h1 { font-size:49px; max-width:440px; line-height:1.04; }
  .hero h1 br { display:block; }
  .lead { font-size:14px; max-width:340px; }
  .actions { flex-direction:row; gap:9px; }
  .button { min-width:0; width:auto; gap:20px; font-size:11px; padding:12px; }
  .terminal { font-size:9px; margin-top:23px; }
  .wonderland { inset:auto 0 0; width:100%; height:auto; aspect-ratio:1672/897; }
  .wonderland::after { background:linear-gradient(#080e12,transparent 12%,transparent 90%,#080e12); }
  .featured,.other-projects,.two-column { width:88%; }
  .featured { padding-block:40px 40px; row-gap:18px; }
  .project-copy>p,.project-shot { max-width:none; }
  .project-shot { justify-self:stretch; }
  .project-shot .deck-card img { max-height:540px; }
  .deck { --peek:22px; }
  .project-grid,.two-column { grid-template-columns:1fr; }
  .project-card { padding:15px; }
  .card-media .deck-card img { height:auto; }
  .two-column { gap:32px; padding-bottom:38px; }
  .contact { height:360px; padding:48px 6%; }
  .contact-copy { margin-top:0; }
  .contact h2 { font-size:45px; }
  .contact-copy>p:not(.eyebrow) { font-size:15px; }
}
@media(prefers-reduced-motion:reduce) { .js .project-shot.reveal,.js .project-card.reveal { opacity:1; transform:none; transition:none; } }

/* The last chapter: a fixed three-o'clock woodland tea party. */
.contact.tea-party { height:510px; padding:55px 7%; background:#080e12; }
.tea-party::before { display:none; }
.tea-party .contact-copy { max-width:440px; position:relative; z-index:2; }
.tea-party h2 { font-size:clamp(40px,4vw,58px); line-height:1.08; margin-bottom:19px; }
.tea-party .contact-copy>p:not(.eyebrow) { font-size:15px; line-height:1.8; }
.tea-party .socials { margin-top:23px; }
.tea-time { display:block; margin-top:25px; font:9px var(--mono); letter-spacing:.1em; color:#82959e; }
.tea-art { position:absolute; width:78%; max-width:1240px; height:80%; inset:auto 7% 0 auto; z-index:0; pointer-events:none; }
.tea-art img { width:100%; height:100%; object-fit:cover; object-position:center; display:block; }
.tea-art::after { content:""; position:absolute; inset:0; background:linear-gradient(90deg,#080e12,transparent 37%,transparent 86%,#080e12),linear-gradient(#080e12,transparent 14%,transparent 84%,#080e12); }
@media(max-width:900px) {
  .tea-party .contact-copy { max-width:330px; }
  .tea-art { width:86%; right:7%; }
}
@media(max-width:650px) {
  .contact.tea-party { height:auto; padding:48px 6% calc(100% * 688 / 2171); align-items:flex-start; }
  .tea-party .contact-copy { max-width:100%; margin:0; }
  .tea-party h2 { font-size:45px; }
  .tea-party .contact-copy>p:not(.eyebrow) { font-size:13px; }
  .tea-art { inset:auto 0 0; width:100%; height:auto; aspect-ratio:2171/688; }
  .tea-art::after { background:linear-gradient(#080e12,transparent 18%,transparent 84%,#080e12); }
  .tea-time { margin-top:19px; }
}

@media(prefers-reduced-motion:reduce) { .deck-card { transition:none; } }
