:root {
    --bg:         #0e1120;
    --tile-bg:    #1e2540;
    --orange:     #E85A1A;
    --text-muted: #626d96;
    --dark-gold:  #8a6a1a;
    /* Empty strip reserved at the very bottom of the mosaic so photo tiles
       stop above the fixed scroll-progress bar instead of running under it
       (the bar sits 14px from the bottom + its own 5px height + a little
       breathing room). */
    --footer-space: 34px;
    /* Heights of the two fixed bars stacked at the top (header + zoom
       bar). Everything that needs to sit below them — #mosaicWrap,
       #dragOverlay, the mobile row-height formula — reads these instead
       of repeating raw pixel numbers, so the two bars can be resized in
       one place. Mobile redefines both inside its own media query. */
    --header-h:  80px;
    --zoombar-h: 44px;
}

* { box-sizing: border-box; }

html, body {
    margin: 0; padding: 0;
    width: 100%; height: 100%;
    background: var(--bg);
    overflow: hidden; /* the mosaic scrolls itself — the page never does */
    font-family: sans-serif;
}

/* ── Static Header ────────────────────────────────────────────────────── */
#mosaicHeader {
    position: fixed;
    top: 0; left: 0;
    width: 100%;
    height: var(--header-h);
    background: linear-gradient(180deg, var(--bg) 0%, rgba(14, 17, 32, 0.95) 100%);
    border-bottom: 2px solid var(--orange);
    z-index: 20;
    display: flex;
    align-items: center;
    padding: 0 24px;
    box-sizing: border-box;
}

#headerContent {
    display: flex;
    align-items: center;
    gap: 16px;
}

#headerTitleGroup {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

#headerTitleRow {
    display: flex;
    align-items: baseline;
    gap: 16px;
}

#headerTitle {
    margin: 0;
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 700;
    font-size: 1.8em;
    color: var(--orange);
    text-transform: uppercase;
    letter-spacing: 3px;
    text-shadow: 0 0 10px rgba(232, 90, 26, 0.65), 0 0 22px rgba(232, 90, 26, 0.35);
}

/* ── Logo ─────────────────────────────────────────────────────────────── */
#headerLogo {
    height: 44px;
    width: 44px;
    flex: 0 0 auto;
    animation: logoSpin 12s linear infinite;
}

@keyframes logoSpin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

#headerSubtitle {
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 600;
    font-size: 1.8em;
    color: #ffffff;
    text-transform: uppercase;
    letter-spacing: 2px;
}

/* ── Slogan — small caption line under the title/subtitle ────────────── */
#headerSlogan {
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 600;
    font-size: 0.62em;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--text-muted);
    white-space: nowrap;
}

/* ── Venue selector — pill dropdown matching the /server/ gallery page ── */
#venueSelect {
    margin-left: 18px;
    padding: 7px 30px 7px 14px;
    font-size: 0.78em;
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    border: 1.5px solid #323c62;
    border-radius: 999px;
    background-color: var(--tile-bg);
    color: #fff;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23E85A1A' d='M6 8L0 0h12z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    flex-shrink: 0;
    transition: border-color 0.2s;
}
#venueSelect:hover,
#venueSelect:focus {
    outline: none;
    border-color: var(--orange);
}
#venueSelect option {
    background: var(--tile-bg);
    color: #fff;
}

#headerStats {
    margin-left: auto;
    font-size: 0.9em;
    color: var(--text-muted);
    letter-spacing: 1px;
    text-transform: uppercase;
}
/* Compact "x601" form only shows on narrow phones (see 480px breakpoint
   below) — everywhere else the full "601 memories" label is used. */
#headerStats .stat-compact { display: none; }

/* ── Zoom bar — second fixed strip under the header, switches tile size ── */
#zoomBar {
    position: fixed;
    top: var(--header-h);
    left: 0;
    width: 100%;
    height: var(--zoombar-h);
    background: var(--bg);
    border-bottom: 1px solid #232a4a;
    z-index: 19;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 24px;
    box-sizing: border-box;
}

