/* =====================================================================
   [BLOG_NAME] — public stylesheet
   Glassmorphism over a colourful backdrop. Dark by default, light on toggle.

   Palette lives in themes/*.css and is loaded before this file, so every
   colour here comes from a custom property and nothing needs editing to
   re-skin the site.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Reset
   --------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body,
h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol {
    margin: 0;
    padding: 0;
}

ul[class], ol[class] { list-style: none; }

img, picture, video, canvas {
    display: block;
    max-width: 100%;
    height: auto;
}

input, button, textarea, select { font: inherit; color: inherit; }

button { cursor: pointer; }

/* Class-based `display` beats the UA rule for [hidden], so anything toggled
   from JavaScript with element.hidden needs this to actually disappear —
   .pagination and .button both set display and would otherwise stay visible. */
[hidden] { display: none !important; }

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* ---------------------------------------------------------------------
   2. Tokens — structural values. Colours come from the theme file.
   --------------------------------------------------------------------- */
:root {
    --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
                 "Helvetica Neue", "Noto Sans", Arial, sans-serif;

    --radius-lg: 22px;
    --radius:    16px;
    --radius-sm: 10px;

    --glass-blur: 18px;
    --glass-blur-strong: 26px;

    --shell-max: 1160px;
    --reading-max: 46rem;

    --space-1: 0.375rem;
    --space-2: 0.75rem;
    --space-3: 1.125rem;
    --space-4: 1.75rem;
    --space-5: 2.75rem;
    --space-6: 4rem;

    --shadow-glass: 0 10px 34px rgba(2, 3, 12, 0.42);
    --shadow-lift:  0 18px 48px rgba(2, 3, 12, 0.55);

    --speed: 220ms;
    --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* ---------------------------------------------------------------------
   3. Base typography — sans-serif throughout, no serif, no monospace
   --------------------------------------------------------------------- */
body {
    font-family: var(--font-sans);
    font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
    line-height: 1.65;
    color: var(--text);
    background: var(--bg);
    min-height: 100vh;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
    font-family: var(--font-sans);
    line-height: 1.15;
    letter-spacing: -0.02em;
    font-weight: 700;
    text-wrap: balance;
}

h1 { font-size: clamp(2rem, 1.4rem + 2.6vw, 3.25rem); }
h2 { font-size: clamp(1.45rem, 1.2rem + 1.1vw, 2rem); }
h3 { font-size: clamp(1.2rem, 1.1rem + 0.5vw, 1.4rem); }

a { color: var(--link); text-decoration-color: rgba(var(--accent-rgb), 0.5); text-underline-offset: 3px; }
a:hover { color: var(--link-hover); }

:focus-visible {
    outline: 2px solid rgb(var(--accent-rgb));
    outline-offset: 3px;
    border-radius: 4px;
}

::selection { background: rgba(var(--accent-rgb), 0.35); color: #fff; }

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    padding: 0.75rem 1.25rem;
    background: var(--surface-solid);
    border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; }

/* ---------------------------------------------------------------------
   4. The colour layer the glass sits on top of.
      Without this, frosted panels have nothing to frost.
   --------------------------------------------------------------------- */
.backdrop {
    position: fixed;
    inset: 0;
    z-index: -2;
    background: var(--bg);
    overflow: hidden;
    pointer-events: none;
}

.backdrop__blob {
    position: absolute;
    border-radius: 50%;
    filter: blur(90px);
    opacity: var(--blob-opacity, 0.55);
    will-change: transform;
}

.backdrop__blob--1 {
    width: 46vw; height: 46vw;
    min-width: 380px; min-height: 380px;
    top: -12vw; left: -8vw;
    background: radial-gradient(circle at 35% 35%, rgb(var(--blob-1)), transparent 68%);
    animation: drift-a 26s var(--ease) infinite alternate;
}
.backdrop__blob--2 {
    width: 40vw; height: 40vw;
    min-width: 320px; min-height: 320px;
    top: 22vh; right: -10vw;
    background: radial-gradient(circle at 60% 40%, rgb(var(--blob-2)), transparent 68%);
    animation: drift-b 32s var(--ease) infinite alternate;
}
.backdrop__blob--3 {
    width: 52vw; height: 52vw;
    min-width: 340px; min-height: 340px;
    bottom: -18vw; left: 18vw;
    background: radial-gradient(circle at 45% 55%, rgb(var(--blob-3)), transparent 70%);
    animation: drift-c 38s var(--ease) infinite alternate;
}

/* A fine grain over the gradients stops banding on wide flat areas. */
.backdrop__grain {
    position: absolute;
    inset: -50%;
    opacity: var(--grain-opacity, 0.035);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

@keyframes drift-a { to { transform: translate3d(6vw, 8vh, 0) scale(1.12); } }
@keyframes drift-b { to { transform: translate3d(-7vw, -6vh, 0) scale(1.08); } }
@keyframes drift-c { to { transform: translate3d(4vw, -9vh, 0) scale(0.92); } }

/* Honour the config switch and the OS motion preference. */
.no-motion .backdrop__blob { animation: none; }

/* ---------------------------------------------------------------------
   5. Glass
   --------------------------------------------------------------------- */
.glass {
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-glass);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    backdrop-filter: blur(var(--glass-blur)) saturate(150%);
}

/* A brighter hairline along the top edge reads as a lit glass edge. */
.glass--lit { position: relative; overflow: hidden; }
.glass--lit::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--glass-sheen), transparent);
}

