@font-face{font-family:"JetBrains Mono";src:url(/jetbrains-mono.woff2) format("woff2");font-weight:100 800;font-display:swap}

/* Catppuccin Mocha */
:root{
  --crust:#11111b; --mantle:#181825; --base:#1e1e2e; --surface0:#313244; --surface1:#45475a; --surface2:#585b70;
  --overlay0:#6c7086; --overlay1:#7f849c; --overlay2:#9399b2; --subtext0:#a6adc8; --subtext1:#bac2de; --text:#cdd6f4;
  --lavender:#b4befe; --blue:#89b4fa; --sapphire:#74c7ec; --sky:#89dceb; --teal:#94e2d5; --green:#a6e3a1; --yellow:#f9e2af;
  --peach:#fab387; --maroon:#eba0ac; --red:#f38ba8; --mauve:#cba6f7; --pink:#f5c2e7; --flamingo:#f2cdcd; --rosewater:#f5e0dc;
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  color-scheme:dark;
}
*{box-sizing:border-box;margin:0}
ol,ul{padding:0}
html{background:var(--crust);color:var(--text);font:15px/1.7 var(--mono);-webkit-font-smoothing:antialiased;scroll-behavior:smooth;scroll-padding-top:72px}
body{min-height:100vh;padding-bottom:30vh;background:radial-gradient(1000px 560px at 82% 4%,rgba(203,166,247,.07),transparent 70%),var(--crust)}
::selection{background:rgba(203,166,247,.3)}
a{color:inherit}
:focus-visible{outline:1px solid var(--mauve);outline-offset:3px;border-radius:3px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.wrap{width:100%;max-width:1200px;margin:0 auto;padding:0 clamp(20px,5vw,64px)}

/* ascii field sits behind everything (its fade over the text column is drawn in the canvas) */
#bg{position:fixed;inset:0;width:100%;height:100%;z-index:0;pointer-events:none}
main,footer,.bar{position:relative;z-index:1}

.bar{position:sticky;top:0;z-index:5;padding:16px 0 26px;font-size:12px;color:var(--overlay1);background:linear-gradient(var(--crust) 55%,rgba(17,17,27,0))}
.bar .wrap{display:flex;align-items:center;gap:20px}
.title{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
nav{display:flex;gap:18px}
nav a{text-decoration:none;position:relative;padding:2px 0;transition:color .2s}
nav a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--peach);transform:scaleX(0);transform-origin:left;transition:transform .25s}
nav a:hover{color:var(--text)}
nav a:hover::after{transform:scaleX(1)}

section{padding:48px 0}
section:first-child{padding-top:24px}

/* prompt line */
.cmd-line{display:flex;align-items:center;margin-bottom:28px;font-size:17px;font-weight:500;white-space:pre}
.path{color:var(--blue)}
.path::after{content:"";display:inline-block;width:.44em;height:.44em;margin:0 1.05em 0 .7em;border:solid var(--peach);border-width:0 2px 2px 0;transform:translateY(-.03em) rotate(-45deg)}
.c{color:var(--green)}
.a{color:var(--text)}
.t{display:inline-block;overflow:hidden;vertical-align:bottom}
.caret{display:none;width:.6em;height:1.15em;margin-left:2px;background:var(--peach);animation:blink 1.05s steps(1) infinite}
@keyframes blink{50%{opacity:0}}

/* typing + reveal (only when JS is on and motion is allowed) */
.anim .t{width:0}
.anim .go .t{width:calc(var(--n)*1ch);transition:width calc(var(--n)*40ms) steps(var(--n),end) .15s}
.anim .go:not(.done) .caret{display:inline-block}
.anim .st{opacity:0;transform:translateY(8px)}
.anim .done .st{opacity:1;transform:none;transition:opacity .6s ease,transform .6s cubic-bezier(.2,.7,.2,1);transition-delay:calc(var(--i,0)*70ms)}
.flash{animation:flash 1.3s ease-out}
@keyframes flash{0%{background:rgba(203,166,247,.10)}100%{background:transparent}}

