/* ==========================================================================
   cozy.css — cozy lofi fall layer (sprites, scattered props, ambient leaves)

   Loaded after styles.css. Colours come from the theme tokens
   (--color-bg, --color-text, --color-accent, ...); sprite colours come
   from the shared palette in tools/sprites/pixel2svg.py.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. .sprite helper

   <span class="sprite" role="img" aria-label="..."
         style="--sprite:url(/sprites/coffee-sheet.svg); --w:20; --h:24;
                --frames:4; --fps:4; --scale:2"></span>

   --w/--h are the sprite's native pixel size (one frame), --frames/--fps
   come from Public/sprites/<name>.json. --scale must be an integer so
   pixels stay square; it is deliberately NOT declared on .sprite so a
   container can set it for every sprite inside.
   -------------------------------------------------------------------------- */
.sprite {
    display: inline-block;
    flex: none;
    vertical-align: bottom;
    width: calc(var(--w) * var(--scale, 2) * 1px);
    height: calc(var(--h) * var(--scale, 2) * 1px);
    background-image: var(--sprite);
    background-repeat: no-repeat;
    background-position: 0 0;
    background-size:
        calc(var(--w) * var(--frames, 1) * var(--scale, 2) * 1px)
        calc(var(--h) * var(--scale, 2) * 1px);
    image-rendering: pixelated;
    animation: sprite-play calc(var(--frames, 1) / var(--fps, 4) * 1s) steps(var(--frames, 1)) infinite;
}

@keyframes sprite-play {
    to {
        background-position: calc(var(--w) * var(--frames, 1) * var(--scale, 2) * -1px) 0;
    }
}

.sprite--flip {
    transform: scaleX(-1);
}

.sprite--paused {
    animation-play-state: paused;
}

/* --------------------------------------------------------------------------
   2. Sprites scattered around the site

   The dog naps on the header, Zach stands in the corner of /about, and
   the odd prop sits beside a page title (.heading-sprite in styles.css).
   -------------------------------------------------------------------------- */

/* Interactive sprites are real buttons, styled away. */
.cozy-prop {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    line-height: 0;
    cursor: pointer;
    position: relative;
}

.cozy-prop:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
}

/* ---- the dog, napping on the header ----
   Last item in .site-nav, pinned to the bottom of the title bar so it
   lies on the bar's bottom edge. Only the sleeping sprite takes up
   layout; the taller awake sprite grows upward out of the same spot. */
.nav-dog {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    margin: 0 0 0 calc(var(--space-sm) * -1);
    font: inherit;
    line-height: 0;
    cursor: pointer;
    position: relative;
    align-self: flex-end;
    width: 64px;
    height: 24px;
    transition: opacity 0.3s;
}

.nav-dog:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
}

.nav-dog .sprite {
    position: absolute;
    left: 0;
    bottom: 0;
}

.nav-dog .sprite--awake { display: none; }
.nav-dog.is-happy .sprite--asleep { display: none; }
.nav-dog.is-happy .sprite--awake { display: inline-block; }
.nav-dog.is-happy .nav-dog__z { display: none; }
.nav-dog.is-happy .sprite--awake {
    animation:
        sprite-play calc(var(--frames, 1) / var(--fps, 4) * 1s) steps(var(--frames, 1)) infinite,
        cozy-hop 0.42s cubic-bezier(0.3, 0.7, 0.4, 1) 3;
}

/* off on its once-a-session run (cozy.js) */
.nav-dog.is-out {
    opacity: 0;
    pointer-events: none;
}

/* little pixel z's drifting up off the nap */
.nav-dog__z {
    position: absolute;
    right: 2px;
    bottom: 18px;
    pointer-events: none;
    font-family: var(--font-pixel);
    font-size: 0.7rem;
    line-height: 1;
    color: var(--color-text-muted);
}

.nav-dog__z i {
    position: absolute;
    font-style: normal;
    opacity: 0;
    animation: cozy-z 3.2s linear infinite;
}

.nav-dog__z i + i {
    font-size: 0.85em;
    animation-delay: 1.6s;
}

@keyframes cozy-z {
    0% { opacity: 0; transform: translate(0, 0); }
    20% { opacity: 0.9; }
    100% { opacity: 0; transform: translate(8px, -14px); }
}

@keyframes cozy-hop {
    0%, 100% { transform: translateY(0) scaleY(1); }
    15% { transform: translateY(0) scale(1.06, 0.9); }
    50% { transform: translateY(-10px) scale(0.96, 1.05); }
}

/* the 404 page has the dog in the content instead ("followed you in") */
body:has(.not-found-dog) .nav-dog { display: none; }

/* stretch the nav to the full bar height so the dog lies on its edge */
.site-header .site-nav {
    align-self: stretch;
    align-items: center;
}

@media (min-width: 769px) and (max-width: 1000px) {
    .site-header .site-nav { gap: var(--space-md); }
}

@media (max-width: 768px) {
    .site-header .site-nav { flex-wrap: wrap; justify-content: center; }
    .nav-dog { margin-left: 0; }
}

/* ---- Zach on /about ---- */
.about-zach {
    float: right;
    margin: 0 0 var(--space-md) var(--space-lg);
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-xs);
    max-width: 15rem;
}