/* Browsers without backdrop-filter get a solid panel rather than a
   washed-out transparent one that would leave text unreadable. */
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
    .glass { background: var(--glass-fallback); }
}

/* ---------------------------------------------------------------------
   6. Shell
   --------------------------------------------------------------------- */
.shell {
    width: min(100% - 2rem, var(--shell-max));
    margin-inline: auto;
}
.shell--wide { width: min(100% - 2rem, 1360px); }

.section { padding-block: var(--space-5); }

.section__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
    flex-wrap: wrap;
}

.kicker {
    text-transform: uppercase;
    letter-spacing: 0.16em;
    font-size: 0.72rem;
    font-weight: 700;
    color: rgb(var(--accent-rgb));
}

.muted { color: var(--text-muted); }

/* ---------------------------------------------------------------------
   7. Header / navigation
   --------------------------------------------------------------------- */
.site-header {
    position: sticky;
    top: 0;
    z-index: 40;
    padding-block: 0.75rem;
    /* The sticky bar keeps its own glass so content scrolls under it. */
    background: var(--nav-bg);
    border-bottom: 1px solid var(--glass-border);
    -webkit-backdrop-filter: blur(var(--glass-blur-strong)) saturate(160%);
    backdrop-filter: blur(var(--glass-blur-strong)) saturate(160%);
}

.nav {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.nav__brand {
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: -0.02em;
    text-decoration: none;
    color: var(--text);
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    white-space: nowrap;
}

.nav__mark {
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 8px;
    background: linear-gradient(135deg, rgb(var(--blob-1)), rgb(var(--blob-2)));
    box-shadow: 0 0 18px rgba(var(--accent-rgb), 0.5);
    flex: none;
}

.nav__links {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin-left: auto;
    list-style: none;
}

.nav__link {
    display: inline-block;
    padding: 0.45rem 0.7rem;
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--text-muted);
    font-size: 0.925rem;
    font-weight: 500;
    transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.nav__link:hover,
.nav__link[aria-current="page"] {
    color: var(--text);
    background: var(--hover-fill);
}

.nav__tools { display: flex; align-items: center; gap: 0.4rem; margin-left: 0.4rem; }

.icon-button {
    width: 2.35rem;
    height: 2.35rem;
    display: grid;
    place-items: center;
    border-radius: 50%;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    color: var(--text);
    transition: background var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.icon-button:hover { background: var(--hover-fill); transform: translateY(-1px); }
.icon-button svg { width: 1.05rem; height: 1.05rem; }

/* Theme toggle swaps which icon is visible. */
.theme-toggle .icon-moon { display: none; }
[data-theme="light"] .theme-toggle .icon-sun { display: none; }
[data-theme="light"] .theme-toggle .icon-moon { display: block; }

.nav__burger { display: none; }

/* Mobile navigation */
@media (max-width: 800px) {
    .nav__burger { display: grid; }

    /* The dropdown goes position:absolute below, taking its margin-left:auto
       out of the flex flow with it — so the brand has to do the pushing, or
       the burger and theme toggle bunch up against the logo. */
    .nav__brand { margin-right: auto; }
    .nav__links {
        position: absolute;
        top: calc(100% + 0.5rem);
        right: 1rem;
        left: 1rem;
        flex-direction: column;
        align-items: stretch;
        gap: 0.15rem;
        padding: 0.6rem;
        border-radius: var(--radius);
        background: var(--surface-solid);
        border: 1px solid var(--glass-border);
        box-shadow: var(--shadow-lift);
        display: none;
    }
    .nav__links.is-open { display: flex; }
    .nav__link { padding: 0.7rem 0.85rem; }
}

/* ---------------------------------------------------------------------
   8. Buttons, chips, forms
   --------------------------------------------------------------------- */
.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.7rem 1.15rem;
    border-radius: 999px;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    color: var(--text);
    font-weight: 600;
    font-size: 0.925rem;
    text-decoration: none;
    transition: transform var(--speed) var(--ease), background var(--speed) var(--ease),
                box-shadow var(--speed) var(--ease);
}
.button:hover { transform: translateY(-1px); background: var(--hover-fill); color: var(--text); }

.button--primary {
    background: linear-gradient(135deg, rgb(var(--blob-1)), rgb(var(--blob-2)));
    border-color: transparent;
    color: #fff;
    box-shadow: 0 8px 24px rgba(var(--accent-rgb), 0.35);
}
.button--primary:hover { color: #fff; box-shadow: 0 12px 30px rgba(var(--accent-rgb), 0.45); }

.button--quiet { background: transparent; }
.button--small { padding: 0.42rem 0.8rem; font-size: 0.85rem; }
.button--danger { border-color: rgba(244, 63, 94, 0.5); color: var(--danger); }

.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.28rem 0.68rem;
    border-radius: 999px;
    font-size: 0.775rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-decoration: none;
    border: 1px solid rgba(var(--chip-rgb, var(--accent-rgb)), 0.45);
    background: rgba(var(--chip-rgb, var(--accent-rgb)), 0.16);
    color: var(--chip-text, var(--text));
    white-space: nowrap;
}
a.chip:hover { background: rgba(var(--chip-rgb, var(--accent-rgb)), 0.28); }

.chip--ghost {
    border-color: var(--glass-border);
    background: var(--glass-bg);
    color: var(--text-muted);
}

.chip-row { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.field { display: block; margin-bottom: var(--space-3); }
.field__label {
    display: block;
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    margin-bottom: 0.4rem;
    color: var(--text-muted);
}
.field__hint { font-size: 0.78rem; color: var(--text-muted); margin-top: 0.35rem; }

.input, .textarea, .select {
    width: 100%;
    padding: 0.7rem 0.9rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--glass-border);
    background: var(--input-bg);
    color: var(--text);
    transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.input:focus, .textarea:focus, .select:focus {
    outline: none;
    border-color: rgba(var(--accent-rgb), 0.75);
    box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.22);
}
.textarea { min-height: 8rem; resize: vertical; }

/* Honeypot — visually gone but still focusable-free for bots to fill in. */
.honeypot {
    position: absolute !important;
    left: -9999px !important;
    width: 1px; height: 1px;
    overflow: hidden;
}

.search-form { display: flex; gap: 0.5rem; }
.search-form .input { flex: 1; }

/* Native dropdown lists are painted by the OS, not by our panel styles. The
   options inherit the near-white text colour but land on a light popup
   background, which made them all but unreadable — so state both here. */
.select option {
    background-color: #12141d;
    color: #edeef5;
}
[data-theme="light"] .select option {
    background-color: #ffffff;
    color: #171a26;
}

/* Sort picker in a section header */
/* The section head aligns on the baseline, which would hang the control
   off the heading's first line — sit it on the bottom edge instead. */
.sort-form { display: flex; align-items: center; align-self: flex-end; gap: 0.5rem; flex-wrap: wrap; }
.sort-form__label {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.13em;
    font-weight: 700;
    color: var(--text-muted);
}
.sort-form .select {
    width: auto;
    min-width: 10.5rem;
    padding: 0.45rem 0.7rem;
    font-size: 0.88rem;
}

/* ---------------------------------------------------------------------
   9. Flash messages
   --------------------------------------------------------------------- */
.flash {
    padding: 0.85rem 1.1rem;
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-3);
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    font-size: 0.92rem;
}
.flash--success { border-color: rgba(34, 197, 94, 0.45); background: rgba(34, 197, 94, 0.12); }
.flash--error   { border-color: rgba(244, 63, 94, 0.45); background: rgba(244, 63, 94, 0.12); }

