/* Shared reset, typography, navigation, panel foundations and transitions.
   Related rules retain their original cascade order. */

/* ---- 01-base ---- */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;700&display=swap');

/* Invisible SVG definitions remain available to filters and clip paths. */
.svg-definitions {
    position: absolute;
    width: 0;
    height: 0;
    pointer-events: none;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    scrollbar-width: none;
}

*::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

/* Hard-lock the viewport — no scrollbars, ever, on any device.
   Fixed-position elements (#cam-bg, .cine-bar, #scene-canvas) sit outside
   body's own overflow:hidden clipping since their containing block is the
   viewport itself, not body. When they get scale()/rotate() transforms
   during the loading camera punch-in, some mobile browsers (iOS Safari
   especially) mistakenly grow the page's scrollable area to fit that
   transformed, off-screen geometry — producing a phantom scrollbar and,
   since the "centered" content then sits in the middle of that wider
   virtual page rather than the middle of the visible screen, elements
   like #tap-start appear off-center too. Pinning html/body to
   position:fixed + 100% removes any scrollable area at the root level,
   so nothing — no matter how far a transform pushes it — can ever be
   scrolled to. */
html {
    overflow: hidden;
    height: 100%;
    width: 100%;
}

body {
    overflow: hidden;
    height: 100%;
    width: 100%;
    overscroll-behavior: none;
    touch-action: manipulation;
}



/* ===================== MAIN CONTENT ===================== */
body {
    overflow: hidden;
    background-color: #f7f4ed;
    background-image:
        /* Red notebook margin line on the left */
        linear-gradient(to right, transparent 72px, rgba(230, 85, 85, 0.32) 72px, rgba(230, 85, 85, 0.32) 73.5px, transparent 73.5px),
        /* Horizontal notebook ruled lines */
        linear-gradient(to bottom, transparent 31px, rgba(120, 155, 195, 0.28) 31px, rgba(120, 155, 195, 0.28) 32px, transparent 32px),
        /* Soft paper texture radial gradient */
        radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.45) 0%, rgba(225, 218, 205, 0.45) 100%);
    background-size: 100% 100%, 100% 32px, 100% 100%;
}

/* ---- 04-transitions ---- */
/* ===================== TRANSITION IMPACT FX =====================
   One shared, reusable layer fired by triggerTransitionFX() in script.js
   every time a face opens its page panel. Positioned in JS via left/top
   set to the exact click point, then "burst" classes trigger the CSS
   animations below. Colors are set inline per-face at trigger time. */
#transition-fx {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 900;
}

#fx-particles {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

#fx-shockwave,
#fx-shockwave-ring2 {
    position: fixed;
    top: 0;
    left: 0;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.9);
    box-shadow: 0 0 24px rgba(255, 255, 255, 0.35), inset 0 0 16px rgba(255, 255, 255, 0.2);
    transform: translate(-50%, -50%) scale(0);
    opacity: 0;
    --fx-scale: 40;
}

#fx-shockwave.burst {
    animation: fxShockwave 650ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

#fx-shockwave-ring2.burst {
    border-color: rgba(255, 255, 255, 0.6);
    animation: fxShockwave 900ms cubic-bezier(0.16, 1, 0.3, 1) 90ms forwards;
}

@keyframes fxShockwave {
    0% {
        transform: translate(-50%, -50%) scale(0);
        opacity: 0.95;
        border-width: 2px;
    }

    55% {
        opacity: 0.55;
    }

    100% {
        transform: translate(-50%, -50%) scale(var(--fx-scale));
        opacity: 0;
        border-width: 0.5px;
    }
}

#fx-glow {
    position: fixed;
    top: 0;
    left: 0;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.35) 35%, transparent 70%);
    transform: translate(-50%, -50%) scale(0);
    opacity: 0;
    /* Screen blending disappears on Contact's nearly white paper. */
    mix-blend-mode: normal;
    --fx-glow-scale: 18;
}

#fx-glow.burst {
    animation: fxGlow 480ms ease-out forwards;
}

@keyframes fxGlow {
    0% {
        transform: translate(-50%, -50%) scale(0);
        opacity: 1;
    }

    100% {
        transform: translate(-50%, -50%) scale(var(--fx-glow-scale));
        opacity: 0;
    }
}