#zoomLabel {
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 600;
    font-size: 0.78em;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--text-muted);
}

#zoomButtons {
    display: flex;
    align-items: center;
    gap: 8px;
}

.zoom-btn {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 1.5px solid #323c62;
    background-color: var(--tile-bg);
    color: #fff;
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 700;
    font-size: 0.8em;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: border-color 0.2s, background-color 0.2s, color 0.2s;
}
.zoom-btn:hover {
    border-color: var(--orange);
}
.zoom-btn.active {
    background-color: var(--orange);
    border-color: var(--orange);
    color: #fff;
}

/* ── Zoom levels — desktop tile sizes ─────────────────────────────────
   Scoped to min-width so it never fights the mobile row-based formula
   below on specificity; only one of the two rule sets is ever "live" at
   a given viewport width. Level 2 matches the original fixed 200px
   default. */
@media (min-width: 769px) {
    body.zoom-1 { --item-height: 140px; }
    body.zoom-2 { --item-height: 200px; }
    body.zoom-3 { --item-height: 280px; }
}

/* ── Viewport that clips the mosaic ────────────────────────────────────── */
#mosaicWrap {
    position: absolute;
    top: calc(var(--header-h) + var(--zoombar-h));
    left: 0;
    width: 100vw;
    height: calc(100vh - var(--header-h) - var(--zoombar-h) - var(--footer-space));  /* fallback for browsers without dvh */
    height: calc(100dvh - var(--header-h) - var(--zoombar-h) - var(--footer-space)); /* tracks the real visible viewport, not the max one behind mobile toolbars */
    overflow: hidden;
    background: var(--bg);
    pointer-events: none; /* overlay handles all interactions */
}

/* ── Full-page drag overlay — captures all pointer events ─────────────────── */
#dragOverlay {
    position: fixed;
    top: calc(var(--header-h) + var(--zoombar-h));
    left: 0;
    width: 100vw;
    height: calc(100vh - var(--header-h) - var(--zoombar-h) - var(--footer-space));  /* fallback for browsers without dvh */
    height: calc(100dvh - var(--header-h) - var(--zoombar-h) - var(--footer-space)); /* tracks the real visible viewport, not the max one behind mobile toolbars */
    cursor: grab;
    touch-action: none; /* we handle drag ourselves — stop the browser's own scroll/zoom gestures */
    z-index: 5;
    pointer-events: auto; /* always live so it can catch the initial pointerdown */
}
#dragOverlay.dragging { cursor: grabbing; }

/* ── The strip of photos — masonry layout with flexible rows ── */
#mosaicTrack {
    position: absolute;
    top: 0; left: 0;
    height: 100%;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    gap: 6px;
    padding: 6px;
    will-change: transform;
    align-content: flex-start;
    /* transform is set purely from JS (translateX) */
}

.mosaic-item {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    overflow: visible; /* allow scaled images to overflow during hover */
    border-radius: 4px;
    background: var(--tile-bg); /* placeholder tile color while the image loads */

    /* Total tile height = image area + the date row below it. Every tile
       (photo or divider) uses this same fixed size — no per-photo variation
       — so every column in the grid is uniform width, always. */
    height: calc(var(--item-height, 200px) + var(--date-height, 34px));
}

/* ── Image area ───────────────────────────────────────────────────────
   Every photo is force-cropped into the same fixed square via
   object-fit: cover, regardless of its original aspect ratio. A static
   size keeps every column in the masonry the same width, so nothing
   (including the month divider) ever throws a column out of alignment. */
.mosaic-img-wrap {
    position: relative;
    flex: 0 0 auto;
    overflow: visible; /* allow scaled images to overflow during hover */
    height: var(--item-height, 200px);
    width: var(--item-height, 200px);
    aspect-ratio: 1 / 1;
}