/* ---------------------------------------------------------------------
   10. Post cards + feed grid
   --------------------------------------------------------------------- */
.feed-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
    gap: var(--space-3);
}

/* Infinite-scroll controls. Hidden until site.js takes over from the
   numbered pagination, so a no-JS reader never sees a dead button. */
.feed-sentinel { height: 1px; }

.feed-more {
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    margin-top: var(--space-4);
}
.feed-more.is-active { display: flex; }
.feed-status { font-size: 0.85rem; color: var(--text-muted); min-height: 1.2em; }

.card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    height: 100%;
    transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}

/* Appended cards fade in so a page of new posts does not just snap in. */
.card.is-entering { animation: card-in 320ms var(--ease) both; }
@keyframes card-in {
    from { opacity: 0; transform: translateY(12px); }
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lift); }

.card__media {
    position: relative;
    aspect-ratio: 16 / 10;
    background: var(--media-placeholder);
    overflow: hidden;
}
.card__media img {
    width: 100%; height: 100%;
    object-fit: cover;
    transition: transform 600ms var(--ease);
}
.card:hover .card__media img { transform: scale(1.04); }

.card__body { padding: var(--space-3); display: flex; flex-direction: column; gap: 0.6rem; flex: 1; }

.card__title { font-size: 1.15rem; }
.card__title a { color: var(--text); text-decoration: none; }
.card__title a:hover { color: rgb(var(--accent-rgb)); }

.card__excerpt { color: var(--text-muted); font-size: 0.92rem; margin: 0; }

.card__meta {
    margin-top: auto;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.78rem;
    color: var(--text-muted);
    flex-wrap: wrap;
}

.dot::before { content: "·"; margin-right: 0.55rem; }

/* The .featured-grid rules that sat here went with the pinned grid section —
   pinned posts are the landing bubbles now. post-card.php still supports an
   optional `showPin` flag, which is what the two rules below dress. */
.card--pinned .card__body { position: relative; }
.pin-badge {
    position: absolute;
    top: var(--space-3);
    left: var(--space-3);
    z-index: 2;
}

/* ---------------------------------------------------------------------
   11. Post hero — title/meta in a glass panel over the lower hero image
   --------------------------------------------------------------------- */
.hero {
    position: relative;
    margin-top: var(--space-4);
    border-radius: var(--radius-lg);
    overflow: hidden;
    min-height: clamp(280px, 46vw, 560px);
    display: flex;
    align-items: flex-end;
    background: var(--media-placeholder);
}

.hero__image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Darkens the lower half so the glass panel always has contrast under it. */
.hero::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, var(--hero-scrim) 0%, transparent 62%);
    pointer-events: none;
}

/* No panel behind the hero text — just the words over the image.
   The .hero::after scrim still darkens the lower image, and the text shadow
   below carries legibility over a busy or light photograph. */
.hero__panel {
    position: relative;
    z-index: 1;
    margin: clamp(0.75rem, 2vw, 1.5rem);
    padding: clamp(1.1rem, 3vw, 2rem);
    width: 100%;
    max-width: 54rem;
    background: none;
    border: 0;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    text-shadow: 0 2px 14px rgba(4, 5, 12, 0.6), 0 1px 3px rgba(4, 5, 12, 0.5);
}