/* Full-screen RGB chromatic split + a stray white scan slice, both flickering
   in short discrete steps (steps() timing) so it reads as a glitch, not a fade. */
#fx-glitch {
    position: fixed;
    inset: 0;
    opacity: 0;
    mix-blend-mode: screen;
}

#fx-glitch.active {
    opacity: 1;
    animation: fxGlitchFade 260ms steps(1) forwards;
}

#fx-glitch:not(.active) > * {
    animation: none;
}

@keyframes fxGlitchFade {
    0% {
        opacity: 1;
    }

    82% {
        opacity: 1;
    }

    100% {
        opacity: 0;
    }
}

.fx-glitch-layer {
    position: absolute;
    inset: 0;
}

.fx-glitch-r {
    background: rgba(255, 45, 65, 0.20);
    animation: fxGlitchShiftR 260ms steps(6) infinite;
}

.fx-glitch-g {
    background: rgba(70, 255, 150, 0.15);
    animation: fxGlitchShiftG 260ms steps(5) infinite;
}

.fx-glitch-b {
    background: rgba(70, 170, 255, 0.20);
    animation: fxGlitchShiftB 260ms steps(7) infinite;
}

@keyframes fxGlitchShiftR {
    0% {
        transform: translate(0, 0);
    }

    20% {
        transform: translate(9px, -3px);
    }

    40% {
        transform: translate(-11px, 3px);
    }

    60% {
        transform: translate(7px, 2px);
    }

    80% {
        transform: translate(-5px, -2px);
    }

    100% {
        transform: translate(0, 0);
    }
}

@keyframes fxGlitchShiftG {
    0% {
        transform: translate(0, 0);
    }

    25% {
        transform: translate(-8px, 2px);
    }

    50% {
        transform: translate(10px, -3px);
    }

    75% {
        transform: translate(-6px, 1px);
    }

    100% {
        transform: translate(0, 0);
    }
}

@keyframes fxGlitchShiftB {
    0% {
        transform: translate(0, 0);
    }

    15% {
        transform: translate(7px, 4px);
    }

    35% {
        transform: translate(-10px, -1px);
    }

    55% {
        transform: translate(5px, 3px);
    }

    75% {
        transform: translate(-7px, -4px);
    }

    100% {
        transform: translate(0, 0);
    }
}

.fx-glitch-slice {
    position: absolute;
    left: 0;
    width: 100%;
    height: 3px;
    background: rgba(255, 255, 255, 0.55);
    transform: scaleX(0);
    animation: fxGlitchSlice 260ms steps(4) infinite;
}

@keyframes fxGlitchSlice {

    0%,
    100% {
        transform: scaleX(0);
        top: 30%;
    }

    25% {
        transform: scaleX(1);
        top: 46%;
    }

    50% {
        transform: scaleX(0.6);
        top: 62%;
    }

    75% {
        transform: scaleX(1);
        top: 18%;
    }
}

/* Fewer particles / smaller rings on small screens for performance —
   the JS side also halves particle counts under 700px, this just trims
   the shockwave/glow reach to match the smaller viewport. */
@media (max-width: 700px) {
    #fx-shockwave.burst {
        --fx-scale: 26;
    }

    #fx-shockwave-ring2.burst {
        --fx-scale: 32;
    }

    #fx-glow.burst {
        --fx-glow-scale: 12;
    }}




/* ===================== PAGE PANELS (FACE-CLICK TRANSITIONS) ===================== */
/* Each face has its own transition "personality" — the type is chosen by
   adding a .tt-<type> class in JS (openPagePanel in script.js), which also
   sets these per-open custom properties:
     --ox / --oy   : click origin, as a % of the viewport
     --dur         : this open's duration (velocity-scaled)
     --speed       : 0-1, how fast the user was scrolling right before the click
     --parallax    : 0-1 baseline intensity for this face (from PAGE_TRANSITIONS)
   Origin stays fixed for the whole transition — only the animated property
   (radius / inset / rotation / scale) moves — so browsers only interpolate
   one value at a time, which keeps things smooth. */
#page-panels {
    position: fixed;
    inset: 0;
    z-index: 60;
    /* above #ui-layer (50), below #fullscreen-overlay/#shutter-flash */
    pointer-events: none;
    perspective: 1800px;
    /* only used by .tt-fold (3DMODELS) */
}