.mosaic-item img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    object-fit: cover;
    pointer-events: none; /* the tile drags, not the image */
    -webkit-user-drag: none; /* prevent native drag */
    user-drag: none;
    user-select: none;
    -webkit-user-select: none;
    opacity: 0;
    transition: opacity 0.25s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.mosaic-item img.loaded { opacity: 1; }

/* ── Picture number badge — top-left of each tile ─────────────────────── */
.mosaic-number {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 6;
    background: var(--dark-gold);
    color: #fff;
    font-size: 0.7em;
    font-weight: 700;
    line-height: 1;
    padding: 4px 8px;
    /* flush against the tile's own top-left rounding, cut on the inner corner
       so it reads as a tag stamped onto the picture rather than a floating chip */
    border-radius: 4px 0 8px 0;
    letter-spacing: 0.3px;
    pointer-events: none;
}

/* Hover effect — scale up with rotation for a fun, playful effect.
   Driven by a JS-toggled class, not native :hover — the drag overlay sits
   on top of the whole track to capture pointer input, so the browser's own
   :hover hit-testing never reaches the tiles underneath it. */
.mosaic-item.is-hovered img.loaded {
    transform: scale(1.5) rotate(2deg);
    z-index: 10;
}

/* ── Date row — the date/time pulled from the photo's original filename ─ */
.mosaic-date {
    position: relative; /* anchors the map pin against its right edge */
    flex: 0 0 auto;
    height: var(--date-height, 34px);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1px;
    padding: 2px 4px;
    overflow: hidden;
}
.mosaic-date-day {
    font-size: 0.72em;
    font-weight: 700;
    color: var(--text-muted);
    letter-spacing: 0.3px;
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}
.mosaic-date-time {
    font-size: 0.62em;
    font-weight: 400;
    color: var(--text-muted);
    opacity: 0.75;
    letter-spacing: 0.3px;
    white-space: nowrap;
}

/* ── Map pin — right side of the date box, opens Google Maps for that
   photo's venue. Icon only, no background chip, sized off the date box's
   own height (--date-height) so it scales with it at every zoom level
   instead of a fixed px. pointer-events must stay auto (not none) —
   script.js finds it via elementFromPoint while the drag overlay is
   briefly hidden, and pointer-events: none would make the browser skip
   straight past it during that hit-test. */
.mosaic-pin {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 4px;
    z-index: 2;
    height: var(--date-height, 34px);
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: auto;
    transition: color 0.2s, opacity 0.2s;
}
.mosaic-pin svg {
    width: calc(var(--date-height, 34px) * 0.55);
    height: calc(var(--date-height, 34px) * 0.55);
    display: block;
}
.mosaic-pin.available {
    color: var(--orange);
}
.mosaic-pin.unavailable {
    color: var(--text-muted);
    opacity: 0.55;
    /* Still tappable (shows the "no longer available" prompt) — only the
       color/opacity differs from .available now, not interactivity. */
}

@media (max-width: 768px) {
    .mosaic-pin {
        right: 3px;
    }
}

/* ── Pin confirm prompt — shown when tapping a pin, above everything
   including the drag overlay (z-index 5) and header (z-index 20). ── */
#pinModalOverlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    background: rgba(6, 8, 16, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease;
}
#pinModalOverlay.visible {
    opacity: 1;
    visibility: visible;
}
#pinModal {
    background: var(--tile-bg);
    border: 1.5px solid #323c62;
    border-radius: 12px;
    padding: 24px 22px;
    max-width: 320px;
    width: 100%;
    text-align: center;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
    transform: translateY(8px);
    transition: transform 0.2s ease;
}
#pinModalOverlay.visible #pinModal {
    transform: translateY(0);
}
#pinModalMascot {
    display: block;
    width: 84px;
    height: 84px;
    object-fit: contain;
    margin: 0 auto 14px;
    filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.35));
}
#pinModalMessage {
    margin: 0 0 20px;
    color: #fff;
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 600;
    font-size: 0.95em;
    line-height: 1.5;
}
#pinModalButtons {
    display: flex;
    gap: 10px;
}
#pinModalButtons button {
    flex: 1 1 0;
    padding: 10px 12px;
    border-radius: 999px;
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 600;
    font-size: 0.78em;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color 0.2s, background-color 0.2s, opacity 0.2s;
}
#pinModalCancel {
    background: transparent;
    border: 1.5px solid #323c62;
    color: var(--text-muted);
}
#pinModalCancel:hover {
    border-color: var(--orange);
    color: #fff;
}
#pinModalConfirm {
    background: var(--orange);
    border: 1.5px solid var(--orange);
    color: #fff;
}
#pinModalConfirm:hover {
    opacity: 0.9;
}
#pinModalConfirm:disabled {
    background: var(--tile-bg);
    border-color: #323c62;
    color: var(--text-muted);
    opacity: 0.6;
    cursor: not-allowed;
}