/* Light mode puts dark text on a light scrim, so the halo has to invert too. */
[data-theme="light"] .hero__panel {
    text-shadow: 0 2px 14px rgba(255, 255, 255, 0.85), 0 1px 3px rgba(255, 255, 255, 0.7);
}

/* The category pill has its own fill; it does not need the halo as well. */
.hero__panel .chip { text-shadow: none; }

/* Sits over the top-left of the hero image. The hero panel is transparent
   now, so this needs its own glass backing to stay readable on a photo. */
.hero__back {
    position: absolute;
    top: clamp(0.75rem, 2vw, 1.5rem);
    left: clamp(0.75rem, 2vw, 1.5rem);
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.9rem;
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
    color: var(--text);
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    box-shadow: var(--shadow-glass);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    transition: background var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.hero__back:hover {
    background: var(--hover-fill);
    color: var(--text);
    transform: translateX(-2px);
}

.hero--plain { min-height: 0; display: block; background: none; }
.hero--plain::after { content: none; }
.hero--plain .hero__panel { margin: 0; }

/* With no hero image there is nothing to float over, so it joins the flow
   above the title rather than covering the category chip. */
.hero--plain .hero__back {
    position: static;
    margin-bottom: var(--space-3);
}

/* Standfirst under the post title. Sized between the h1 and the body so it
   reads as a bridge rather than a first paragraph. */
.hero__deck {
    margin-top: 0.9rem;
    max-width: 48ch;
    font-size: clamp(1rem, 0.95rem + 0.4vw, 1.25rem);
    line-height: 1.5;
    color: var(--text-body);
}

.post-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-top: 0.9rem;
}

.preview-banner {
    margin-top: var(--space-3);
    padding: 0.7rem 1rem;
    border-radius: var(--radius-sm);
    border: 1px dashed rgba(var(--accent-rgb), 0.6);
    background: rgba(var(--accent-rgb), 0.12);
    font-size: 0.88rem;
}

/* ---------------------------------------------------------------------
   12. Article body
       Long-form text sits on a near-solid chip: readable text directly over
       a blurred backdrop is the classic glassmorphism failure.
   --------------------------------------------------------------------- */
.article {
    margin-top: var(--space-4);
    padding: clamp(1.25rem, 4vw, 3rem);
    background: var(--surface-solid);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-glass);
}

.prose { max-width: var(--reading-max); margin-inline: auto; }
.prose > * + * { margin-top: 1.15em; }
.prose h2 { margin-top: 2em; }
.prose h3 { margin-top: 1.6em; }
.prose p { color: var(--text-body); }
.prose img { border-radius: var(--radius); margin-inline: auto; }
.prose figure { margin-block: 1.8em; }
.prose figcaption {
    margin-top: 0.6rem;
    font-size: 0.85rem;
    color: var(--text-muted);
    text-align: center;
}
.prose blockquote {
    padding: 0.4rem 0 0.4rem 1.25rem;
    border-left: 3px solid rgb(var(--accent-rgb));
    color: var(--text);
    font-size: 1.08em;
}
.prose ul, .prose ol { padding-left: 1.35em; }
.prose li + li { margin-top: 0.4em; }
.prose hr { border: 0; height: 1px; background: var(--glass-border); margin-block: 2.4em; }
.prose table { width: 100%; border-collapse: collapse; font-size: 0.92em; }
.prose th, .prose td { padding: 0.6rem 0.7rem; border-bottom: 1px solid var(--glass-border); text-align: left; }