.page-panel {
    --ox: 50%;
    --oy: 50%;
    --dur: 900ms;
    --speed: 0;
    --parallax: 0.5;
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transform-style: preserve-3d;
    z-index: 1;
}

.page-panel.active {
    visibility: visible;
    opacity: 1;
    pointer-events: auto;
}


/* Keep the panel solid while its shape closes, then fade only at the end. */
.page-panel.is-closing {
    animation: panelExitFade var(--dur) linear both;
}

@keyframes panelExitFade {
    0%, 72% { opacity: 1; }
    to { opacity: 0; }
}

.page-panel-content {
    position: relative;
    z-index: 2;
    will-change: transform, opacity;
}

.page-panel[data-page="DECO"] {
    background: #5f2e8a;
}

/* Blinds & liquid paint themselves via their own child elements, so the
   panel's flat background would otherwise hide the reveal — keep it clear.
   (Placed after the color rules above so it wins on equal specificity.) */
.page-panel.tt-blinds,
.page-panel.tt-liquid {
    background: transparent;
}

/* ===================== HOME — IRIS / CIRCLE DISSOLVE ===================== */
/* Clean & cinematic. Mask grows from the click point; content very subtly
   zooms back out (starts slightly punched-in) over a slightly longer
   duration than the mask, so it keeps gently settling after the mask
   finishes — that lag is the "camera pulling back" parallax. */
.page-panel.tt-iris {
    clip-path: circle(0% at var(--ox) var(--oy));
    opacity: 0;
    transition: clip-path var(--dur) cubic-bezier(0.65, 0, 0.35, 1),
        opacity calc(var(--dur) * 0.42) ease-out;
}

.page-panel.tt-iris.open {
    clip-path: circle(170% at var(--ox) var(--oy));
    opacity: 1;
}

.page-panel.tt-iris .page-panel-content {
    transform: scale(calc(1 + var(--parallax) * 0.16));
    transition: transform calc(var(--dur) * 1.3) cubic-bezier(0.22, 1, 0.36, 1);
}

.page-panel.tt-iris.open .page-panel-content {
    transform: scale(1);
}

/* ===================== ABOUT — CENTER PAPER REVEAL ===================== */
/* A compact paper square opens from the center until it fills the page. */
.page-panel.tt-shear {
    clip-path: inset(48% 48% 48% 48% round 22px);
    opacity: 0;
    transition: clip-path var(--dur) cubic-bezier(0.76, 0, 0.24, 1),
        opacity calc(var(--dur) * 0.42) ease-out;
    will-change: clip-path;
}

.page-panel.tt-shear.open {
    clip-path: inset(0 0 0 0 round 0);
    opacity: 1;
}

.page-panel.tt-shear .page-panel-content {
    opacity: 0.4;
    transform: translateY(18px);
    transition: opacity calc(var(--dur) * 0.72) ease-out,
        transform var(--dur) cubic-bezier(0.16, 1, 0.3, 1);
}

.page-panel.tt-shear.open .page-panel-content {
    opacity: 1;
    transform: translateY(0);
}

/* ===================== 3DMODELS — 3D PERSPECTIVE FOLD + ZOOM PUNCH ===================== */
/* The most impactful one. The whole panel rotates in on a hinge (like a
   subtle page fold, NOT a literal flip-book turn) while punching past its
   final scale and settling back — the overshoot easing does the "punch". */
.page-panel.tt-fold {
    transform-origin: left center;
    transform: perspective(1800px) rotateY(-72deg) scale(0.82);
    opacity: 0;
    transition: transform var(--dur) cubic-bezier(0.34, 1.56, 0.64, 1),
        opacity calc(var(--dur) * 0.42) ease-out;
    will-change: transform;
    backface-visibility: hidden;
}

.page-panel.tt-fold.open {
    transform: perspective(1800px) rotateY(0deg) scale(1);
    opacity: 1;
}

.page-panel.tt-fold .page-panel-content {
    transform: scale(calc(1.4 - var(--parallax) * 0.4));
    transition: transform calc(var(--dur) * 0.85) cubic-bezier(0.34, 1.56, 0.64, 1);
}

.page-panel.tt-fold.open .page-panel-content {
    transform: scale(1);
}