/* Prevent context menu and selection on tiles */
.mosaic-item {
    user-select: none;
    -webkit-user-select: none;
}
.mosaic-item * {
    user-select: none;
    -webkit-user-select: none;
}

/* ── Month Divider ────────────────────────────────────────────────────────
   Same fixed square footprint as every photo tile — since all tiles are
   now a static, uniform size, the divider just slots into the grid like
   any other item and never throws a column's width off. */
.month-divider {
    flex: 0 0 auto;
    height: calc(var(--item-height, 200px) + var(--date-height, 34px));
    width: var(--item-height, 200px);
    background: var(--tile-bg);
    border: 2px solid var(--orange);
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 12px;
    text-align: center;
    letter-spacing: 0.5px;
}
.month-divider-month {
    font-size: 1.3em;
    font-weight: 700;
    color: var(--orange);
    line-height: 1.25;
}
.month-divider-line {
    width: 32px;
    height: 2px;
    background: var(--orange);
    opacity: 0.55;
    flex: 0 0 auto;
}
.month-divider-year {
    font-size: 1em;
    font-weight: 700;
    color: var(--orange);
    letter-spacing: 1px;
    opacity: 0.85;
}

/* ── Seasonal mascot — floats and glows above the month/year text ──────
   Sits on top of a slightly softened "glow pool" so it reads as if it's
   hovering just above the divider's surface. The image itself gently
   bobs up and down (mascotFloat); the glow pulses on its own, slower,
   slightly out of sync so the effect doesn't look too mechanical. */
.month-divider-mascot {
    width: 64px;
    height: 64px;
    object-fit: contain;
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
    filter:
        drop-shadow(0 0 10px rgba(232, 90, 26, 0.65))
        drop-shadow(0 0 22px rgba(232, 90, 26, 0.35));
    animation:
        mascotFloat 3.2s ease-in-out infinite,
        mascotGlow 4s ease-in-out infinite;
}

/* Slightly tighter text/gap once a mascot is present, so mascot + month
   + line + year all still fit inside the fixed tile height without
   needing a bigger divider than any other tile in the grid. */
.month-divider.has-mascot {
    gap: 6px;
}
.month-divider.has-mascot .month-divider-month {
    font-size: 1.1em;
}

@keyframes mascotFloat {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-9px); }
}

@keyframes mascotGlow {
    0%, 100% {
        filter:
            drop-shadow(0 0 8px rgba(232, 90, 26, 0.55))
            drop-shadow(0 0 16px rgba(232, 90, 26, 0.28));
    }
    50% {
        filter:
            drop-shadow(0 0 14px rgba(232, 90, 26, 0.85))
            drop-shadow(0 0 28px rgba(232, 90, 26, 0.45));
    }
}


/* ── Scroll progress bar — shows how far through the whole mosaic you are ── */
#scrollProgress {
    position: fixed;
    left: 16px;
    right: 16px;
    bottom: 14px;
    height: 5px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 3px;
    z-index: 15;
    pointer-events: none; /* purely informational, never blocks the drag overlay */
    overflow: hidden;
}
#scrollProgressThumb {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 20%;
    background: var(--orange);
    border-radius: 3px;
}