.article__footer {
    max-width: var(--reading-max);
    margin: var(--space-5) auto 0;
    padding-top: var(--space-4);
    border-top: 1px solid var(--glass-border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    justify-content: space-between;
}

/* Previous / next */
.neighbours {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
    margin-top: var(--space-4);
}
.neighbour {
    padding: var(--space-3);
    text-decoration: none;
    color: var(--text);
    display: block;
}
.neighbour span { font-size: 0.75rem; color: var(--text-muted); display: block; margin-bottom: 0.3rem; }
.neighbour strong { font-weight: 650; }
.neighbour--next { text-align: right; }

/* ---------------------------------------------------------------------
   13. Masonry gallery + lightbox
   --------------------------------------------------------------------- */
.gallery { margin-top: var(--space-5); }

/* CSS multi-column gives a true masonry flow with no JS and no layout jump. */
.masonry {
    columns: 3 260px;
    column-gap: var(--space-2);
}
.masonry__item {
    break-inside: avoid;
    margin: 0 0 var(--space-2);
    border-radius: var(--radius);
    overflow: hidden;
    position: relative;
    background: var(--media-placeholder);
    border: 1px solid var(--glass-border);
}
.masonry__item button {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    line-height: 0;
}
.masonry__item img { width: 100%; transition: transform 500ms var(--ease), filter 500ms var(--ease); }
.masonry__item:hover img { transform: scale(1.03); filter: brightness(1.06); }
.masonry__caption {
    position: absolute;
    inset: auto 0 0 0;
    padding: 0.6rem 0.8rem;
    font-size: 0.8rem;
    color: #fff;
    background: linear-gradient(to top, rgba(4, 5, 14, 0.85), transparent);
    text-align: left;
    pointer-events: none;
}

.lightbox {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: none;
    place-items: center;
    padding: clamp(0.75rem, 3vw, 2.5rem);
    background: var(--lightbox-scrim);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
}
.lightbox.is-open { display: grid; }

.lightbox__figure {
    margin: 0;
    max-width: 100%;
    max-height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
}
.lightbox__image {
    max-height: min(78vh, 1100px);
    width: auto;
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
}

.lightbox__bar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0.55rem 0.75rem;
    border-radius: 999px;
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(160%);
    backdrop-filter: blur(var(--glass-blur)) saturate(160%);
    color: #fff;
    max-width: 100%;
}
.lightbox__caption {
    font-size: 0.85rem;
    padding-inline: 0.4rem;
    max-width: 46ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lightbox__count { font-size: 0.78rem; opacity: 0.75; white-space: nowrap; }

.lightbox__close {
    position: absolute;
    top: clamp(0.75rem, 3vw, 1.75rem);
    right: clamp(0.75rem, 3vw, 1.75rem);
}

/* ---------------------------------------------------------------------
   14. Map
   --------------------------------------------------------------------- */
.map-block { margin-top: var(--space-5); }

.map-frame {
    border-radius: var(--radius-lg);
    overflow: hidden;
    border: 1px solid var(--glass-border);
    box-shadow: var(--shadow-glass);
}
.map-canvas { height: clamp(300px, 45vh, 480px); width: 100%; z-index: 0; }

.map-caption {
    padding: 0.75rem 1rem;
    font-size: 0.85rem;
    color: var(--text-muted);
    background: var(--glass-bg);
    border-top: 1px solid var(--glass-border);
}

/* Leaflet's own chrome, restyled to match the glass. */
.leaflet-container { background: var(--media-placeholder); font-family: var(--font-sans); }
.leaflet-control-zoom a,
.leaflet-control-attribution {
    background: var(--surface-solid) !important;
    color: var(--text) !important;
    border-color: var(--glass-border) !important;
}
.leaflet-control-attribution a { color: rgb(var(--accent-rgb)) !important; }
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
    background: var(--surface-solid);
    color: var(--text);
    box-shadow: var(--shadow-glass);
}

/* Darken the OSM tiles in dark mode so the map does not glare. */
[data-theme="dark"] .map-canvas .leaflet-tile-pane {
    filter: invert(1) hue-rotate(180deg) brightness(0.92) contrast(0.9) saturate(0.7);
}

/* ---------------------------------------------------------------------
   15. Comments
   --------------------------------------------------------------------- */
.comments { margin-top: var(--space-5); }

.comment-list { display: flex; flex-direction: column; gap: var(--space-2); list-style: none; }

.comment {
    padding: var(--space-3);
    border-radius: var(--radius);
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
}
.comment__head {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;
}
.comment__author { font-weight: 650; }
.comment__date { font-size: 0.78rem; color: var(--text-muted); }
.comment__body { white-space: pre-wrap; overflow-wrap: anywhere; }
.comment__replies { list-style: none; margin: var(--space-2) 0 0 clamp(0.75rem, 3vw, 2rem); display: flex; flex-direction: column; gap: var(--space-2); }

.comment-form { padding: var(--space-4); margin-top: var(--space-3); }
.comment-form__grid {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
}
.moderation-note {
    font-size: 0.82rem;
    color: var(--text-muted);
    margin-top: 0.75rem;
}

/* ---------------------------------------------------------------------
   16. Pagination
   --------------------------------------------------------------------- */
.pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.35rem;
    margin-top: var(--space-5);
    flex-wrap: wrap;
}
.pagination__link {
    min-width: 2.5rem;
    padding: 0.5rem 0.75rem;
    text-align: center;
    border-radius: var(--radius-sm);
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    color: var(--text-muted);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
}
.pagination__link:hover { background: var(--hover-fill); color: var(--text); }
.pagination__link[aria-current="page"] {
    background: rgba(var(--accent-rgb), 0.9);
    border-color: transparent;
    color: #fff;
}
.pagination__gap { padding-inline: 0.35rem; color: var(--text-muted); }

/* ---------------------------------------------------------------------
   17. Empty states, footer
   --------------------------------------------------------------------- */
.empty {
    padding: var(--space-5);
    text-align: center;
    color: var(--text-muted);
}

.site-footer {
    margin-top: var(--space-6);
    padding-block: var(--space-4);
    border-top: 1px solid var(--glass-border);
    background: var(--nav-bg);
    -webkit-backdrop-filter: blur(var(--glass-blur));
    backdrop-filter: blur(var(--glass-blur));
}
.footer__row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
}
.footer__brand { display: flex; flex-direction: column; gap: 0.75rem; }
.footer__tagline { font-size: 0.9rem; max-width: 34ch; }
.footer__copy { font-size: 0.82rem; color: var(--text-muted); }

/* ---------------------------------------------------------------------
   17b. Support page
   --------------------------------------------------------------------- */
.donate-card { padding: clamp(1.25rem, 4vw, 2.25rem); }

.donate-amounts { border: 0; padding: 0; margin: 0 0 var(--space-3); }
.donate-amounts legend { padding: 0; margin-bottom: 0.75rem; }

.donate-presets {
    display: grid;
    gap: 0.6rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
}

/* The radio itself is hidden but still focusable, so keyboard users get the
   ring on the visible box via :focus-visible + sibling. */