/* ===================== MOTIONGRAPH — CENTER CIRCLE REVEAL ===================== */
/* A motion lens expands from the center to fill the page. */
.page-panel.tt-wipe {
    clip-path: circle(0% at 50% 50%);
    opacity: 0;
    transition: clip-path var(--dur) cubic-bezier(0.16, 1, 0.3, 1),
        opacity calc(var(--dur) * 0.42) ease-out;
    will-change: clip-path;
}

.page-panel.tt-wipe.open {
    clip-path: circle(150% at 50% 50%);
    opacity: 1;
    /* INTRO = the OUTRO played backwards: the close curve is ease-out
       (.16,1,.3,1), so the open uses its mirror (.7,0,.84,0). Same duration,
       same 150% end radius, so it reads exactly like the close in reverse. */
    transition: clip-path var(--dur) cubic-bezier(0.7, 0, 0.84, 0),
        -webkit-clip-path var(--dur) cubic-bezier(0.7, 0, 0.84, 0),
        opacity calc(var(--dur) * 0.42) ease-out;
}

.page-panel.tt-wipe .page-panel-content {
    transform: translateY(18px);
    transition: transform var(--dur) cubic-bezier(0.16, 1, 0.3, 1);
}

.page-panel.tt-wipe.open .page-panel-content {
    transform: translateY(0);
}

.page-panel.tt-blinds .page-panel-content {
    opacity: 0;
    transition: opacity calc(var(--dur) * 0.4) ease-out;
    transition-delay: calc(var(--dur) * 0.45);
}

.page-panel.tt-blinds.open .page-panel-content {
    opacity: 1;
}

.page-panel.tt-liquid .page-panel-content {
    opacity: 0;
    transform: scale(0.85);
    transition: opacity calc(var(--dur) * 0.4) ease-out, transform calc(var(--dur) * 0.5) ease-out;
    transition-delay: calc(var(--dur) * 0.5);
}

.page-panel.tt-liquid.open .page-panel-content {
    opacity: 1;
    transform: scale(1);
}

/* ---- 05-nav-responsive ---- */
/* ===================== UI OVERLAY ===================== */
#ui-layer {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    /* Let clicks pass through to 3D Canvas */
    z-index: 50;
    /* Above the 3D scene */
    color: #2b292983;
    font-family: 'Michroma', sans-serif;
    opacity: 0;
    transition: opacity 0.5s ease;
}

#ui-layer.show-ui {
    opacity: 1;
}

/* Only the actual UI elements should capture clicks */
.ui-top-left,
.ui-top-right,
.ui-bottom-left {
    pointer-events: auto;
}

.ui-top-left {
    position: absolute;
    top: 40px;
    left: 50px;
    font-size: 1.2rem;
    letter-spacing: 2px;
    font-weight: bold;
    text-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
}

.ui-top-right {
    position: absolute;
    top: 40px;
    right: 50px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 15px;
}

.nav-row {
    display: flex;
    gap: 30px;
}

/* Stagger the bottom row */
.nav-row.bottom-row {
    padding-right: 50px;
    /* offset it left slightly */
}

/* Per-face plain solid colors matching transition page colors */
.nav-link[data-face="HOME"] {
    --nav-color: #22222688;
}

.nav-link[data-face="ABOUT"] {
    --nav-color: #ff5555;
}

.nav-link[data-face="CONTACT"] {
    --nav-color: #d49b17;
}

.nav-link[data-face="3DMODELS"] {
    --nav-color: #3b93d3;
}

.nav-link[data-face="MOTIONGRAPH"] {
    --nav-color: #22b870;
}

.nav-link {
    position: relative;
    color: #1111113b;
    text-decoration: none;
    font-size: 0.9rem;
    letter-spacing: 1.5px;
    display: flex;
    align-items: center;
    transition: color 0.3s ease, transform 0.3s ease;
    cursor: pointer;
    padding-bottom: 4px;
    font-weight: 600;
    outline: none;
    -webkit-tap-highlight-color: transparent;
}

.nav-link:focus,
.nav-link:focus-visible {
    outline: none;
}

/* Plain solid underline — hidden by default */
.nav-link::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 2px;
    background-color: var(--nav-color);
    opacity: 0;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s ease, opacity 0.3s ease;
    border-radius: 1px;
}

/* Reveal underline & arrow on hover */
.nav-link:hover {
    color: #000000;
    transform: translateX(3px);
}

.nav-link:hover::after {
    opacity: 1;
    transform: scaleX(1);
}

