@font-face {
    font-family: 'zed';
    src: url('/assets/fonts/zed_said/zed-said.otf') format('opentype');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Dark-brutal';
    src: url('/assets/fonts/DarkBrutalRegular.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}

@view-transition {
    navigation: auto;
}

/* On every same-origin page switch, the outgoing content waddles off to the
   right and the incoming content waddles in from the left — stop-motion style.
   The default root snapshot just cross-fades underneath. */
::view-transition-old(stage) {
    animation: stage-walk-out 0.6s both;
}
::view-transition-new(stage) {
    animation: stage-walk-in 0.6s both;
}

/* Foolproofing: kill the browser's automatic group geometry morph. <main> is
   a different size/position on each page (content height differs + it's
   flex-centered), so by default the UA tweens the group's position AND size
   between those two boxes — on top of our walk. That extra motion is what
   makes the transition look inconsistent (fine when pages match, janky when
   they don't). Pinning the group means ONLY our keyframes drive the snapshots. */
::view-transition-group(stage) { animation: none; }

/* The whole logo lockup (pig + wordmark) is one shared group (view-transition-name:
   logo on .logo), so it stays anchored while the stage walks. Hard-cut its
   snapshots instead of the default cross-fade, otherwise the pig's float and the
   letters' twitch shimmer between frames. Pin the group so it can't drift. */
::view-transition-group(logo) { animation: none; }
::view-transition-old(logo) { animation: none; opacity: 0; }
::view-transition-new(logo) { animation: none; opacity: 1; }

@keyframes stage-walk-out {
    /* step-end = hard stop-motion frames; waddle leans + bob while exiting right */
    0%   { opacity: 1; transform: translate(0, 0)      rotate(0deg);  animation-timing-function: step-end; }
    20%  { opacity: 1; transform: translate(8vw, -6px)  rotate(3deg);  animation-timing-function: step-end; }
    40%  { opacity: 1; transform: translate(22vw, 0)    rotate(-4deg); animation-timing-function: step-end; }
    60%  { opacity: 1; transform: translate(45vw, -6px) rotate(5deg);  animation-timing-function: step-end; }
    80%  { opacity: 1; transform: translate(75vw, 0)    rotate(-6deg); animation-timing-function: step-end; }
    100% { opacity: 0; transform: translate(110vw, -4px) rotate(7deg); animation-timing-function: step-end; }
}

@keyframes stage-walk-in {
    /* mirror of walk-in: waddle in from off-screen left and settle */
    0%   { opacity: 0; transform: translate(-110vw, 0)   rotate(-7deg); animation-timing-function: step-end; }
    8%   { opacity: 1; transform: translate(-110vw, 0)   rotate(-7deg); animation-timing-function: step-end; }
    25%  { opacity: 1; transform: translate(-72vw, -7px)  rotate(6deg);  animation-timing-function: step-end; }
    45%  { opacity: 1; transform: translate(-45vw, 0)     rotate(-6deg); animation-timing-function: step-end; }
    65%  { opacity: 1; transform: translate(-22vw, -6px)  rotate(5deg);  animation-timing-function: step-end; }
    82%  { opacity: 1; transform: translate(-8vw, 0)      rotate(-3deg); animation-timing-function: step-end; }
    93%  { opacity: 1; transform: translate(-2vw, -2px)   rotate(2deg);  animation-timing-function: step-end; }
    100% { opacity: 1; transform: translate(0, 0)         rotate(0deg); }
}

@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(stage),
    ::view-transition-new(stage),
    ::view-transition-old(logo),
    ::view-transition-new(logo) { animation: none; }
}



:root {
    --header-height: calc(100lvh - 4em);
    --main-padding: 3rem;
    --front-color: #2d2d2d;
    --back-color: #fff8e6;
    --paper-shadow: 4px 4px 0 #1a1a1a;
    --paper-shadow-light: 1px 2px 0px -1px rgba(0,0,0,0.34);
    --background-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.61'/%3E%3C/svg%3E");

    /* Sky gradient — defaults to a daytime sky; the head script overrides these
       per the current hour (no-JS users keep this pleasant day sky). */
    --sky-top: #3f81e0;
    --sky-mid: #8fc3f3;
    --sky-bottom: #cfeafd;

    /* Celestial defaults (daytime); the head script overrides per the hour. */
    --sun-op: 1;   --sun-x: 75%; --sun-y: 14%;
    --moon-op: 0;  --moon-x: 25%; --moon-y: 14%;
    --stars-op: 0;
    @media (max-width: 960px) and (min-width: 720px) {
        --main-padding: 2rem;
    }
    @media (max-width: 719px) {
        --main-padding: 1.1rem;
    }
}

@media (prefers-color-scheme: light) {
    :root {
        --front-color: #2d2d2d;
        --back-color: #fff8e6;
    }
}

@media (prefers-color-scheme: dark) {
    :root {
       /*
       --front-color: #2d2d2d;
        --back-color: #fff8e6;
        */
    }
}

html {
    box-sizing: border-box;
    /* Lives here, not on body: overflow on body (even just overflow-x) makes
       body itself a scroll container, which hijacks position:sticky's
       "nearest scrolling ancestor" — main's curtain-reveal stickiness would
       silently no-op (it'd track scroll 1:1 instead of pinning) if this were
       on body instead. On html it configures the viewport directly. */
    overflow-x: clip;           /* scattered tilts/offsets never spawn a h-scrollbar */
    background:
        radial-gradient(ellipse at 20% 30%, rgba(200,180,150,0.1) 0%, transparent 50%),
        radial-gradient(ellipse at 80% 70%, rgba(180,160,140,0.1) 0%, transparent 50%),
        var(--back-color);
}
*, *:before, *:after {
    box-sizing: inherit;
    vertical-align: baseline;
}

html, body {
    padding: 0;
    margin: 0;
    min-height: 100vh;
}

body {
    display: flex;
    flex-direction: column;
    align-items: center;        /* centers the footer; main centers its own content */
    min-height: 100vh;
    color: var(--front-color);
    font-size: 18px;
    line-height: 1.45;
    font-family: Georgia, 'Times New Roman', Times, serif;

    /* CSS-only paper-yellow texture: SVG film-grain tile over a warm radial
       base. The grain is fractalNoise rendered at low opacity; the gradient
       gives the subtle center glow / edge vignette. No image file needed. */
    background-color: #ffdfbc;

    @media (max-width: 719px) {
        font-size: 16px;
    }

}
/* Banner header holds the logo lockup at the top, centered. Sits ABOVE
   <main> (z-index) so that as it scrolls away, it visually uncovers the
   sticky-pinned <main> beneath it — see the "curtain reveal" comment there. */
.site-header {
    position: relative;
    z-index: 2;
    overflow: clip;             /* clip keeps the sticky logo working */
    display: flex;
    justify-content: center;
    align-items: center;
    width: calc(100% - 4em);
    margin: 2em auto -2em;
    height: var(--header-height);
    border: 10px solid var(--back-color);
    border-radius: 5px;
    @media (max-width: 719px) {
        margin: 0 auto;
        border-radius: 0;
        width: 100%;
    }
    padding: calc(var(--main-padding) + 2em) var(--main-padding) calc(var(--main-padding) + 4em);
    background-image: var(--background-noise), linear-gradient(180deg, var(--sky-top), var(--sky-mid) 55%, var(--sky-bottom));
    background-size: 180px 180px, cover;
    background-blend-mode: multiply;
    /* torn-paper mat, all the way around (replaces a plain CSS border). Two
       things rule out the obvious approaches: a filled sheet here would
       paint OVER the sky gradient (an element's own background is always
       its bottom-most layer, no pseudo-element z-index gets behind it), and
       `overflow: clip` on .site-header (needed to contain the oversized
       logotype) clips anything the pseudo paints OUTSIDE the header's own
       box — an outward box-shadow ring just gets cut off. So instead this
       exactly overlays the header (inset: 0) with a TRANSPARENT center and
       an INSET 15px box-shadow ring, drawn inward along the inside edge —
       fully within the clip boundary. The sky shows through the transparent
       middle; the paper-cut filter wobbles the ring's inner edge into a
       hand-torn look. */
    &:before {
        content: "";
        position: absolute;
        inset: -10px;
        border-radius: 22px 9px 30px 12px / 12px 26px 8px 20px;
        border-radius: 0;
        box-shadow: inset 0 0 0 20px var(--back-color), var(--paper-shadow-light);
        filter: url(#paper-cut);
    }
}

/* Sun / moon / stars — positioned within .site-header's own sky (they're
   DOM children of it), not the viewport, so they're contained/clipped to
   the header (its `overflow: clip`) and scroll away together with it
   instead of floating fixed over the whole page. Opacity and position come
   from the time-of-day script via custom properties. */
.sun, .moon, .star {
    position: absolute;
    z-index: 0;
    pointer-events: none;
    user-select: none;
    height: auto;
}
.sun {
    width: clamp(70px, 9vw, 120px);
    top: var(--sun-y); left: var(--sun-x);
    translate: -50% 0;
    opacity: var(--sun-op);
    filter: drop-shadow(0 0 34px rgba(255, 210, 90, 0.6));
}
.moon {
    width: clamp(54px, 7vw, 96px);
    top: var(--moon-y); left: var(--moon-x);
    translate: -50% 0;
    opacity: var(--moon-op);
    filter: drop-shadow(0 0 24px rgba(220, 230, 255, 0.5));
}
.star {
    width: clamp(7px, 1.3vw, 15px);
    opacity: var(--stars-op);
}
.star--1 { top: 9%;  left: 18%; }
.star--2 { top: 16%; left: 43%; }
.star--3 { top: 11%; left: 67%; }
.star--4 { top: 23%; left: 83%; }
.star--5 { top: 27%; left: 29%; }
.star--6 { top: 7%;  left: 55%; }




/* Curtain reveal: .stage-wrap is pulled up (negative margin, exactly the
   header's height) to sit in the SAME flow position as .site-header. <main>
   inside it is `sticky`, so from scroll 0 it's already sitting fully behind
   the header (lower z-index), just covered by it — scrolling doesn't move
   it, the header just slides up and off, like a rising curtain, uncovering
   main rather than main sliding into view.

   A sticky element stays stuck only while its CONTAINING BLOCK (its parent,
   here .stage-wrap — not <main> itself) has flow-box left below the viewport
   to "release into". If <main> were both the sticky element AND the thing
   whose height creates that slack, its height would cancel itself out of the
   equation — the release point would end up completely decoupled from the
   header's height (and instead governed by whatever comes after, e.g. a
   footer). .stage-spacer is what supplies exactly the header's height of
   slack, independent of how tall <main>'s own content is, so the curtain
   reliably finishes rising exactly when the header has fully scrolled away —
   no more, no less — regardless of page content length. */
.stage-wrap {
    width: 100%;
    flex: 1;
    margin-top: calc(-1 * var(--header-height));
}

.stage-spacer {
    height: var(--header-height);
}

main {
    min-height: 100lvh;
    width: 100%;
    position: sticky;
    top: 0;
    z-index: 1;                  /* behind .site-header while they overlap */
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 0;
    view-transition-name: stage;
    /* opaque paper so it occludes the logo as it scrolls over */
    background-color: transparent;
    background-size: 180px 180px;
    background-blend-mode: multiply;
    background-position: center;
}

.content {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
}

.part-content {
    min-height: 100lvh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 33%;
    background: #6c6053;
    padding: var(--main-padding);
    @media (max-width: 719px) {
        margin: 0 auto;
        border-radius: 0;
        width: 100%;
    }

    p {
        width: 100%;
    }

}

.part-content:nth-of-type(1) {
    width: 100%;
    color: #fff;
}
.part-content:nth-of-type(2) { background: #e800d9; }
.part-content:nth-of-type(3) { background: #00b2e8; }
.part-content:nth-of-type(4) { background: #e8c100; }


/* Logo lockup: one shared transition group → it stays anchored across page
   switches while the stage walks. The pig and wordmark each walk in
   separately (own timing below) rather than as one rigid block, then the
   pig keeps its own idle float on top of that. */
.logo {
    width: 95%;
    z-index: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    view-transition-name: logo;
    filter: drop-shadow(#2227 1px 1px);
}

.pig {
    margin: 1em auto 0;
    width: 490px;
    max-width: 100%;
    /* walks in first, then settles into the idle bob (delayed to start
       right as the walk-in ends, so the two hand off without a jump) */
    animation: walk-in 2.2s forwards, paper-float 10s 1.3s infinite;
    @media (min-width: 1920px) {
        width: 500px;
    }
    @media (max-width: 960px) {
        width: 300px;
    }
}

.logotype {
    font-family: 'Dark-brutal','BlackironDemoRegular', 'westend-ridge', Georgia, 'Times New Roman', Times, serif;
    width: auto;
    max-width: 90%;
    position: relative;
    paint-order: stroke fill;
    font-size: clamp(10rem, 18vw, 12rem);
    text-align: center;
    margin: -0.23em 0 0;
    text-decoration: none;
    color: #3b2201;
    -webkit-text-stroke: 0;
    line-height: 0.65;
    word-spacing: -0.2em;
    text-transform: uppercase;
    display: inline-block;
    /* no z-index here on purpose: an explicit value would isolate .logotype
       into its own stacking context and trap .letter/.under-paw inside it,
       making it impossible for individual letters to sit on either side of
       .pig. Leaving it auto lets their z-index compete directly with .pig's. */
    /* Walks in a beat after the pig — same walk-in keyframes, just delayed.
       `backwards` (not `both`): fill-forward would leave a permanent, merely
       identity `transform` sitting on the element after the animation ends,
       which — unlike z-index:auto — WOULD isolate a new stacking context and
       break the .under-paw/.pig/.letter ordering above. `backwards` only
       holds the pre-start (off-screen) pose during the delay, then hands
       back off to the plain untransformed styles once done. */
    animation: walk-in 1.3s backwards;
}
.logotype::before {
    content: attr(data-char);
    position: absolute;
    inset: 0;
    z-index: -1;
    -webkit-text-stroke: 0.08em var(--back-color);
    paint-order: stroke fill;
}

/* "The" is split into individual letters so each gets its own halo (matches
   the rest of the wordmark, which is haloed as one block via .logotype::before
   above — only "The" needs the per-letter treatment). */
.logotype .letter {
    display: inline-block;
    position: relative;
    z-index: 2;
}

.logotype .letter::before {
    content: attr(data-char);
    position: absolute;
    inset: 0;
    z-index: -1;
    -webkit-text-stroke: 0.05em var(--back-color);
    paint-order: stroke fill;
}

/* Stacking, low to high: .under-paw < .pig < .letter — "he" tucks behind
   the pig's paw/feet, the rest of the wordmark stays in front of it. The
   higher specificity here (over the plain .logotype .letter rule above)
   is what overrides those letters back down below .pig. */
.logotype .letter.under-paw {
    z-index: 0;
    display: inline;
}

/* Plain page heading — lives inside a .paper block now, so no stroked letters
   or walk-in animation. */
h1 {
    font-family: 'zed', Georgia, 'Times New Roman', Times, serif;
    font-size: 2.6rem;
    line-height: 1.05;
    margin: 0 0 0.5em;
}

h2, h3 {
    font-family: 'zed', Georgia, 'Times New Roman', Times, serif;
}

p, ul {
    width: min(56ch, calc(100vw - 68px));
    text-align: left;

    a {
        display: inline;
    }
}

/* Drop .paper on any element to sit its content on a torn-edge paper sheet.
   The sheet itself lives on ::before so the irregular-edge filter only
   wobbles the paper border — the text on top stays perfectly crisp. */
.paper {
    /* resting pose the walk-in settles into — per-sheet offsets + rotation
       (set below) scatter the sheets out of the tidy column */
    --rest-rotate: -0.5deg;
    --rest-x: 0;
    --rest-y: 0;
    --paper-tint: var(--back-color);   /* per-sheet shade, set below */
    width: fit-content;
    position: relative;
    padding: 14px;
    margin: 1em 0 2em;
    transform: translate(var(--rest-x), var(--rest-y)) rotate(var(--rest-rotate));
}

/* Scatter the sheets like paper tossed on a desk — varied tilt everywhere,
   plus horizontal drift on wider screens (kept off mobile to avoid overflow).
   Each also gets its own subtle shade (cream base mixed with a faint hue). */
.paper:nth-of-type(1) { --rest-rotate: -2.6deg; --paper-tint: color-mix(in srgb, var(--back-color) 88%, #ff9d6e); }
.paper:nth-of-type(2) { --rest-rotate:  2.2deg; --rest-y: -0.6em; --paper-tint: color-mix(in srgb, var(--back-color) 88%, #7fc8a0); }
.paper:nth-of-type(3) { --rest-rotate: -1.5deg; --rest-y: -0.3em; --paper-tint: color-mix(in srgb, var(--back-color) 88%, #7cb6e6); }
.paper:nth-of-type(4) { --rest-rotate:  1.4deg; --paper-tint: color-mix(in srgb, var(--back-color) 88%, #c79be0); }
.paper:nth-of-type(5) { --rest-rotate: -2deg;   --paper-tint: color-mix(in srgb, var(--back-color) 88%, #f2c14e); }
.paper:nth-of-type(6) { --rest-rotate:  1.8deg; --paper-tint: color-mix(in srgb, var(--back-color) 88%, #e88aa6); }

@media (min-width: 720px) {
    .paper:nth-of-type(1) { --rest-x: -3.5vw; }
    .paper:nth-of-type(2) { --rest-x:  4vw; }
    .paper:nth-of-type(3) { --rest-x: -2vw; }
    .paper:nth-of-type(4) { --rest-x:  2.5vw; }
}

.paper > * {
    position: relative;   /* keep content above the ::before sheet */
    z-index: 1;
}

.paper::before {
    content: "";
    position: absolute;
    inset: -0.4em;        /* overshoot so the torn edge has room to wander */
    z-index: 0;

    /* paper-look background: per-sheet tinted base */
    background-color: var(--paper-tint);
    box-shadow: var(--paper-shadow-light);

    /* lopsided elliptical corners (h-radii / v-radii), then the filter
       wobbles them = organic, hand-torn look — no two corners alike */
    border-radius: 22px 9px 30px 12px / 12px 26px 8px 20px;

    /* the irregular, hand-cut edge */
    filter: url(#paper-cut);
}

.cta {
    --rest-rotate: 0.2deg;
    display: inline-block;
    width: fit-content;
    text-align: center;
    padding: 1em 1em;
    margin: 2rem 0;
    background: var(--front-color);
    text-decoration: none;
    color: var(--back-color);
    font-family: 'zed', Georgia, 'Times New Roman', Times, serif;
    font-weight: bold;
    transition: background-color 0.2s ease-in-out;
    transform: rotate(var(--rest-rotate));
    &:hover {}
}

/* ── One-time stop-motion entrance ──────────────────────────────────
   Each piece waddles in from the left, staggered, then holds its pose. */
/* Scroll-reveal: sheets/buttons stay hidden until they scroll into view, then
   waddle in once (.in-view added by IntersectionObserver) and settle into the
   idle breathe. Hidden state is gated to html.js so no-JS users still see them. */
html.js .paper,
html.js .cta {
    opacity: 0;
}
.paper.in-view,
.cta.in-view {
    animation: walk-in 1.3s both, paper-breathe 22s infinite;
    /* --reveal-d staggers items revealed together; breathe trails the 1.3s walk-in */
    animation-delay: var(--reveal-d, 0s), calc(var(--reveal-d, 0s) + 1.3s);
}

@media (prefers-reduced-motion: reduce) {
    /* no reveal animation — just show everything */
    html.js .paper,
    html.js .cta { opacity: 1; animation: none; }
}

@keyframes walk-in {
    /* step-end on every frame = hard cuts between poses (stop-motion).
       Rotation alternates each frame like footsteps; Y bobs up on the
       swing and plants back down; X slides in from off-screen left. */
    0%   { opacity: 0; transform: translate(-100vw, 0)   rotate(-7deg); animation-timing-function: step-end; }
    8%   { opacity: 1; transform: translate(-100vw, 0)   rotate(-7deg); animation-timing-function: step-end; }
    20%  { opacity: 1; transform: translate(-78vw, -8px)  rotate(7deg);  animation-timing-function: step-end; }
    32%  { opacity: 1; transform: translate(-58vw, 0)     rotate(-7deg); animation-timing-function: step-end; }
    44%  { opacity: 1; transform: translate(-40vw, -7px)  rotate(6deg);  animation-timing-function: step-end; }
    56%  { opacity: 1; transform: translate(-25vw, 0)     rotate(-5deg); animation-timing-function: step-end; }
    68%  { opacity: 1; transform: translate(-13vw, -5px)  rotate(4deg);  animation-timing-function: step-end; }
    80%  { opacity: 1; transform: translate(-5vw, 0)      rotate(-3deg); animation-timing-function: step-end; }
    90%  { opacity: 1; transform: translate(-1vw, -2px)   rotate(2deg);  animation-timing-function: step-end; }
    100% { opacity: 1; transform: translate(var(--rest-x, 0), var(--rest-y, 0)) rotate(var(--rest-rotate, 0deg)); }
}

@media (prefers-reduced-motion: reduce) {
    .pig, .logotype, .paper, .cta { animation: none; }
}

@keyframes paper-float {
    /* step-end on every frame = each pose snaps to the next, like
       hand-shot stop-motion frames rather than a smooth tween */
    0%   { transform: translate(0, 0) rotate(0deg);          animation-timing-function: step-end; }
    5%   { transform: translate(-1px, -1.5px) rotate(0.5deg);  animation-timing-function: step-end; }
    10%  { transform: translate(1px, -3px) rotate(-0.6deg);   animation-timing-function: step-end; }
    15%  { transform: translate(-1.5px, -1px) rotate(0.4deg); animation-timing-function: step-end; }
    20%  { transform: translate(2px, -4px) rotate(-0.3deg);   animation-timing-function: step-end; }
    25%  { transform: translate(-0.5px, -5px) rotate(0.7deg); animation-timing-function: step-end; }
    30%  { transform: translate(1.5px, -2px) rotate(-0.5deg); animation-timing-function: step-end; }
    35%  { transform: translate(-2px, -3.5px) rotate(0.25deg); animation-timing-function: step-end; }
    40%  { transform: translate(0.5px, -1px) rotate(-0.65deg); animation-timing-function: step-end; }
    45%  { transform: translate(-1px, -4.5px) rotate(0.45deg); animation-timing-function: step-end; }
    50%  { transform: translate(2.5px, -2.5px) rotate(-0.2deg); animation-timing-function: step-end; }
    55%  { transform: translate(-1.5px, -0.5px) rotate(0.55deg); animation-timing-function: step-end; }
    60%  { transform: translate(1px, -3.5px) rotate(-0.75deg); animation-timing-function: step-end; }
    65%  { transform: translate(-2.5px, -1.5px) rotate(0.35deg); animation-timing-function: step-end; }
    70%  { transform: translate(0.5px, -5px) rotate(-0.4deg);  animation-timing-function: step-end; }
    75%  { transform: translate(2px, -1px) rotate(0.6deg);     animation-timing-function: step-end; }
    80%  { transform: translate(-1px, -3px) rotate(-0.55deg);  animation-timing-function: step-end; }
    85%  { transform: translate(1.5px, -2px) rotate(0.3deg);   animation-timing-function: step-end; }
    90%  { transform: translate(-1.5px, -4px) rotate(-0.25deg); animation-timing-function: step-end; }
    95%  { transform: translate(1px, -1px) rotate(0.65deg);    animation-timing-function: step-end; }
    100% { transform: translate(0, 0) rotate(0deg);          animation-timing-function: step-end; }
}

@keyframes paper-breathe {
    /* Like paper-float but slower (14s) and with breaks — the sheet sits
       still for a beat, twitches through a few stop-motion frames, rests
       again. Holds are made by repeating a pose across two keyframe stops. */
    0%, 14%  { transform: translate(0, 0)     rotate(0deg);    animation-timing-function: step-end; } /* rest */
    19%      { transform: translate(2px, -3px) rotate(0.6deg);  animation-timing-function: step-end; }
    24%      { transform: translate(-1px, -1px) rotate(-0.5deg); animation-timing-function: step-end; }
    29%      { transform: translate(3px, -2px) rotate(0.4deg);  animation-timing-function: step-end; }
    34%, 50% { transform: translate(1px, -1px) rotate(-0.3deg); animation-timing-function: step-end; } /* break */
    55%      { transform: translate(-2px, -3px) rotate(0.7deg); animation-timing-function: step-end; }
    60%      { transform: translate(2px, -1px) rotate(-0.6deg); animation-timing-function: step-end; }
    65%      { transform: translate(-1px, -4px) rotate(0.5deg); animation-timing-function: step-end; }
    70%, 86% { transform: translate(0, -1px)   rotate(-0.2deg); animation-timing-function: step-end; } /* break */
    91%      { transform: translate(2px, -2px) rotate(0.6deg);  animation-timing-function: step-end; }
    96%      { transform: translate(-1px, -1px) rotate(-0.4deg); animation-timing-function: step-end; }
    100%     { transform: translate(0, 0)     rotate(0deg);    animation-timing-function: step-end; } /* back to rest */
}

@keyframes paper-tilt {
    0%, 100% { transform: rotate(-2deg); }
    50% { transform: rotate(1deg); }
}

@keyframes paper-settle {
    0%, 100% { transform: rotate(0.5deg) translateY(0); }
    50% { transform: rotate(0.3deg) translateY(-1px); }
}

/* ── Brand logos ────────────────────────────────────────────────────── */
.brands {
    width: min(56ch, calc(100vw - 68px));
    margin: 3rem 0 1rem;
    text-align: center;
}

.brands h2 {
    font-family: 'zed', Georgia, 'Times New Roman', Times, serif;
    font-size: 1.4rem;
    font-weight: normal;
    letter-spacing: 0.02em;
    margin: 0 0 1.5rem;
    opacity: 0.85;
}

.brands ul {
    list-style: none;
    margin: 0;
    padding: 0;
    width: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 1.5rem 2.25rem;
}

/* Each logo is painted in ink via a mask, so mixed-colour (and white) logos
   render uniformly on the paper background. The file comes in as --logo. */
.brands .brand {
    width: 96px;
    height: 34px;
    background-color: var(--front-color);
    opacity: 0.72;
    transition: opacity 0.2s ease-in-out;

    /* longhands, not the `mask` shorthand: substituting a url() custom property
       into the shorthand is unreliable and silently drops the mask. */
    -webkit-mask-image: var(--logo);
    mask-image: var(--logo);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
}
.brands .brand:hover {
    opacity: 1;
}

/* ── Footer ─────────────────────────────────────────────────────────── */
.site-footer {
    width: 90%;
    max-width: min(60%, calc(100vw - 68px));
    margin-top: 3rem;
    padding-top: 1.5rem;
    text-align: center;
    font-size: 0.9rem;
    color: var(--front-color);
    animation: unset;
}

.site-footer .social ul {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 1.25rem;
    width: auto;
}

.site-footer .social a {
    color: var(--front-color);
    text-decoration: none;
    font-weight: bold;
    border-bottom: 2px solid transparent;
    transition: border-color 0.2s ease-in-out;
}

.site-footer .social a:hover,
.site-footer .social a:focus-visible {
    border-bottom-color: var(--front-color);
}

.site-footer .colophon {
    width: auto;
    margin: 0;
    opacity: 0.7;
}


/* ── "Get in touch" button + popup ──────────────────────────────────────
   A floating button fixed bottom-right; clicking opens a dialog that pops
   out of that corner. Same behaviour on mobile and desktop. */
/* The contact button is a little cream envelope; its flap lifts open on hover. */
.contact-btn.envelope {
    --env: #ece5d3;           /* envelope cream */
    --env-shade: #c9bfa6;     /* edges / seams  */
    --env-flap: #ded5bf;      /* flap (slightly darker) */
    position: fixed;
    right: 1.4rem;
    bottom: 1.4rem;
    z-index: 50;
    width: 96px;
    height: 66px;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
    transform: rotate(-2deg);
    transition: transform 0.18s ease;
    filter: drop-shadow(2px 5px 6px rgba(0, 0, 0, 0.3));
}
.contact-btn.envelope:hover,
.contact-btn.envelope:focus-visible { transform: rotate(2deg) translateY(-2px); }

.contact-btn.envelope span { position: absolute; inset: 0; }

/* the envelope body + the two front pocket seams (diagonals from the bottom) */
.envelope__body {
    background: var(--env);
    border-radius: 5px;
    box-shadow:
        inset 0 0 0 2px var(--env-shade),
        inset 0 -6px 10px -8px rgba(0, 0, 0, 0.3);
}
.envelope__body::before {
    content: "";
    position: absolute;
    inset: 2px;
    background:
        linear-gradient(to top right, transparent calc(50% - 0.8px), var(--env-shade) 50%, transparent calc(50% + 0.8px)) bottom left / 50% 100% no-repeat,
        linear-gradient(to top left,  transparent calc(50% - 0.8px), var(--env-shade) 50%, transparent calc(50% + 0.8px)) bottom right / 50% 100% no-repeat;
}

/* the closed flap: a triangle from the top corners to a point ~62% down.
   On hover it flips up (scaleY -1 about the top edge) like it's opening. */
.envelope__flap {
    z-index: 1;
    background: var(--env-flap);
    clip-path: polygon(2px 2px, calc(100% - 2px) 2px, 50% 62%);
    box-shadow: inset 0 0 0 2px var(--env-shade);
    transform-origin: top;
    transition: transform 0.3s ease;
}
.contact-btn.envelope:hover .envelope__flap,
.contact-btn.envelope:focus-visible .envelope__flap { transform: scaleY(-1); }

/* The popup, anchored just above the button in the bottom-right corner. */
.contact-dialog {
    inset: auto 1.25rem 5rem auto;   /* bottom-right, above the button */
    margin: 0;
    width: min(300px, 82vw);
    padding: 1.8rem 1.8rem 1.6rem;
    color: var(--back-color);
    background: var(--front-color);
    border: 3px solid var(--front-color);
    border-radius: 6px;
    box-shadow:
        inset 0 0 0 3px var(--front-color),
        inset 0 0 0 5px var(--back-color),
        0 12px 30px -8px rgba(0, 0, 0, 0.5);

    /* pop out of the button corner */
    transform-origin: bottom right;
    opacity: 0;
    transform: scale(0.8) translate(8px, 12px);
    transition:
        opacity 0.18s ease,
        transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1.2),
        display 0.22s allow-discrete,
        overlay 0.22s allow-discrete;
}
.contact-dialog[open] {
    opacity: 1;
    transform: scale(1) translate(0, 0);
}
@starting-style {
    .contact-dialog[open] {
        opacity: 0;
        transform: scale(0.8) translate(8px, 12px);
    }
}
.contact-dialog::backdrop {
    background: rgba(26, 26, 26, 0);
    transition:
        background 0.2s ease,
        display 0.2s allow-discrete,
        overlay 0.2s allow-discrete;
}
.contact-dialog[open]::backdrop {
    background: rgba(26, 26, 26, 0.4);
}
@starting-style {
    .contact-dialog[open]::backdrop {
        background: rgba(26, 26, 26, 0);
    }
}

.contact-dialog__close {
    position: absolute;
    top: 0.6rem;
    right: 0.8rem;
    width: 36px;
    height: 36px;
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    background: transparent;
    color: var(--back-color);
    border: none;
}

.contact-dialog__title {
    font-family: 'zed', Georgia, 'Times New Roman', Times, serif;
    font-size: 1.6rem;
    margin: 0 0 0.8rem;
    width: auto;
    text-align: left;
}

.contact-dialog ul {
    list-style: none;
    margin: 0;
    padding: 0;
    width: auto;
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}
.contact-dialog a {
    color: var(--back-color);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    transition: border-color 0.2s ease-in-out;
}
.contact-dialog a:hover,
.contact-dialog a:focus-visible {
    border-bottom-color: var(--back-color);
}

@media (prefers-reduced-motion: reduce) {
    .contact-btn,
    .contact-dialog,
    .contact-dialog::backdrop { transition: none; }
}

/* ── Scroll-driven animations ──────────────────────────────────────────
   Placed at the very end so they win over the base .pig rule (the
   `animation` shorthand there would otherwise reset animation-timeline to auto).
   Wrapped in @supports so browsers WITHOUT scroll-driven animations skip them
   entirely — otherwise the 0s time-based fallback + `both` fill would snap
   elements to their end state. */
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: scroll()) {
        /* The pig flies a loop-the-loop, finishing by 50vh of scroll. The loop is
           a circle sampled with translate + rotate longhands (rotate follows the
           tangent = nose-first). Start and end are exactly translate:0 0; rotate:0,
           so it begins and lands in its original spot. These longhands compose with
           paper-float's transform, so the idle bob is preserved. Also re-lists the
           base rule's walk-in/paper-float here — this shorthand fully replaces the
           base one rather than merging, so anything not repeated would be lost. */
        .pig {
            animation: walk-in 1.3s both, paper-float 10s 1.3s infinite, pig-loop step-end both;
            animation-timeline: auto, auto, scroll(root block);
            animation-range: normal, normal, 0 50vh;
        }

        /* .site-header isn't full-width, so <main> peeks through on either
           side of it while the curtain's still up. Blurring <main> at rest
           and smoothly sharpening it over the same 0–header-height scroll
           range as the curtain reveal hides that peek until the header's
           fully out of the way — blur reaches exactly 0 right as main
           finishes un-sticking (see the .stage-wrap comment above). Kept
           smooth/linear rather than the site's usual step-end stop-motion —
           a stepped blur radius reads as flickery rather than charming. */
        main {
            animation: stage-sharpen linear both;
            animation-timeline: scroll(root block);
            animation-range: 0 var(--header-height);
        }
    }
}

@keyframes stage-sharpen {
    0%   { filter: blur(14px); }
    100% { filter: blur(0); }
}

@keyframes pig-loop {
    0%    { translate: 0 0;          rotate: 0deg; }
    12.5% { translate: 99px -41px;   rotate: -45deg; }
    25%   { translate: 140px -140px; rotate: -90deg; }
    37.5% { translate: 99px -239px;  rotate: -135deg; }
    50%   { translate: 0 -280px;     rotate: -180deg; }
    62.5% { translate: -99px -239px; rotate: -225deg; }
    75%   { translate: -140px -140px; rotate: -270deg; }
    87.5% { translate: -99px -41px;  rotate: -315deg; }
    100%  { translate: 0 0;          rotate: -360deg; }
}

/* ── Sky clock (draggable 24h analog clock, always visible) ─────────────── */
.sky-clock {
    display: none;
    position: fixed;
    left: 1rem;
    bottom: 1rem;
    z-index: 100;
    user-select: none;
    touch-action: none;          /* dragging shouldn't scroll the page */
}
.sky-clock.is-on { display: block; }
.sky-clock svg { width: 104px; height: 104px; cursor: grab; overflow: visible; }
.sky-clock svg:active { cursor: grabbing; }
/* dark ink elements on a faint cream disc so it reads on the page (no box) */
.clock-face { fill: color-mix(in srgb, var(--back-color) 72%, transparent); stroke: var(--front-color); stroke-width: 2; }
.tick { stroke: var(--front-color); stroke-width: 1; opacity: 0.4; }
.tick--major { stroke-width: 2.2; opacity: 0.85; }
.clock-num { fill: var(--front-color); font: 700 9px/1 ui-monospace, monospace; text-anchor: middle; dominant-baseline: central; }
.hand { stroke: var(--front-color); stroke-linecap: round; }
.hand--hour { stroke-width: 3.4; }
.hand--min { stroke-width: 2; opacity: 0.75; }
.clock-cap { fill: var(--front-color); }