/* hero */
.fetch{display:grid;grid-template-columns:minmax(300px,470px) 1fr;gap:clamp(8px,3vw,40px);align-items:center}
.portrait{position:relative;isolation:isolate;margin:-26px 0 -26px -56px}
.portrait::before{content:"";position:absolute;inset:4%;z-index:-1;background:radial-gradient(closest-side,rgba(203,166,247,.16),rgba(250,179,135,.05) 65%,transparent)}
#face,.portrait img{display:block;width:100%;aspect-ratio:1;image-rendering:pixelated}
h1{font-size:clamp(28px,4.6vw,44px);line-height:1.12;font-weight:700;letter-spacing:-.03em;color:#fff}
.sub{margin-top:8px;color:var(--subtext0)}
.kv{margin-top:24px;display:grid;gap:2px}
.kv>div{display:grid;grid-template-columns:11ch 1fr;gap:12px;padding:2px 0}
dt{color:var(--mauve)}
dt::first-letter,.tl time::first-letter{text-transform:uppercase}
dd{min-width:0;overflow-wrap:anywhere}
.live{position:relative;display:inline-block;width:7px;height:7px;margin-right:8px;border-radius:50%;background:var(--green);vertical-align:middle}
.live::after,.tl li.now::after{content:"";position:absolute;border-radius:50%;background:var(--green);pointer-events:none;animation:ping 2.4s ease-out infinite}
.live::after{inset:0}
@keyframes ping{0%{transform:scale(1);opacity:.55}75%,100%{transform:scale(3.2);opacity:0}}
time{font-variant-numeric:tabular-nums}
.pal{display:flex;gap:6px;margin-top:24px}
.pal i{width:20px;height:12px;border-radius:2px;transition:transform .2s}
.pal i:hover{transform:translateY(-3px)}
.pal i:nth-child(1){background:var(--red)}.pal i:nth-child(2){background:var(--peach)}.pal i:nth-child(3){background:var(--yellow)}.pal i:nth-child(4){background:var(--green)}
.pal i:nth-child(5){background:var(--teal)}.pal i:nth-child(6){background:var(--blue)}.pal i:nth-child(7){background:var(--mauve)}.pal i:nth-child(8){background:var(--text)}

/* timelines: start at the oldest entry (bottom), time flows up.
   --dot and --rail are even, the rail sits at (dot - rail) / 2: dot centre and rail centre are the same whole pixel.
   --cy = height of the dot centre below the top of an entry. Pseudo-elements don't get the global border-box, hence the explicit one. */
.tl{--col:5ch;--fut:22px;--dot:12px;--rail:2px;--cy:23px;--x:calc((var(--dot) - var(--rail))/2);list-style:none;position:relative;margin-left:4px}
.tl.wide{--col:12ch}
.tl::before{content:"";position:absolute;left:var(--x);top:calc(var(--fut) + var(--cy));bottom:var(--tail,80px);width:var(--rail);border-radius:var(--rail);background:linear-gradient(to top,var(--mauve),var(--peach));opacity:.55;transform-origin:bottom}
.tl li{position:relative;display:grid;grid-template-columns:var(--col) 1fr;gap:22px;padding:10px 0 10px 34px}
.tl li::before{content:"";box-sizing:border-box;position:absolute;left:0;top:calc(var(--cy) - var(--dot)/2);width:var(--dot);height:var(--dot);border-radius:50%;background:var(--crust);border:2px solid var(--mauve);transition:background .25s,border-color .25s,box-shadow .25s,transform .25s}
.tl li:not(.cap):hover::before{background:var(--peach);border-color:var(--peach);box-shadow:0 0 12px rgba(250,179,135,.5);transform:scale(1.25)}
.tl li.now::before{background:var(--green);border-color:var(--green)}
.tl li.now::after{left:0;top:calc(var(--cy) - var(--dot)/2);width:var(--dot);height:var(--dot)}
.tl time{color:var(--peach);font-size:13px;padding-top:1px}
.tl .body{display:grid;transition:transform .25s}
.tl li:hover .body{transform:translateX(4px)}
.tl b{color:#fff;font-weight:600}
.tl span{color:var(--text)}
.tl small{font-size:13px;color:var(--overlay1)}
/* cap: the open dashed line on top = the part not yet lived */
.tl li.cap{display:block;padding:0}
.tl li.future{height:var(--fut)}
.tl li.future::before{left:var(--x);top:0;width:var(--rail);height:calc(100% + var(--cy));border:0;border-radius:0;background:linear-gradient(var(--peach) 0 3px,transparent 3px 8px) 0 0/var(--rail) 8px repeat-y;-webkit-mask-image:linear-gradient(to top,#000 40%,transparent);mask-image:linear-gradient(to top,#000 40%,transparent);opacity:.9;transform-origin:bottom;animation:flow 2.2s steps(8) infinite}
@keyframes flow{to{background-position:0 -8px}}
.anim .tl::before{transform:scaleY(0)}
.anim .done .tl::before{transform:none;transition:transform calc(var(--cnt,4)*.24s + .3s) cubic-bezier(.45,0,.25,1)}
.anim .tl li.st::before{transform:scale(0)}
.anim .tl li.future.st::before{transform:scaleY(0)}
.anim .done .tl li.st::before{transform:none;transition:transform .55s cubic-bezier(.34,1.56,.64,1) calc(var(--i,0)*.24s + .1s)}
.anim .done .tl li.st{transition-delay:calc(var(--i,0)*.24s + .1s)}

/* skills */
.skills{columns:230px 4;column-gap:40px}
.skills>div{break-inside:avoid;margin-bottom:34px}
h3{font-size:inherit;font-weight:600;color:var(--blue);margin-bottom:10px}
h3::first-letter{text-transform:uppercase}
.skills ul{list-style:none;display:flex;flex-wrap:wrap;gap:8px}
.chip{padding:2px 11px;border:1px solid var(--surface0);border-radius:6px;background:rgba(255,255,255,.012);font-size:13px;cursor:default;transition:border-color .2s,box-shadow .2s,transform .2s,color .2s}
.chip:hover{color:#fff;border-color:rgba(203,166,247,.6);box-shadow:0 8px 22px -10px rgba(203,166,247,.6);transform:translateY(-2px)}

/* links */
.lnk{position:relative;text-decoration:none;color:var(--text);background:linear-gradient(var(--peach),var(--peach)) 0 100%/0 1px no-repeat;transition:background-size .3s,color .2s;padding-bottom:1px}
.lnk::after{content:"→";position:absolute;left:100%;margin-left:.45em;color:var(--peach);opacity:0;transform:translateX(-6px);transition:opacity .25s,transform .25s}
.lnk:hover{background-size:100% 1px;color:#fff}
.lnk.at{color:var(--peach)}
.lnk.at:hover{color:var(--rosewater)}
.lnk-u{background:none;text-decoration:underline;text-decoration-color:rgba(250,179,135,.5);text-decoration-thickness:1px;text-underline-offset:4px;transition:text-decoration-color .25s,color .2s}
.lnk-u:hover{text-decoration-color:var(--peach)}
.lnk:hover::after{opacity:1;transform:none}

/* interactive prompt */
#shell{padding-top:8px;padding-bottom:24px}
.sh-out{display:grid;gap:2px;margin-bottom:6px;font-size:14.5px}
.sh-out>div{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--subtext0)}
.sh-out .echo{color:var(--text);font-size:17px;font-weight:500;margin-top:12px}
.sh-out .ok{color:var(--green)}
.sh-out .err{color:var(--red)}
.sh-out .dir{color:var(--blue)}
.sh-out button{font:inherit;color:var(--mauve);background:none;border:0;padding:0;margin:0 14px 0 0;cursor:pointer;position:relative}
.sh-out button::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;background:var(--mauve);transform:scaleX(0);transform-origin:left;transition:transform .25s}
.sh-out button:hover::after,.sh-out button:focus-visible::after{transform:scaleX(1)}
.sh{margin:0;cursor:text}
.sh-field{position:relative;flex:1;display:block}
#sh-in{display:block;width:100%;font:inherit;color:var(--green);background:none;border:0;padding:0;caret-color:transparent;outline:0}
.sh .caret{display:block;position:absolute;left:calc(var(--pos,0)*1ch);top:50%;margin:0;translate:0 -50%;pointer-events:none;transition:left .06s}
.sh:not(:focus-within) .caret{background:none;border:1px solid var(--peach);width:calc(.6em - 2px);height:calc(1.15em - 2px)}
.sh:focus-within .caret{animation:none}
.sh .caret{animation:blink 1.05s steps(1) infinite}
.flashline{animation:flash 1.3s ease-out}

@media (max-width:760px){
  nav{display:none}
  .fetch{grid-template-columns:1fr}
  .portrait{max-width:340px;margin:0 auto -6px}
  .kv>div{grid-template-columns:1fr;gap:0;padding:4px 0}
  dt{font-size:12px}
  .tl{--cy:22px}
  .tl li{grid-template-columns:1fr;gap:0}
  .tl time{order:-1}
  .sh-out button{margin-bottom:6px}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{transition:none!important;animation:none!important}
  .toast{animation:none!important}
  .live::after,.tl li.now::after{display:none}
  .sh .caret{animation:none!important}
}

/* download button */
.actions{margin-top:26px}
.btn{display:inline-flex;align-items:center;gap:10px;font:inherit;font-size:14px;color:var(--text);padding:8px 18px 8px 14px;border:1px solid rgba(203,166,247,.38);border-radius:8px;background:rgba(203,166,247,.05);cursor:pointer;transition:border-color .25s,background .25s,box-shadow .25s,transform .2s}
.btn[hidden]{display:none}
.btn .ico{color:var(--mauve);display:inline-block;transition:transform .25s}
.btn:hover{border-color:var(--mauve);background:rgba(203,166,247,.1);box-shadow:0 10px 28px -14px rgba(203,166,247,.7)}
.btn:hover .ico{transform:translateY(2px)}
.btn:active{transform:translateY(1px)}
.btn[aria-busy]{pointer-events:none;opacity:.7}
.pal{margin-top:20px}

/* "screenshot" when the PDF is generated: viewfinder corners close in, a scan line sweeps down, soft flash */
.snap{position:fixed;inset:0;z-index:60;pointer-events:none;overflow:hidden}
.snap i{position:absolute;width:38px;height:38px;border:0 solid rgba(205,214,244,.85);opacity:0;animation:corner .5s cubic-bezier(.2,.8,.2,1) forwards,cornerOut .5s ease-in 1.4s forwards}
.snap i:nth-child(1){top:22px;left:22px;border-width:2px 0 0 2px;--dx:-1;--dy:-1}
.snap i:nth-child(2){top:22px;right:22px;border-width:2px 2px 0 0;--dx:1;--dy:-1}
.snap i:nth-child(3){bottom:22px;left:22px;border-width:0 0 2px 2px;--dx:-1;--dy:1}
.snap i:nth-child(4){bottom:22px;right:22px;border-width:0 2px 2px 0;--dx:1;--dy:1}
@keyframes corner{from{opacity:0;transform:translate(calc(var(--dx)*54px),calc(var(--dy)*54px)) scale(1.6)}to{opacity:1;transform:none}}
@keyframes cornerOut{to{opacity:0;transform:scale(.9)}}
.snap b{position:absolute;left:0;right:0;top:0;height:2px;opacity:0;background:linear-gradient(90deg,transparent,var(--mauve) 30%,var(--peach) 70%,transparent);box-shadow:0 0 22px 3px rgba(203,166,247,.5);animation:scan .62s cubic-bezier(.5,0,.3,1) .3s forwards}
.snap b::before{content:"";position:absolute;left:0;right:0;bottom:2px;height:150px;background:linear-gradient(transparent,rgba(203,166,247,.1))}
@keyframes scan{0%{opacity:0;transform:translateY(0)}12%{opacity:1}88%{opacity:1}100%{opacity:0;transform:translateY(100vh)}}
.snap em{position:absolute;inset:0;opacity:0;background:radial-gradient(circle at 50% 42%,rgba(245,224,220,.14),rgba(203,166,247,.06) 55%,rgba(203,166,247,0) 85%);animation:flashpdf 1.6s ease-in-out .9s forwards}
@keyframes flashpdf{0%{opacity:0}35%{opacity:1}100%{opacity:0}}
.toast{position:fixed;right:28px;bottom:28px;z-index:61;display:flex;gap:10px;align-items:baseline;padding:10px 16px;font-size:13px;color:var(--text);background:rgba(24,24,37,.94);border:1px solid var(--surface0);border-radius:10px;box-shadow:0 18px 40px -18px #000;animation:toast 3.6s cubic-bezier(.2,.8,.2,1) forwards}
.toast .ok{color:var(--green)}.toast .err{color:var(--red)}.toast small{color:var(--overlay1)}
@keyframes toast{0%{opacity:0;transform:translateY(14px)}9%,86%{opacity:1;transform:none}100%{opacity:0;transform:translateY(6px)}}
