@font-face { font-family: "New Tegomin"; src: url(fonts/new-tegomin.woff2) format("woff2"); font-display: swap; }
@font-face { font-family: "Shippori Mincho"; src: url(fonts/shippori-mincho-500.woff2) format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "Shippori Mincho"; src: url(fonts/shippori-mincho-700.woff2) format("woff2"); font-weight: 700; font-display: swap; }

:root {
  --ink: #14110F; --dark: #3B2A1E; --wood: #6E4E35; --beige: #D9CBB5; --egg: #F1EADB; --off: #FAF8F2; --grey: #8E8A83;
  --settle: cubic-bezier(.16, 1, .3, 1);
}

* { box-sizing: border-box; }
html { background: var(--egg); color: var(--ink); font: 500 1.05rem/1.55 "Shippori Mincho", serif; }
body { margin: 0; overflow-x: clip; }
img { display: block; }
a { color: var(--dark); font-weight: 700; text-decoration-thickness: 1.5px; text-underline-offset: 4px; }
a:hover { color: var(--ink); text-decoration-thickness: 3px; }
:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }

/* With reduced motion or no JavaScript this is simply the page: gate, about, education, table, then skills. */
.room, #door-left, #door-right, #stone-left, #stone-right, .gate:not(#front), #hint, .tapestry img { display: none; }
#camera { display: flex; flex-direction: column; }
#room2, #room3 { display: contents; }
#skills { width: min(1120px, 100vw - 16px); margin: 0 auto 3rem; text-align: center; }
#skills ul { display: grid; grid-template-columns: repeat(auto-fill, 150px); justify-content: center; gap: .5rem; margin: 0; padding: 0; list-style: none; }
#skills li { display: flex; flex-direction: column; align-items: center; gap: 5px; width: 150px; height: 160px; padding-top: 42px; background: url(art/frame.webp) 0 0 / 100% 100%; }
#skills li img { width: 50px; height: 50px; }
#skills span { font-size: 13.5px; font-weight: 700; line-height: 1.1; }
.tapestry { width: min(40rem, 100vw - 16px); margin: 2rem auto 0; padding: 1.5rem 1.75rem; background: var(--off); border: 3px solid var(--ink); }
.tapestry p { margin: 0 0 .7rem; text-wrap: pretty; }
#front { position: relative; height: 62vh; overflow: hidden; border-bottom: 6px solid var(--ink); }
#front img { position: absolute; left: 50%; top: 0; width: 1700px; max-width: none; margin-left: -850px; }
h1 { position: absolute; left: 50%; top: 124px; width: 160px; height: 50px; margin: 0 0 0 -80px; display: grid; place-items: center; color: var(--off); font: 400 1.15rem/1 "New Tegomin", serif; white-space: nowrap; }
#table { position: relative; width: min(1320px, 100vw - 16px); margin: 2rem auto; }

/* The table: planks underneath, a hard black edge, and pages laid out on a loose grid. */
#table { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr)); gap: 2rem; padding: clamp(1rem, 3vw, 3rem); background: url(art/tabletop.webp) 0 0 / 100% auto; border: 7px solid var(--ink); }
.thing, #dim, #read { display: none; }
.sheet { position: relative; height: 100%; aspect-ratio: 1 / 1.414; padding: 2rem 1.9rem; rotate: var(--r); transition: rotate .55s var(--settle), translate .55s var(--settle), scale .55s var(--settle); }
/* Rotating makes the sheet a stacking context, so shadow and paper are both layers behind its text. */
.sheet::before, .sheet::after { content: ""; position: absolute; inset: 0; z-index: -1; }
.sheet::before { z-index: -2; inset: -9px -2px -3px -9px; background: var(--ink); opacity: .34; transition: translate .55s var(--settle), opacity .55s var(--settle); }
.sheet::after { background: url(art/paper.svg) 0 0 / 100% 100%; }
h2 { margin: 0 0 .5rem; font: 400 clamp(1.5rem, 2.5vw, 2.1rem)/1.15 "New Tegomin", serif; }
.sheet p { margin: 0 0 .7rem; text-wrap: pretty; }
.made { color: var(--wood); font-weight: 700; font-size: .92rem; }
@media (hover: hover) {
  .page:hover .sheet { rotate: 0deg; translate: 0 -8px; scale: 1.04; }
  .page:hover .sheet::before { translate: -5px -5px; opacity: .42; }
}
.page:focus-visible { outline: 0; }
.page:focus-visible .sheet { outline: 3px solid var(--ink); outline-offset: 2px; }