/* Reveal underline & arrow when active (scrolled to that page face) */
.nav-link.active {
    color: #000000;
}

.nav-link.active::after {
    opacity: 1;
    transform: scaleX(1);
}

.nav-link[data-face="HOME"]::after {
    display: none;
}

.nav-link[data-face="HOME"]:hover,
.nav-link[data-face="HOME"].active {
    transform: none;
}

.ui-bottom-left {
    position: absolute;
    bottom: 40px;
    left: 50px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-width: 350px;
}

.sound-toggle {
    background: transparent;
    border: none;
    color: #3d3c3cc0;
    font-family: 'Michroma', sans-serif;
    font-size: 0.65rem;
    letter-spacing: 1px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    padding: 4px 0;
    pointer-events: auto;
    transition: color 0.3s ease, transform 0.2s ease;
    margin-bottom: 2px;
    text-transform: uppercase;
    width: fit-content;
}

.sound-toggle:hover {
    color: #aa2727cc;
    transform: translateX(3px);
}

.sound-toggle .sound-icon {
    width: 35px;
    height: 35px;
    transition: transform 0.3s ease;
    flex-shrink: 0;
}

.sound-toggle:hover .sound-icon {
    transform: scale(1.1);
}

.sound-toggle .sound-wave {
    transition: opacity 0.25s ease;
    opacity: 1;
}

.sound-toggle .sound-mute-line {
    transition: opacity 0.25s ease;
    opacity: 0;
    stroke: #aa2727cc;
}

/* Muted state */
.sound-toggle.muted {
    color: #3d3c3cc0;
}

.sound-toggle.muted:hover {
    color: #aa2727cc;
}

.sound-toggle.muted .sound-wave {
    opacity: 0;
}

.sound-toggle.muted .sound-mute-line {
    opacity: 1;
    stroke: #aa2727ee;
}

/* ===================== NAV GUIDE HINT ===================== */
#nav-guide {
    position: absolute;
    bottom: clamp(24px, 5vh, 45px);
    left: 50%;
    transform: translateX(-50%);
    pointer-events: none;
    z-index: 55;
    opacity: 1;
    width: max-content;
    max-width: calc(100% - 32px);
    display: grid;
    gap: 5px;
}

#nav-guide.visible {
    opacity: 1;
    transform: translateX(-50%);
}
.guide-mobile { display: none; }

@media (hover: none), (pointer: coarse) {
    .guide-desktop { display: none; }
    .guide-mobile { display: inline; }}

@media (min-width: 701px) and (max-width: 1200px) {
    #nav-guide,
#nav-guide.visible {
        left: auto;
        right: 32px;
        transform: none;
    }}

/* ===================== PORTFOLIO TITLE ===================== */
.portfolio-title {
    font-family: 'Audiowide', cursive;
    font-size: 1.1rem;
    letter-spacing: 1px;
    display: flex;
    align-items: center;
    gap: 10px;
}

.portfolio-desc {
    font-family: 'Michroma', sans-serif;
    font-size: 0.75rem;
    color: #888;
    line-height: 1.5;
    letter-spacing: 1px;
}

/* =====================================================================
   MOBILE RESPONSIVE — Main Breakpoints
   • ≤700px  : large phone / small tablet  (most layout shifts)
   • ≤480px  : phone portrait              (further scale-down)
   ===================================================================== */