.donate-preset input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}
.donate-preset__box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    padding: 0.9rem 0.6rem;
    border-radius: var(--radius);
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    cursor: pointer;
    text-align: center;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease),
                transform var(--speed) var(--ease);
}
.donate-preset__box:hover { transform: translateY(-2px); background: var(--hover-fill); }

.donate-preset input:checked + .donate-preset__box {
    border-color: rgb(var(--accent-rgb));
    background: rgba(var(--accent-rgb), 0.16);
    box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.5) inset;
}
.donate-preset input:focus-visible + .donate-preset__box {
    outline: 2px solid rgb(var(--accent-rgb));
    outline-offset: 3px;
}

.donate-preset__count { font-size: 1.5rem; font-weight: 750; line-height: 1.1; }
.donate-preset__label { font-size: 0.85rem; }
.donate-preset__price { font-size: 0.75rem; color: var(--text-muted); }

.donate-submit { width: 100%; margin-top: var(--space-3); }
.donate-note { font-size: 0.8rem; margin-top: 0.9rem; text-align: center; }

/* --- Supporters ticker ----------------------------------------------
   Drifts left forever. The track holds two identical runs of cards and
   travels exactly -50%, so the copy lands where the original started and
   the seam is invisible. */
.supporter-marquee {
    position: relative;
    overflow: hidden;
    /* Fades the cut edges rather than slicing cards off mid-word. */
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}

.supporter-marquee__track {
    display: flex;
    width: max-content;
    animation: supporter-scroll var(--marquee-duration, 48s) linear infinite;
    will-change: transform;
}

/* Paused while a reader is looking at it, or tabbing through the links. */
.supporter-marquee:hover .supporter-marquee__track,
.supporter-marquee:focus-within .supporter-marquee__track {
    animation-play-state: paused;
}

@keyframes supporter-scroll {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}

.supporter-card {
    width: min(320px, 78vw);
    flex: none;
    padding: var(--space-3);
    /* margin, not the track's gap: with a gap, half the track width is not a
       whole number of cards and the loop slips a little further each cycle. */
    margin-inline-end: var(--space-3);
}
.supporter-card__head {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    flex-wrap: wrap;
}
.supporter-card__head .muted { font-size: 0.78rem; margin-left: auto; }
.supporter-card__amount {
    font-size: 0.72rem;
    padding: 0.15rem 0.5rem;
    white-space: nowrap;
}
.supporter-card__note {
    margin-top: 0.6rem;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* No drifting for anyone who asked for stillness — it becomes an ordinary
   swipeable strip, and the duplicate run is dropped so nothing reads twice. */
@media (prefers-reduced-motion: reduce) {
    .supporter-marquee {
        overflow-x: auto;
        -webkit-mask-image: none;
        mask-image: none;
    }
    .supporter-marquee__track { animation: none; }
    .supporter-card--copy { display: none; }
}

/* ---------------------------------------------------------------------
   18. Landing — the home page opener.
       Text sits directly on the gradient backdrop, no glass panel, so the
       colour layer does the work and the words stay the only content.
   --------------------------------------------------------------------- */
.landing {
    position: relative;
    /* Fill the viewport below the sticky header. svh stops mobile browser
       chrome from cropping the section; the vh line is the fallback. */
    min-height: calc(100vh - 4.75rem);
    min-height: calc(100svh - 4.75rem);
    display: grid;
    align-content: center;
    padding-block: var(--space-6);
}

.landing__inner {
    position: relative;
    z-index: 1;
    /* Shrink to the text rather than filling the shell. A full-width box here
       sits over the floating bubbles and eats clicks meant for them — which
       is why only part of each bubble used to respond. */
    width: fit-content;
    max-width: min(100% - 2rem, 48rem);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 1rem;
    text-shadow: 0 2px 24px rgba(4, 5, 12, 0.45);
}
[data-theme="light"] .landing__inner {
    text-shadow: 0 2px 24px rgba(255, 255, 255, 0.75);
}

.landing__title {
    font-size: clamp(2.75rem, 1.4rem + 8vw, 6.5rem);
    line-height: 1.02;
    letter-spacing: -0.045em;
}

.landing__lede {
    font-size: clamp(1.05rem, 0.95rem + 0.55vw, 1.4rem);
    color: var(--text-muted);
    max-width: 46ch;
}

.landing__stats { font-size: 0.85rem; color: var(--text-muted); }

/* --- Pinned posts as drifting bubbles -------------------------------
   Two layouts, same markup: a centred wrapping row under the headline on
   phones and tablets, and free-floating around it from 1000px up. */
.landing__bubbles {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: clamp(0.9rem, 4vw, 1.75rem);
    width: min(100% - 2rem, var(--shell-max));
    margin: var(--space-4) auto 0;
}

.bubble {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.45rem;
    width: clamp(63px, 14.6vw, 97px);
    text-decoration: none;
    color: var(--text);
    text-align: center;
    animation: bubble-drift var(--drift-time, 7s) ease-in-out infinite;
    will-change: transform;
}
.bubble:focus-visible { outline-offset: 6px; }

.bubble__circle {
    position: relative;
    width: 100%;
    aspect-ratio: 1;
    border-radius: 50%;
    overflow: hidden;
    /* Tinted with the post's category accent, so it reads even with no photo. */
    background: radial-gradient(circle at 35% 30%,
                rgba(var(--chip-rgb, var(--accent-rgb)), 0.45),
                var(--glass-bg-strong) 72%);
    border: 1px solid rgba(var(--chip-rgb, var(--accent-rgb)), 0.55);
    box-shadow: 0 16px 40px rgba(3, 4, 12, 0.45),
                0 0 26px rgba(var(--chip-rgb, var(--accent-rgb)), 0.28);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    /* Scaled on hover here rather than on .bubble, which is busy drifting. */
    transition: transform var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.bubble:hover .bubble__circle,
.bubble:focus-visible .bubble__circle {
    transform: scale(1.06);
    box-shadow: 0 22px 52px rgba(3, 4, 12, 0.55),
                0 0 40px rgba(var(--chip-rgb, var(--accent-rgb)), 0.5);
}

.bubble__image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.8;
    transition: opacity var(--speed) var(--ease);
}
.bubble:hover .bubble__image { opacity: 0.95; }

.bubble__title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 0.72rem;
    font-weight: 650;
    line-height: 1.3;
    text-shadow: 0 1px 8px rgba(4, 5, 12, 0.65);
    transition: color var(--speed) var(--ease);
}
[data-theme="light"] .bubble__title { text-shadow: 0 1px 8px rgba(255, 255, 255, 0.8); }
.bubble:hover .bubble__title { color: rgb(var(--chip-rgb, var(--accent-rgb))); }