#cat { position: fixed; right: clamp(10px, 2vw, 28px); bottom: clamp(6px, 1.4vw, 18px); z-index: 5; width: clamp(38px, 4.4vw, 62px); pointer-events: none; will-change: transform; }
/* At rest the paw is hidden against the body; it pivots at the shoulder. */
#catPaw { transform-origin: 24px 42px; }
#catPaw.swat { animation: swat .36s ease-out; }
@keyframes swat { 30% { transform: rotate(125deg); } 45% { transform: rotate(108deg); } }

/* The 3D scene. Nothing on the camera's path may set overflow, filter or opacity, which would flatten it. */
.on #stage { position: sticky; top: 0; height: 100vh; overflow: clip; perspective: 1000px; background: #BDB9B0; }
.on #camera { display: block; position: absolute; left: 50%; top: 50%; width: 0; height: 0; transform-style: preserve-3d; will-change: transform; }
.on .room, .on .gate:not(#front), .on #front, .on #table, .on #hint { display: block; }
.on #camera > *, .on #room2 > *, .on #room3 > * { position: absolute; left: 0; top: 0; margin: 0; max-width: none; transform-origin: 0 0; will-change: transform; }
.on #room2, .on #room3 { display: block; width: 0; height: 0; transform-style: preserve-3d; }
.on #room2 { transform: translate3d(-800px, 0, -850px) rotateY(90deg); }
.on #room3 { transform: translate3d(-1800px, 0, -1650px); }
.on #table { display: grid; }
.on #front { overflow: visible; border: 0; width: 3200px; height: 1700px; transform: translate3d(-1600px, -950px, 0); }
.on #front img { position: static; margin: 0; width: 3200px; height: 1700px; }
.on h1 { left: 1450px; top: 234px; width: 300px; height: 94px; margin: 0; font-size: 2.25rem; text-shadow: 2px 2px 0 var(--ink); }

.on #door-left { width: 620px; height: 1220px; }
.on #door-right { width: 620px; height: 1220px; }
.on #stone-left, .on #stone-right { width: 220px; height: 440px; }
.on #stone-left { transform: translate3d(-720px, -410px, 170px); }
.on #stone-right { transform: translate3d(500px, -410px, 170px); }
.on #reveal-left { width: 60px; height: 610px; background: var(--dark); transform: translate3d(-310px, -610px, 0) rotateY(90deg); }
.on #reveal-right { width: 60px; height: 610px; background: var(--dark); transform: translate3d(310px, -610px, -60px) rotateY(-90deg); }
.on #reveal-top { width: 620px; height: 60px; background: var(--ink); transform: translate3d(-310px, -610px, -60px) rotateX(-90deg); }

/* Both rooms share one layout: 1600 wide, 1700 deep, 800 high, with the entrance at z = 0. */
.on #floor, .on #floor1 { width: 1600px; height: 1700px; transform: translate3d(-800px, 0, -1700px) rotateX(90deg); }
.on #ceiling, .on #ceiling1 { width: 1600px; height: 1700px; backface-visibility: hidden; transform: translate3d(-800px, -800px, 0) rotateX(-90deg); }
.on #back, .on #back1 { width: 1600px; height: 800px; transform: translate3d(-800px, -800px, -1700px); }
.on #left, .on #left1 { width: 1700px; height: 800px; transform: translate3d(-800px, -800px, 0) rotateY(90deg); }
.on #right, .on #right1 { width: 1700px; height: 800px; transform: translate3d(800px, -800px, -1700px) rotateY(-90deg); }
.on #hanging, .on #hanging1 { width: 160px; height: 472px; transform-origin: 80px 0; }
.on #slide-near, .on #slide-far { width: 360px; height: 618px; }

/* The tapestries hang just off the wall. Their words are real text, laid out on the woven centre. */
.on .tapestry { display: block; padding: 0; background: none; border: 0; text-rendering: geometricPrecision; }
.on .tapestry.sharp { will-change: auto; }
.on .tapestry img { display: block; position: absolute; inset: 0; width: 100%; height: 100%; z-index: -1; }
.on #about { width: 640px; height: 540px; padding: 92px 78px 0; font-size: 23px; line-height: 1.42; transform: translate3d(-530px, -615px, -1695px); }
.on #education { width: 340px; height: 420px; padding: 84px 66px 0; font-size: 19px; line-height: 1.4; transform: translate3d(170px, -615px, -1695px); }
.on .tapestry h2 { font-size: 46px; margin-bottom: .3em; }
.on #education h2 { font-size: 33px; }
@media (max-width: 899px) { .on #about { padding-top: 88px; font-size: 25px; line-height: 1.34; } .on #about h2 { font-size: 44px; margin-bottom: .15em; } }
.on #swing { width: 400px; height: 618px; }
.on #floor3 { width: 1600px; height: 1700px; transform: translate3d(-800px, 0, -1700px) rotateX(90deg); }
.on #ceiling3 { width: 1600px; height: 1700px; backface-visibility: hidden; transform: translate3d(-800px, -800px, 0) rotateX(-90deg); }
.on #back3 { width: 1600px; height: 800px; transform: translate3d(-800px, -800px, -1700px); }
.on #left3 { width: 1700px; height: 800px; transform: translate3d(-800px, -800px, 0) rotateY(90deg); }
.on #right3 { width: 1700px; height: 800px; transform: translate3d(800px, -800px, -1700px) rotateY(-90deg); }

/* The skills wall: a plaque, then nine small frames across four rows, each hanging a little unevenly from its own nail. */
.on #skills { width: 1180px; height: 546px; margin: 0; text-rendering: geometricPrecision; transform: translate3d(-590px, -612px, -1695px); }
.on #skills.sharp { will-change: auto; }
.on #skills:not(.sharp) { pointer-events: none; }
.on #skills h2 { display: grid; place-items: center; width: 220px; height: 56px; margin: 0 auto 8px; background: url(art/plaque.webp) 0 0 / 100% 100%; color: var(--off); font-size: 33px; text-shadow: 1px 1px 0 var(--ink); }
.on #skills ul { grid-template-columns: repeat(9, 108px); justify-content: space-between; gap: 6px 0; }
.on #skills li { width: 108px; height: 116px; padding: 25px 8px 0; gap: 3px; text-align: center; rotate: var(--tilt, 0deg); translate: 0 var(--drop, 0); transform-origin: 50% 5px; }
.on #skills li img { width: 38px; height: 38px; }
.on #skills span { max-width: 70px; font-size: 10.5px; line-height: 1.05; }
.on #skills li:nth-child(3n + 1) { --tilt: -1.3deg; --drop: 3px; }
.on #skills li:nth-child(4n + 2) { --tilt: 1.1deg; }
.on #skills li:nth-child(5n) { --tilt: -.7deg; --drop: -3px; }
@media (hover: hover) { .on #skills li:hover { animation: swing 1.3s var(--settle) both; } }
@keyframes swing { 0% { rotate: var(--tilt, 0deg); } 22% { rotate: -5deg; } 48% { rotate: 3deg; } 74% { rotate: -1.2deg; } 100% { rotate: 0deg; } }

.on #shadow { background: var(--ink); opacity: .3; }
.on #edge { height: 18px; }
.on #hint { position: absolute; left: 0; right: 0; bottom: clamp(.6rem, 2.4vh, 1.5rem); width: fit-content; max-width: calc(100% - 8rem); margin: 0 auto; padding: .3rem .9rem; background: var(--off); border: 2px solid var(--ink); text-align: center; font-weight: 700; color: var(--ink); will-change: opacity; }

/* The table in the scene: a fixed square that the camera fits to the screen. Each page's right edge and
   foot sit under its neighbours, so those two margins are wider and the overlap only ever covers paper. */
.on #table { display: block; width: 1320px; height: 1320px; padding: 0; margin: 0; text-rendering: geometricPrecision; }
.on #table.arrived { will-change: auto; }
.on .page { position: absolute; left: var(--left); top: var(--top); z-index: var(--z); width: 368px; height: 520px; cursor: pointer; }
.on .sheet { width: 100%; height: 100%; aspect-ratio: auto; padding: 34px 76px 42px 32px; font-size: 19.5px; line-height: 1.42; }
.on .sheet p { margin-bottom: .55em; }
.on h2 { margin-bottom: .3em; font-size: 37px; line-height: 1.1; }
.on .made { font-size: .9em; }
.on .thing { display: block; position: absolute; }
/* Reading size: the page comes to the middle of the screen, which is the middle of the table. */
.on .page.open { z-index: 60; cursor: default; }
.on .page.open .sheet { rotate: 0deg; translate: calc(660px - 7px - 184px - var(--left)) calc(660px - 7px - 260px - var(--top)); scale: var(--read); }
/* Unpainted until a page opens: painted, even at opacity 0, this oversized box stretches the table's layer to 4320px and stalls the first turn towards it. */
.on #dim { display: block; position: absolute; inset: -1500px; z-index: 50; background: var(--ink); opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .45s ease, visibility 0s .45s; }
.on .reading #dim { opacity: .5; visibility: visible; transition: opacity .45s ease; }
.on .reading .page:not(.open) { pointer-events: none; }
.on #read { display: block; position: absolute; left: 0; right: 0; bottom: clamp(.5rem, 1.6vh, 1rem); width: fit-content; max-width: calc(100% - 8rem); margin: 0 auto; padding: .2rem .8rem; background: var(--off); border: 2px solid var(--ink); text-align: center; font-weight: 700; opacity: 0; will-change: opacity; pointer-events: none; }
.on #table:not(.at) { pointer-events: none; }

/* Hides the camera visiting every position at load. Almost opaque, because a fully opaque cover would stop the browser preparing what is behind it. */
.on #cover { position: fixed; inset: 0; z-index: 4; background: var(--egg); opacity: .995; pointer-events: none; transition: opacity .7s ease; }
.on.ready #cover { opacity: 0; }

/* The floor plan: a small button, then the house from above, with each stop a link. Only the walk needs it. */
#plan-button { display: none; }
.on #plan-button { position: fixed; left: 10px; top: 10px; z-index: 6; display: flex; align-items: center; gap: .45rem; min-height: 44px; padding: 0 .75rem 0 .5rem; background: var(--off); border: 2px solid var(--ink); color: var(--ink); font: 700 .95rem/1 "Shippori Mincho", serif; cursor: pointer; }
.on #plan-button:hover { background: var(--beige); }
.on:not(.ready) #plan-button { visibility: hidden; }
#plan { position: fixed; inset: 62px auto auto 10px; width: min(296px, 100vw - 20px); max-height: calc(100dvh - 72px); margin: 0; padding: 0; overflow: auto; background: var(--off); border: 3px solid var(--ink); color: var(--ink); }
#plan svg { display: block; width: 100%; height: auto; }
#plan text { fill: var(--ink); font: 400 14px "New Tegomin", serif; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
#plan .note { font: 500 14px "Shippori Mincho", serif; text-anchor: start; }
#plan a rect { fill: transparent; }
#plan a:is(:hover, :focus-visible) rect { fill: var(--beige); }
#plan a:focus-visible { outline: 3px solid var(--wood); outline-offset: -5px; }
#plan a[aria-current] rect { fill: var(--ink); }
#plan a[aria-current] text { fill: var(--off); }
#plan .drawn { pointer-events: none; fill: none; stroke: var(--ink); }
#plan .route { stroke: var(--grey); stroke-width: 2; stroke-dasharray: 2 6; stroke-linecap: round; }
#plan .desk { fill: var(--wood); stroke-width: 2; }
#plan .wall { stroke-width: 3; stroke-linejoin: miter; }
#plan .door { stroke: var(--off); stroke-width: 5; }
/* Stays almost opaque for the same reason as at load: the browser keeps preparing what is behind it. */
.on.ready.jumping #cover { opacity: .995; transition-duration: .2s; }

/* The contact panel on the ceiling. scene.js places it, because its size depends on the screen. */
#contact { position: relative; }
/* The whole panel is the link to the email composer. */
#contact a::after { content: ""; position: absolute; inset: 0; }
#contact:hover a { color: var(--ink); text-decoration-thickness: 3px; }
.on #contact { width: 720px; height: 420px; padding: 74px 88px 0; font-size: 26px; line-height: 1.38; }
.on #contact h2 { font-size: 58px; margin-bottom: .22em; }
.on #contact:not(.sharp) { pointer-events: none; }
@media (max-width: 899px) { .on #contact { width: 420px; height: 560px; padding: 76px 54px 0 70px; font-size: 23px; } .on #contact h2 { font-size: 50px; } .on #contact a { white-space: nowrap; font-size: .94em; } }

/* The profiles wall: three hanging signboards under a plaque. On a narrow screen they stack as wide, short boards. */
#profiles { width: min(40rem, 100vw - 16px); margin: 0 auto 2rem; text-align: center; }
#profiles ul { display: grid; gap: .75rem; margin: 0; padding: 0; list-style: none; }
.profile { display: flex; align-items: center; gap: .9rem; padding: 1rem 1.25rem; background: var(--off); border: 3px solid var(--ink); font-weight: 500; text-decoration: none; text-align: left; }
.profile img { width: 44px; height: 44px; }
.profile strong { font: 400 1.5rem/1.1 "New Tegomin", serif; }
.profile span { margin-left: auto; color: var(--wood); overflow-wrap: anywhere; }
.on #profiles { width: 1080px; height: 406px; margin: 0; text-rendering: geometricPrecision; }
.on #profiles.sharp { will-change: auto; }
.on #profiles:not(.sharp) { pointer-events: none; }
.on #profiles h2 { display: grid; place-items: center; width: 240px; height: 56px; margin: 0 auto 10px; background: url(art/plaque.webp) 0 0 / 100% 100%; color: var(--off); font-size: 33px; text-shadow: 1px 1px 0 var(--ink); }
.on #profiles ul { grid-template-columns: repeat(3, 320px); justify-content: space-between; gap: 10px; }
.on .profile { flex-direction: column; justify-content: flex-start; gap: 8px; width: 320px; height: 340px; padding: 66px 44px 0; background: url(art/sign-tall.webp) 0 0 / 100% 100%; border: 0; text-align: center; transform-origin: 50% 4px; }
.on .profile img { width: 104px; height: 104px; margin-bottom: 6px; }
.on .profile strong { font-size: 38px; }
.on .profile span { margin: 0; font-size: 19px; line-height: 1.25; overflow-wrap: normal; }
.on a.profile[href]:hover { animation: swing 1.3s var(--settle) both; color: var(--ink); }
.on a.profile[href]:hover strong { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 5px; }
.on a.profile:not([href]) { cursor: default; }
@media (max-width: 899px) {
  .on #profiles { width: 380px; height: 546px; }
  .on #profiles ul { grid-template-columns: 380px; }
  .on .profile { display: grid; grid-template-columns: 70px 1fr; align-content: center; column-gap: 18px; row-gap: 2px; width: 380px; height: 160px; padding: 26px 34px 12px; background-image: url(art/sign-wide.webp); text-align: left; }
  .on .profile img { grid-row: span 2; width: 70px; height: 70px; margin: 0; }
  .on .profile strong { font-size: 32px; align-self: end; }
  .on .profile span { font-size: 16.5px; line-height: 1.15; align-self: start; }
}