.about-zach__bubble {
    position: relative;
    margin: 0;
    padding: var(--space-xs) var(--space-sm);
    background: var(--color-surface);
    color: var(--color-text);
    font-family: var(--font-pixel);
    font-size: 0.85rem;
    line-height: 1.35;
    box-shadow:
        0 calc(var(--px) * -1) 0 0 var(--color-frame),
        0 var(--px) 0 0 var(--color-frame),
        calc(var(--px) * -1) 0 0 0 var(--color-frame),
        var(--px) 0 0 0 var(--color-frame);
}

/* stepped tail pointing down at Zach */
.about-zach__bubble::after {
    content: "";
    position: absolute;
    right: 3.5rem;
    top: 100%;
    width: calc(var(--px) * 3);
    height: calc(var(--px) * 3);
    background: var(--color-frame);
    clip-path: polygon(0 0, 100% 0, 100% 100%);
}

.about-zach__stage {
    display: flex;
    align-items: flex-end;
    gap: var(--space-xs);
    padding: 0 var(--space-md) 0 0;
    border-bottom: var(--px) solid var(--color-frame);
}

.about-zach__sprite .sprite--wave { display: none; }
.about-zach__sprite:hover .sprite--idle,
.about-zach__sprite:focus-visible .sprite--idle,
.about-zach__sprite.is-waving .sprite--idle { display: none; }
.about-zach__sprite:hover .sprite--wave,
.about-zach__sprite:focus-visible .sprite--wave,
.about-zach__sprite.is-waving .sprite--wave { display: inline-block; }

@media (max-width: 600px) {
    .about-zach {
        float: none;
        margin: 0 0 var(--space-lg);
        max-width: none;
        flex-direction: row-reverse;
        justify-content: flex-end;
        align-items: flex-end;
    }
    .about-zach__bubble { flex: 1; }
    .about-zach__bubble::after {
        top: auto;
        bottom: 0.6rem;
        right: auto;
        left: calc(var(--px) * -3);
        clip-path: polygon(100% 0, 100% 100%, 0 100%);
    }
    .about-zach__stage { padding: 0; }
}

/* ---- end of a blog post: a leaf pile ---- */
.post-end {
    display: flex;
    justify-content: center;
    margin: var(--space-xl) 0 var(--space-md);
    opacity: 0.9;
}

/* --------------------------------------------------------------------------
   3. Ambient falling leaves (site-wide, built by cozy.js)

   The layer sits at z-index -1: above the page canvas (body background
   propagates to the canvas) but below every in-flow box, so leaves drift
   behind text and disappear behind cards / code blocks / the header
   rather than over them.
   -------------------------------------------------------------------------- */
.cozy-leaves {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    overflow: hidden;
    contain: strict;
}

.cozy-leaf {
    position: absolute;
    top: -48px;
    left: var(--x, 50%);
    opacity: var(--alpha, 0.8);
    will-change: transform;
    animation: cozy-fall var(--fall, 16s) linear var(--delay, 0s) infinite;
}

.cozy-leaf__sway {
    display: block;
    animation: cozy-sway var(--sway-dur, 3.4s) ease-in-out var(--sway-delay, 0s) infinite alternate;
}

@keyframes cozy-fall {
    from { transform: translateY(0); }
    to { transform: translateY(calc(100vh + 96px)); }
}

@keyframes cozy-sway {
    from { transform: translateX(calc(var(--sway, 40px) * -1)) rotate(var(--rot-a, -25deg)); }
    to { transform: translateX(var(--sway, 40px)) rotate(var(--rot-b, 30deg)); }
}

.cozy-leaves.is-paused .cozy-leaf,
.cozy-leaves.is-paused .cozy-leaf__sway,
.cozy-leaves.is-paused .sprite {
    animation-play-state: paused;
}

/* footer switch — matches .keynav-toggle */
.cozy-leaves-toggle {
    background: transparent;
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    padding: 0.3em 0.7em;
    border-radius: 6px;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    cursor: pointer;
}

.cozy-leaves-toggle:hover,
.cozy-leaves-toggle[aria-pressed="true"] {
    border-color: var(--color-accent);
    color: var(--color-text);
}

.cozy-leaves-toggle:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   4. The dog occasionally trots along the bottom of the viewport
   -------------------------------------------------------------------------- */
.cozy-runner {
    position: fixed;
    left: 0;
    bottom: 6px;
    z-index: 30;
    pointer-events: none;
    --scale: 2;
    animation: cozy-run var(--run-dur, 9s) linear forwards;
}

@keyframes cozy-run {
    from { transform: translateX(-80px); }
    to { transform: translateX(calc(100vw + 20px)); }
}

/* --------------------------------------------------------------------------
   5. Reduced motion: everything holds still on frame 1
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .sprite,
    .nav-dog.is-happy .sprite--awake,
    .nav-dog__z i {
        animation: none !important;
    }
    .nav-dog__z,
    .cozy-leaves,
    .cozy-runner { display: none !important; }
}

/* --------------------------------------------------------------------------
   6. Odds and ends
   -------------------------------------------------------------------------- */
/* pumpkin by day, jack-o'-lantern by night */
.cozy-night { display: none; }
@media (prefers-color-scheme: dark) {
    .cozy-day { display: none; }
    .cozy-night { display: inline-block; }
}