/* Gentle vertical bob in the stacked layout — the fallback when site.js
   cannot take over (no JS, or prefers-reduced-motion). */
.bubble:nth-child(1) { --drift-y: -9px; --drift-time: 6.5s; }
.bubble:nth-child(2) { --drift-y: 8px;  --drift-time: 7.5s; animation-delay: -2s; }
.bubble:nth-child(3) { --drift-y: -7px; --drift-time: 8.5s; animation-delay: -4s; }
.bubble:nth-child(4) { --drift-y: 9px;  --drift-time: 7s;   animation-delay: -1s; }

/* Once site.js drives the free-roaming motion — at every screen size — the
   resting spots and the CSS drift both get out of the way. Position comes
   entirely from a JS-set transform off the container's top-left corner. */
.landing__bubbles.is-floating .bubble {
    position: absolute;
    animation: none;
    inset: 0 auto auto 0;
    will-change: transform;
}

/* The roaming area is the whole landing section at every screen size. The
   bubbles pass behind the headline, which sits at z-index 1 above them. */
.landing__bubbles.is-floating {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 0;
    width: auto;
    height: auto;
    margin: 0;
    /* Covers the headline and the scroll cue, so it must not eat their clicks. */
    pointer-events: none;
}
.landing__bubbles.is-floating .bubble { pointer-events: auto; }

@media (min-width: 1000px) {
    .landing__bubbles {
        position: absolute;
        inset: 0;
        display: block;
        width: auto;
        margin: 0;
        /* Must not swallow clicks aimed at the headline buttons. */
        pointer-events: none;
    }

    .bubble {
        position: absolute;
        pointer-events: auto;
        width: clamp(74px, 7.3vw, 106px);
    }


    /* Four resting spots clear of the centred text, each on its own timing
       so they never drift as a block. */
    .bubble:nth-child(1) { top: 14%; left: 5%;   --drift-x: 16px;  --drift-y: -20px; --drift-time: 17s; }
    .bubble:nth-child(2) { top: 54%; left: 11%;  --drift-x: -14px; --drift-y: -16px; --drift-time: 23s; animation-delay: -6s; }
    .bubble:nth-child(3) { top: 17%; right: 6%;  --drift-x: -18px; --drift-y: 18px;  --drift-time: 20s; animation-delay: -3s; }
    .bubble:nth-child(4) { top: 56%; right: 12%; --drift-x: 15px;  --drift-y: 20px;  --drift-time: 26s; animation-delay: -11s; }
}

/* Resting state is 0,0 — so when prefers-reduced-motion collapses the
   animation, every bubble stays exactly where it was placed. */
@keyframes bubble-drift {
    0%, 100% { transform: translate3d(0, 0, 0); }
    50%      { transform: translate3d(var(--drift-x, 0), var(--drift-y, 0), 0); }
}

/* Shallow desktop viewports would push the lower bubbles into the scroll cue. */
@media (min-width: 1000px) and (max-height: 700px) {
    .bubble:nth-child(2), .bubble:nth-child(4) { display: none; }
}

.landing__scroll {
    position: absolute;
    left: 50%;
    bottom: clamp(1rem, 4vh, 2.5rem);
    width: 2.6rem;
    height: 2.6rem;
    display: grid;
    place-items: center;
    border-radius: 50%;
    /* Same fill as .button--primary, which used to sit above it. */
    border: 1px solid transparent;
    background: linear-gradient(135deg, rgb(var(--blob-1)), rgb(var(--blob-2)));
    color: #fff;
    box-shadow: 0 8px 24px rgba(var(--accent-rgb), 0.35);
    transition: box-shadow var(--speed) var(--ease);
    animation: landing-nudge 2.4s var(--ease) infinite;
}
.landing__scroll:hover {
    color: #fff;
    box-shadow: 0 12px 30px rgba(var(--accent-rgb), 0.45);
}
.landing__scroll svg { width: 1.2rem; height: 1.2rem; }