@media (max-width: 700px) {

    /* ── UI OVERLAY — collapse nav to bottom, keep branding small ── */

    /* Top-left PORTFOLIO label */
    .ui-top-left {
        top: max(16px, env(safe-area-inset-top));
        left: 16px;
        font-size: clamp(0.68rem, 3.2vw, 0.85rem);
        letter-spacing: 1px;
    }

    /* Nav moves to bottom-center of the screen: top row (HOME / ABOUT /
       CONTACT) spans full width, bottom row (3DMODEL / MOTIONGRAPH) sits
       centered underneath, staggered between the gaps of the top row —
       so the whole block always occupies the bottom-center of the page. */
    .ui-top-right {
        top: auto;
        right: auto;
        bottom: max(18px, calc(env(safe-area-inset-bottom) + 14px));
        left: 50%;
        transform: translateX(-50%);
        align-items: center;
        gap: 7px;
        width: min(360px, calc(100vw - 20px));
    }

    .nav-row {
        gap: 10px;
        flex-wrap: nowrap;
        justify-content: space-between;
        width: 100%;
    }

    .nav-row.top-row {
        justify-content: space-between;
    }

    .nav-row.bottom-row {
        justify-content: center;
        gap: clamp(14px, 7vw, 26px);
        padding-right: 0;
    }

    .nav-link {
        font-size: clamp(0.52rem, 2.9vw, 0.68rem);
        letter-spacing: 0.06em;
        white-space: nowrap;
    }

    /* Bottom-left branding — move to top-right since nav moved down */
    .ui-bottom-left {
        bottom: auto;
        top: max(16px, env(safe-area-inset-top));
        left: auto;
        right: 16px;
        align-items: flex-end;
        text-align: right;
        max-width: min(42vw, 170px);
        gap: 4px;
    }

    .sound-toggle .sound-icon {
        width: 24px;
        height: 24px;
    }

    .portfolio-title {
        font-size: clamp(0.58rem, 2.8vw, 0.75rem);
        letter-spacing: 0.5px;
        gap: 5px;
    }

    .portfolio-desc {
        display: none;
        /* too crowded on small screens */
    }

    /* ── RIPPLE SIZE ──────────────────────────────────────────────── */
    :root {
        --size: 55vw;
        --scalingFactor: 6;
    }}

/* ── Extra small phones (≤480px) ───────────────────────────────────── */
@media (max-width: 480px) {
    .sound-toggle .sound-icon {
        width: 20px;
        height: 20px;
    }

    .ui-top-left {
        left: 12px;
        font-size: 0.62rem;
    }

    .ui-bottom-left {
        right: 12px;
        max-width: 45vw;
    }

    .ui-top-right {
        width: min(330px, calc(100vw - 16px));
        gap: 6px;
    }

    .nav-row {
        gap: 7px;
    }

    .nav-row.bottom-row {
        gap: clamp(10px, 5vw, 20px);
    }

    .nav-link {
        font-size: clamp(0.47rem, 2.75vw, 0.62rem);
        letter-spacing: 0.045em;
    }

    /* Nav wraps tighter */
    .nav-row {
        gap: 10px;
    }

    .nav-link {
        font-size: 0.65rem;
        letter-spacing: 0.8px;
    }}

/* Keep the cube visible under page panels, but disable interaction while a
   panel is open so scrolling/clicking belongs to the page. */
body.page-panel-open #main-content {
    z-index: 120;
}
body.site-hidden * {
    animation-play-state: paused !important;
}
.page-panel:not(.active) * { animation-play-state: paused !important; }
#scene-load-error {
    position: fixed;
    bottom: 80px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 200;
    max-width: calc(100% - 32px);
    padding: 12px 16px;
    background: #f7f4ed;
    color: #1b1b1e;
    border: 1px solid #849d8e;
    font: 400 14px/1.5 Inter, sans-serif;
}
#scene-load-error button { min-height: 44px; padding: 6px 12px; border: 0; background: #356953; color: #fff; }

@media (max-width: 700px) {

    .page-panel-content {
        width: 100%;
        padding-inline: 18px;
        box-sizing: border-box;
    }}

@media (max-width: 480px) {

    .page-panel-content {
        padding-inline: 14px;
    }}

/* ===================== GLITCH BOX GRID TRANSITION (.tt-glitchbox) ===================== */
.glitch-grid-wrap {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 10;
    overflow: hidden;
}

.glitch-grid-cell {
    position: absolute;
    background: #181820;
    opacity: 0;
    transform: scale(0.1) rotate(10deg);
}

.page-panel.tt-glitchbox.open .glitch-grid-cell {
    animation: glitchBoxPop 1.25s cubic-bezier(0.25, 0.9, 0.35, 1) forwards;
}

@keyframes glitchBoxPop {
    0% {
        opacity: 0;
        transform: scale(0.1) rotate(15deg);
        background: #111116;
    }

    22% {
        opacity: 0.95;
        transform: scale(1.08) rotate(-6deg);
        background: #b06be2;
    }

    40% {
        opacity: 1;
        transform: scale(1) rotate(0deg);
        background: #181824;
    }

    60% {
        opacity: 1;
        transform: scale(1) rotate(0deg);
        background: #181824;
    }

    78% {
        opacity: 0.8;
        transform: scale(0.8) rotate(5deg);
        background: #b06be2;
    }

    100% {
        opacity: 0;
        transform: scale(0) rotate(-12deg);
        background: transparent;
    }
}