#mosaicStatus {
    position: absolute;
    top: 14px; left: 16px;
    z-index: 10;
    font-size: 0.75em;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--text-muted);
    pointer-events: none;
}
#mosaicStatus.accent { color: var(--orange); }

/* ── Centered loading indicator shown while photos are being fetched ───── */
#mosaicLoader {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    z-index: 8;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.35s ease;
}
#mosaicLoader.visible {
    opacity: 1;
    visibility: visible;
}
#mosaicLoaderChar {
    width: 150px;
    height: auto;
    display: block;
    filter: drop-shadow(0 6px 18px rgba(0, 0, 0, 0.35));
}
#mosaicLoaderBar {
    width: 180px;
    height: 6px;
    border-radius: 3px;
    background: var(--tile-bg);
    overflow: hidden;
}
#mosaicLoaderBarFill {
    height: 100%;
    width: 0%;
    background: var(--orange);
    border-radius: 3px;
    transition: width 0.25s ease;
}
#mosaicLoaderPercent {
    font-family: 'Chakra Petch', sans-serif;
    font-weight: 700;
    font-size: 1.25em;
    letter-spacing: 1px;
    color: var(--orange);
}

@media (max-width: 768px) {
    #mosaicLoaderChar { width: 110px; }
    #mosaicLoaderBar  { width: 140px; }
    #mosaicLoaderPercent { font-size: 1.05em; }
}

/* ── Mobile ──────────────────────────────────────────────────────────────
   Desktop keeps its fixed 200px tiles (set as CSS defaults above and
   untouched here). Below 768px we shrink the header and derive tile size
   from the actual viewport height so a fixed number of rows fit
   vertically, instead of the ~2 rows a 200px tile gives on a phone
   screen. That row count is --mobile-rows below (5 by default); the zoom
   levels further down override it to show fewer/more rows. */
