/* 15.dev's styles. Plain CSS, no build step: the homepage (src/pages/index.astro) loads this file as-is. */
@font-face{font-family:'Grandstander';src:url(grandstander.woff2) format('woff2');font-weight:100 900;font-style:normal;font-display:swap}
html,body{margin:0}
/* A double tap doesn't zoom: on a page made of taps, an accidental one would throw the layout off the screen. A pinch
   still zooms, for anyone who needs the text bigger. */
html{touch-action:manipulation}
body{background:#0E0B1A;overflow:hidden}
/* Springs are written as linear() curves; browsers without linear() get plain curves instead of dropping the transitions. */
/* --px is one pixel of the layout as designed at 1440x900. Up to that size it's 1px; beyond it, it grows with the screen
   (by whichever of width or height is tighter), and every length below is a multiple of it, so a big monitor gets the
   same composition, larger, instead of the same pixel sizes adrift in colour. (The phone-only rules keep plain px.) */
.hy{--vh:1vh;--px:max(1px,min(100vw / 1440,calc(100 * var(--vh)) / 900));--sp:cubic-bezier(.2,.9,.1,1);--pop:cubic-bezier(.3,1.4,.5,1);--snap:cubic-bezier(.2,.9,.1,1);--ease-s:cubic-bezier(.37,0,.63,1);--ink:#0E0B1A;--bg:#FF3D9A;--acc:#FFE14D;position:relative;width:100vw;height:100vh;height:100dvh;min-height:calc(520 * var(--px));overflow:hidden;font-family:'Grandstander',ui-rounded,system-ui,sans-serif;letter-spacing:.01em;color:#fff;-webkit-font-smoothing:antialiased}
/* --vh is one hundredth of the height a visitor actually sees. On a phone, vh is the taller height with the browser's bars
   hidden, but a page that never scrolls is always seen with them showing, which svh measures; browsers without svh
   keep vh. */
@supports (height:1svh){
	.hy{--vh:1svh}
}
@supports (animation-timing-function:linear(0, 1)){
	.hy{--sp:linear(0,0.028,0.101,0.203,0.321,0.443,0.563,0.674,0.774,0.858,0.928,0.983,1.024,1.053,1.071,1.081,1.084,1.081,1.075,1.066,1.056,1.046,1.036,1.027,1.019,1.012,1.006,1.001,0.998,0.995,0.994,0.993,0.993,0.993,0.994,0.995,1);--pop:linear(0,0.031,0.112,0.227,0.362,0.503,0.642,0.77,0.882,0.976,1.049,1.103,1.139,1.158,1.163,1.158,1.144,1.125,1.103,1.08,1.057,1.037,1.018,1.003,0.991,0.983,0.977,0.974,0.973,0.974,0.977,0.98,0.983,0.987,0.991,0.994,0.997,1,1.001,1.003,1)}
}
.hy button{font-family:inherit;cursor:pointer}
.hy :focus-visible{outline:calc(4 * var(--px)) solid #fff;outline-offset:calc(4 * var(--px))}
.f-aa{--bg:#FF3D9A;--acc:#FFE14D}.f-tp{--bg:#1F7BFF;--acc:#3DFFB4}.f-hc{--bg:#FFC21A;--acc:#7A3CFF}.f-ai{--bg:#10C9A6;--acc:#FF7A59}.f-about{--bg:#FF6B1A;--acc:#5BC8FF}.f-hi{--bg:#5BC8FF;--acc:#FF3D9A}

/* the world behind everything: one bold colour, ink slabs, halftone and speed lines */
.bg{position:absolute;inset:0;background:var(--bg);transition:background-color .26s var(--snap)}
.slab{position:absolute;display:block;transition:transform .45s var(--sp),background-color .26s var(--snap)}
.s1{left:-14vw;top:calc(-30 * var(--vh));width:60vw;height:calc(160 * var(--vh));background:var(--ink);transform:rotate(13deg)}
.s2{left:43vw;top:calc(-30 * var(--vh));width:2.2vw;height:calc(160 * var(--vh));background:#fff;transform:rotate(13deg)}
/* s3 is a band far wider than any screen (centred 77vw across, where it always was), so however it tilts its ends stay
   off screen. A shorter one showed its corner as a darker tilted square: on every page, once s1 swung out of the way,
   and on a tall phone even on the menu. */
.s3{left:-34vw;bottom:calc(-46 * var(--vh));width:222vw;height:calc(100 * var(--vh));background:rgba(14,11,26,.16);transform:rotate(-19deg)}
.dots{position:absolute;inset:calc(-40 * var(--px));background-image:radial-gradient(rgba(14,11,26,.24) calc(1.6 * var(--px)),transparent calc(1.9 * var(--px)));background-size:calc(15 * var(--px)) calc(15 * var(--px));-webkit-mask-image:linear-gradient(118deg,transparent 52%,#000 82%);mask-image:linear-gradient(118deg,transparent 52%,#000 82%)}
/* The speed lines: the outer box holds the fade (and stays still), the stripes inside it stream left forever. The stripe
   spacing, 58.4622px across the 103deg stripes, makes them repeat every exactly 60px sideways (60 * sin 103deg), and each
   loop moves exactly 60px, so the end of one loop is pixel for pixel the start of the next. */
.lines{position:absolute;inset:0;overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent 40%,#000 70%,transparent 100%);mask-image:linear-gradient(90deg,transparent 40%,#000 70%,transparent 100%)}
.lines b{position:absolute;top:0;bottom:0;left:0;right:calc(-60 * var(--px));background-image:repeating-linear-gradient(103deg,rgba(255,255,255,.22) 0 calc(3 * var(--px)),transparent calc(3 * var(--px)) calc(58.4622 * var(--px)));background-position:0 0;transition:background-position .7s var(--snap);animation:stream 4s linear infinite}
@keyframes stream{to{translate:calc(-60 * var(--px)) 0}}
.f-aa .s1{transform:rotate(13deg)}.f-tp .s1{transform:rotate(9deg) translateX(-1.5vw)}.f-hc .s1{transform:rotate(16deg) translateX(1vw)}.f-ai .s1{transform:rotate(15deg) translateX(1.5vw)}.f-about .s1{transform:rotate(10deg)}.f-hi .s1{transform:rotate(14deg) translateX(-1vw)}
.f-aa .s2{transform:rotate(13deg)}.f-tp .s2{transform:rotate(9deg) translateX(-2vw)}.f-hc .s2{transform:rotate(16deg) translateX(1vw)}.f-ai .s2{transform:rotate(15deg) translateX(2vw)}.f-about .s2{transform:rotate(10deg) translateX(-.5vw)}.f-hi .s2{transform:rotate(14deg) translateX(-1.5vw)}
.f-aa .s3{transform:rotate(-19deg)}.f-tp .s3{transform:rotate(-15deg) translateY(calc(-3 * var(--vh)))}.f-hc .s3{transform:rotate(-23deg) translateY(calc(2 * var(--vh)))}.f-ai .s3{transform:rotate(-21deg) translateY(calc(-2 * var(--vh)))}.f-about .s3{transform:rotate(-14deg) translateX(2vw)}.f-hi .s3{transform:rotate(-20deg) translateY(calc(3 * var(--vh)))}
.f-tp .lines b{background-position:calc(120 * var(--px)) 0}.f-hc .lines b{background-position:calc(240 * var(--px)) 0}.f-ai .lines b{background-position:calc(480 * var(--px)) 0}.f-about .lines b{background-position:calc(600 * var(--px)) 0}.f-hi .lines b{background-position:calc(720 * var(--px)) 0}
.on-page .s1{transform:rotate(-8deg) translateX(-34vw)}.on-page .s2{transform:rotate(-8deg) translateX(-30vw)}.on-page .s3{transform:rotate(-24deg) translateY(calc(-6 * var(--vh)))}
.on-page .lines b{background-position:calc(-520 * var(--px)) 0}
/* The background never quite stops: the slabs and halftone drift on slow back-and-forth loops (the speed lines stream,
   above). They move with translate, which stacks with the transforms above, so the per-item and page moves still run on
   top. */
@keyframes drift{0%,100%{translate:0 0}50%{translate:var(--dx) var(--dy)}}
.s1,.s2{--dx:1.4vw;--dy:calc(-1 * var(--vh));animation:drift 13s var(--ease-s) infinite}
.s3{--dx:-1.8vw;--dy:calc(1.2 * var(--vh));animation:drift 17s var(--ease-s) -6s infinite}
.dots{--dx:calc(26 * var(--px));--dy:calc(-18 * var(--px));animation:drift 11s var(--ease-s) -2s infinite}
/* the menu */
.menu{position:absolute;inset:0;z-index:5;visibility:hidden}
.menu.on{visibility:visible}
/* your name, big, in the same lettering as the page titles */
.logo{position:absolute;left:3.6vw;top:calc(3.4 * var(--vh));z-index:8;margin:0;font-weight:900;font-size:clamp(calc(80 * var(--px)),min(10.5vw,calc(18 * var(--vh))),calc(180 * var(--px)));line-height:.8;letter-spacing:-.01em;white-space:nowrap;rotate:-8deg}
.intro .menu.on .logo{animation:logoIn .6s var(--pop) .05s backwards}
@keyframes logoIn{from{opacity:0;scale:.4;rotate:-26deg}}
.items{--its:clamp(calc(30 * var(--px)),min(4.9vw,calc(7.8 * var(--vh))),calc(80 * var(--px)));position:absolute;left:5.5vw;top:56%;z-index:6;margin:0;padding:0;list-style:none;translate:0 -50%;rotate:-7deg}
/* four projects and two about-you items, with a little air between the steps. The words are as big as they can be
   while the longest one, "HarmonyCon", still clears the ponytail sticking out of the hero when it's chosen, with its
   highlight at the top of its breathe: about 20px at the tightest (tall windows, where the width sets both sizes),
   checked against the pony's real outline from 1024 to 2560px wide */
.items li{display:block}
.items li + li{margin-top:calc(var(--its) * .1)}
.items li:nth-child(2){margin-left:1.3vw}.items li:nth-child(3){margin-left:2.6vw}.items li:nth-child(4){margin-left:3.9vw}.items li:nth-child(5){margin-left:5.2vw}.items li:nth-child(6){margin-left:6.5vw}
/* "About me" and "Contact" are about you rather than your work, so they sit a step below the projects, smaller */
.items li:not(.me) + li.me{margin-top:calc(var(--its) * .42)}
/* On a touch screen a press lights a word, a slide along the words browses them, and lifting opens the lit one
   (touchStart in hype.js), so the browser's own touch gestures, its tap flash and long-press text selection are off */
.items{touch-action:none;-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}
.it{position:relative;display:block;margin:0;padding:.08em .45em .14em .35em;border:0;background:none;color:#fff;font-weight:900;font-size:var(--its);line-height:.95;letter-spacing:-.005em;text-align:left;white-space:nowrap;transform-origin:left center;transition:translate .34s var(--sp),scale .34s var(--sp),rotate .34s var(--sp),color .12s ease,opacity .2s ease}
.items li.me .it{font-size:calc(var(--its) * .7)}
.it:focus-visible{outline:none}
.it .hl{position:absolute;inset:.04em -.25em .02em -.1em;z-index:0;background:#fff;clip-path:polygon(3% 8%,100% 0,97% 100%,0 90%);transform-origin:left center;scale:0 1;transition:scale .2s var(--snap)}
.it .sh{position:absolute;left:.35em;top:.08em;z-index:1;color:var(--acc);opacity:0;transition:opacity .12s ease,translate .3s var(--pop)}
.it .tx{position:relative;z-index:2}
/* a word that isn't chosen has a thin ink outline, like the page titles: invisible on the ink slab, but it keeps a word
   readable if it ever runs onto the white slash or the colour beside it */
.it:not(.on) .tx{text-shadow:.06em .06em 0 var(--ink),-.06em -.06em 0 var(--ink),.06em -.06em 0 var(--ink),-.06em .06em 0 var(--ink),0 .06em 0 var(--ink),0 -.06em 0 var(--ink),.06em 0 0 var(--ink),-.06em 0 0 var(--ink)}
.it.on{color:var(--ink);translate:1.6vw 0;scale:1.14;rotate:-2deg}
.it.on .hl{scale:1 1;transition:scale .34s var(--pop)}
.it.on .sh{opacity:1;translate:.08em .07em}
.it.off{opacity:.58}
/* the intro plays on first load only; after that the morph moves everything */
.intro .menu.on .items li{animation:itIn .55s var(--sp) backwards}
.intro .menu.on .items li:nth-child(1){animation-delay:.04s}.intro .menu.on .items li:nth-child(2){animation-delay:.08s}.intro .menu.on .items li:nth-child(3){animation-delay:.12s}.intro .menu.on .items li:nth-child(4){animation-delay:.16s}.intro .menu.on .items li:nth-child(5){animation-delay:.2s}.intro .menu.on .items li:nth-child(6){animation-delay:.24s}
@keyframes itIn{from{opacity:0;translate:-34vw 0;rotate:-14deg}to{opacity:1;translate:0 0;rotate:0deg}}

/* the hero: you, climbing out of a slanted frame that punches on every change. The frame's inside is 1.092 wide to 1
   tall (its height is worked out from its width, less the 16px border) so your bust and laptop fit inside it while
   your mane breaks out over the top. It takes 42% of the width: as big as it gets while the ponytail sticking out on
   its left still clears the longest menu word at its widest (selected). 74vh keeps the mane and the bubble on screen. */
.hero{--hw:min(42vw,calc(74 * var(--vh)),calc(900 * var(--px)));position:absolute;right:4vw;top:50%;z-index:4;width:var(--hw);height:calc((var(--hw) - calc(32 * var(--px))) / 1.092 + calc(32 * var(--px)));translate:0 -48%}
.hero-in{position:absolute;inset:0}
.intro .menu.on .hero-in{animation:heroIn .6s var(--pop) .15s backwards}
@keyframes heroIn{from{opacity:0;translate:30vw 0;rotate:18deg;scale:.7}to{opacity:1;translate:0 0;rotate:0deg;scale:1}}
.frame{position:absolute;inset:0;rotate:4deg;transition:rotate .45s var(--sp)}
.f-tp .frame{rotate:2deg}.f-hc .frame{rotate:6deg}.f-ai .frame{rotate:5deg}.f-about .frame{rotate:1deg}.f-hi .frame{rotate:6deg}
.pA .frame-k{animation:punchA .48s var(--pop)}.pB .frame-k{animation:punchB .48s var(--pop)}
@keyframes punchA{0%{scale:.9;rotate:-3deg}100%{scale:1;rotate:0deg}}
@keyframes punchB{0%{scale:.9;rotate:-3deg}100%{scale:1;rotate:0deg}}
.frame-k{position:absolute;inset:0}
.fr-sh{position:absolute;inset:0;translate:calc(18 * var(--px)) calc(18 * var(--px));background:var(--ink);clip-path:polygon(7% 3%,100% 0,93% 100%,0 95%)}
.fr-b{position:absolute;inset:0;background:#fff;clip-path:polygon(7% 3%,100% 0,93% 100%,0 95%)}
/* the frame's inside: the chosen item's accent, a soft spotlight behind your head, and halftone dots toward the corners */
.fr-i{position:absolute;inset:calc(16 * var(--px));overflow:hidden;background:var(--acc);clip-path:polygon(7% 3%,100% 0,93% 100%,0 95%);transition:background-color .26s var(--snap)}
.fr-i::before{content:"";position:absolute;inset:0;background:radial-gradient(circle at 40% 36%,rgba(255,255,255,.62),rgba(255,255,255,0) 58%)}
.fr-i::after{content:"";position:absolute;inset:0;background-image:radial-gradient(rgba(14,11,26,.2) calc(1.5 * var(--px)),transparent calc(1.8 * var(--px)));background-size:calc(13 * var(--px)) calc(13 * var(--px));-webkit-mask-image:radial-gradient(circle at 40% 36%,transparent 40%,#000 95%);mask-image:radial-gradient(circle at 40% 36%,transparent 40%,#000 95%)}
/* You, cut out of your badge as a die-cut sticker. The badge's box used to cut you off flat along the bottom (your
   bust) and down the left (your tail), so the clip hides exactly those edges behind the frame's border, while your mane
   and ponytail break out over the top and left. The clip follows the frame's inner bottom edge, then its left edge up to
   the row your ponytail starts at (61%), then opens out. Your shadow sits under you, and you float a little off it. */
.pony{position:absolute;inset:calc(16 * var(--px));pointer-events:none;clip-path:polygon(-60% -60%,160% -60%,160% 103.6%,0 95%,2.58% 61%,-60% 61%)}
.pony img{position:absolute;left:-14.5%;top:-9.4%;display:block;width:107.3%;height:auto;max-width:none}
.pony .pony-sh{left:calc(-14.5% + calc(10 * var(--px)));top:calc(-9.4% + calc(10 * var(--px)))}
.stk{position:absolute;left:-9%;bottom:-5%;z-index:3;width:31%;aspect-ratio:1/1}
.stk i{position:absolute;inset:0;display:grid;place-items:center;border-radius:50%;background:#fff;box-shadow:0 0 0 calc(6 * var(--px)) var(--ink),calc(9 * var(--px)) calc(9 * var(--px)) 0 calc(6 * var(--px)) var(--ink);overflow:hidden;opacity:0;scale:.2;rotate:-90deg;transition:opacity .1s ease,scale .1s ease,rotate .1s ease}
.stk i.on{opacity:1;scale:1;rotate:-8deg;transition:opacity .12s ease,scale .45s var(--pop),rotate .45s var(--pop)}
.stk img{display:block;width:100%;height:100%;object-fit:cover}
.stk img.fit{width:76%;height:auto;object-fit:contain}
/* the bubble says what the chosen item is in a line from the old site, up to three lines long. It's pinned by its
   bottom edge, so its tail always lands on the frame's top edge and longer lines grow up, away from your face.
   Its ink outline is a box-shadow ring, but its hard offset shadow is a drop-shadow filter: that follows the whole
   outline, tail included, so the tail casts the same 8px shadow the box does (a box-shadow skips the tail). */
.bub{position:absolute;right:-6%;bottom:96%;z-index:3;max-width:min(17em,30vw);padding:.5em .8em .55em;background:#fff;color:var(--ink);font-weight:800;font-size:clamp(calc(15 * var(--px)),calc(var(--hw) * .037),calc(26 * var(--px)));line-height:1.12;text-wrap:balance;box-shadow:0 0 0 calc(5 * var(--px)) var(--ink);filter:drop-shadow(calc(8 * var(--px)) calc(8 * var(--px)) 0 var(--ink));rotate:5deg;transform-origin:10% 100%}
.bub::after{content:"";position:absolute;left:14%;bottom:-.5em;width:.7em;height:.7em;background:#fff;box-shadow:calc(5 * var(--px)) calc(5 * var(--px)) 0 0 var(--ink);rotate:45deg;translate:0 -30%}
.pA .bub{animation:bubA .4s var(--pop)}.pB .bub{animation:bubB .4s var(--pop)}
/* About me and Contact have no bubble */
.hy.nobub .bub{display:none}
@keyframes bubA{from{scale:.3;opacity:0}to{scale:1;opacity:1}}
@keyframes bubB{from{scale:.3;opacity:0}to{scale:1;opacity:1}}

/* Idle: a screen that has landed floats a little. Every loop starts and ends at rest, and a screen only gets .calm once
   its pieces have landed (and loses it once it's hidden), so starting or stopping the float can never make anything
   jump. Each loop moves a property nothing else on that element animates. */
@keyframes bob{0%,100%{translate:0 0}50%{translate:0 var(--bob)}}
@keyframes sway{0%,100%{rotate:0deg}30%{rotate:var(--sway)}70%{rotate:calc(var(--sway) * -.8)}}
@keyframes breathe{0%,100%{scale:1 1}50%{scale:1.03 1.07}}
@keyframes shLift{0%,100%{translate:calc(18 * var(--px)) calc(18 * var(--px))}50%{translate:calc(18 * var(--px)) calc(24 * var(--px))}}
@keyframes shDrift{0%,100%{translate:.08em .07em}50%{translate:.12em .1em}}
/* the frame lifts while its shadow stays nearly put, so it reads as floating rather than sliding */
.menu.calm .frame{--bob:calc(-8 * var(--px));animation:bob 5.6s var(--ease-s) infinite}
.menu.calm .fr-sh{animation:shLift 5.6s var(--ease-s) infinite}
/* you float on your own beat, a little higher than your shadow does, so you lift off it. The bob is a share of your own
   height, so on a small screen it stays small enough never to lift your tucked edges into view. */
.menu.calm .pony-im{--bob:-1.4%;animation:bob 4.4s var(--ease-s) .3s infinite}
.menu.calm .pony-sh{--bob:-.5%;animation:bob 4.4s var(--ease-s) .3s infinite}
.menu.calm .stk{--sway:5deg;--bob:calc(-5 * var(--px));animation:sway 6.4s var(--ease-s) infinite,bob 4.6s var(--ease-s) infinite}
/* the bubble keeps its pop on every change; the bob rides alongside and isn't restarted by it */
.menu.calm .hero.pA .bub{--bob:calc(-6 * var(--px));animation:bubA .4s var(--pop),bob 3.8s var(--ease-s) .4s infinite}
.menu.calm .hero.pB .bub{--bob:calc(-6 * var(--px));animation:bubB .4s var(--pop),bob 3.8s var(--ease-s) .4s infinite}
.menu.calm .logo{--bob:calc(-5 * var(--px));animation:bob 6.8s var(--ease-s) infinite}
/* the chosen item's highlight breathes, starting once its wipe-in has finished */
.menu.calm .it.on .hl{animation:breathe 2.8s var(--ease-s) .45s infinite}
.menu.calm .it.on .sh{animation:shDrift 2.8s var(--ease-s) .45s infinite}

/* the morph: copies of the shared pieces fly across this layer between screens; the old screen stays up until they land */
.fly{position:absolute;inset:0;z-index:30;pointer-events:none}
.fly *,.fly *::before,.fly *::after{animation:none !important;transition:none !important}
.fly .fly-w{position:absolute;inset:0;width:auto;height:auto;aspect-ratio:auto;z-index:auto;translate:none;rotate:none;scale:none}
.menu.lv,.pg.lv{visibility:visible;pointer-events:none}
/* the real pieces hide while their copies fly; visibility, because nothing here transitions it, so revealing can't fade */
.hold{visibility:hidden !important}
.sr{position:absolute;width:calc(1 * var(--px));height:calc(1 * var(--px));margin:calc(-1 * var(--px));overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* pages */
.pg{position:absolute;inset:0;z-index:6;visibility:hidden;pointer-events:none}
.pg.on{visibility:visible;pointer-events:auto}
.back{position:absolute;left:3vw;top:calc(3.4 * var(--vh));z-index:9;display:inline-flex;align-items:center;gap:.4em;margin:0;padding:.45em .9em .5em .7em;border:0;background:var(--ink);color:#fff;font-weight:900;font-size:clamp(calc(15 * var(--px)),1.4vw,calc(22 * var(--px)));line-height:1;rotate:-5deg;box-shadow:calc(6 * var(--px)) calc(6 * var(--px)) 0 var(--acc);transition:translate .2s var(--pop),scale .2s var(--pop)}
.back:hover{translate:calc(-4 * var(--px)) 0;scale:1.06}
.back svg{width:1em;height:1em;fill:none;stroke:currentColor;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round}
.a{display:inline-block}
/* the page header: the title, in a row that tilts as a whole and floats when idle */
.hd{position:absolute;left:5vw;top:calc(12 * var(--vh));z-index:7;display:flex;align-items:center;font-size:clamp(calc(54 * var(--px)),min(8.6vw,calc(15 * var(--vh))),calc(150 * var(--px)));rotate:-6deg}
.ttl{position:relative;margin:0;font-weight:900;font-size:1em;line-height:.88;white-space:nowrap}
/* The title's line height (.88) is tighter than its letters, and Grandstander declares a descent (0.171em) shallower
   than its real descenders (y .263em, j .267em), so a y's tail, its outline and the shadow copy below it hang outside
   the title's box. A browser that sizes the floating title's layer by the box and the font's metrics (Safari) cut
   that tail off flat. The padding takes the box down past the tail (and right, past the shadow copy's offset), and
   the equal negative margin keeps every line and position exactly where it was. */
.ttl .a{position:relative;padding:0 .1em .3em 0;margin:0 -.1em -.3em 0}
.t-sh{position:absolute;left:0;top:0;color:var(--acc);translate:.07em .075em}
.t-tx{position:relative;color:#fff;text-shadow:calc(3 * var(--px)) calc(3 * var(--px)) 0 var(--ink),calc(-3 * var(--px)) calc(-3 * var(--px)) 0 var(--ink),calc(3 * var(--px)) calc(-3 * var(--px)) 0 var(--ink),calc(-3 * var(--px)) calc(3 * var(--px)) 0 var(--ink),0 calc(3 * var(--px)) 0 var(--ink),0 calc(-3 * var(--px)) 0 var(--ink),calc(3 * var(--px)) 0 0 var(--ink),calc(-3 * var(--px)) 0 0 var(--ink)}
/* The same tails everywhere else (see .ttl .a above). Safari can give any element that's positioned, faded or animated
   a layer of its own, and it sizes that layer by the element's box and the font's declared descent. So every such
   element holding Grandstander text in a tight line height is padded past its tails, whichever one Safari picks. The
   menu word's text needed it most: its accent copy drifts underneath it, and a layer moving underneath gives the text
   above a layer of its own, which cut the y off flat as the copy swung. A positioned copy needs only the padding; the
   others cancel it with an equal negative margin, so nothing moves. The padding takes no taps or clicks, so it can't
   catch one meant for the word below. `pnpm check:tails` finds any text this misses. */
.it .tx,.t-tx{display:inline-block}
.it .tx,.t-tx,.ct span span{padding-bottom:.3em;margin-bottom:-.3em}
.it .sh,.t-sh{padding-bottom:.3em}
.it .tx,.it .sh,.ct span span{pointer-events:none}
/* HarmonyCon's year switch: tabs under the screenshot's bottom edge, next to what they change (its top edge holds the
   sticker) */
.yrs{position:absolute;right:5%;top:100%;z-index:3;display:flex;gap:calc(8 * var(--px));margin-top:calc(14 * var(--px))}
.yr{display:inline-block;margin:0;padding:.42em .85em .46em;border:0;background:#fff;color:var(--ink);font-weight:900;font-size:clamp(calc(14 * var(--px)),1.3vw,calc(20 * var(--px)));line-height:1;clip-path:polygon(4% 0,100% 0,96% 100%,0 100%);transition:background-color .15s ease,color .15s ease,scale .2s var(--pop)}
.yr:hover{scale:1.08}
.yr.on{background:var(--ink);color:var(--acc)}
/* a few sentences on what the project is, under the title */
.desc{position:absolute;left:6vw;top:calc(calc(12 * var(--vh)) + clamp(calc(54 * var(--px)),min(8.6vw,calc(15 * var(--vh))),calc(150 * var(--px))) + calc(6 * var(--vh)));z-index:7;width:min(34vw,30em);font-size:clamp(calc(15 * var(--px)),1.2vw,calc(19 * var(--px)));rotate:-4deg}
.desc .a{display:block;margin:0;padding:.85em 1.05em .9em;background:#fff;color:var(--ink);font-weight:500;line-height:1.5;box-shadow:0 0 0 calc(5 * var(--px)) var(--ink),calc(10 * var(--px)) calc(10 * var(--px)) 0 calc(5 * var(--px)) var(--ink)}
/* Until a project has its write-up, its card says so, over three lines of pencil scribble (joined-up loops) that write
   themselves in, one after another, as the page lands. The card stays about as big as the text to come, so the
   page keeps its balance. Each line repeats whole loops (round), so it ends on a finished stroke. */
.soon b{display:block;font-weight:900;font-size:1.25em;line-height:1.2}
.soon span{display:block;padding-top:.2em}
.soon i{display:block;height:.82em;margin-top:.6em;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18.85' height='14.3' viewBox='0 0 18.85 14.3'%3E%3Cpath d='M0 13.15L1.19 13.1L2.39 12.95L3.59 12.69L4.77 12.35L5.93 11.91L7.04 11.39L8.09 10.8L9.09 10.15L10 9.45L10.83 8.7L11.57 7.93L12.21 7.15L12.74 6.37L13.18 5.6L13.5 4.85L13.72 4.15L13.84 3.5L13.87 2.91L13.8 2.39L13.65 1.95L13.42 1.61L13.13 1.35L12.79 1.2L12.42 1.15L12.01 1.2L11.6 1.35L11.18 1.61L10.79 1.95L10.42 2.39L10.09 2.91L9.82 3.5L9.61 4.15L9.48 4.85L9.44 5.6L9.49 6.37L9.63 7.15L9.88 7.93L10.24 8.7L10.7 9.45L11.26 10.15L11.93 10.8L12.69 11.39L13.54 11.91L14.48 12.35L15.49 12.69L16.56 12.95L17.69 13.1L18.85 13.15' fill='none' stroke='%230E0B1A' stroke-opacity='.26' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 0 0/auto 100% round no-repeat}
.soon i:nth-child(2){width:91%}
.soon i:nth-child(3){width:57%}
.pg.on .soon i{animation:scrib .3s linear .38s backwards}
.pg.on .soon i:nth-child(2){animation-delay:.68s}
.pg.on .soon i:nth-child(3){animation-duration:.2s;animation-delay:.98s}
/* The screenshot, with its sticker above it and (on HarmonyCon) the year tabs under it: as big as fits, up to 52vw or
   900px wide, and never so tall that the sticker (--stk, plus its 34px gap) and the tabs (54px) stop fitting on screen
   with ~30px to spare. The whole stack, sticker to tabs, is centred on the screen (the 4deg tilt drops the bottom-right
   corner by 3.5% of the width, which the centring allows for). */
.media{--stk:min(14vw,calc(24 * var(--vh)),calc(230 * var(--px)));--mw:min(52vw,calc((calc(100 * var(--vh)) - var(--stk) - calc(160 * var(--px))) * 1.515),calc(900 * var(--px)));--ty:calc(-50% + (var(--stk) - calc(14 * var(--px)) - var(--mw) * .035) / 2);position:absolute;right:4.5vw;top:50%;z-index:6;width:var(--mw);translate:0 var(--ty);rotate:4deg}
.media .a{display:block}
.m-fr{position:relative;aspect-ratio:16/10;border:calc(8 * var(--px)) solid #fff;background:var(--ink);box-shadow:calc(18 * var(--px)) calc(18 * var(--px)) 0 var(--ink);overflow:hidden}
.m-fr img{display:block;width:100%;height:100%;object-fit:cover;object-position:50% 20%}
/* A page frame can play a loop over its still (3place's city turning, Artist Alley's homepage with its animated listings).
   The video starts on its first frame, which is the still, and fades in once it is really playing, so a slow load (or
   Reduce Motion, where it never plays) just shows the still. */
.m-fr .m-vid{position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .25s ease}
.m-fr .m-vid.on{opacity:1}
.hcs{position:absolute;inset:0;opacity:0;scale:1.08;transition:opacity .2s ease,scale .5s var(--sp)}
.hcs.on{opacity:1;scale:1}
.m-stk{--r:-10deg;position:absolute;left:-7%;bottom:-13%;z-index:2;width:28%;aspect-ratio:1/1;border-radius:50%;background:#fff;box-shadow:0 0 0 calc(6 * var(--px)) var(--ink),calc(9 * var(--px)) calc(9 * var(--px)) 0 calc(6 * var(--px)) var(--ink);overflow:hidden;display:grid;place-items:center;rotate:var(--r)}
.m-stk img{display:block;width:100%;height:100%;object-fit:cover}
.m-stk img.fit{position:absolute;width:78%;height:auto;object-fit:contain;opacity:0;scale:.4;transition:opacity .15s ease,scale .4s var(--pop)}
.m-stk img.fit.on,.m-stk img.fit.only{opacity:1;scale:1}
/* About and Contact have no description card, so you fill the left column instead, as die-cut stickers like the menu's
   hero: lying down under the title on About, and peeking in from the edge of the screen on Contact, eyeing the links
   (the cut edge sits past the screen edge, even while leaning in). The shadow is its own image a little lower right, so
   you lift off it while idling. The box comes in; the pictures inside it idle, so the two never move the same thing. */
.ill{position:absolute;z-index:5;pointer-events:none}
.ill img{position:absolute;left:0;top:0;display:block;width:100%;height:auto}
.ill .ill-sh{left:calc(10 * var(--px));top:calc(10 * var(--px))}
.ill-me{left:4vw;bottom:calc(7 * var(--vh));width:min(36vw,calc(62 * var(--vh)),calc(640 * var(--px)));aspect-ratio:1066/1065}
/* On Contact she's big: two thirds of the screen tall, peeking in from just under the title with her hooves behind the
   email button. The link grid then starts a little way past how far she reaches in (centred in the room she leaves, up
   to 1200px wide, with bigger cards), so the middle of the page isn't empty. --ph is her height, --pw how far in she
   reaches (her width, less the 30px past the screen edge). */
.pg[data-pg="hi"]{--ph:min(calc(66 * var(--vh)),84vw);--pw:calc(var(--ph) * .5735 - calc(30 * var(--px)))}
.ill-peek{left:calc(-30 * var(--px));top:calc(calc(12 * var(--vh)) + clamp(calc(54 * var(--px)),min(8.6vw,calc(15 * var(--vh))),calc(150 * var(--px))) + calc(3 * var(--vh)));height:var(--ph);aspect-ratio:519/905}
/* (sideways screens only: an upright one, however wide, has its own Contact below) */
@media (orientation:landscape){
	.pg[data-pg="hi"] .cts{left:calc(var(--pw) + 6vw);right:5vw;width:auto;max-width:calc(1200 * var(--px));margin:0 auto}
	.pg[data-pg="hi"] .ct svg{width:clamp(calc(28 * var(--px)),3vw,calc(54 * var(--px)));height:clamp(calc(28 * var(--px)),3vw,calc(54 * var(--px)))}
	.pg[data-pg="hi"] .ct b{font-size:clamp(calc(17 * var(--px)),1.8vw,calc(34 * var(--px)))}
	.pg[data-pg="hi"] .ct span span{font-size:clamp(calc(12 * var(--px)),1.1vw,calc(19 * var(--px)))}
}
.cta{position:absolute;left:6vw;bottom:calc(10 * var(--vh));z-index:7;rotate:-4deg;text-decoration:none}
.cta .a{display:inline-flex;align-items:center;gap:.5em;padding:.5em 1.1em .55em;background:var(--ink);color:#fff;font-weight:900;font-size:clamp(calc(18 * var(--px)),1.9vw,calc(30 * var(--px)));line-height:1;box-shadow:calc(8 * var(--px)) calc(8 * var(--px)) 0 var(--acc);transition:scale .2s var(--pop),rotate .2s var(--pop)}
.cta:hover .a{scale:1.07;rotate:-3deg}
.cta svg{width:.8em;height:.8em;fill:none;stroke:currentColor;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round}
/* the essay card, centred on the screen together with the photo sticker above it. Its five pages share one grid cell
   (only the current one is visible), so the card is always exactly as tall as the longest page: no empty band under a
   short page, and no jump in size from page to page. */
.ess{--stk:min(12vw,calc(20 * var(--vh)),calc(200 * var(--px)));position:absolute;right:5vw;top:50%;z-index:6;width:min(52vw,calc(820 * var(--px)));translate:0 calc(-50% + (var(--stk) + calc(40 * var(--px))) / 2);rotate:2deg}
.ess .a{display:block}
.ess-in{position:relative;display:grid;padding:clamp(calc(22 * var(--px)),2.8vw,calc(42 * var(--px))) clamp(calc(24 * var(--px)),3vw,calc(46 * var(--px))) clamp(calc(78 * var(--px)),8vw,calc(96 * var(--px)));background:#fff;color:var(--ink);box-shadow:0 0 0 calc(6 * var(--px)) var(--ink),calc(18 * var(--px)) calc(18 * var(--px)) 0 calc(6 * var(--px)) var(--ink)}
.ep{grid-area:1/1;visibility:hidden;margin:0;font-weight:500;font-size:clamp(calc(15 * var(--px)),1.22vw,calc(19 * var(--px)));line-height:1.6}
.ep.on{visibility:inherit;animation:epIn .42s var(--sp)}
@keyframes epIn{from{opacity:0;translate:calc(34 * var(--px)) 0}to{opacity:1;translate:0 0}}
.ep a{color:var(--ink);font-weight:900;text-decoration:none;background:linear-gradient(transparent 60%,var(--acc) 60%)}
.ep-ctl{position:absolute;left:clamp(calc(24 * var(--px)),3vw,calc(46 * var(--px)));right:clamp(calc(24 * var(--px)),3vw,calc(46 * var(--px)));bottom:clamp(calc(18 * var(--px)),1.8vw,calc(26 * var(--px)));display:flex;align-items:center;justify-content:space-between}
.pgb{display:grid;place-items:center;width:calc(48 * var(--px));height:calc(48 * var(--px));margin:0;padding:0;border:0;background:var(--ink);color:#fff;clip-path:polygon(10% 0,100% 6%,90% 100%,0 94%);transition:scale .2s var(--pop)}
.pgb:hover{scale:1.12}
.pgb svg{width:calc(20 * var(--px));height:calc(20 * var(--px));fill:none;stroke:currentColor;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round}
.pgn{font-weight:900;font-size:calc(17 * var(--px))}
/* The project's sticker, big, on the far right: it sits just above the top-right corner of the page's main panel (the
   screenshot frame, or the essay card on About), outside it, so it never covers the picture. It lives inside the panel,
   so it tilts and floats with it. Sized to stay on screen above the panel from 1024 to 2560px wide (the essay card is
   taller, so About's is a little smaller). */
.media > .m-stk,.ess > .m-stk{--r:8deg;left:auto;right:2%;bottom:calc(100% + calc(34 * var(--px)));z-index:3;width:var(--stk)}
.cts{position:absolute;right:5vw;top:50%;z-index:6;width:min(54vw,calc(860 * var(--px)));translate:0 -46%;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:calc(20 * var(--px)) calc(24 * var(--px));rotate:-3deg}
.ct{display:flex;align-items:center;gap:.7em;padding:.6em .85em;background:#fff;color:var(--ink);text-decoration:none;box-shadow:0 0 0 calc(5 * var(--px)) var(--ink),calc(9 * var(--px)) calc(9 * var(--px)) 0 calc(5 * var(--px)) var(--ink);transition:translate .2s var(--pop),background-color .15s ease}
/* the cards tilt like stickers, each the other way from its neighbours across the row and down the column (a
   checkerboard: plain odd/even would tilt each whole column of a two-column grid the same way) */
.ct:nth-child(4n+1),.ct:nth-child(4n+4){rotate:-2deg}
.ct:nth-child(4n+2),.ct:nth-child(4n+3){rotate:2deg}
.ct:hover{translate:calc(-3 * var(--px)) calc(-5 * var(--px));background:var(--acc)}
.ct svg{flex:none;width:clamp(calc(28 * var(--px)),2.6vw,calc(40 * var(--px)));height:clamp(calc(28 * var(--px)),2.6vw,calc(40 * var(--px)))}
.ct b{display:block;font-weight:900;font-size:clamp(calc(17 * var(--px)),1.6vw,calc(26 * var(--px)));line-height:1}
.ct span span{display:block;margin-top:calc(3 * var(--px));font-weight:600;font-size:clamp(calc(12 * var(--px)),1vw,calc(15 * var(--px)));opacity:.75}

/* the page's extras pop in around the pieces that fly over, right behind the flight (which lands at 0.12 s) */
.pg.on .back{animation:popS .3s var(--pop) .16s backwards}
.pg.on .a-p2{animation:popIn .36s var(--pop) .22s backwards}
.pg.on .a-p3{animation:popIn .36s var(--pop) .26s backwards}
.pg.on .a-p4{animation:popIn .36s var(--pop) .3s backwards}
.pg.on .a-d{animation:descIn .36s var(--sp) .2s backwards}
.pg.on .ill-me{animation:illUp .5s var(--sp) .18s backwards}
.pg.on .ill-peek{animation:illPeek .55s var(--sp) .22s backwards}
.pg.on .a-b{animation:popIn .34s var(--pop) .3s backwards}
.pg.on .ct{animation:popIn .34s var(--pop) backwards}
.pg.on .ct:nth-child(1){animation-delay:.16s}.pg.on .ct:nth-child(2){animation-delay:.19s}.pg.on .ct:nth-child(3){animation-delay:.22s}.pg.on .ct:nth-child(4){animation-delay:.25s}.pg.on .ct:nth-child(5){animation-delay:.28s}.pg.on .ct:nth-child(6){animation-delay:.31s}.pg.on .ct:nth-child(7){animation-delay:.34s}
/* arriving straight at a page's address, nothing flies in from the menu, so the title and the main panel pop in too */
.hy.deep .pg.on .hd{animation:popIn .4s var(--pop) .04s backwards}
.hy.deep .pg.on .media,.hy.deep .pg.on .ess{animation:popIn .46s var(--pop) .1s backwards}
/* idle on a page: the title and the screenshot float, the sticker sways; the text panels stay still for reading */
@keyframes mbob{0%,100%{translate:0 var(--ty)}50%{translate:0 calc(var(--ty) - calc(8 * var(--px)))}}
@keyframes rsway{0%,100%{rotate:var(--r)}30%{rotate:calc(var(--r) + 6deg)}70%{rotate:calc(var(--r) - 5deg)}}
.pg.calm .hd{--bob:calc(-5 * var(--px));animation:bob 6.2s var(--ease-s) infinite}
.pg.calm .media{animation:mbob 5.4s var(--ease-s) infinite}
.pg.calm .ill-me .ill-im{--bob:-1.6%;animation:bob 4.6s var(--ease-s) .2s infinite}
.pg.calm .ill-me .ill-sh{--bob:-.5%;animation:bob 4.6s var(--ease-s) .2s infinite}
.pg.calm .ill-peek img{animation:lean 6.2s var(--ease-s) .3s infinite}
.pg.calm .m-stk{animation:rsway 6.6s var(--ease-s) infinite}

/* sound on/off, for every sound including the music */
.snd{position:absolute;right:2.2vw;bottom:calc(3 * var(--vh));z-index:40;width:calc(50 * var(--px));height:calc(50 * var(--px))}
.mute{position:relative;display:grid;place-items:center;width:calc(50 * var(--px));height:calc(50 * var(--px));margin:0;padding:0;border:0;border-radius:50%;background:#fff;color:var(--ink);box-shadow:0 0 0 calc(4 * var(--px)) var(--ink),calc(5 * var(--px)) calc(5 * var(--px)) 0 calc(4 * var(--px)) var(--ink);rotate:-6deg;transition:scale .2s var(--pop),rotate .2s var(--pop)}
.mute:hover{scale:1.1;rotate:0deg}
.mute:active{scale:.92}
.mute svg{width:calc(26 * var(--px));height:calc(26 * var(--px));fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.mute path:first-child{fill:currentColor}
.mute .s-off,.mute[aria-pressed="false"] .s-on{display:none}
.mute[aria-pressed="false"] .s-off{display:inline}
/* The soundtrack's credit, to the button's left. Its box reaches right up to the button (the chip is the span inside),
   so the pointer can cross from one to the other without the credit closing. Hidden, it takes no clicks. */
.mcred{position:absolute;right:100%;top:50%;display:none;padding-right:calc(16 * var(--px));text-decoration:none;opacity:0;translate:calc(10 * var(--px)) -50%;pointer-events:none;transition:opacity .15s ease,translate .2s var(--pop)}
.hy.ost .mcred{display:block}
.snd:hover .mcred,.mute:focus-visible ~ .mcred,.mcred:focus-visible{opacity:1;translate:0 -50%;pointer-events:auto}
.mcred span{display:inline-flex;align-items:center;gap:.4em;padding:.5em .85em .55em;background:#fff;color:var(--ink);font-weight:800;font-size:calc(15 * var(--px));line-height:1;white-space:nowrap;box-shadow:0 0 0 calc(4 * var(--px)) var(--ink),calc(5 * var(--px)) calc(5 * var(--px)) 0 calc(4 * var(--px)) var(--ink);rotate:-4deg;transition:scale .2s var(--pop)}
.mcred:hover span{scale:1.06}
.mcred svg{width:.8em;height:.8em;fill:none;stroke:currentColor;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round}
@keyframes popS{from{opacity:0;scale:.3}}
/* pops in to wherever the element rests (no end keyframe), so a tilted contact card lands on its own tilt instead of
   snapping to it when the pop ends */
@keyframes popIn{from{opacity:0;scale:.3;rotate:-12deg}}
@keyframes descIn{from{opacity:0;translate:-5vw 0;rotate:-3deg}}
@keyframes scrib{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
@keyframes illUp{from{opacity:0;translate:-3vw calc(10 * var(--vh));rotate:-6deg}}
@keyframes illPeek{from{translate:-105% 0}}
@keyframes lean{0%,100%{translate:0 0}50%{translate:calc(14 * var(--px)) 0}}
/* Side by side up to 1100px wide (and on a short, wide window, like a phone held sideways): the description's column
   is narrow, so it runs long, and a slightly smaller screenshot keeps clear of it */
@media (orientation:landscape) and (max-width:1100px){
	.media{--mw:min(48vw,calc((calc(100 * var(--vh)) - var(--stk) - 160px) * 1.515),900px)}
}
/* A short, wide window, like a phone held sideways: still side by side, compacted to fit the height */
@media (orientation:landscape) and (max-height:500px){
	.hy{min-height:0}
	.logo{font-size:clamp(48px,calc(17 * var(--vh)),80px)}
	.items{--its:clamp(20px,calc(7.4 * var(--vh)),34px)}
	.hero{--hw:min(42vw,calc(62 * var(--vh)));translate:0 -44%}
	.bub{max-width:min(17em,34vw)}
	.back{font-size:12px}
	.pg{--ts:clamp(32px,calc(14 * var(--vh)),58px)}
	.hd{top:calc(8 * var(--vh));font-size:var(--ts)}
	.desc{top:calc(calc(8 * var(--vh)) + var(--ts) + calc(3.5 * var(--vh)));width:42vw;font-size:12px}
	.desc .a{padding:.7em .85em .75em}
	.media{--stk:min(14vw,calc(19 * var(--vh)));--mw:min(46vw,calc((calc(100 * var(--vh)) - var(--stk) - 96px) * 1.515))}
	.media > .m-stk{bottom:calc(100% + 18px)}
	.yrs{margin-top:10px}
	.yr{font-size:12px}
	.cta{bottom:calc(6 * var(--vh))}
	.cta .a{font-size:14px}
	.snd,.mute{width:40px;height:40px}
	.mute svg{width:21px;height:21px}
	/* the essay card stops short of the sound button and sits a little below centre, its sticker just above it */
	.ess{--stk:min(12vw,calc(18 * var(--vh)));right:calc(2.2vw + 76px);width:54vw;translate:0 calc(-50% + 7 * var(--vh))}
	.ess > .m-stk{bottom:calc(100% + 6px)}
	.ess-in{min-height:0;padding:14px 18px 50px}
	.ep{font-size:11.5px;line-height:1.5}
	.ep-ctl{left:18px;right:18px;bottom:10px}
	.pgb{width:32px;height:32px}
	.pgb svg{width:15px;height:15px}
	.pgn{font-size:13px}
	.ill-me{width:min(26vw,calc(50 * var(--vh)))}
	.ill-peek{top:calc(calc(8 * var(--vh)) + var(--ts) + calc(3 * var(--vh)))}
	.cts{gap:10px 14px;top:calc(calc(8 * var(--vh)) + var(--ts) + calc(6 * var(--vh)));translate:0 0}
	.ct{padding:.45em .7em}
	.pg[data-pg="hi"] .ct svg{width:22px;height:22px}
	.pg[data-pg="hi"] .ct b{font-size:13px}
	.pg[data-pg="hi"] .ct span span{font-size:10px}
}
/* Stacked: any screen held upright (a phone, an iPad, a portrait monitor). The screen is taller than it is
   wide, so the pieces stack: title and text on top, the picture below. Sizes follow --u, one hundredth of the width on a
   tall phone, but held back by the height on a squarer screen (a tablet, or a phone with its browser bars showing), so
   the stack always fits: a tablet gets the phone's composition, larger, rather than one too tall for it. */
@media (orientation:portrait){
	.hy{--u:min(1vw,calc(.55 * var(--vh)))}
	.menu{--lg:clamp(56px,calc(var(--u) * 20.5),200px)}
	.logo{font-size:var(--lg)}
	/* the menu words start just under your name, so a bigger name never runs into them. They're big enough to tap
	   comfortably, and on the menu the ink slab reaches further right (wider, its left edge still off screen) and its white slash moves
	   with it, so every word is on ink (a
	   phone's slab is narrow where the words are, so the long ones ran onto the white slash and the colour) */
	.hy:not(.on-page) .s1{left:-30vw;width:98vw}
	.hy:not(.on-page) .s2{left:65.5vw}
	.slab{transition:transform .45s var(--sp),left .45s var(--sp),width .45s var(--sp),background-color .26s var(--snap)}
	.items{--its:clamp(28px,calc(var(--u) * 9.5),72px);left:5vw;top:calc(calc(3.4 * var(--vh)) + var(--lg) * .8 + var(--vh));translate:0 0}
	.hero{--hw:min(64vw,calc(40 * var(--vh)));right:6vw;left:auto;top:auto;bottom:calc(5 * var(--vh));translate:0 0}
	.bub{right:0;max-width:58vw;font-size:clamp(14px,calc(var(--u) * 3.8),26px)}
	.media,.ess,.cts{right:5vw;left:5vw;width:auto;top:auto;bottom:calc(16 * var(--vh));translate:0 0}
	/* the screenshot spans the width on a phone; on a squarer screen it narrows (from the left) to what the height allows */
	.media{--ty:0px;--fw:min(90vw,calc(var(--u) * 92));left:auto;width:var(--fw)}
	.cts{grid-template-columns:minmax(0,1fr);gap:12px}
	/* stacked, there's no empty left column: the panels already fill the screen */
	.ill{display:none}
	/* one column, so plain alternation is the checkerboard */
	.ct:nth-child(odd){rotate:-2deg}.ct:nth-child(even){rotate:2deg}
	.back{font-size:clamp(13px,calc(var(--u) * 3.6),24px)}
	/* the header, and the description a little under it; the sticker shrinks to fit above the panel, and on a project
	   page it sits on the screenshot's corner, leaving the room above it to the description */
	.pg{--ts:clamp(36px,calc(var(--u) * 12.5),120px);--sk:min(calc(var(--u) * 20),160px)}
	.hd{font-size:var(--ts)}
	.desc{left:5vw;width:auto;right:5vw;top:calc(calc(12 * var(--vh)) + var(--ts) + calc(4 * var(--vh)));font-size:clamp(12.5px,calc(var(--u) * 3.6),24px)}
	.desc .a{padding:.7em .85em .75em}
	.media > .m-stk,.ess > .m-stk{width:var(--sk);bottom:calc(100% + 8px)}
	.media > .m-stk{bottom:calc(100% - var(--sk) * .55)}
	.yr{font-size:clamp(13px,calc(var(--u) * 2.8),22px)}
	.cta{bottom:calc(4 * var(--vh))}
	.cta .a{font-size:clamp(16px,calc(var(--u) * 4.6),32px)}
	.ep{font-size:clamp(13px,calc(var(--u) * 3.7),24px)}
	/* Contact's links fill the room between the title and the email button, centred in it */
	.pg[data-pg="hi"] .cts{top:calc(calc(12 * var(--vh)) + var(--ts) + calc(3 * var(--vh)));bottom:calc(calc(6 * var(--vh)) + var(--u) * 9.4 + 12px);align-content:center}
	.ct b{font-size:clamp(16px,calc(var(--u) * 3.4),26px)}
	.ct svg{width:clamp(26px,calc(var(--u) * 5),44px);height:clamp(26px,calc(var(--u) * 5),44px)}
	.ct span span{font-size:clamp(11px,calc(var(--u) * 2.3),17px)}
	/* the sound button sits top right, clear of the picture and the link along the bottom */
	.snd{top:calc(3 * var(--vh));bottom:auto}
}
/* stacked and wide enough (a tablet): the links go in two columns, tilting in a checkerboard */
@media (orientation:portrait) and (min-width:600px){
	.cts{grid-template-columns:repeat(2,minmax(0,1fr));gap:20px 24px}
	.ct:nth-child(4n+1),.ct:nth-child(4n+4){rotate:-2deg}
	.ct:nth-child(4n+2),.ct:nth-child(4n+3){rotate:2deg}
}
/* A big iPad held upright (an 11" or 13", wider than 1100px with Display Zoom set to More Space), or any upright
   screen 800px wide or more. It's too wide for the phone's stack, whose sizes follow the height there,
   so everything sat small in a big empty screen. Here each piece is sized by the width and the height both:
   - the menu's words and frame grow a little, and the frame moves further into the corner. They're measured to stay
     clear of each other on every size here (tools/menu-gap.mjs), a chosen word's grown highlight included;
   - a project's screenshot widens to what the room between its description (allowing five lines) and its link takes,
     and sits lower, the link just under it;
   - on About you lie on top of the essay card's left end (your photo on its right end), the card in bigger type;
   - on Contact you peek in from the left edge again, smaller, with a column of links beside you, the two of you
     together in the middle of the room between the title and the email button.
   An iPad mini or an older 768px iPad keeps the phone's stack, which suits their proportions. */
@media (orientation:portrait) and (min-width:800px){
	.items{--its:min(8.4vw,calc(5.7 * var(--vh)))}
	/* your frame sits further into the corner, so your hair (which sticks out past its top left) stays clear of the words */
	.hero{--hw:min(62vw,calc(41 * var(--vh)));right:3.5vw;bottom:calc(4 * var(--vh))}
	.bub{max-width:50vw;font-size:min(2.9vw,calc(2.2 * var(--vh)))}
	.back{font-size:min(2.7vw,calc(2.1 * var(--vh)))}
	.pg{--ts:min(10.5vw,calc(8 * var(--vh)));--sk:min(17vw,calc(13 * var(--vh)));--ih:min(calc(26 * var(--vh)),34vw)}
	/* the text size goes on the root, not the page: the essay card flies in as a copy on the layer above the pages, and
	   a size set on the page would leave the copy's text at 16px until it landed */
	.hy{--dfs:min(2.6vw,calc(2 * var(--vh)))}
	.desc{top:calc(calc(12 * var(--vh)) + var(--ts) + calc(3 * var(--vh)));font-size:var(--dfs)}
	.media{--fw:min(76vw,calc((calc(73 * var(--vh)) - var(--ts) - var(--dfs) * 9.5 - var(--sk) * .45 - 16px) * 1.6));bottom:calc(12 * var(--vh))}
	.yr{font-size:min(2.4vw,calc(1.85 * var(--vh)))}
	.cta .a{font-size:min(3.6vw,calc(2.8 * var(--vh)))}
	/* you lie just under the title, in front of the card, which starts 12px above your bottom edge (inside its top
	   padding, so you never cover a line of the essay) */
	.ill-me{display:block;z-index:7;left:6vw;top:calc(calc(15 * var(--vh)) + var(--ts));bottom:auto;width:var(--ih)}
	.ess{top:calc(calc(15 * var(--vh)) + var(--ts) + var(--ih) - 12px);bottom:auto;translate:0 0}
	.ep{font-size:var(--dfs)}
	/* --ct: the top of you and your links, centred in the room from under the title to above the email button */
	.pg[data-pg="hi"]{--ph:min(calc(42 * var(--vh)),64vw);--ct:calc((calc(15 * var(--vh)) + var(--ts) + calc(88 * var(--vh)) - var(--ph)) / 2)}
	.ill-peek{display:block;top:var(--ct)}
	.pg[data-pg="hi"] .cts{left:calc(var(--pw) + 4vw);right:auto;width:min(52vw,calc(91vw - var(--pw)));top:var(--ct);bottom:auto;height:var(--ph);grid-template-columns:minmax(0,1fr);gap:min(2.2vw,calc(1.7 * var(--vh)))}
	/* one column again, so plain alternation is the checkerboard */
	.ct:nth-child(odd){rotate:-2deg}.ct:nth-child(even){rotate:2deg}
	.ct b{font-size:min(3vw,calc(2.3 * var(--vh)))}
	.ct svg{width:min(5.2vw,calc(4 * var(--vh)));height:min(5.2vw,calc(4 * var(--vh)))}
	.ct span span{font-size:min(1.9vw,calc(1.45 * var(--vh)))}
}
/* a phone with its browser bars showing is short, so the text gets smaller and the screenshot narrower */
@media (orientation:portrait) and (max-width:760px) and (max-height:740px){
	.desc{font-size:13px}
	.media{--fw:71vw}
	.ess{bottom:calc(12 * var(--vh))}
	.ess-in{min-height:0;padding:14px 16px 54px}
	.ep{line-height:1.5}
	.ep-ctl{left:16px;right:16px;bottom:10px}
	.pgb{width:38px;height:38px}
}
/* ...and a short one (an iPhone SE in Safari, with its bars showing) tightens everything to fit the height: the title
   moves up, HarmonyCon's year tabs sit above the screenshot's left corner, and the contact links close up */
@media (orientation:portrait) and (max-width:760px) and (max-height:620px){
	.items{--its:clamp(26px,calc(var(--u) * 9),72px)}
	.hero{--hw:min(58vw,calc(36 * var(--vh)))}
	.back{top:calc(2.6 * var(--vh))}
	.hd{top:calc(8.5 * var(--vh))}
	.desc{top:calc(calc(8.5 * var(--vh)) + var(--ts) + calc(2.5 * var(--vh)));font-size:12.5px}
	.desc .a{line-height:1.45}
	.media{--fw:64vw;bottom:calc(14 * var(--vh))}
	.yrs{top:auto;bottom:100%;left:4%;right:auto;margin:0 0 10px}
	.cta{bottom:calc(3 * var(--vh))}
	.pg[data-pg="hi"] .cts{top:calc(calc(8.5 * var(--vh)) + var(--ts) + calc(2.5 * var(--vh)));gap:8px}
	.ct{padding:.42em .8em}
}

/* Reduce Motion: everything swaps instantly, and a note says why, with a way to play the motion anyway */
.hy.rm *,.hy.rm *::before,.hy.rm *::after{animation:none !important;transition-duration:1ms !important;transition-delay:0s !important}
.rmn{position:absolute;left:50%;bottom:calc(2.4 * var(--vh));z-index:60;display:flex;align-items:center;gap:calc(12 * var(--px));margin:0;padding:calc(10 * var(--px)) calc(10 * var(--px)) calc(10 * var(--px)) calc(16 * var(--px));background:#fff;color:var(--ink);font-weight:800;font-size:calc(14 * var(--px));line-height:1.25;white-space:nowrap;box-shadow:0 0 0 calc(4 * var(--px)) var(--ink),calc(7 * var(--px)) calc(7 * var(--px)) 0 calc(4 * var(--px)) var(--ink);translate:-50% 0}
.rmn[hidden]{display:none}
.rmn button{margin:0;padding:calc(9 * var(--px)) calc(13 * var(--px));border:0;background:var(--ink);color:#fff;font-weight:900;font-size:calc(13 * var(--px))}