/* The transform carries the centring, so every keyframe must restate it. */
@keyframes landing-nudge {
    0%, 100% { transform: translate(-50%, 0); }
    50%      { transform: translate(-50%, 6px); }
}

/* Where the bubbles stack under the headline there is no spare room at the
   bottom of the section, so the cue joins the flow instead of floating over
   them. Its animation assumes absolute centring, hence dropping it here. */
@media (max-width: 999px) {
    .landing__scroll {
        /* relative, not static: it has to keep a z-index so a passing bubble
           cannot paint over it. left/bottom are reset from the absolute rule. */
        position: relative;
        left: auto;
        bottom: auto;
        z-index: 2;
        margin: var(--space-4) auto 0;
        animation: none;
    }
}

/* Short landscape screens: let the content set the height instead. */
@media (max-height: 520px) {
    .landing { min-height: 0; padding-block: var(--space-5); }
    .landing__scroll { display: none; }
}

/* Jump targets must clear the sticky header, or the heading lands under it. */
#recent, #comments, #browse, #comment-form { scroll-margin-top: 5.5rem; }

/* ---------------------------------------------------------------------
   19. Modals — the search and browse pop-ups opened from the nav
   --------------------------------------------------------------------- */
.modal {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: none;
    align-items: flex-start;
    justify-content: center;
    padding: clamp(1rem, 6vh, 5rem) 1rem 2rem;
    background: var(--lightbox-scrim);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    overflow-y: auto;
    overscroll-behavior: contain;
}
.modal.is-open { display: flex; }

.modal__panel {
    position: relative;
    width: min(100%, 44rem);
    padding: clamp(1.25rem, 4vw, 2rem);
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lift);
    -webkit-backdrop-filter: blur(var(--glass-blur-strong)) saturate(160%);
    backdrop-filter: blur(var(--glass-blur-strong)) saturate(160%);
    animation: modal-rise 220ms var(--ease);
}
@keyframes modal-rise {
    from { opacity: 0; transform: translateY(14px); }
}

.modal__head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.25rem; }
.modal__title { font-size: 1.3rem; }
.modal__close { margin-left: auto; flex: none; }
.modal__hint { font-size: 0.85rem; color: var(--text-muted); margin-top: 0.85rem; }
.modal__foot { margin-top: 1rem; font-size: 0.9rem; }
.modal__section + .modal__section { margin-top: 1.75rem; }
.modal__section .kicker { display: block; margin-bottom: 0.85rem; }

/* Live search results */
.modal-results { display: flex; flex-direction: column; gap: 0.2rem; margin-top: 0.85rem; }
.result {
    display: flex;
    gap: 0.85rem;
    align-items: center;
    padding: 0.6rem;
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--text);
}
.result:hover, .result:focus-visible { background: var(--hover-fill); color: var(--text); }
.result__thumb {
    width: 72px;
    height: 54px;
    object-fit: cover;
    border-radius: 8px;
    flex: none;
    background: var(--media-placeholder);
}
.result__body { min-width: 0; }
.result__title { font-weight: 650; display: block; }
.result__meta { font-size: 0.78rem; color: var(--text-muted); display: block; margin-top: 0.15rem; }

/* Category / tag browser */
.browse-grid {
    display: grid;
    gap: 0.5rem;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
}
.browse-item {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.7rem 0.85rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    text-decoration: none;
    color: var(--text);
    transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.browse-item:hover {
    background: var(--hover-fill);
    color: var(--text);
    border-color: rgba(var(--chip-rgb, var(--accent-rgb)), 0.5);
}
.browse-item__dot {
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    flex: none;
    background: rgb(var(--chip-rgb, var(--accent-rgb)));
    box-shadow: 0 0 10px rgba(var(--chip-rgb, var(--accent-rgb)), 0.8);
}
.browse-item__body { min-width: 0; display: flex; flex-direction: column; }
.browse-item__name { font-weight: 640; }
.browse-item__desc { font-size: 0.78rem; color: var(--text-muted); }
.browse-item__count { margin-left: auto; font-size: 0.78rem; color: var(--text-muted); }

/* ---------------------------------------------------------------------
   20. Mobile — cap the number of simultaneously blurred layers.
       backdrop-filter is GPU-expensive; on phones only the sticky bar and
       the hero panel stay frosted, everything else becomes a solid panel.
   --------------------------------------------------------------------- */
@media (max-width: 700px) {
    :root { --glass-blur: 12px; --glass-blur-strong: 16px; }

    .card,
    .comment,
    .comment-form,
    .lightbox__bar,
    .map-caption,
    .flash,
    .pagination__link,
    .hero__back,
    /* The overlay behind it is already blurred — one layer is enough. */
    .modal__panel {
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        background: var(--glass-fallback);
    }

    .backdrop__blob { filter: blur(70px); }
    .masonry { columns: 2 150px; }
}

@media (max-width: 420px) {
    .masonry { columns: 1; }
}

/* Devices that cannot hover (phones/tablets) get no lift animations. */
@media (hover: none) {
    .card:hover { transform: none; }
    .card:hover .card__media img { transform: none; }
}

/* ---------------------------------------------------------------------
   21. Print
   --------------------------------------------------------------------- */
@media print {
    .backdrop, .site-header, .site-footer, .comment-form,
    .lightbox, .modal, .map-block, .landing__scroll { display: none !important; }
    body { background: #fff; color: #000; }
    .article { border: 0; box-shadow: none; background: #fff; }
}