.page-panel.tt-glitchbox {
    clip-path: none !important;
    background: transparent !important;
    opacity: 0;
    transition: opacity 180ms ease-out;
}

.page-panel.tt-glitchbox.open {
    opacity: 1;
    pointer-events: auto;
}

/* =====================================================================
   MOBILE RESPONSIVE — Final polish overrides
   ===================================================================== */

@media (max-width: 700px) {

    /* ── NAV GUIDE — placed higher up above bottom nav block ─────── */
    #nav-guide {
        bottom: max(118px, calc(env(safe-area-inset-bottom) + 112px));
    }

    /* ── GLITCH BOX — faster on mobile (fewer cells = less delay) ── */
    .page-panel.tt-glitchbox.open .glitch-grid-cell {
        animation-duration: 0.9s;
    }}

@media (max-width: 480px) {

    /* Guide nudges slightly higher on extra small phones */
    #nav-guide {
        bottom: max(112px, calc(env(safe-area-inset-bottom) + 106px));
    }}

/* ---- 06-page-base ---- */
/* =====================================================================
   PAGE CONTENT — ABOUT / 3DMODELS / MOTIONGRAPH / CONTACT
   Everything below is scoped to .pg so it never touches the cube UI.
   Look: notebook paper cards with hard offset shadows + tape, sitting
   on each face's own panel color (same vocabulary as the site stickers).
   ===================================================================== */
.page-panel-content.pg-host {
    width: 100%;
    flex: 1 1 auto;
    min-height: 0;
    align-self: stretch;
    padding: 0;
    overflow: hidden;
}

.pg {
    --paper: #f7f4ed;
    --ink: #1b1b1e;
    --gold: #d49b17;
    --red: #ff5555;
    --blue: #3b93d3;
    --green: #22b870;
    --tag-gold: rgba(212, 155, 23, 0.5);
    --tag-red: rgba(255, 85, 85, 0.45);
    --tag-blue: rgba(59, 147, 211, 0.45);
    --tag-green: rgba(34, 184, 112, 0.4);
    --tag-purple: rgba(138, 95, 208, 0.45);
    height: 100%;
    color: var(--paper);
    font-family: 'Inter', sans-serif;
    text-align: left;
}

.pg,
.pg *,
.pg *::before,
.pg *::after {
    box-sizing: border-box;
}

.pg :focus-visible {
    outline: 3px solid var(--gold);
    outline-offset: 3px;
}

.paper {
    position: relative;
    background: var(--paper);
    color: var(--ink);
    box-shadow: 5px 6px 0 rgba(0, 0, 0, 0.28);
}

/* ---- 08-contact-core ---- */


/* Contact uses the same centered paper reveal as About. */
.page-panel.tt-paper {
    clip-path: inset(48% 48% 48% 48% round 22px);
    opacity: 0;
    transition: clip-path var(--dur) cubic-bezier(0.76, 0, 0.24, 1),
        opacity calc(var(--dur) * 0.42) ease-out;
    will-change: clip-path;
}

/* All page controls stay unavailable until the current reveal has settled. */
body.panel-transitioning .page-panel.active {
    pointer-events: none;
}
.page-panel.tt-paper.open {
    clip-path: inset(0 0 0 0 round 0);
    opacity: 1;
}
.page-panel.tt-paper .page-panel-content {
    opacity: 0.4;
    transform: translateY(9px);
    transition: opacity calc(var(--dur) * 0.72) ease-out,
        transform var(--dur) cubic-bezier(0.16, 1, 0.3, 1);
}
.page-panel.tt-paper.open .page-panel-content {
    opacity: 1;
    transform: translateY(0);
    transition-delay: 0ms;
}

/* Low-power desktops use the same compositor-friendly fallback as constrained
   phones: no animated masks, no content parallax, just a short scale fade. */
.page-panel.low-power-transition {
    clip-path: none !important;
    opacity: 0;
    transform: translate3d(0, 0, 0) scale(0.975) !important;
    transition: transform var(--dur) cubic-bezier(0.22, 0.8, 0.25, 1),
        opacity calc(var(--dur) * 0.72) ease-out !important;
    will-change: transform, opacity;
    backface-visibility: hidden;
}

.page-panel.low-power-transition.open {
    clip-path: none !important;
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1) !important;
}