@media (max-width: 768px) {
    :root {
        --header-h:  56px;
        --zoombar-h: 36px;
    }

    #mosaicHeader {
        padding: 0 14px;
    }
    #headerContent { gap: 10px; }
    #headerLogo {
        height: 32px;
        width: 32px;
    }
    #headerTitle {
        font-size: 1.25em;
        letter-spacing: 1px;
    }
    #headerSubtitle { font-size: 0.85em; }
    #headerTitleRow { gap: 8px; }
    #headerSlogan {
        font-size: 0.58em;
        letter-spacing: 1px;
    }
    #headerStats { font-size: 0.7em; }

    #venueSelect {
        margin-left: 8px;
        padding: 5px 22px 5px 10px;
        font-size: 0.62em;
        background-position: right 8px center;
    }

    #zoomBar {
        padding: 0 14px;
        gap: 8px;
    }
    #zoomLabel {
        font-size: 0.68em;
        letter-spacing: 1.5px;
    }
    #zoomButtons { gap: 6px; }
    .zoom-btn {
        width: 24px;
        height: 24px;
        font-size: 0.72em;
    }

    #mosaicTrack {
        gap: 4px;
        padding: 4px;
    }

    body {
        /* --mobile-rows tiles + (--mobile-rows - 1) gaps (4px each) +
           top/bottom track padding (4px * 2), all inside
           (100dvh - header-h - zoombar-h - footer-space). Each tile's own
           height is then that row height minus --date-height. dvh
           (not vh) is what makes this match the space actually visible
           above the mobile browser's toolbar. footer-space keeps the last
           row from running under the fixed scroll-progress bar.
           Declared on body (not :root) so that the zoom classes below,
           which also override --mobile-rows on body, are picked up here —
           custom properties only inherit downward, so a :root/html-level
           calc can never see an override made on its own body child. */
        --date-height: 28px;
        --mobile-rows: 5;
        --item-height: calc((100vh - var(--header-h) - var(--zoombar-h) - var(--footer-space) - 8px - (var(--mobile-rows) - 1) * 4px) / var(--mobile-rows) - var(--date-height));   /* fallback */
        --item-height: calc((100dvh - var(--header-h) - var(--zoombar-h) - var(--footer-space) - 8px - (var(--mobile-rows) - 1) * 4px) / var(--mobile-rows) - var(--date-height));
    }

    /* Zoom levels on mobile change the row count instead of a raw pixel
       size, since tile size there is always derived from viewport height. */
    body.zoom-1 { --mobile-rows: 7; }
    body.zoom-2 { --mobile-rows: 5; }
    body.zoom-3 { --mobile-rows: 3; }

    .mosaic-number {
        font-size: 0.6em;
        padding: 3px 6px;
    }
    .mosaic-date-day { font-size: 0.6em; }
    .mosaic-date-time { font-size: 0.5em; }

    /* Sized off --item-height (the actual tile size, which changes with
       zoom via --mobile-rows) instead of a fixed em value — otherwise text
       sized for the zoom-2 default overflows past the border at zoom-1's
       much smaller tiles. min-width: 0 lets the text actually shrink/wrap
       inside its fixed-width flex parent instead of forcing an overflow
       (flex items default to a content-based min-width, which is what let
       "September" spill outside the box); overflow-wrap is a last-resort
       safety net so even a long name always stays inside the border. */
    .month-divider {
        padding: clamp(4px, calc(var(--item-height, 60px) * 0.06), 8px);
        gap: clamp(2px, calc(var(--item-height, 60px) * 0.05), 6px);
    }
    .month-divider-month {
        font-size: clamp(8px, calc(var(--item-height, 60px) * 0.16), 15px);
        letter-spacing: 0.2px;
        min-width: 0;
        max-width: 100%;
        overflow-wrap: break-word;
        word-break: break-word;
    }
    .month-divider-year {
        font-size: clamp(7px, calc(var(--item-height, 60px) * 0.13), 13px);
        min-width: 0;
    }
    .month-divider-line { width: clamp(12px, calc(var(--item-height, 60px) * 0.28), 22px); }

    .month-divider-mascot {
        width: clamp(18px, calc(var(--item-height, 60px) * 0.5), 48px);
        height: clamp(18px, calc(var(--item-height, 60px) * 0.5), 48px);
    }
    .month-divider.has-mascot {
        gap: clamp(1px, calc(var(--item-height, 60px) * 0.03), 4px);
    }
    .month-divider.has-mascot .month-divider-month {
        font-size: clamp(7px, calc(var(--item-height, 60px) * 0.13), 12px);
    }
}

/* ── Narrow phones ───────────────────────────────────────────────────────
   Below ~480px, logo + title + subtitle + venue dropdown + memory count
   no longer all fit on one 56px-tall row at the 768px-breakpoint sizes.
   Shrink the title/subtitle further and let the least essential piece
   (the memory count) drop out first, so the venue picker always stays
   visible and usable. */
@media (max-width: 480px) {
    #headerContent { gap: 6px; }

    #headerLogo {
        height: 26px;
        width: 26px;
    }
    #headerTitle {
        font-size: 0.95em;
        letter-spacing: 0.5px;
    }
    #headerSubtitle {
        font-size: 0.68em;
        letter-spacing: 1px;
    }
    #headerTitleRow { gap: 4px; }
    #headerSlogan {
        font-size: 0.5em;
        letter-spacing: 0.5px;
    }

    #venueSelect {
        margin-left: 4px;
        padding: 4px 18px 4px 8px;
        font-size: 0.56em;
        background-position: right 6px center;
    }

    /* At this width there's no room for "601 memories" next to the venue
       dropdown, but the compact "x601" form is short enough to stay
       inline right after it — no need to pull it out of the flex row. */
    #headerStats {
        font-size: 0.62em;
        margin-left: 6px;
        letter-spacing: 0;
    }
    #headerStats .stat-full { display: none; }
    #headerStats .stat-compact { display: inline; }
}