.page-panel.low-power-transition .page-panel-content,
.page-panel.low-power-transition.open .page-panel-content {
    filter: none !important;
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
}

/* ---- 11-contact-beam ---- */


/* ---- 12-about-effects ---- */


/* Low-power mobile panels use a compositor-only scale reveal. */
@media (max-width: 700px) {
    .page-panel.tt-iris,
.page-panel.tt-shear,
.page-panel.tt-fold,
.page-panel.tt-wipe,
.page-panel.tt-paper {
        clip-path: none;
        opacity: 0;
        transform: scale(0.96);
        transform-origin: center;
        transition: transform var(--dur) cubic-bezier(0.22, 0.8, 0.25, 1),
            opacity calc(var(--dur) * 0.72) ease-out;
        will-change: transform, opacity;
        backface-visibility: hidden;
    }

    .page-panel.tt-iris.open,
.page-panel.tt-shear.open,
.page-panel.tt-fold.open,
.page-panel.tt-wipe.open,
.page-panel.tt-paper.open {
        opacity: 1;
        transform: translate3d(0, 0, 0) scale(1);
        transition: transform var(--dur) cubic-bezier(0.22, 0.8, 0.25, 1),
            opacity calc(var(--dur) * 0.72) ease-out;
    }

    .page-panel.tt-iris .page-panel-content,
.page-panel.tt-shear .page-panel-content,
.page-panel.tt-fold .page-panel-content,
.page-panel.tt-wipe .page-panel-content,
.page-panel.tt-paper .page-panel-content,
.page-panel.tt-iris.open .page-panel-content,
.page-panel.tt-shear.open .page-panel-content,
.page-panel.tt-fold.open .page-panel-content,
.page-panel.tt-wipe.open .page-panel-content,
.page-panel.tt-paper.open .page-panel-content {
        filter: none;
        opacity: 1;
        transform: none;
        transition: none;
    }

    /* Capable mobile devices keep each face's own center-origin reveal. */
    .page-panel.tt-iris:not(.mobile-lite-transition) {
        clip-path: circle(0% at var(--ox) var(--oy));
        opacity: 0;
        transform: none;
        transition: clip-path var(--dur) cubic-bezier(0.65, 0, 0.35, 1),
            opacity calc(var(--dur) * 0.42) ease-out;
        will-change: clip-path, opacity;
    }

    .page-panel.tt-iris:not(.mobile-lite-transition).open {
        clip-path: circle(170% at var(--ox) var(--oy));
        opacity: 1;
        transform: none;
    }

    .page-panel.tt-shear:not(.mobile-lite-transition),
.page-panel.tt-paper:not(.mobile-lite-transition) {
        clip-path: inset(48% 48% 48% 48% round 22px);
        opacity: 0;
        transform: none;
        transition: clip-path var(--dur) cubic-bezier(0.76, 0, 0.24, 1),
            opacity calc(var(--dur) * 0.42) ease-out;
        will-change: clip-path, opacity;
    }

    .page-panel.tt-shear:not(.mobile-lite-transition).open,
.page-panel.tt-paper:not(.mobile-lite-transition).open {
        clip-path: inset(0 0 0 0 round 0);
        opacity: 1;
        transform: none;
    }

    .page-panel.tt-wipe:not(.mobile-lite-transition) {
        clip-path: circle(0% at 50% 50%);
        opacity: 0;
        transform: none;
        transition: clip-path var(--dur) cubic-bezier(0.16, 1, 0.3, 1),
            opacity calc(var(--dur) * 0.42) ease-out;
        will-change: clip-path, opacity;
    }

    .page-panel.tt-wipe:not(.mobile-lite-transition).open {
        clip-path: circle(150% at 50% 50%);
        opacity: 1;
        transform: none;
        transition: clip-path var(--dur) cubic-bezier(0.7, 0, 0.84, 0),
            opacity calc(var(--dur) * 0.42) ease-out;
    }}

/* ---- 15-about-layout ---- */

@font-face {
    font-family: 'Outfit';
    src: url('../assets/shared/fonts/Outfit-Variable.ttf') format('truetype');
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
}
@font-face {
    font-family: 'Anton';
    src: url('../assets/shared/fonts/Anton-Regular.ttf') format('truetype');
    font-style: normal;
    font-weight: 400;
    font-display: swap;
}
