/* AION — Dark theme, zero-gap grid, overlay postcards */

:root {
    --bg: #0a0a0b;
    --surface: #141416;
    --surface-2: #1c1c1f;
    --text: #f5f5f7;
    --dim: #a0a0a8;
    --border: rgba(255,255,255,0.08);
    --accent: #7c3aed;
    --accent-hover: #8b5cf6;
    --accent-link: #a855f7;
    --like: #ff3b5c;
    --cols: 5;
    --topbar-h: 56px;

    /* ── Canonical "soft" button finish ────────────────────────────────────
       Single source of truth for primary buttons, active chips, pills, the
       slider thumb — anywhere the UI says "this is the action you want."
       Reference: the Post button on the comment composer. Translucent
       accent over surface, light-purple text, subtle border + shadow.
       Replaces the loud var(--accent)-fill treatment used previously. */
    --btn-soft-bg:           rgba(124,58,237,0.18);
    --btn-soft-bg-hover:     rgba(124,58,237,0.32);
    --btn-soft-border:       rgba(124,58,237,0.35);
    --btn-soft-border-hover: rgba(124,58,237,0.55);
    --btn-soft-color:        #c4b5fd;
    --btn-soft-color-hover:  #ffffff;
    --btn-soft-shadow:       0 1px 2px rgba(0,0,0,0.35), inset 0 -1px 0 rgba(0,0,0,0.18);
}

/* ---------- Cosmos theme ----------------------------------------------------
 * Same dark base, with a *whisper* of indigo through every surface. The
 * accent purple stays put — it's the brand. What changes is the neutral
 * grays: they shift toward a deep cosmic indigo-violet so the whole UI
 * feels like it's lit by distant stars rather than a fluorescent bulb.
 *
 * Hue shift is intentionally < 4% saturation. Side-by-side, it reads as
 * "warmer, more specific"; on its own, you barely notice the color — just
 * the personality. If you find yourself wanting to push it further, don't.
 * Phase 2 (gradient washes, starfield empty states) is where the cosmic
 * theme gets louder; the palette stays this restrained.
 */
:root[data-theme="cosmos"] {
    --bg: #0a0913;             /* was #0a0a0b — adds a faint indigo cast */
    --surface: #141320;        /* was #141416 — cards pick up the hue */
    --surface-2: #1c1a28;      /* was #1c1c1f — raised/hover surfaces */
    --dim: #a4a0b4;            /* was #a0a0a8 — secondary text leans purple */
    --border: rgba(168,140,255,0.07);  /* was rgba(255,255,255,0.08) — lavender hairlines */
    /* --accent / --accent-hover / --accent-link / --like / --text deliberately
       unchanged. The brand purple stays the brand purple. */
}

/* ---------- Astro theme ----------------------------------------------------
 * Cosmos turned up: same indigo palette base, but the body itself becomes a
 * sky. A fixed starfield (CSS-only, single repeating 400px tile, ~30 stars
 * of varied sizes/tints) sits behind everything. A soft purple nebula glow
 * pools at the top center and a fainter one bleeds in from bottom-right —
 * subtle vignettes that give the whole UI depth.
 *
 * Accent elements pick up faint halos: primary buttons, hover states, focus
 * rings. Hashtags and links glow gently. This is "loud cosmic," but loud
 * here means *atmospheric* — never gaudy, never animated to distraction.
 *
 * If it ever feels like a parody, the rule is: dial back the body glow
 * opacity first (it's the loudest layer), then the star tint saturation.
 * The starfield itself is non-negotiable — it's the soul of the theme.
 */
:root[data-theme="astro"] {
    --bg: #07061a;             /* deeper, more saturated cosmic indigo */
    --surface: #16142a;        /* cards lift visibly off the bg */
    --surface-2: #1f1c34;
    --dim: #b0a8c0;            /* slightly lighter so it stays readable on darker bg */
    --border: rgba(168,140,255,0.12);  /* more visible lavender hairlines */
}

/* Starfield + nebula vignette. Body becomes a stacking-context root so the
   pseudo-elements paint above the bg color but below all positioned content. */
:root[data-theme="astro"] body {
    position: relative;
    isolation: isolate;
    background:
        radial-gradient(ellipse 100% 70% at 50% -10%, rgba(124,58,237,0.10) 0%, transparent 60%),
        radial-gradient(ellipse 80% 60% at 90% 100%, rgba(168,140,255,0.06) 0%, transparent 60%),
        var(--bg);
    background-attachment: fixed;
}

/* Star tile — one 400×400 pattern with ~60 stars in three tints (white,
   cool lavender, faint blue) and four sizes. The tile repeats; ~60 stars
   per 160k px² ≈ ~250-400 visible stars on a typical viewport. Positions
   are hand-tuned to avoid obvious clusters or grids — the eye should read
   it as a real (random-looking) sky. */
:root[data-theme="astro"] body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        /* Bright whites — main stars */
        radial-gradient(1px 1px at 32px 18px,   rgba(255,255,255,0.85), transparent 50%),
        radial-gradient(1px 1px at 78px 62px,   rgba(255,255,255,0.7),  transparent 50%),
        radial-gradient(1.5px 1.5px at 142px 24px, rgba(255,255,255,0.9), transparent 50%),
        radial-gradient(1px 1px at 196px 88px,  rgba(255,255,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 220px 156px, rgba(255,255,255,0.7),  transparent 50%),
        radial-gradient(2px 2px at 268px 36px,  rgba(255,255,255,0.95), transparent 50%),
        radial-gradient(1px 1px at 314px 112px, rgba(255,255,255,0.6),  transparent 50%),
        radial-gradient(1px 1px at 360px 200px, rgba(255,255,255,0.75), transparent 50%),
        radial-gradient(1px 1px at 384px 64px,  rgba(255,255,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 48px 248px,  rgba(255,255,255,0.7),  transparent 50%),
        radial-gradient(1.5px 1.5px at 218px 268px, rgba(255,255,255,0.85), transparent 50%),
        radial-gradient(1px 1px at 286px 188px, rgba(255,255,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 130px 380px, rgba(255,255,255,0.65), transparent 50%),
        radial-gradient(1px 1px at 254px 380px, rgba(255,255,255,0.6),  transparent 50%),
        radial-gradient(2px 2px at 366px 312px, rgba(255,255,255,0.9),  transparent 50%),
        radial-gradient(1px 1px at 70px 200px,  rgba(255,255,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 92px 130px,  rgba(255,255,255,0.65), transparent 50%),
        radial-gradient(1px 1px at 178px 138px, rgba(255,255,255,0.55), transparent 50%),
        /* Faint lavenders */
        radial-gradient(1px 1px at 14px 92px,   rgba(196,168,255,0.7),  transparent 50%),
        radial-gradient(1.5px 1.5px at 106px 144px, rgba(196,168,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 168px 218px, rgba(196,168,255,0.6),  transparent 50%),
        radial-gradient(1px 1px at 252px 188px, rgba(196,168,255,0.5),  transparent 50%),
        radial-gradient(2px 2px at 296px 270px, rgba(196,168,255,0.7),  transparent 50%),
        radial-gradient(1px 1px at 348px 326px, rgba(196,168,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 12px 280px,  rgba(196,168,255,0.6),  transparent 50%),
        radial-gradient(1px 1px at 200px 320px, rgba(196,168,255,0.6),  transparent 50%),
        radial-gradient(1px 1px at 320px 78px,  rgba(196,168,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 60px 60px,   rgba(196,168,255,0.5),  transparent 50%),
        radial-gradient(1.5px 1.5px at 226px 30px, rgba(196,168,255,0.65), transparent 50%),
        radial-gradient(1px 1px at 396px 244px, rgba(196,168,255,0.55), transparent 50%),
        /* Faint cool blues */
        radial-gradient(1px 1px at 56px 348px,  rgba(168,200,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 124px 304px, rgba(168,200,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 232px 350px, rgba(168,200,255,0.6),  transparent 50%),
        radial-gradient(1.5px 1.5px at 312px 152px, rgba(168,200,255,0.45), transparent 50%),
        radial-gradient(1px 1px at 156px 360px, rgba(168,200,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 282px 232px, rgba(168,200,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 38px 152px,  rgba(168,200,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 110px 240px, rgba(168,200,255,0.45), transparent 50%),
        radial-gradient(1px 1px at 364px 144px, rgba(168,200,255,0.5),  transparent 50%),
        /* Tiny dim background stars (adds depth — dust between the bright ones) */
        radial-gradient(0.5px 0.5px at 84px 232px,  rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 188px 60px,  rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 244px 116px, rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 332px 232px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 372px 16px,  rgba(255,255,255,0.45),transparent 50%),
        radial-gradient(0.5px 0.5px at 16px 168px,  rgba(196,168,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 156px 100px, rgba(196,168,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 22px 372px,  rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 96px 90px,   rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 144px 196px, rgba(168,200,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 200px 250px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 260px 80px,  rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 304px 220px, rgba(196,168,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 340px 4px,   rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 352px 256px, rgba(168,200,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 388px 340px, rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 44px 304px,  rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 162px 320px, rgba(196,168,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 240px 308px, rgba(168,200,255,0.4), transparent 50%);
    background-size: 400px 400px;
    background-repeat: repeat;
    opacity: 1;
}

/* Single "hero" twinkle — one larger star pulsing softly somewhere off-center.
   Just enough motion to feel alive without being distracting. */
:root[data-theme="astro"] body::after {
    content: '';
    position: fixed;
    z-index: 0;
    pointer-events: none;
    top: 18vh; right: 16vw;
    width: 3px; height: 3px;
    border-radius: 50%;
    background: rgba(255,255,255,0.95);
    box-shadow:
        0 0 6px 1px rgba(255,255,255,0.7),
        0 0 14px 3px rgba(168,140,255,0.4);
    animation: astro-twinkle 5.5s ease-in-out infinite;
}
@keyframes astro-twinkle {
    0%, 100% { opacity: 0.3; transform: scale(0.85); }
    50%      { opacity: 1;   transform: scale(1);    }
}

/* Subtle halo on primary buttons — preserves the soft-fill base treatment
   but layers a gentle outer cosmic glow over it so the button still pops
   against the starfield. Composes with the canonical inset shading. */
:root[data-theme="astro"] .btn-primary,
:root[data-theme="astro"] .nav-link-primary,
:root[data-theme="astro"] .chip.active,
:root[data-theme="astro"] .interest-chip.is-on,
:root[data-theme="astro"] .interests-banner-cta,
:root[data-theme="astro"] .interests-save,
:root[data-theme="astro"] .search-submit {
    box-shadow:
        0 0 0 1px rgba(124,58,237,0.4),
        0 0 18px rgba(124,58,237,0.25),
        inset 0 -1px 0 rgba(0,0,0,0.18);
}
:root[data-theme="astro"] .btn-primary:hover,
:root[data-theme="astro"] .nav-link-primary:hover,
:root[data-theme="astro"] .chip.active:hover,
:root[data-theme="astro"] .interest-chip.is-on:hover,
:root[data-theme="astro"] .interests-banner-cta:hover,
:root[data-theme="astro"] .interests-save:hover,
:root[data-theme="astro"] .search-submit:hover {
    box-shadow:
        0 0 0 1px rgba(139,92,246,0.6),
        0 0 26px rgba(139,92,246,0.4),
        inset 0 -1px 0 rgba(0,0,0,0.18);
}

/* Hashtags + mentions glow gently — the "links to elsewhere in the universe"
   feel. Very low intensity so paragraph-dense pages stay calm. */
:root[data-theme="astro"] .hashtag,
:root[data-theme="astro"] .mention {
    text-shadow: 0 0 8px rgba(168,85,247,0.35);
}

/* Focus rings on form fields pick up a faint cosmic glow */
:root[data-theme="astro"] input:focus,
:root[data-theme="astro"] textarea:focus,
:root[data-theme="astro"] select:focus {
    box-shadow: 0 0 0 3px rgba(124,58,237,0.2);
}

/* ---------- Dark Matter theme --------------------------------------------
 * Design statement: "everything you can't see is there."
 *
 * The interface dissolves. Cards lose their fill — they become wireframes,
 * defined entirely by hairline borders. The starfield stays (monochrome
 * now, no color tints) and is the only visible matter on screen. The
 * accent purple is preserved as the *single point of color* on the entire
 * page — used on primary CTAs only. Everything else is white-on-black
 * or dimmed gray. Like reading scientific notation: every letter neutral,
 * the equation result is the only thing that catches the eye.
 *
 * One faint distant pulse off-center, like a white dwarf observed across
 * light-years. That's the only motion. Otherwise: stillness.
 *
 * If something feels too "heavy," look for a hardcoded rgba background
 * that wasn't routed through --surface. That's where the wireframe is
 * leaking. Fix at the source, not with another override.
 */
:root[data-theme="dark-matter"] {
    --bg: #000000;
    --surface: rgba(255,255,255,0.015);  /* near-zero — borders define cards */
    --surface-2: rgba(255,255,255,0.03);
    --text: #e8e8ee;                     /* slightly dimmer than dark theme */
    --dim: #6a6a72;                      /* dim-to-faint secondary */
    --border: rgba(255,255,255,0.13);    /* hairlines a touch more visible */
    /* --accent / --accent-hover / --accent-link kept — but used sparingly */
}

:root[data-theme="dark-matter"] body {
    position: relative;
    isolation: isolate;
    background: #000000;
}

/* Starfield — monochrome (pure white only, no lavender or blue tints from
   Astro). Density bumped up but kept slightly emptier than Astro to
   preserve the "void" character: ~30 stars per 400×400 tile vs Astro's
   ~60. Opacity dialed back so individual stars feel observed-from-far,
   not painted-on. */
:root[data-theme="dark-matter"] body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        /* Brighter named stars */
        radial-gradient(1px 1px at 32px 18px,     rgba(255,255,255,0.75), transparent 50%),
        radial-gradient(1px 1px at 142px 24px,    rgba(255,255,255,0.55), transparent 50%),
        radial-gradient(1.5px 1.5px at 268px 36px, rgba(255,255,255,0.85), transparent 50%),
        radial-gradient(1px 1px at 360px 200px,   rgba(255,255,255,0.6),  transparent 50%),
        radial-gradient(1px 1px at 220px 156px,   rgba(255,255,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 12px 280px,    rgba(255,255,255,0.5),  transparent 50%),
        radial-gradient(2px 2px at 296px 270px,   rgba(255,255,255,0.7),  transparent 50%),
        radial-gradient(1px 1px at 56px 348px,    rgba(255,255,255,0.4),  transparent 50%),
        radial-gradient(1px 1px at 380px 80px,    rgba(255,255,255,0.45), transparent 50%),
        radial-gradient(1px 1px at 78px 80px,     rgba(255,255,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 200px 240px,   rgba(255,255,255,0.6),  transparent 50%),
        radial-gradient(1.5px 1.5px at 130px 360px, rgba(255,255,255,0.7), transparent 50%),
        radial-gradient(1px 1px at 246px 96px,    rgba(255,255,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 320px 360px,   rgba(255,255,255,0.55), transparent 50%),
        radial-gradient(1px 1px at 96px 220px,    rgba(255,255,255,0.45), transparent 50%),
        radial-gradient(1.5px 1.5px at 348px 136px, rgba(255,255,255,0.65), transparent 50%),
        /* Tiny dim background stars (depth) */
        radial-gradient(0.5px 0.5px at 188px 60px, rgba(255,255,255,0.4), transparent 50%),
        radial-gradient(0.5px 0.5px at 84px 232px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 156px 100px, rgba(255,255,255,0.3),transparent 50%),
        radial-gradient(0.5px 0.5px at 244px 116px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 396px 264px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 22px 168px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 168px 320px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 280px 180px, rgba(255,255,255,0.3), transparent 50%),
        radial-gradient(0.5px 0.5px at 64px 132px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 120px 280px, rgba(255,255,255,0.3), transparent 50%),
        radial-gradient(0.5px 0.5px at 220px 380px, rgba(255,255,255,0.3), transparent 50%),
        radial-gradient(0.5px 0.5px at 304px 12px, rgba(255,255,255,0.35),transparent 50%),
        radial-gradient(0.5px 0.5px at 364px 312px, rgba(255,255,255,0.3), transparent 50%),
        radial-gradient(0.5px 0.5px at 44px 388px, rgba(255,255,255,0.35),transparent 50%);
    background-size: 400px 400px;
    background-repeat: repeat;
    opacity: 0.7;
}

/* The lone pulse — a single distant white dwarf, off-center, slow heartbeat.
   In a world without color and without other motion, this is the entire
   theatre. ~9-second cycle is intentional: longer than Astro's twinkle so
   it feels meditative, not nervous. */
:root[data-theme="dark-matter"] body::after {
    content: '';
    position: fixed;
    z-index: 0;
    pointer-events: none;
    top: 28vh; left: 14vw;
    width: 2px; height: 2px;
    border-radius: 50%;
    background: #fff;
    box-shadow:
        0 0 4px 1px rgba(255,255,255,0.6),
        0 0 14px 4px rgba(255,255,255,0.18);
    animation: dark-matter-pulse 9s ease-in-out infinite;
}
@keyframes dark-matter-pulse {
    0%, 100% { opacity: 0.35; }
    50%      { opacity: 1; }
}

/* ── Element overrides ──────────────────────────────────────────────────
   Most surfaces dissolve automatically because they read var(--surface)
   (now near-transparent). The pieces below either hardcode their own
   bg, or earn an extra override because they define the structural feel
   of the theme. */

/* Topbar — translucent black with a heavy blur, so the starfield streams
   beneath it as you scroll. The thin bottom border is the structural line. */
:root[data-theme="dark-matter"] .topbar {
    background: rgba(0,0,0,0.55);
    backdrop-filter: blur(24px) saturate(120%);
    -webkit-backdrop-filter: blur(24px) saturate(120%);
}

/* Primary CTA — the ONE point of color. Solid purple, no translucency,
   no halo. It should feel like a beacon: the only thing on the page
   actively emitting light. The same beacon treatment applies to every
   soft-button surface so the theme stays internally consistent — in
   dark-matter, "primary action" means "bright spot of color," full stop. */
:root[data-theme="dark-matter"] .btn-primary,
:root[data-theme="dark-matter"] .nav-link-primary,
:root[data-theme="dark-matter"] .dropdown-item-primary,
:root[data-theme="dark-matter"] .chip.active,
:root[data-theme="dark-matter"] .interest-chip.is-on,
:root[data-theme="dark-matter"] .interests-banner-cta,
:root[data-theme="dark-matter"] .interests-save,
:root[data-theme="dark-matter"] .search-submit,
:root[data-theme="dark-matter"] .modal-post-compose > button[type="submit"],
:root[data-theme="dark-matter"] .tr-ex-compose button,
:root[data-theme="dark-matter"] .forum-view-btn.active,
:root[data-theme="dark-matter"] .compose-group-chip {
    background: var(--accent);
    color: #fff;
    border: 0;
    box-shadow: none;
}
:root[data-theme="dark-matter"] .btn-primary:hover,
:root[data-theme="dark-matter"] .nav-link-primary:hover,
:root[data-theme="dark-matter"] .dropdown-item-primary:hover,
:root[data-theme="dark-matter"] .chip.active:hover,
:root[data-theme="dark-matter"] .interest-chip.is-on:hover,
:root[data-theme="dark-matter"] .interests-banner-cta:hover,
:root[data-theme="dark-matter"] .interests-save:hover,
:root[data-theme="dark-matter"] .search-submit:hover,
:root[data-theme="dark-matter"] .modal-post-compose > button[type="submit"]:hover,
:root[data-theme="dark-matter"] .tr-ex-compose button:hover {
    background: var(--accent-hover);
    color: #fff;
    box-shadow: 0 0 28px rgba(124,58,237,0.35);
}
/* Slider thumb stays the soft pill in dark-matter — too small to function
   as a beacon, and the bright fill made it look detached from the track. */

/* Hashtags + mentions — strip color, leave them as plain underlined links.
   In Dark Matter every text element is monochrome, hashtags included. */
:root[data-theme="dark-matter"] .hashtag,
:root[data-theme="dark-matter"] .mention {
    color: var(--text);
    text-decoration: underline;
    text-decoration-color: rgba(255,255,255,0.3);
    text-underline-offset: 3px;
    text-shadow: none;
}
:root[data-theme="dark-matter"] .hashtag:hover,
:root[data-theme="dark-matter"] .mention:hover {
    text-decoration-color: rgba(255,255,255,0.8);
}

/* Grid tiles dissolve. The default .postcard bg is hardcoded #1d1e23 (and
   .card-media #111, .link-tile #222) so my surface-var change couldn't
   reach them — overriding directly here. Result: the tile is now defined
   purely by its hairline border, the starfield streams through any empty
   media zone, and image/video tiles still render normally because the
   media itself is opaque. */
:root[data-theme="dark-matter"] .postcard {
    background: transparent;
    border-color: var(--border);
}
:root[data-theme="dark-matter"] .card-media {
    background: transparent;
}
:root[data-theme="dark-matter"] .link-tile {
    background: transparent;
}

/* Card hover — borders brighten instead of fills lifting */
:root[data-theme="dark-matter"] .group-card:hover,
:root[data-theme="dark-matter"] .postcard:hover {
    border-color: rgba(255,255,255,0.28);
    box-shadow: none;
    transform: none;
}

/* Focus rings — thin white instead of cosmic glow */
:root[data-theme="dark-matter"] input:focus,
:root[data-theme="dark-matter"] textarea:focus,
:root[data-theme="dark-matter"] select:focus {
    border-color: rgba(255,255,255,0.4);
    box-shadow: 0 0 0 2px rgba(255,255,255,0.06);
}

/* ---------- Plasma theme ----------------------------------------------------
 * Cosmic-energy plasma over a violet-black base. A pink→violet current runs
 * through links, buttons, borders, and focus rings, each carrying a soft glow
 * that brightens on hover. A faint aurora wash pools behind everything. The
 * intent is "charged + elegant," never gaudy — the loud layers are the aurora
 * wash and the button glows; dial those first if it ever feels like too much.
 */
:root[data-theme="plasma"] {
    --bg: #0b0710;                          /* violet-black */
    --surface: #160f1e;
    --surface-2: #20162c;
    --text: #f6eefb;
    --dim: #b09ec6;
    --border: rgba(232,121,249,0.16);       /* plasma-pink hairlines */
    --accent: #c93cff;                      /* electric violet-magenta */
    --accent-hover: #e36bff;
    --accent-link: #ff6ad5;                 /* plasma pink links */
    --like: #ff3b6b;
    /* Soft buttons become a subtle plasma gradient + glow — every primary
       action, chip, pill, and slider thumb inherits this via these vars. */
    --btn-soft-bg:           linear-gradient(135deg, rgba(255,92,213,0.20), rgba(168,85,247,0.22));
    --btn-soft-bg-hover:     linear-gradient(135deg, rgba(255,92,213,0.40), rgba(168,85,247,0.42));
    --btn-soft-border:       rgba(255,106,213,0.42);
    --btn-soft-border-hover: rgba(255,138,232,0.70);
    --btn-soft-color:        #f3c4ff;
    --btn-soft-color-hover:  #ffffff;
    --btn-soft-shadow:       0 2px 12px rgba(201,60,255,0.22), inset 0 1px 0 rgba(255,255,255,0.10);
}

/* Aurora wash — two slow plasma pools behind everything (magenta top-left,
   violet bottom-right). Fixed + subtle. */
:root[data-theme="plasma"] body {
    position: relative;
    isolation: isolate;
    background:
        radial-gradient(60% 50% at 12% 0%,   rgba(232,80,255,0.10), transparent 70%),
        radial-gradient(55% 55% at 100% 100%, rgba(124,80,255,0.10), transparent 70%),
        var(--bg);
    background-attachment: fixed;
}
/* A faint charged sheen drifting across the top horizon. */
:root[data-theme="plasma"] body::before {
    content: '';
    position: fixed; inset: 0 0 auto 0; height: 320px; z-index: 0; pointer-events: none;
    background: radial-gradient(120% 100% at 50% -40%, rgba(255,92,213,0.10), rgba(168,85,247,0.05) 45%, transparent 70%);
    animation: plasma-drift 16s ease-in-out infinite;
}
@keyframes plasma-drift { 0%,100% { opacity:.65; transform: translateX(-2%); } 50% { opacity: 1; transform: translateX(2%); } }

/* Primary CTAs — a true plasma gradient with a glow; brighten on hover. */
:root[data-theme="plasma"] .btn-primary,
:root[data-theme="plasma"] .nav-link-primary,
:root[data-theme="plasma"] .dropdown-item-primary,
:root[data-theme="plasma"] .chip.active,
:root[data-theme="plasma"] .interest-chip.is-on,
:root[data-theme="plasma"] .interests-banner-cta,
:root[data-theme="plasma"] .interests-save,
:root[data-theme="plasma"] .search-submit,
:root[data-theme="plasma"] .modal-post-compose > button[type="submit"] {
    background: linear-gradient(135deg, #ff5cd5, #a855f7);
    color: #fff;
    border: 1px solid rgba(255,138,232,0.55);
    box-shadow: 0 2px 16px rgba(201,60,255,0.32), inset 0 1px 0 rgba(255,255,255,0.18);
    transition: filter .15s ease, box-shadow .15s ease, transform .12s ease;
}
:root[data-theme="plasma"] .btn-primary:hover,
:root[data-theme="plasma"] .nav-link-primary:hover,
:root[data-theme="plasma"] .dropdown-item-primary:hover,
:root[data-theme="plasma"] .chip.active:hover,
:root[data-theme="plasma"] .interest-chip.is-on:hover,
:root[data-theme="plasma"] .interests-banner-cta:hover,
:root[data-theme="plasma"] .interests-save:hover,
:root[data-theme="plasma"] .search-submit:hover,
:root[data-theme="plasma"] .modal-post-compose > button[type="submit"]:hover {
    filter: brightness(1.12) saturate(1.08);
    box-shadow: 0 3px 26px rgba(232,80,255,0.55), inset 0 1px 0 rgba(255,255,255,0.22);
    transform: translateY(-1px);
}

/* Links, hashtags, mentions, source links — plasma pink with a soft glow. */
:root[data-theme="plasma"] a.body-url,
:root[data-theme="plasma"] .hashtag,
:root[data-theme="plasma"] .mention,
:root[data-theme="plasma"] .card-link-host {
    color: var(--accent-link);
    text-shadow: 0 0 10px rgba(255,106,213,0.35);
}
:root[data-theme="plasma"] a.body-url:hover,
:root[data-theme="plasma"] .hashtag:hover,
:root[data-theme="plasma"] .mention:hover {
    filter: brightness(1.18);
    text-shadow: 0 0 14px rgba(255,106,213,0.6);
}

/* Cards — plasma hairline that ignites with a gentle glow on hover. */
:root[data-theme="plasma"] .postcard:hover,
:root[data-theme="plasma"] .group-card:hover,
:root[data-theme="plasma"] .ptab-card:hover {
    border-color: rgba(255,106,213,0.5);
    box-shadow: 0 6px 28px rgba(201,60,255,0.20);
}

/* Focus rings — a plasma halo. */
:root[data-theme="plasma"] input:focus,
:root[data-theme="plasma"] textarea:focus,
:root[data-theme="plasma"] select:focus {
    border-color: rgba(255,106,213,0.6);
    box-shadow: 0 0 0 3px rgba(201,60,255,0.18), 0 0 18px rgba(232,80,255,0.25);
}

/* Topbar — translucent with a faint plasma underline. */
:root[data-theme="plasma"] .topbar {
    background: rgba(12,7,16,0.7);
    backdrop-filter: blur(22px) saturate(140%);
    -webkit-backdrop-filter: blur(22px) saturate(140%);
    border-bottom: 1px solid rgba(255,106,213,0.18);
}

/* Active profile tab + verified star pick up the plasma glow. */
:root[data-theme="plasma"] .ptab.active { box-shadow: 0 3px 14px -5px rgba(255,106,213,0.7); }
:root[data-theme="plasma"] .aion-star { filter: drop-shadow(0 0 4px rgba(255,106,213,0.7)); }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
/* Lock the page to the viewport width so the user can't side-pan at 1:1 zoom.
   `overflow-x: clip` is purely visual (no scroll container, doesn't interfere
   with pinch-zoom or panning a zoomed viewport — that's handled by the browser
   one layer above CSS). Applied to html AND body because body alone leaves the
   html element free to scroll horizontally on iOS / Android Chrome when an
   inner element is wider than the screen. */
html, body { overflow-x: clip; max-width: 100%; }

/* iOS Safari auto-zooms the entire viewport when the user focuses an input
   or textarea whose computed font-size is below 16px — and the zoom persists
   after blur, so closing the modal leaves the whole site stuck zoomed in.
   Pinning every text-entry control to ≥16px on mobile suppresses the
   focus-time zoom without touching pinch-zoom (different layer). The
   !important is intentional: it overrides existing class-scoped 14px rules
   (.modal-post-compose input, .post-page-compose input, etc.) so any input
   added later inherits the safe size automatically. Desktop is untouched. */
@media (max-width: 768px) {
    input, textarea, select { font-size: 16px !important; }
}
a { color: var(--text); text-decoration: none; }
a:hover { color: var(--accent-link); }
button { font-family: inherit; cursor: pointer; }
img { display: block; max-width: 100%; }

/* ---------- Topbar ---------- */
.topbar {
    position: sticky; top: 0; z-index: 100;
    background: rgba(10,10,11,0.92);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
    height: var(--topbar-h);
}
.topbar-inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    height: var(--topbar-h);
    padding: 0 16px;
    max-width: 100%;
}
.topbar-inner .logo { grid-column: 1; justify-self: start; }
.topbar-inner .topnav-center { grid-column: 2; justify-self: center; }
.topbar-inner .topnav-right { grid-column: 3; justify-self: end; }
.logo { display: flex; align-items: center; gap: 8px; font-weight: 700; letter-spacing: .5px; }
.logo-mark { color: var(--accent); font-size: 22px; }
.logo-text { font-size: 16px; }
.logo-sub { color: var(--dim); font-weight: 400; margin-left: 4px; font-size: 11px; letter-spacing: 2px; }
.logo-img { height: 28px; width: auto; display: block; }

.topnav { display: flex; align-items: center; gap: 2px; }
.nav-btn {
    position: relative;
    width: 40px; height: 40px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; color: var(--text);
    border: none; border-radius: 50%;
    transition: background .15s ease;
}
.nav-btn:hover { background: rgba(255,255,255,0.08); color: var(--text); }
.nav-btn .icon { width: 20px; height: 20px; }
.nav-btn .badge {
    position: absolute; top: 4px; right: 4px;
    background: var(--like); color: #fff;
    min-width: 16px; height: 16px;
    border-radius: 8px;
    padding: 0 4px;
    font-size: 10px; font-weight: 700;
    display: inline-flex; align-items: center; justify-content: center;
    border: 2px solid var(--bg);
}
.nav-link { padding: 8px 14px; border-radius: 999px; font-size: 14px; }
.nav-link-primary {
    background: var(--btn-soft-bg);
    color: var(--btn-soft-color);
    border: 1px solid var(--btn-soft-border);
    border-radius: 999px;
    box-shadow: var(--btn-soft-shadow);
}
.nav-link-primary:hover {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
    color: var(--btn-soft-color-hover);
}

/* ---------- Dropdowns ---------- */
.dropdown {
    display: none;
    position: absolute; top: var(--topbar-h); right: 0;
    width: 320px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-top: none;
    border-radius: 0 0 12px 12px;
    padding: 16px;
    box-shadow: 0 12px 40px rgba(0,0,0,0.6);
    z-index: 99;
}
.dropdown.open { display: block; }

/* Topbar content stacks above the search dropdown so the dropdown can slide
   in from behind it. The topbar element itself already has z-index 100; we
   raise its inner row above the dropdown that lives as its sibling-child. */
.topbar-inner { position: relative; z-index: 3; }

/* ── Search dropdown: motorized slide-down reveal ────────────────────── */
#dropdown-search {
    /* Override the generic .dropdown display:none — we keep this one in the
       layout and use transform/visibility for the animation. */
    display: block !important;
    visibility: hidden;
    opacity: 0;
    /* Center on the page (overrides positionUnder()'s inline left:NNNpx) */
    left: 50% !important;
    right: auto !important;
    transform: translateX(-50%) translateY(-100%);
    transform-origin: top center;
    z-index: 1;
    transition:
        transform .38s cubic-bezier(0.22, 1.0, 0.36, 1),
        opacity .22s ease,
        visibility 0s linear .38s;
    pointer-events: none;
    will-change: transform, opacity;
}
#dropdown-search.open {
    visibility: visible;
    opacity: 1;
    transform: translateX(-50%) translateY(0);
    pointer-events: auto;
    transition:
        transform .38s cubic-bezier(0.22, 1.0, 0.36, 1),
        opacity .22s ease,
        visibility 0s linear 0s;
}
.dropdown-inner { display: flex; flex-direction: column; gap: 16px; }

/* Compact menu-style dropdown (e.g. account menu) */
.dropdown-menu { width: 212px; padding: 6px; }
.dropdown-menu .dropdown-inner { gap: 2px; }
.dropdown-item {
    display: block;
    padding: 10px 14px;
    color: var(--text);
    border-radius: 8px;
    font-size: 14px;
    text-decoration: none;
    transition: background .12s ease;
}
.dropdown-item:hover { background: rgba(255,255,255,0.08); }
.dropdown-item-primary {
    background: var(--btn-soft-bg);
    color: var(--btn-soft-color);
    border: 1px solid var(--btn-soft-border);
    border-radius: 999px;
    box-shadow: var(--btn-soft-shadow);
}
.dropdown-item-primary:hover {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
    color: var(--btn-soft-color-hover);
}

/* ── Avatar as the account button (your identity lives in the topbar) ──
   Replaces the old separate profile + gear icons: the avatar IS the
   account menu, and on mobile it tells you who you're signed in as
   without needing room for a username on the bar. */
.nav-btn-avatar { padding: 0; }
.nav-btn-avatar .topbar-avatar {
    width: 30px; height: 30px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    border: 1px solid var(--border);
    transition: border-color .15s ease, box-shadow .15s ease;
}
.nav-btn-avatar:hover { background: transparent; }
.nav-btn-avatar:hover .topbar-avatar {
    border-color: var(--btn-soft-border-hover);
    box-shadow: 0 0 0 2px rgba(124,58,237,0.25);
}

/* Identity header at the top of the account menu — "signed in as" row that
   also opens your profile (the old standalone profile icon now lives here). */
.dropdown-account-head {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px;
    margin: 0 0 4px;
    border-bottom: 1px solid var(--border);
    border-radius: 8px 8px 0 0;
    text-decoration: none;
    transition: background .12s ease;
}
.dropdown-account-head:hover { background: rgba(255,255,255,0.06); }
.dropdown-account-avatar {
    width: 38px; height: 38px;
    border-radius: 50%;
    object-fit: cover;
    flex: none;
    border: 1px solid var(--border);
}
.dropdown-account-id { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.dropdown-account-name {
    font-size: 14px; font-weight: 700; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dropdown-account-handle {
    font-size: 12px; color: var(--accent);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.filter-group { display: flex; flex-direction: column; gap: 8px; }
.filter-group > label { font-size: 12px; color: var(--dim); text-transform: uppercase; letter-spacing: 1px; }
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
    padding: 8px 16px;
    background: var(--surface-2);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: all .15s ease;
}
.chip:hover { border-color: rgba(255,255,255,0.25); }
.chip.active {
    background: var(--btn-soft-bg);
    border-color: var(--btn-soft-border);
    color: var(--btn-soft-color);
    box-shadow: var(--btn-soft-shadow);
}
.chip.active:hover {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
    color: var(--btn-soft-color-hover);
}
/* BREAKING gets a pulsing red dot to feel urgent — like a "live" indicator. */
.chip.is-breaking { color: #ff5252; border-color: rgba(255,82,82,0.35); }
.chip.is-breaking::before {
    content: ""; display: inline-block; width: 6px; height: 6px;
    border-radius: 50%; background: #ff5252;
    margin-right: 6px; vertical-align: 1px;
    box-shadow: 0 0 8px rgba(255,82,82,0.6);
}
.chip.is-breaking.active {
    background: rgba(255,82,82,0.18);
    border-color: rgba(255,82,82,0.5);
    color: #ff8a8a;
    box-shadow: var(--btn-soft-shadow);
}
.chip.is-breaking.active::before { background: #ff5252; box-shadow: 0 0 8px rgba(255,82,82,0.6); }
/* Columns slider — fat thumb so it's grabbable on touch screens. */
.slider {
    width: 100%;
    height: 36px;
    background: transparent;
    -webkit-appearance: none;
    appearance: none;
    accent-color: var(--accent);
    cursor: pointer;
}
/* Track */
.slider::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: 3px;
    background: var(--surface-2, #2a2a2a);
}
.slider::-moz-range-track {
    height: 6px;
    border-radius: 3px;
    background: var(--surface-2, #2a2a2a);
}
/* Thumb — matches the shaded-shadow-flat button family. Translucent purple
   over surface, just like the .chip.active and .btn-primary buttons. */
.slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 20px; height: 20px;
    border-radius: 50%;
    background: var(--btn-soft-bg);
    border: 1px solid var(--btn-soft-border);
    box-shadow: var(--btn-soft-shadow);
    margin-top: -7px;           /* center on 6px track */
    cursor: grab;
}
.slider::-webkit-slider-thumb:active { cursor: grabbing; background: var(--btn-soft-bg-hover); border-color: var(--btn-soft-border-hover); }
.slider::-moz-range-thumb {
    width: 20px; height: 20px;
    border-radius: 50%;
    background: var(--btn-soft-bg);
    border: 1px solid var(--btn-soft-border);
    box-shadow: var(--btn-soft-shadow);
    cursor: grab;
}
.slider::-moz-range-thumb:active { background: var(--btn-soft-bg-hover); border-color: var(--btn-soft-border-hover); }
.slider::-moz-range-thumb:active { cursor: grabbing; }
.slider:focus { outline: none; }

.search-form { display: flex; gap: 8px; }
.search-input {
    flex: 1; background: var(--surface-2); color: var(--text);
    border: 1px solid var(--border); border-radius: 999px;
    padding: 9px 16px; font-size: 14px;
}
.search-input:focus { outline: none; border-color: var(--accent); }
.search-submit {
    background: var(--btn-soft-bg);
    color: var(--btn-soft-color);
    border: 1px solid var(--btn-soft-border);
    border-radius: 999px; padding: 9px 18px;
    font-size: 14px; font-weight: 600; cursor: pointer;
    box-shadow: var(--btn-soft-shadow);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.search-submit:hover {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
    color: var(--btn-soft-color-hover);
}

/* Mobile dropdowns: full width by default — except the grid-density slider,
   which is just one tiny range input and reads as awkward when stretched
   edge-to-edge. Keep it narrow + right-anchored so it feels like a quick
   utility toggle rather than a full settings panel. */
@media (max-width: 640px) {
    .dropdown { width: 100%; right: 0; left: 0; border-radius: 0; }
    #dropdown-grid {
        width: 180px;
        right: 8px;
        left: auto;
        border-radius: 0 0 12px 12px;
        padding: 12px 14px;
    }
    .topbar-inner { padding: 0 8px; }
    .nav-btn { width: 34px; height: 34px; }
    .nav-btn .icon { width: 18px; height: 18px; }
}

/* ---------- Main ---------- */
.main-content { min-height: calc(100vh - var(--topbar-h)); }

/* ---------- Grid (row-span masonry, preserves DOM order) ----------
   Bump 2026-05-03: gap 3px → 10px and tile padding for a calmer, more
   organized feed. The dense magazine feel is sacrificed deliberately —
   the new design language reads as polish over density. */
.grid {
    display: grid;
    grid-template-columns: repeat(var(--cols), 1fr);
    gap: 10px;
    width: 100%;
    margin: 0 auto;
    padding: 12px 12px 0;
    box-sizing: border-box;
}
.grid[data-columns="1"] { max-width: 640px; }
.grid[data-columns="2"] { max-width: 1040px; }

@media (max-width: 600px) {
    .grid {
        grid-template-columns: repeat(min(var(--cols), 2), 1fr) !important;
        max-width: 100% !important;
        gap: 6px;
        padding: 6px 0 0;
    }
}

/* ── Mobile (≤600px): media grids/galleries run fully edge-to-edge; text and
   information content keeps a small gutter. The home/search/profile-feed .grid
   is already edge-to-edge via the rule above. */
@media (max-width: 600px) {
    /* Studio gallery — gapless masonry flush to the screen edges (the head keeps
       its own small margin). */
    .studio-main { padding-left: 0; padding-right: 0; }

    /* News — the grid breaks out to the edges; the wrap keeps a small gutter so
       the header / filters / category chips stay readable. */
    .news-wrap { padding-left: 10px; padding-right: 10px; }
    .news-grid { margin-left: -10px; margin-right: -10px; }

    /* Profile tabs — text panes keep a small gutter; the Gallery (masonry) pane
       goes fully edge-to-edge. */
    .profile-tab-pane { padding-left: 12px; padding-right: 12px; }
    .profile-tab-pane:has(.cre-masonry) { padding-left: 0; padding-right: 0; }

    /* Text / information / content pages — minimal gutter (down from 20px). */
    .compose-container, .settings-container { padding-left: 12px; padding-right: 12px; }
}

/* Profile-only: constrain grid container to header width */
.profile-feed {
    max-width: 1300px;
    margin: 0 auto;
    padding: 0 20px;
}
@media (max-width: 600px) {
    .profile-feed { padding: 0; }
}

/* ==========================================================================
   AION Card — universal content tile
   ========================================================================== */

/* Every tile: 2:3 aspect, contained overflow, container query context. */
/* ── Profile / Page result cards in the search grid ──────────────────────────
   Global (NOT inline in search.php) so they keep their styling when
   inline-search.js injects them into the feed grid — that path copies only
   #feed-grid's children, so any page-local <style> would be left behind. */
.postcard-user-result {
    background: linear-gradient(180deg, rgba(124,58,237,0.12), rgba(124,58,237,0.02));
    border: 1px solid rgba(124,58,237,0.40);
    border-radius: 12px;
    overflow: hidden;
    aspect-ratio: 2 / 3;
    display: flex;
    flex-direction: column;
    cursor: pointer;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.postcard-user-result:hover {
    transform: translateY(-2px);
    border-color: rgba(124,58,237,0.70);
    box-shadow: 0 8px 22px rgba(124,58,237,0.18);
}
.postcard-user-result .user-result-link {
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    padding: 26px 16px 18px;
    text-decoration: none; color: var(--text, #eee);
    height: 100%; box-sizing: border-box;
}
.postcard-user-result .user-result-avatar {
    width: 84px; height: 84px; border-radius: 50%; object-fit: cover;
    background: #222; border: 3px solid rgba(124,58,237,0.50); margin-bottom: 6px;
}
.postcard-user-result .user-result-name {
    font-size: 16px; font-weight: 800; text-align: center; color: #fff; line-height: 1.2;
    max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.postcard-user-result .user-result-handle {
    font-size: 12.5px; text-align: center; color: var(--btn-soft-color, #c4b5fd); margin-bottom: 4px;
}
.postcard-user-result .user-result-bio {
    font-size: 12px; line-height: 1.5; color: var(--dim, #aaa); text-align: center;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.postcard-user-result .user-result-cta {
    margin-top: auto; padding: 8px 16px;
    background: var(--btn-soft-bg, rgba(124,58,237,0.20));
    color: var(--btn-soft-color, #c4b5fd);
    border: 1px solid var(--btn-soft-border, rgba(124,58,237,0.45));
    border-radius: 999px; font-size: 12px; font-weight: 700; text-align: center;
}

.postcard {
    position: relative;
    overflow: hidden;
    /* Tile uses the canonical surface + border tokens so cards feel like
       siblings of every other surface in the design system (settings cards,
       admin panels, dropdowns). The visual weight stays on the content —
       chrome is intentionally quiet until hover. */
    background: var(--surface);
    border: 1px solid var(--border);
    cursor: pointer;
    isolation: isolate;
    aspect-ratio: 2 / 3;
    container-type: inline-size;
    border-radius: 10px;
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
/* ── Sponsored badge — small "Sponsored" pill on every paid placement.
   Required by the FTC + every other regulator; also a credibility signal
   that AION is honest about what's an ad and what isn't. */
.sponsored-badge {
    position: absolute;
    top: 10px; right: 10px;
    z-index: 7;
    padding: 3px 9px;
    background: rgba(0,0,0,0.65);
    -webkit-backdrop-filter: blur(10px);
            backdrop-filter: blur(10px);
    border: 1px solid rgba(255,255,255,0.18);
    border-radius: 999px;
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    pointer-events: none;
}
.postcard.card-sponsored {
    /* Subtle warm-tinted border so paid placements visually separate from
       organic posts at a glance — without screaming. */
    border-color: rgba(255,180,0,0.22);
}
.postcard.card-sponsored:hover {
    border-color: rgba(255,180,0,0.45);
}
/* Hover state — a hairline accent on the border, a 1px lift, and a soft
   drop shadow. The whole grid feels lightly responsive on mouse-move
   without ever shouting. */
@media (hover: hover) {
    .postcard:hover {
        transform: translateY(-1px);
        border-color: rgba(124,58,237,0.35);
        box-shadow: 0 4px 16px rgba(0,0,0,0.35);
    }
}

/* ── Media zone fills the tile; each type partial plugs in here ────────── */
.card-media {
    position: absolute;
    inset: 0;
    display: block;
    background: #111;
}
.card-media > img,
.card-media > video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Image card: letterbox the real photo inside a blurred self-background so
   square memes (and any non-2:3 aspect) don't crop. The blur gives us an
   automatic "dominant color" backdrop without needing to sample pixels. */
.card-image .cm-image-bg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    filter: blur(36px) saturate(1.25) brightness(0.85);
    /* Scale up slightly to hide blur-edge artifacts at the container bounds */
    transform: scale(1.15);
    z-index: 0;
}
.card-image .cm-image-fg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: contain;       /* default: square/landscape stay uncropped (memes with text) */
    z-index: 1;
}
/* Tall/portrait images (ratio < 0.9) — fill the card full width; top/bottom
   clipping is fine. Class added by image.php onload handler. */
.card-image .cm-image-fg.cm-fill { object-fit: cover; }
.card-fallback {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: rgba(255,255,255,0.85);
}

/* ── Author pill — ALWAYS top-left, every type ─────────────────────────── */
.card-author {
    position: absolute;
    top: 10px; left: 10px;
    display: inline-flex; align-items: center; gap: 7px;
    padding: 3px 11px 3px 3px;
    background: rgba(0,0,0,0.6);
    -webkit-backdrop-filter: blur(12px);
            backdrop-filter: blur(12px);
    border-radius: 999px;
    opacity: 0.7;
    z-index: 6;
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    text-decoration: none;
    max-width: calc(100% - 84px);
    transition: opacity .18s ease, background .15s ease;
}
.card-author:hover { opacity: 1; background: rgba(0,0,0,0.85); color: #fff; }
.postcard:hover .card-author { opacity: 1; }
@media (hover: none) { .card-author { opacity: 0.95; } }

/* Tiny author variant for link cards — just the avatar in a corner */
.card-author-mini {
    padding: 2px;
    gap: 0;
}
.card-author-mini .card-author-avatar {
    width: 20px; height: 20px;
}
.card-author-avatar {
    width: 22px; height: 22px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: rgba(255,255,255,0.1);
}
.card-author-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    letter-spacing: 0.1px;
}
/* Web-result sites render a favicon rather than an avatar; tiny polish. */
.card-author-site .card-author-avatar { border-radius: 4px; }

/* ── Type glyph (top-right). image/text omit this. ─────────────────────── */
.card-glyph {
    position: absolute;
    top: 12px; right: 12px;
    color: rgba(255,255,255,0.95);
    line-height: 1;
    z-index: 4;
    pointer-events: none;
    filter: drop-shadow(0 1px 3px rgba(0,0,0,0.65));
}
.card-glyph svg { display: block; }
/* Shift glyph left when owner menu is present so they don't collide. */
.postcard[data-mine] .card-glyph { right: 44px; }

/* ── Owner menu (AION posts only) ───────────────────────────────────────── */
.card-menu {
    position: absolute;
    top: 8px; right: 8px;
    width: 28px; height: 28px;
    border: none;
    border-radius: 50%;
    background: rgba(0,0,0,0.55);
    color: #fff;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    z-index: 6;
    padding: 0;
    -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
    transition: background .15s ease;
}
.card-menu:hover { background: rgba(0,0,0,0.8); }
/* Legacy alias so existing menu JS keeps working. */
.postcard-menu-btn { /* same rules as .card-menu */ }

/* ── Badges (duration, gallery count, doc ext) ─────────────────────────── */
.card-badge {
    position: absolute;
    background: rgba(0,0,0,0.78);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    padding: 3px 8px;
    border-radius: 4px;
    z-index: 3;
    pointer-events: none;
    letter-spacing: 0.4px;
    display: inline-flex; align-items: center; gap: 4px;
    -webkit-backdrop-filter: blur(4px);
            backdrop-filter: blur(4px);
}
/* Media-zone badges sit above the footer, not at the absolute bottom edge. */
.card-badge-tr-media { top: 46px; right: 10px; }
.card-badge-bl-media { bottom: 88px; left: 10px; }

/* ── Centered play button (video / audio / game) ───────────────────────── */
.card-play {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 54px; height: 54px;
    background: rgba(0,0,0,0.55);
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    color: #fff;
    z-index: 3;
    pointer-events: none;
    -webkit-backdrop-filter: blur(6px);
            backdrop-filter: blur(6px);
    border: 2px solid rgba(255,255,255,0.85);
    transition: transform .18s ease, background .18s ease;
}
.card-play svg { margin-left: 2px; }
.card-play { transition: opacity .25s ease, transform .18s ease, background .18s ease; }

/* ── Video card — same letterbox as images so portrait AND landscape clips show
   uncropped in the 2:3 tile: blurred poster fills behind, real video contained. */
.card-video .cm-video-bg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    filter: blur(36px) saturate(1.25) brightness(0.82);
    transform: scale(1.15);
    z-index: 0;
}
.card-media > video.cm-video-fg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: contain;          /* show the whole frame — no crop */
    background: transparent;
    z-index: 1;
}
/* Small "muted" chip — only while autoplaying; the big play button fades out. */
.card-media .card-mute { display: none; }
.card-media.is-playing .card-play { opacity: 0; }
.card-media.is-playing .card-mute {
    display: inline-flex; align-items: center; justify-content: center;
    position: absolute; left: 10px; top: 10px; z-index: 3;
    width: 26px; height: 26px; border-radius: 50%;
    background: rgba(0,0,0,0.6); color: #fff;
    -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
    pointer-events: none;
}

/* Video still encoding: placeholder shown until the worker fills media_url. */
.card-video-processing {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 8px; text-align: center;
    background:
        radial-gradient(120% 120% at 50% 0%, rgba(124,58,237,0.22), rgba(10,9,19,0.92));
    color: #e9e3ff;
}
.cvp-spinner {
    width: 34px; height: 34px; border-radius: 50%;
    border: 3px solid rgba(196,181,253,0.25);
    border-top-color: #c4b5fd;
    animation: cvp-spin 0.8s linear infinite;
}
@keyframes cvp-spin { to { transform: rotate(360deg); } }
.cvp-label { font-size: 13px; font-weight: 600; letter-spacing: .2px; }
.cvp-sub   { font-size: 11px; color: rgba(233,227,255,0.6); }

/* ── News digest tile — curated bundle of news links in one 2:3 card. ────────
   Fills the frame as a flex column: header (fixed) + equal-height item rows
   (flex) + action bar (fixed). @container queries adapt to the tile's actual
   width so it stays readable from 1 wide column down to 3 narrow mobile cols. */
/* The article tile keeps its own class (card-digest = card-<type>); the inner
   .dg-wrap fills the 2:3 frame. Classes are dg-* (digest), NOT cd-*, because
   the creator dashboard already owns .cd-* — reusing them hijacked the dashboard. */
.dg-wrap {
    position: absolute; inset: 0;
    display: flex; flex-direction: column;
    background: var(--surface);
    overflow: hidden;
}
.dg-head {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: clamp(7px, 1.8cqi, 12px);
    padding: clamp(9px, 2.2cqi, 18px) clamp(11px, 2.6cqi, 20px);
    text-decoration: none;
    border-bottom: 1px solid var(--border);
}
.dg-head-avatar { width: clamp(24px, 5.2cqi, 42px); height: clamp(24px, 5.2cqi, 42px); border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
.dg-head-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.dg-eyebrow { font-size: clamp(9px, 2cqi, 12px); font-weight: 800; letter-spacing: 1px; text-transform: uppercase; color: #c4b5fd; }
.dg-title { font-size: clamp(13px, 3.4cqi, 21px); font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dg-list {
    flex: 1 1 auto; min-height: 0;
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column;
    overflow: hidden;
}
.dg-item { flex: 1 1 0; min-height: 0; display: flex; border-bottom: 1px solid var(--border); }
.dg-item:last-child { border-bottom: none; }
.dg-item-link {
    display: flex; align-items: stretch; gap: 0;
    width: 100%; min-width: 0; height: 100%;
    padding: 0;
    text-decoration: none; color: var(--text);
    transition: background .12s ease;
}
.dg-item-link:hover { background: rgba(255,255,255,0.05); }
/* Thumbnail fills the row vertically, flush — no padding, no rounding. Width
   scales with the tile so it stays proportional from 1 wide col to 3 narrow. */
.dg-thumb {
    flex: 0 0 auto;
    height: 100%;
    width: clamp(58px, 26cqi, 170px);
    border-radius: 0;
    background-size: cover; background-position: center;
    background-color: var(--surface-2);
}
.dg-thumb-empty { background: linear-gradient(135deg, rgba(124,58,237,0.28), rgba(124,58,237,0.05)); }
.dg-item-text {
    display: flex; flex-direction: column; justify-content: center; min-width: 0; gap: 2px;
    padding: clamp(6px, 1.6cqi, 14px) clamp(9px, 2.4cqi, 16px);
}
.dg-item-headline {
    font-size: clamp(12px, 3.1cqi, 18px); font-weight: 600; line-height: 1.25; color: var(--text);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.dg-item-excerpt {
    font-size: clamp(10.5px, 2.5cqi, 14.5px); line-height: 1.35; color: var(--dim);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.dg-item-source { font-size: clamp(10px, 2.1cqi, 13px); font-weight: 700; color: #c4b5fd; margin-top: 2px; }
.card-digest .card-actions-bar {
    flex: 0 0 auto;
    position: static;
    border-top: 1px solid var(--border);
    background: var(--surface);
    opacity: 1;
}
/* Digest — full list in the post modal (rich TWIS-style read). */
.md-digest { display: flex; flex-direction: column; }
.md-digest-item {
    display: flex; gap: 12px; padding: 13px 2px;
    text-decoration: none; color: var(--text);
    border-bottom: 1px solid var(--border);
}
.md-digest-item:last-child { border-bottom: none; }
.md-digest-item:hover { background: rgba(255,255,255,0.04); }
.md-digest-thumb {
    flex: 0 0 auto; width: 84px; height: 84px; border-radius: 8px;
    background-size: cover; background-position: center; background-color: var(--surface-2);
}
.md-digest-thumb-empty { background: linear-gradient(135deg, rgba(124,58,237,0.28), rgba(124,58,237,0.05)); }
.md-digest-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.md-digest-headline { font-size: 15px; font-weight: 600; line-height: 1.3; color: var(--text); }
.md-digest-excerpt { font-size: 13px; line-height: 1.4; color: var(--dim); }
.md-digest-source { font-size: 12px; font-weight: 700; color: #c4b5fd; }

/* Narrow tiles (≈3-col desktop): drop the excerpt; the thumb keeps filling
   the row height, fonts keep shrinking via the clamp() above. */
@container (max-width: 250px) {
    .dg-item-excerpt { display: none; }
}
/* Very narrow (mobile 3-col): clean headline-only list, no thumbs. */
@container (max-width: 172px) {
    .dg-thumb { display: none; }
    .dg-item-source { display: none; }
    .dg-item-text { padding: 6px 10px; }
}
@media (hover: hover) {
    .postcard:hover .card-play {
        background: rgba(255,255,255,0.95);
        color: #111;
        transform: translate(-50%, -50%) scale(1.06);
    }
    .postcard:hover .card-play svg { fill: #111; }
}

/* ── Gallery stacked-edges effect ──────────────────────────────────────────
   Two faint card silhouettes peek out behind the cover to hint "there's
   more here." Sits at z-index -1 within the .card-media stacking context
   so it can never tint the photo above. The count badge in the top-right
   is the primary "multi-image" signal; this is just visual reinforcement.
   Previously the stack had z-index 0, which combined with the static-
   positioned <img> caused the dark overlay to paint ON TOP of the image —
   tinting most of the tile and leaving a 3-8px lighter strip at the top-
   left where the translated pseudos didn't quite reach. */
.card-gallery-stack {
    position: absolute; inset: 0;
    z-index: -1;
    pointer-events: none;
}
.card-gallery-stack::before,
.card-gallery-stack::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 10px;
    background: var(--surface);
    border: 1px solid var(--border);
}
.card-gallery-stack::before { transform: translate(3px, 4px); opacity: 0.7; }
.card-gallery-stack::after  { transform: translate(6px, 8px); opacity: 0.4; }
/* The image is explicitly above the stack so future CSS can't accidentally
   re-stack things. */
.postcard.card-gallery .card-media > img { position: relative; z-index: 1; }

/* ── Audio card specifics ──────────────────────────────────────────────── */
.card-audio-fallback {
    position: absolute; inset: 0;
}
.card-audio-scrim {
    position: absolute; inset: 0;
    background: radial-gradient(ellipse at center, rgba(0,0,0,0.25) 0%, rgba(0,0,0,0.7) 100%);
    z-index: 1;
}
.card-audio-wave {
    position: absolute;
    left: 14px; right: 14px; bottom: 92px;
    height: 42px;
    display: flex; align-items: center; justify-content: space-between; gap: 2px;
    z-index: 2;
    pointer-events: none;
    opacity: 0.85;
}
.card-audio-wave span {
    flex: 1;
    background: rgba(255,255,255,0.7);
    border-radius: 1px;
    animation: cardWave 1.4s ease-in-out infinite;
}
.card-audio-wave span:nth-child(1)  { height: 20%; animation-delay: 0s;   }
.card-audio-wave span:nth-child(2)  { height: 50%; animation-delay: .05s; }
.card-audio-wave span:nth-child(3)  { height: 80%; animation-delay: .1s;  }
.card-audio-wave span:nth-child(4)  { height: 40%; animation-delay: .15s; }
.card-audio-wave span:nth-child(5)  { height: 70%; animation-delay: .2s;  }
.card-audio-wave span:nth-child(6)  { height: 30%; animation-delay: .25s; }
.card-audio-wave span:nth-child(7)  { height: 90%; animation-delay: .3s;  }
.card-audio-wave span:nth-child(8)  { height: 55%; animation-delay: .35s; }
.card-audio-wave span:nth-child(9)  { height: 65%; animation-delay: .4s;  }
.card-audio-wave span:nth-child(10) { height: 35%; animation-delay: .45s; }
.card-audio-wave span:nth-child(11) { height: 75%; animation-delay: .5s;  }
.card-audio-wave span:nth-child(12) { height: 45%; animation-delay: .55s; }
.card-audio-wave span:nth-child(13) { height: 85%; animation-delay: .6s;  }
.card-audio-wave span:nth-child(14) { height: 25%; animation-delay: .65s; }
.card-audio-wave span:nth-child(15) { height: 60%; animation-delay: .7s;  }
.card-audio-wave span:nth-child(16) { height: 35%; animation-delay: .75s; }
.card-audio-wave span:nth-child(17) { height: 70%; animation-delay: .8s;  }
.card-audio-wave span:nth-child(18) { height: 45%; animation-delay: .85s; }
.card-audio-wave span:nth-child(19) { height: 80%; animation-delay: .9s;  }
.card-audio-wave span:nth-child(20) { height: 30%; animation-delay: .95s; }
.card-audio-wave span:nth-child(21) { height: 55%; animation-delay: 1.0s; }
.card-audio-wave span:nth-child(22) { height: 70%; animation-delay: 1.05s;}
.card-audio-wave span:nth-child(23) { height: 40%; animation-delay: 1.1s; }
.card-audio-wave span:nth-child(24) { height: 60%; animation-delay: 1.15s;}
@keyframes cardWave { 0%,100% { transform: scaleY(.55); } 50% { transform: scaleY(1); } }

/* ── Link card specifics ───────────────────────────────────────────────── */
/* Legacy link-as-image rules retained but rules below scope to old centered fallback only */
.card-link-bg-legacy {
    position: absolute; inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.card-link-bg-legacy::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.1) 55%, rgba(0,0,0,0) 100%);
    pointer-events: none;
}
.card-link-commentary-legacy {
    position: absolute;
    top: 44px; left: 10px; right: 10px;
    padding: 10px 12px;
    background: rgba(0,0,0,0.55);
    border-radius: 8px;
    color: #fff;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
    z-index: 2;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
}
.card-link-centered {
    position: absolute; inset: 0;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    padding: 18% 10% 32%;
    text-align: center;
    color: #fff;
    gap: 10px;
}
.card-link-centered .cl-commentary {
    font-size: clamp(12px, 1.6cqw, 15px);
    font-weight: 500;
    margin: 0;
    padding-bottom: 10px;
    border-bottom: 1px solid rgba(255,255,255,0.25);
    color: rgba(255,255,255,0.95);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.card-link-centered .cl-title {
    margin: 0;
    font-size: clamp(16px, 2.4cqw, 22px);
    font-weight: 700;
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.card-link-centered .cl-desc {
    margin: 0;
    font-size: clamp(11px, 1.4cqw, 14px);
    line-height: 1.4;
    color: rgba(255,255,255,0.8);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ── Document fallback (no thumbnail) ──────────────────────────────────── */
.card-doc-fallback {
    position: absolute; inset: 0;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    gap: 14px;
    color: #fff;
}
.card-doc-ext {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 2.5px;
    background: rgba(255,255,255,0.18);
    padding: 4px 12px;
    border-radius: 4px;
    -webkit-backdrop-filter: blur(6px);
            backdrop-filter: blur(6px);
}

/* ── Text card specifics ───────────────────────────────────────────────── */
/* .ct-tile is the INNER tile div. The article itself has .card-text as a type
   class — these MUST stay different so the inset:0/absolute rules below don't
   cascade up to the article and blow the grid apart. */
.ct-tile {
    position: absolute; inset: 0;
    padding: 12% 9% 28%;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    text-align: center;
    color: #fff;
    overflow: hidden;
}
.ct-tile .ct-title {
    margin: 0 0 .4em;
    font-weight: 700;
    line-height: 1.2;
    font-size: clamp(16px, 2.6cqw, 28px);
}
.ct-tile .ct-body {
    margin: 0;
    line-height: 1.35;
    overflow: hidden;
    word-wrap: break-word;
}
.ct-tile .ct-body a { color: inherit; text-decoration: underline; }

/* Short text: scales big. */
.ct-tile.text-short .ct-body  { font-size: clamp(20px, 5cqw, 52px); font-weight: 600; line-height: 1.15; }
.ct-tile.text-short .ct-title { font-size: clamp(18px, 3.8cqw, 38px); }
.ct-tile.text-short .ct-title + .ct-body { font-size: clamp(16px, 3.2cqw, 34px); }

/* Long text: readable, left-aligned, vertically centered. */
.ct-tile.text-long            { justify-content: center; text-align: left; padding: 14% 10% 30%; }
.ct-tile.text-long .ct-title  { font-size: clamp(17px, 2cqw, 24px); width: 100%; }
.ct-tile.text-long .ct-body   { font-size: clamp(14px, 1.65cqw, 18px); line-height: 1.5; width: 100%; min-height: 0; }

/* Gradient palette on gradient-backed text: text colors for light gradients. */
.gradient-7 .ct-body a, .gradient-8 .ct-body a { color: #222; }

/* Gradient-backed cards (text + link-without-image): let article bg show through. */
.card-on-gradient .card-media { background: transparent; }
.postcard-text .card-footer {
    background: linear-gradient(to top, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 100%);
}

/* Gradient palette (unchanged) */
.gradient-1 { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.gradient-2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
.gradient-3 { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
.gradient-4 { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
.gradient-5 { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
.gradient-6 { background: linear-gradient(135deg, #30cfd0 0%, #330867 100%); }
.gradient-7 { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); color: #222; }
.gradient-8 { background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%); color: #222; }
.gradient-7 .ct-body a, .gradient-8 .ct-body a { color: #222; }

/* ── Text-card background library (smooth, dark-to-medium-dark) ──────────────
   Stored in posts.gradient_style as the class name (e.g. 'ctbg-spot-purple').
   Chosen in the compose picker; randomGradient() falls back to a random one.
   Legacy gradient-1..8 above still render for older posts. */

/* SPOTLIGHT — soft multi-stop colour glow over a near-black base. One formula;
   each variant just sets colour (--gc), glow position (--gp) and peak alpha
   (--ga). The fade reaches transparent inside the tile so there's no hard ring. */
[class*="ctbg-spot-"] {
    background:
        radial-gradient(98% 82% at var(--gp, 50% 50%),
            rgba(var(--gc), var(--ga, .38)) 0%,
            rgba(var(--gc), calc(var(--ga, .38) * .58)) 26%,
            rgba(var(--gc), calc(var(--ga, .38) * .26)) 48%,
            rgba(var(--gc), calc(var(--ga, .38) * .08)) 66%,
            transparent 86%),
        #111114 !important;
}
.ctbg-spot-purple   { --gc:168,85,247; --gp:50% 50%;  --ga:.38; }
.ctbg-spot-purple-t { --gc:168,85,247; --gp:50% -2%;  --ga:.42; }
.ctbg-spot-purple-b { --gc:168,85,247; --gp:50% 102%; --ga:.42; }
.ctbg-spot-blue-t   { --gc:59,130,246; --gp:50% -2%;  --ga:.42; }
.ctbg-spot-teal     { --gc:45,212,191; --gp:50% 50%;  --ga:.36; }
.ctbg-spot-cyan-b   { --gc:34,211,238; --gp:50% 102%; --ga:.38; }
.ctbg-spot-indigo   { --gc:99,102,241; --gp:50% 50%;  --ga:.40; }
.ctbg-spot-rose     { --gc:244,63,94;  --gp:50% 50%;  --ga:.34; }
.ctbg-spot-amber-b  { --gc:251,146,60; --gp:50% 102%; --ga:.40; }
.ctbg-spot-green    { --gc:34,197,94;  --gp:50% 50%;  --ga:.32; }

/* GLASS QUOTE — dark glass + faint accent quote-mark + sheen. Uses inset
   box-shadow (not a real border) so the zero-gap grid stays seamless. */
[class*="ctbg-glass-"] {
    background:
        radial-gradient(120% 70% at 50% -10%, rgba(var(--gc), .17) 0%, rgba(var(--gc), .07) 40%, transparent 72%),
        #141416 !important;
    box-shadow: inset 0 0 0 1px rgba(var(--gc), .24);
}
.card-text[class*="ctbg-glass-"]::before {
    content: "\201C";
    position: absolute; top: -6px; left: 14px;
    font: 700 120px/1 Georgia, serif;
    color: rgba(var(--gc), .16);
    z-index: 0; pointer-events: none;
}
.ctbg-glass-purple { --gc:124,58,237; }
.ctbg-glass-teal   { --gc:13,148,136; }
.ctbg-glass-amber  { --gc:217,119,6; }
.ctbg-glass-rose   { --gc:225,29,72; }

/* DUSK — muted full gradient. */
.ctbg-dusk-plum  { background: linear-gradient(150deg, #3a2d5c, #21243f 58%, #15162a) !important; }
.ctbg-dusk-ocean { background: linear-gradient(150deg, #123438, #16263a 55%, #101622) !important; }
.ctbg-dusk-ember { background: linear-gradient(150deg, #3d2540, #2a1c33 55%, #1a1320) !important; }
.ctbg-dusk-slate { background: linear-gradient(150deg, #2b2f3a, #1d2029 55%, #141519) !important; }

/* ── Compose: background-picker icon, tray & swatches ───────────────────────── */
.compose-bg-btn { position: relative; }
.bg-tray {
    /* Anchored to the icon's right edge so it opens INWARD (leftward) and never
       spills past the dialog's right edge (which would trigger a scrollbar). */
    position: absolute; bottom: calc(100% + 8px); right: 0; left: auto;
    display: none; flex-wrap: wrap; gap: 7px;
    width: 232px; padding: 10px;
    background: rgba(20,20,26,0.94);
    -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
    border: 1px solid rgba(124,58,237,0.30); border-radius: 14px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.6);
    z-index: 30;
}
.bg-tray.open { display: flex; }
.bgswatch {
    width: 34px; height: 34px; border-radius: 9px; cursor: pointer;
    border: 1px solid rgba(255,255,255,0.14);
    overflow: hidden; padding: 0; position: relative;
    transition: transform .1s ease, border-color .12s ease;
}
.bgswatch::before { display: none !important; }
.bgswatch:hover { transform: translateY(-1px); border-color: rgba(255,255,255,0.4); }
.bgswatch.is-active { border-color: #c4b5fd; box-shadow: 0 0 0 2px rgba(124,58,237,0.55); }
/* Live preview: chosen background sits behind the textarea as you type. */
.compose-form textarea.compose-bg-live { color: #fff; text-shadow: 0 1px 10px rgba(0,0,0,0.55); }
.compose-form textarea.compose-bg-live::placeholder { color: rgba(255,255,255,0.55); }
/* Purple all-caps eyebrow heading inside the swatch tray. */
.bg-tray-eyebrow {
    width: 100%; margin: 0 0 4px;
    font-size: 10px; font-weight: 800; letter-spacing: 1.2px;
    text-transform: uppercase; color: #c4b5fd;
}

/* ── Footer (group · title · metrics/actions) ──────────────────────────── */
.card-footer {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 30px 12px 10px;
    background: linear-gradient(to top, rgba(0,0,0,0.88) 0%, rgba(0,0,0,0.55) 55%, rgba(0,0,0,0) 100%);
    color: #fff;
    z-index: 4;
    display: flex;
    flex-direction: column;
    gap: 3px;
}
.card-group {
    font-size: 10.5px;
    font-weight: 700;
    color: rgba(255,255,255,0.82);
    text-decoration: none;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
.card-group:hover { color: #fff; }
.card-title {
    margin: 0;
    font-size: 14px;
    font-weight: 700;
    line-height: 1.25;
    color: #fff;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-shadow: 0 1px 2px rgba(0,0,0,0.5);
    letter-spacing: -0.1px;
}
/* Link cards are headline-first — give the title more room and more punch. */
.card-link .card-title { font-size: 15px; -webkit-line-clamp: 3; }

/* Metrics + actions share the same slot — actions slide in on hover. */
.card-meta {
    position: relative;
    margin-top: 4px;
    min-height: 22px;
}
.card-metrics {
    display: flex; align-items: center; gap: 12px;
    font-size: 12px; font-weight: 600;
    color: rgba(255,255,255,0.95);
    transition: opacity .18s ease;
}
.card-metrics .m { display: inline-flex; align-items: center; gap: 4px; }
.card-metrics .m .num { font-variant-numeric: tabular-nums; }
.card-metrics .m.muted { color: rgba(255,255,255,0.72); font-weight: 500; }
.card-metrics .emoji-img { width: 14px; height: 14px; vertical-align: -2px; }
/* Link-card description + inline source credit. Rendered directly under
   .card-title, above the metrics/actions row. Source sits at the tail of
   the description like a news-card credit line. */
.card-desc {
    margin: 2px 0 0;
    font-size: 12px;
    line-height: 1.4;
    color: rgba(255,255,255,0.82);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    word-break: break-word;
}
/* Dedicated source line — its own block element so it can never be clipped
   by .card-desc overflow. Blue, truncated with ellipsis on narrow tiles. */
.card-source {
    display: block;
    margin-top: 4px;
    font-size: 12px;
    font-weight: 700;
    color: #4ea3ff;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    pointer-events: auto;
    position: relative;
    z-index: 3;
}
.card-source:hover { color: #7fbcff; text-decoration: underline; }
/* Stay solid on hover zoom (footer scrim is already dark enough). */
.postcard:hover .card-source { color: #7fbcff; }

/* Legacy standalone chip — still used for video duration, pdf page count, etc.
   Link cards no longer render this (they use .card-inline-source instead). */
.card-meta-extra {
    position: absolute;
    right: 12px;
    bottom: 10px;
    font-size: 11px;
    font-weight: 500;
    max-width: 60%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: rgba(255,255,255,0.78);
    z-index: 4;
    pointer-events: auto;
}
a.card-meta-extra { text-decoration: none; }
a.card-meta-extra:hover { color: #fff; text-decoration: underline; }
/* Stay visible on zoom hover */
.postcard:hover .card-meta-extra { opacity: 1; color: #fff; }

/* Legacy passive metrics row — fully retired. The action buttons carry
   their own counts now, so any cached HTML still rendering .card-metrics
   is hidden defensively to prevent a redundant second reaction chip. */
.card-metrics { display: none !important; }

.card-actions {
    /* Inline centered action bar, always visible at 33% → 100% on hover.
       Each button carries its own count; no separate metrics row.
       Gap + icon scale shrink with the tile via container queries below. */
    position: relative;
    width: 100%;                 /* MUST span full footer or justify-content has no room */
    display: flex; align-items: center; justify-content: center;
    gap: clamp(6px, 7cqw, 28px);
    margin-top: 4px;
    opacity: 0.33;
    pointer-events: auto;
    transition: opacity .18s ease;
}
/* Icons scale down inside small tiles too, so the whole bar tightens up
   together. 24px @ ~260px tile → 14px @ ~90px tile. */
.card-actions .action-btn svg,
.card-actions .action-btn .emoji {
    width: clamp(14px, 8cqw, 24px);
    height: clamp(14px, 8cqw, 24px);
}
.card-actions .action-btn {
    font-size: clamp(10px, 3.5cqw, 13px);
    gap: clamp(2px, 1.2cqw, 6px);
}
.card-actions .action-btn .count {
    font-size: clamp(10px, 3.2cqw, 12px);
}
.card-actions .action-btn {
    background: transparent; border: none;
    color: #fff;
    font-size: 12px; font-weight: 600;
    display: inline-flex; align-items: center; gap: 4px;
    cursor: pointer;
    padding: 2px 0;
    text-decoration: none;
}
.card-actions .action-btn:hover { transform: scale(1.08); }
.card-actions .action-btn.liked { color: var(--like); }
.card-actions .action-btn .count { min-width: 10px; text-align: left; font-variant-numeric: tabular-nums; }

/* ── Reveal behavior ───────────────────────────────────────────────────── */
@media (hover: hover) {
    .postcard:hover .card-actions { opacity: 1; }
}
/* Touch devices: full opacity (no hover signal available). */
@media (hover: none) {
    .card-actions { opacity: 1; }
}
/* Single-column list view: full opacity always. */
.grid[data-columns="1"] .card-actions { opacity: 1; }

/* Desktop grid zoom — subtle 1.1x on hover. Pre-2026-04-25 was 1.2x and
   read as too aggressive; halving the delta to 0.1 keeps the affordance
   without making the card jump. */
@media (hover: hover) {
    .grid:not([data-columns="1"]) .postcard {
        transition: transform .18s ease, box-shadow .18s ease;
    }
    .grid:not([data-columns="1"]) .postcard:hover {
        transform: scale(1.1);
        z-index: 50;
        box-shadow: 0 16px 48px rgba(0,0,0,0.6);
    }
}

/* Reaction glyph inside action button */
.react-glyph { display: inline-flex; align-items: center; justify-content: center; }
.react-glyph .emoji { font-size: 18px; line-height: 1; }
.emoji-img {
    width: 1em; height: 1em;
    vertical-align: -0.15em;
    display: inline-block;
    user-select: none;
}

/* ── Reaction picker (compact emoji strip, no labels) ──────────────────── */
.reaction-picker {
    position: fixed;
    z-index: 9999;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1px;
    padding: 5px 7px;
    background: rgba(24,24,27,0.96);
    border: 1px solid var(--border);
    border-radius: 999px;
    box-shadow: 0 10px 32px rgba(0,0,0,0.55);
    -webkit-backdrop-filter: blur(14px);
            backdrop-filter: blur(14px);
    animation: reaction-pop .22s cubic-bezier(.2,.9,.3,1.2);
    max-width: min(360px, calc(100vw - 16px));
    transform-origin: bottom center;
}
.reaction-opt {
    background: transparent !important;
    border: none !important;
    padding: 3px !important;
    margin: 0 !important;
    font-size: 0;
    line-height: 1;
    cursor: pointer;
    transition: transform .18s cubic-bezier(.2,.9,.3,1.2), background .15s ease;
    will-change: transform;
    border-radius: 999px !important;
    min-width: 0 !important;
    width: auto !important;
    height: auto !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: inherit !important;
}
.reaction-opt:hover,
.reaction-opt:focus-visible {
    transform: translateY(-2px) scale(1.18);
    background: rgba(255,255,255,0.08) !important;
    outline: none;
}
.reaction-opt .emoji-img { width: 26px; height: 26px; vertical-align: 0; display: block; }
@media (max-width: 480px) {
    .reaction-picker { padding: 4px 6px; gap: 0; max-width: calc(100vw - 12px); }
    .reaction-opt { padding: 2px !important; }
    .reaction-opt .emoji-img { width: 22px; height: 22px; }
}

/* ── Emoji picker (full set, categorized) ──────────────────────────────── */
.emoji-trigger {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 999px;
    width: 34px; height: 34px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--dim);
    cursor: pointer;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
    flex-shrink: 0;
    margin-left: 6px;
    vertical-align: middle;
}
.emoji-trigger:hover,
.emoji-trigger.is-active {
    background: rgba(124,58,237,0.12);
    border-color: rgba(124,58,237,0.4);
    color: var(--text);
}

.emoji-picker {
    position: fixed;
    z-index: 9999;
    width: min(360px, calc(100vw - 16px));
    background: rgba(20,20,24,0.98);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 16px 48px rgba(0,0,0,0.6);
    -webkit-backdrop-filter: blur(14px);
            backdrop-filter: blur(14px);
    overflow: hidden;
    animation: reaction-pop .18s cubic-bezier(.2,.9,.3,1.2);
    display: flex;
    flex-direction: column;
    max-height: 360px;
}
.emoji-tabs {
    display: flex;
    align-items: center;
    justify-content: space-around;
    gap: 2px;
    padding: 6px 8px;
    border-bottom: 1px solid var(--border);
    background: rgba(255,255,255,0.02);
    flex-shrink: 0;
}
.emoji-tab {
    background: transparent;
    border: none;
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 20px;
    line-height: 1;
    border-radius: 8px;
    cursor: pointer;
    opacity: 0.5;
    transition: opacity .15s ease, background .15s ease;
}
.emoji-tab:hover { opacity: 1; background: rgba(255,255,255,0.06); }
.emoji-tab.active {
    opacity: 1;
    background: rgba(124,58,237,0.18);
}
.emoji-panes {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    /* Keep touch-scroll momentum inside the panes — without `contain`, a flick
       past the bottom chains up to the page and the JS scroll handler closes
       the picker. `contain` (and the legacy iOS -webkit-* prefix) blocks the
       chain so the user can flick freely through emoji categories. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
.emoji-panes::-webkit-scrollbar { width: 6px; }
.emoji-panes::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 3px; }
.emoji-pane { display: none; padding: 8px; }
.emoji-pane.active { display: block; }
.emoji-grid {
    display: grid;
    grid-template-columns: repeat(9, 1fr);
    gap: 2px;
}
.emoji-cell {
    background: transparent;
    border: none;
    aspect-ratio: 1;
    border-radius: 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 4px;
    transition: background .12s ease, transform .12s ease;
}
.emoji-cell:hover {
    background: rgba(255,255,255,0.08);
    transform: scale(1.15);
}
.emoji-cell img {
    width: 100%; height: 100%;
    display: block;
    pointer-events: none;
}

@media (max-width: 480px) {
    /* Taller picker on mobile — 320px was too tight for 56-emoji categories,
       last row got clipped. min(60vh, 480px) gives ~6-7 rows visible while
       still leaving room for the comment input and the sticky compose bar. */
    .emoji-picker { width: calc(100vw - 16px); max-height: min(60vh, 480px); }
    .emoji-grid { grid-template-columns: repeat(8, 1fr); }
}
@keyframes reaction-pop {
    0%   { opacity: 0; transform: translateY(10px) scale(.88); }
    60%  { opacity: 1; transform: translateY(-2px) scale(1.02); }
    100% { opacity: 1; transform: translateY(0)   scale(1); }
}

/* ── Post-menu popover (unchanged) ─────────────────────────────────────── */
.post-menu-popover {
    position: absolute;
    z-index: 9999;
    min-width: 140px;
    background: rgba(24,24,27,0.98);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5);
    overflow: hidden;
    animation: reaction-pop .12s ease-out;
}
.post-menu-popover button {
    display: block;
    width: 100%;
    padding: 10px 14px;
    background: transparent;
    border: none;
    color: var(--text);
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}
.post-menu-popover button:hover { background: rgba(255,255,255,0.06); }
.post-menu-popover button.danger { color: #ff6b6b; }

/* Profile action row (referenced elsewhere — kept to avoid regressions). */
.profile-actions  { display: flex; gap: 8px; align-items: center; }
.profile-menu-btn { padding: 6px 10px; display: inline-flex; align-items: center; justify-content: center; }

/* Mobile density — tighter paddings so 2-col feeds still breathe. */
@media (max-width: 640px) {
    .card-footer     { padding: 24px 10px 8px; }
    .card-title      { font-size: 12.5px; }
    .card-metrics    { gap: 10px; font-size: 11px; }
    .card-author     { top: 8px; left: 8px; font-size: 11px; padding: 2px 9px 2px 2px; }
    .card-author-avatar { width: 20px; height: 20px; }
    .card-glyph      { top: 10px; right: 10px; }
    .card-play       { width: 44px; height: 44px; }
    .card-badge      { font-size: 10px; padding: 2px 6px; }
}

/* ── Legacy compat: the post-modal (js/modal.js) still emits .text-tile and
      .link-tile classes. These aren't used in the grid card anymore, but the
      modal relies on base styles to exist. Minimal rules so the modal reads
      well; modal.css below further specializes them. Remove once modal.js is
      refactored to render via the AION Card template. */
.text-tile {
    width: 100%;
    padding: 36px 28px;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    text-align: center;
    color: #fff;
    overflow: hidden;
}
.text-tile-title { margin: 0 0 .4em; font-weight: 700; line-height: 1.2; font-size: clamp(18px, 2.4vw, 28px); }
.text-tile-body  { margin: 0; line-height: 1.4; font-size: clamp(15px, 1.6vw, 18px); word-wrap: break-word; }
.text-tile-body a { color: inherit; text-decoration: underline; }
.text-tile.text-short .text-tile-body  { font-size: clamp(22px, 3.2vw, 36px); font-weight: 600; line-height: 1.2; }
.text-tile.text-short .text-tile-title { font-size: clamp(20px, 2.6vw, 32px); }

.link-tile {
    width: 100%;
    display: flex; flex-direction: column;
    color: #fff;
    overflow: hidden;
    text-decoration: none;
    background: #222;
}
.link-tile-image {
    position: relative;
    background-size: cover;
    background-position: center;
    justify-content: flex-end;
}
.link-tile-image::before {
    content: '';
    position: absolute; left: 0; right: 0; bottom: 0;
    height: 55%;
    background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0) 100%);
    pointer-events: none;
}
.link-tile-caption {
    position: relative;
    padding: 20px 22px;
    display: flex; flex-direction: column; gap: 5px;
}
.link-tile-text {
    padding: 32px 28px;
    justify-content: center;
    text-align: center;
    align-items: center;
}
.link-tile-title {
    margin: 0;
    font-size: 1.2em;
    font-weight: 700;
    line-height: 1.3;
    color: #fff;
}
.link-tile-desc {
    margin: 8px 0 10px;
    font-size: 0.9em;
    line-height: 1.4;
    color: rgba(255,255,255,0.85);
}
.link-tile-host {
    font-size: 0.7em;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    font-weight: 600;
    color: rgba(255,255,255,0.72);
}

/* End AION Card */

/* ---------- Modal ---------- */
.modal {
    display: none;
    position: fixed; inset: 0;
    z-index: 1000;
    align-items: center; justify-content: center;
}
.modal.open { display: flex; }
.modal-backdrop {
    position: absolute; inset: 0;
    /* Frosted, blurred, lightly-shadowed background — matches the landing
       register/login modals. The heavy blur + soft dim lets the page read
       through the glass dialog without fighting it for legibility. */
    background: rgba(8,6,14,0.46);
    backdrop-filter: blur(18px) saturate(120%);
    -webkit-backdrop-filter: blur(18px) saturate(120%);
    /* Fades as the dialog is swiped down. --drag-progress is 0..1, set by modal.js. */
    opacity: calc(1 - var(--drag-progress, 0) * 0.75);
}
.modal-dialog {
    position: relative;
    /* Transparent frosted glass — same recipe as the landing-card / login
       modals: translucent panel + backdrop blur + purple border + soft glow. */
    background: rgba(20,20,26,0.55);
    backdrop-filter: blur(26px) saturate(140%);
    -webkit-backdrop-filter: blur(26px) saturate(140%);
    border: 1px solid rgba(124,58,237,0.30);
    border-radius: 18px;
    max-width: 1000px;
    width: 90%;
    max-height: 90vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    box-shadow:
        0 40px 100px rgba(0,0,0,0.7),
        0 0 0 1px rgba(124,58,237,0.06),
        inset 0 1px 0 rgba(255,255,255,0.05),
        0 0 70px rgba(124,58,237,0.12);
}
.modal-close {
    position: absolute; top: 12px; right: 12px;
    background: rgba(0,0,0,0.6); color: #fff;
    border: none; border-radius: 50%;
    width: 36px; height: 36px;
    display: flex; align-items: center; justify-content: center;
    z-index: 10;
}
.modal-close:hover { background: rgba(0,0,0,0.85); }

/* Modal back button — top-left arrow, kept clear of the 3-dot owner menu
   (which lives top-right on the post card inside the modal). */
.modal-back {
    position: absolute; top: 12px; left: 12px;
    background: rgba(0,0,0,0.6); color: #fff;
    border: none; border-radius: 50%;
    width: 36px; height: 36px;
    display: flex; align-items: center; justify-content: center;
    z-index: 10;
    cursor: pointer;
    transition: background .15s ease;
}
.modal-back:hover { background: rgba(0,0,0,0.85); }
.modal-back svg { display: block; }
.modal-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.2) transparent;
}
.modal-body::-webkit-scrollbar { width: 6px; }
.modal-body::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
.modal-loading { padding: 40px; text-align: center; color: var(--dim); }

/* Compose / share / report modals: let the padded inner div scroll when content is tall */
#compose-modal .modal-dialog > div,
#share-modal .modal-dialog > div,
#report-modal .modal-dialog > div {
    overflow-y: auto;
    /* Without this, overflow-y:auto makes overflow-x compute to auto too, so any
       element that spills sideways (e.g. the open background tray) draws a stray
       horizontal scrollbar along the bottom. Pin X hidden to kill it. */
    overflow-x: hidden;
    max-height: 90vh;
    width: 100%;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.2) transparent;
}

/* Share modal — social platform buttons (X, Facebook, WhatsApp, …). Brand-tinted
   glyph on a neutral chip; on hover the chip fills with the brand colour. */
.share-soc-label {
    font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--dim, #888); margin: 0 0 10px;
}
.share-socials { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 22px; }
.share-soc {
    width: 36px; height: 36px; flex: 0 0 auto; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; cursor: pointer;
    background: var(--surface-2, #1c1c1f);
    border: 1px solid var(--border, #2a2a2a);
    color: var(--soc, var(--text, #eee));
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.share-soc svg { width: 18px; height: 18px; display: block; }
.share-soc:hover {
    border-color: var(--soc, var(--accent, #7c3aed));
    background: color-mix(in srgb, var(--soc, var(--accent, #7c3aed)) 16%, transparent);
    transform: translateY(-2px);
}
.share-soc:active { transform: translateY(0); }
.share-soc:focus-visible { outline: 2px solid var(--soc, var(--accent, #7c3aed)); outline-offset: 2px; }

/* "Shared from AION" attribution on the standalone post page. */
.post-attribution { display: flex; justify-content: center; padding: 14px 0 4px; }
.post-attribution-link {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: 12.5px; color: var(--dim, #888); text-decoration: none;
    padding: 5px 12px; border-radius: 999px; border: 1px solid transparent;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.post-attribution-link svg { color: var(--accent-link, #a855f7); }
.post-attribution-link strong { color: var(--text, #eee); font-weight: 700; letter-spacing: 0.02em; }
.post-attribution-link:hover {
    color: var(--text, #eee);
    border-color: var(--btn-soft-border, rgba(124,58,237,0.35));
    background: var(--btn-soft-bg, rgba(124,58,237,0.10));
}

@media (max-width: 768px) {
    .modal { align-items: stretch; justify-content: stretch; }
    .modal-dialog {
        width: 100%;
        max-width: 100%;
        height: 100dvh;
        max-height: 100dvh;
        border-radius: 0;
        border: 0;
    }
    /* Sticky comment bar uses safe-area padding so it sits flush on iPhones with a home indicator */
    .modal-post-compose {
        padding-bottom: calc(12px + env(safe-area-inset-bottom));
    }
}

/* Modal post layout.
   Mobile (<900px): single-column stack (media on top, side content below).
   Desktop (≥900px): two-column — media left, header/body/actions/comments right.
   Text-only posts (no .has-media): always single-column, side fills dialog. */
.modal-post { display: flex; flex-direction: column; width: 100%; }
.modal-post-header { padding: 14px 16px 14px 60px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }
.modal-post-header .avatar-sm { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.modal-post-header .meta { flex: 1; min-width: 0; }
.modal-post-header .meta .name { font-weight: 600; }
.modal-post-header .meta .handle { font-size: 12px; color: var(--dim); }
.modal-post-body { padding: 14px 16px; border-bottom: 1px solid var(--border); }
.modal-post-body h2 { margin: 0 0 8px; font-size: 18px; }
.modal-post-body p { margin: 0; line-height: 1.5; color: var(--text); white-space: pre-wrap; word-break: break-word; }
.modal-post-body a.body-url,
.modal-post-body a.hashtag,
.modal-post-body a.mention { color: #4ea3ff; text-decoration: none; }
.modal-post-body a.body-url:hover,
.modal-post-body a.hashtag:hover,
.modal-post-body a.mention:hover { text-decoration: underline; }
/* Source link credit inside the modal — always visible under body. */
.modal-post-source {
    display: inline-block;
    margin-top: 8px;
    color: #4ea3ff;
    font-size: 13px;
    font-weight: 700;
    text-decoration: none;
    word-break: break-all;
}
.modal-post-source:hover { color: #7fbcff; text-decoration: underline; }
.modal-post-media {
    background: #000;
    display: flex; align-items: flex-start; justify-content: center;
}
.modal-post-media img,
.modal-post-media video {
    width: 100%;
    height: auto;
    display: block;
}
.modal-post-media .text-tile {
    width: 100%; min-height: 320px;
    padding: 40px;
}
.modal-post-actions {
    display: flex; justify-content: center; align-items: center; gap: 36px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    border-top: 1px solid var(--border);
}
.modal-post-actions .action-btn {
    background: transparent; border: none; padding: 4px 6px;
    color: var(--text); font-size: 14px; font-weight: 600;
    display: inline-flex; align-items: center; gap: 6px;
    cursor: pointer;
}
.modal-post-actions .action-btn:hover { color: var(--accent); }
.modal-post-actions .action-btn.liked { color: var(--like); }
.modal-post-actions .action-btn .count { font-variant-numeric: tabular-nums; }
.modal-post-comments { padding: 12px 16px; }
.modal-post-compose { padding: 12px 16px; border-top: 1px solid var(--border); display: flex; gap: 8px; position: sticky; bottom: 0; background: var(--surface); }
.modal-post-compose input {
    flex: 1; background: var(--surface-2); color: var(--text);
    border: 1px solid var(--border); border-radius: 999px;
    padding: 8px 14px; font-size: 14px;
}
.modal-post-compose input:focus { outline: none; border-color: var(--accent); }
.modal-post-compose > button[type="submit"] {
    background: var(--btn-soft-bg);
    color: var(--btn-soft-color);
    border: 1px solid var(--btn-soft-border);
    border-radius: 999px; padding: 8px 16px;
    font-size: 13px; font-weight: 600; cursor: pointer;
    box-shadow: var(--btn-soft-shadow);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.modal-post-compose > button[type="submit"]:hover {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
    color: var(--btn-soft-color-hover);
}

/* Narrower dialog for the post modal specifically (desktop only — mobile
 * always uses the full screen via the @media rule above). */
@media (min-width: 769px) {
    #post-modal .modal-dialog { max-width: 760px; }
}

/* Mobile: reorder so header (avatar+username) and body (commentary) sit ABOVE
   the media, matching the link-card pattern. Desktop keeps its two-column. */
@media (max-width: 899px) {
    .modal-post.has-media {
        display: flex;
        flex-direction: column;
    }
    .modal-post.has-media .modal-post-side { display: contents; }
    .modal-post.has-media .modal-post-header   { order: -2; }
    .modal-post.has-media .modal-post-body     { order: -1; }
    /* .modal-post-main is order 0 (default) */
    .modal-post.has-media .modal-post-actions  { order: 1; }
    .modal-post.has-media .modal-post-comments { order: 2; }
    .modal-post.has-media .modal-post-compose  { order: 3; }
}

/* ── Desktop two-column modal (≥ 900px, posts with media only) ──────────── */
@media (min-width: 900px) {
    /* Wider dialog just for posts that have media to show off */
    #post-modal .modal-dialog:has(.modal-post.has-media) { max-width: 1320px; }

    .modal-post.has-media {
        flex-direction: row;
        align-items: stretch;
        max-height: 88vh;
        min-height: 560px;
    }
    .modal-post.has-media .modal-post-main {
        flex: 1 1 60%;
        min-width: 0;
        background: #000;
        display: flex;
        align-items: center;
        justify-content: center;
        overflow: hidden;
    }
    .modal-post.has-media .modal-post-main .modal-post-media {
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #000;
    }
    /* Media fits within the left column without cropping */
    .modal-post.has-media .modal-post-main .modal-post-media > img,
    .modal-post.has-media .modal-post-main .modal-post-media > video {
        max-width: 100%;
        max-height: 88vh;
        width: auto;
        height: auto;
        object-fit: contain;
    }
    /* Link tile also fits nicely */
    .modal-post.has-media .modal-post-main .link-tile {
        width: 100%;
        height: 100%;
        min-height: 0;
        aspect-ratio: auto;
    }
    /* Text-tile fallback for audio / inline-text-with-media */
    .modal-post.has-media .modal-post-main .text-tile {
        width: 100%;
        height: 100%;
        min-height: 0;
    }

    /* Right column is ONE big scroll surface. Header pins to the top, compose
       pins to the bottom, everything between flows naturally — body grows to
       fit, actions sit below, comments stack after. User scrolls the whole
       column as a single unit. */
    .modal-post.has-media .modal-post-side {
        /* Bumped from 380→440px so comment text + threaded replies have more
           breathing room. The dialog stays at 1100px max — media side just
           shrinks ~60px, but object-fit:contain (line 1371) and iframe
           width:100% (line 3692+) absorb that without clipping or cropping. */
        flex: 0 0 440px;
        display: flex;
        flex-direction: column;
        min-height: 0;
        border-left: 1px solid var(--border);
        background: var(--surface);
        overflow-y: auto;
    }
    .modal-post.has-media .modal-post-header {
        position: sticky;
        top: 0;
        z-index: 2;
        background: var(--surface);
        flex-shrink: 0;
    }
    .modal-post.has-media .modal-post-body,
    .modal-post.has-media .modal-post-actions,
    .modal-post.has-media .modal-post-comments {
        flex-shrink: 0;
    }
    .modal-post.has-media .modal-post-compose {
        position: sticky;
        bottom: 0;
        z-index: 2;
        background: var(--surface);
        flex-shrink: 0;
    }
}

/* ── Read-more collapse for long body text in the modal ──────────────────
   Wraps the post body in a clipped block that hides everything past ~5 lines
   with a soft fade-to-surface gradient. The "Read more" pill sits flush at
   the bottom-right of the clipped area. Clicking removes .is-collapsed and
   the full text expands; layout below (actions, comments, compose) gets
   pushed down naturally inside the side scroll surface. */
.modal-post-text { position: relative; margin: 0; }
.modal-post-text-clip { position: relative; }
.modal-post-text-clip.is-collapsed {
    max-height: 7.5em;
    overflow: hidden;
    /* Gradient fades the bottom of the clipped paragraph into the surface. */
    -webkit-mask-image: linear-gradient(180deg, #000 70%, transparent 100%);
            mask-image: linear-gradient(180deg, #000 70%, transparent 100%);
}
/* Read-more pill: hidden by default, only appears while .is-collapsed is on
   the clip. Floats at the bottom-right of the clipped text, with a fade-to-
   surface backdrop so it sits cleanly over the masked paragraph. Normal
   weight + dimmer purple per design call — meant to feel like a quiet
   affordance, not a CTA shout. */
.modal-post-readmore {
    display: none;
    background: none;
    border: 0;
    padding: 0;
    color: rgba(168,85,247,0.75);
    font: inherit;
    font-weight: 400;
    cursor: pointer;
}
.modal-post-readmore:hover { color: var(--accent-link); text-decoration: underline; }
.modal-post-text:has(.is-collapsed) .modal-post-readmore {
    display: inline-block;
    position: absolute;
    bottom: 0;
    right: 0;
    /* Backdrop fades from transparent on the left to surface on the right
       so the link reads cleanly without a hard-edged background pill. */
    padding: 1px 2px 1px 22px;
    background: linear-gradient(to right, transparent, var(--surface) 35%);
    z-index: 1;
}
/* Standalone post page (templates/post.php) reuses the same collapse classes
   to keep a long blog article from shoving the action bar + comment box below
   the fold. Its reading column is wider than the modal's, so show a taller
   preview (≈7 lines) before the "Read more" link. */
.post-detail-text { margin-bottom: 16px; }
.post-detail-text .modal-post-text-clip.is-collapsed { max-height: 13em; }
.post-detail-text .post-body-text { margin-bottom: 0; }

/* Browsers without :has() — gracefully fall back (dialog stays 680px, layout
   still two-column because the rules above with :has use a single selector). */
@supports not (selector(:has(*))) {
    @media (min-width: 900px) {
        #post-modal .modal-dialog { max-width: 1320px; }
    }
}

/* Link preview tile inside the modal — give it real height, dark gradient
 * anchor at the bottom so the title is readable over any image. */
#post-modal .modal-post-media .link-tile {
    position: relative;
    width: 100%;
    height: auto;
    min-height: 340px;
    aspect-ratio: 4 / 3;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
}
#post-modal .modal-post-media .link-tile-image::before {
    height: 70%;
    background: linear-gradient(to top,
        rgba(0,0,0,0.92) 0%,
        rgba(0,0,0,0.75) 35%,
        rgba(0,0,0,0.25) 75%,
        rgba(0,0,0,0) 100%);
}
#post-modal .modal-post-media .link-tile-caption {
    padding: 20px 22px 22px;
    gap: 6px;
}
#post-modal .modal-post-media .link-tile-title {
    font-size: 1.25em;
    -webkit-line-clamp: 3;
    text-shadow: 0 1px 3px rgba(0,0,0,0.9);
}
#post-modal .modal-post-media .link-tile-host {
    text-shadow: 0 1px 2px rgba(0,0,0,0.9);
}
/* Text-only link tile in modal — give it breathing room */
#post-modal .modal-post-media .link-tile-text {
    min-height: 300px;
    padding: 36px 28px;
}

/* ── Link card with article excerpt ────────────────────────────────────────
   When a link post has a scraped og:description, the modal stacks the
   image-with-headline-overlay on top and an excerpt panel (50-100 words)
   underneath with a "Read the full article" CTA. The whole stack uses
   NATURAL height and rides the parent .modal-post-media's flex-center
   alignment, so it's vertically centered in the desktop pane with black
   letterbox above/below. Long excerpts allow internal scroll as a safety
   net so the card never punches past the pane height. */
#post-modal .modal-post-media .modal-link-card {
    width: 100%;
    max-height: 100%;               /* hard cap = pane height; flex below handles overflow */
    display: flex;
    flex-direction: column;
    background: var(--surface);
}
#post-modal .modal-post-media .modal-link-card .link-tile {
    flex: 0 0 auto;
    aspect-ratio: 16 / 10;          /* slightly wider than 4/3 to leave room for the excerpt */
    min-height: 0;                  /* override the standalone link-tile min-height */
    width: 100%;
}
.modal-link-excerpt {
    flex: 0 1 auto;                 /* natural height, may shrink if pane height is tight */
    min-height: 0;
    padding: 16px 22px 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    overflow-y: auto;
    background: var(--surface);
    border-top: 1px solid var(--border);
}
.modal-link-excerpt p {
    margin: 0;
    color: var(--text);
    opacity: 0.82;
    font-size: 14px;
    line-height: 1.55;
}
.modal-link-cta {
    align-self: flex-start;
    color: var(--accent-link);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    transition: color .15s ease;
}
.modal-link-cta:hover { text-decoration: underline; }

/* Mobile: media pane stacks above the side content. The link-card uses its
   natural height (no flex stretch) so the excerpt sits flush below the image
   without leaving a tall empty area. */
@media (max-width: 899px) {
    #post-modal .modal-post-media .modal-link-card {
        height: auto;
    }
    #post-modal .modal-post-media .modal-link-card .link-tile {
        aspect-ratio: 4 / 3;
    }
    .modal-link-excerpt {
        overflow-y: visible;        /* mobile flows into the natural page scroll */
    }
}

/* ---------- Comments ---------- */
.comment {
    display: flex; gap: 10px;
    padding: 12px 6px;
    border-bottom: 1px solid var(--border);
    border-radius: 6px;
    transition: background-color .12s ease;
}
.comment:hover { background: rgba(255,255,255,0.02); }
.comment .avatar-sm {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: 50%;
    object-fit: cover;
}
.comment-body { flex: 1; min-width: 0; }
.comment-head { display: flex; align-items: baseline; gap: 6px; font-size: 14px; position: relative; padding-right: 24px; }
.comment-head .name { font-weight: 600; }
.comment-head .time { color: var(--dim); font-size: 12px; }
/* "·" separator between username and timestamp — matches the post-header style. */
.comment-head .name + .time::before {
    content: '·';
    margin-right: 6px;
    color: var(--dim);
}
.comment-menu-btn {
    position: absolute; right: 0; top: -2px;
    background: none; border: none; color: var(--dim);
    padding: 2px 4px; cursor: pointer; line-height: 0;
    border-radius: 4px;
}
.comment-menu-btn:hover { color: var(--text); background: rgba(255,255,255,0.06); }
.comment-text { font-size: 14px; line-height: 1.45; margin: 3px 0 8px; word-wrap: break-word; }
.comment-actions { display: flex; gap: 12px; font-size: 12px; color: var(--dim); }
.comment-actions button { background: none; border: none; color: var(--dim); padding: 0; font-size: 12px; }
.comment-actions button:hover { color: var(--text); }
.comment-actions .action-btn-sm { display: inline-flex; align-items: center; gap: 4px; padding: 2px 4px; color: var(--dim); }
.comment-actions .action-btn-sm.liked { color: var(--accent); }
.comment-actions .action-btn-sm .emoji { font-size: 15px; line-height: 1; }
.comment-actions .action-btn-sm .count { font-size: 12px; }
.comment-edit-form textarea {
    width: 100%; background: var(--surface-2); color: var(--text);
    border: 1px solid var(--border); border-radius: 8px;
    padding: 8px 10px; font-family: inherit; font-size: 14px; line-height: 1.45; resize: vertical;
    box-sizing: border-box;
}
.comment-edit-actions { display: flex; gap: 6px; justify-content: flex-end; margin-top: 6px; }
.comment-edit-actions .btn { padding: 4px 10px; font-size: 12px; }

/* ---------- Reply threading (per-reply L-shape, no overlap) ----------
   Each reply draws its own slice of the thread line out of two pseudo-
   elements that butt up against the neighboring reply's slice:
     ::after  — top vertical (y:0→16) + quarter-arc + horizontal stub.
                The whole L drawn from one box: border-left + border-bottom +
                border-bottom-left-radius:12. Used by every reply.
     ::before — below-curve vertical (y:28→bottom). Only on replies that
                AREN'T the last child — the last reply has nothing below
                its curve so the line ends cleanly at its avatar with no
                dangling tail.
   No continuous .comment-replies::before spine, so vertical + curve never
   draw at the same pixel — eliminates the bright intersection where two
   semi-transparent strokes used to stack. */
.comment-replies {
    margin-left: 21px;       /* aligns the line with the parent avatar's center (avatar 6→38, center=22) */
    padding-left: 22px;
}
/* Reply rows inside the thread don't get their own bottom border — the line
   on the left already provides visual continuity, and the border was creating
   a 1px gap between consecutive replies' line segments. */
.comment-replies > .comment {
    position: relative;
    border-bottom: 0;
}

/* The L-shape on every reply: vertical above + arc into stub. */
.comment-replies > .comment::after {
    content: '';
    position: absolute;
    left: -22px;
    top: 0;
    width: 22px;
    height: 28px;             /* y=0 (top of reply) → y=28 (avatar center) */
    border-left: 2px solid rgba(255,255,255,0.12);
    border-bottom: 2px solid rgba(255,255,255,0.12);
    border-bottom-left-radius: 12px;
}

/* Below-curve extension — fills the gap from the curve end down to the
   next reply's L-top. Skipped on the LAST reply so the line terminates
   cleanly at its avatar instead of trailing past the bottom of the thread. */
.comment-replies > .comment:not(:last-child)::before {
    content: '';
    position: absolute;
    left: -22px;
    top: 28px;
    bottom: 0;
    width: 2px;
    background: rgba(255,255,255,0.12);
}

.comment-replies > .comment:hover::after {
    border-left-color: rgba(255,255,255,0.20);
    border-bottom-color: rgba(255,255,255,0.20);
}
.comment-replies > .comment:not(:last-child):hover::before {
    background: rgba(255,255,255,0.20);
}

/* Defensive: API flattens to depth 1 (post.php topAncestor), but if any deeper
   nesting ever leaks through (legacy data, future edits), kill the second-level
   indent + lines so the UI stays at one visual level. */
.comment-replies .comment-replies {
    margin-left: 0;
    padding-left: 0;
}
.comment-replies .comment-replies > .comment::after,
.comment-replies .comment-replies > .comment::before { display: none; }

/* ── Reply button: small back-arrow icon + label, balances the heart on the
   left of the actions row. SVG inherits color so hover/focus states work. */
.comment-actions .reply-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 4px;
    line-height: 1;
}
.comment-actions .reply-btn svg { display: block; flex-shrink: 0; }

/* ── Collapse toggle: lives in .comment-actions, hides next-sibling .comment-replies.
   Glyph is a literal − / + character (swapped by JS) inside a small pill. */
.comment-collapse-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 8px;
    margin-left: auto;            /* push to the right of the actions row */
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--dim);
    font: inherit;
    font-size: 12px;
    cursor: pointer;
    transition: color .12s, border-color .12s, background .12s;
}
.comment-collapse-btn:hover {
    color: var(--text);
    border-color: rgba(255,255,255,0.18);
    background: rgba(255,255,255,0.04);
}
.comment-collapse-btn .ccb-glyph {
    width: 12px;
    text-align: center;
    font-weight: 700;
    line-height: 1;
}

/* ── Replying-to chip: lives at the top of the comment form when the user
   clicks Reply on a comment. Subtle accent-tinted pill with a × cancel. */
.modal-post-compose .reply-to-chip,
#comment-form .reply-to-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 6px 4px 12px;
    margin: 0 0 8px;
    align-self: flex-start;
    background: rgba(124, 58, 237, 0.12);
    border: 1px solid rgba(124, 58, 237, 0.35);
    border-radius: 999px;
    color: var(--accent-link);
    font-size: 13px;
    font-weight: 500;
    max-width: 100%;
}
.reply-to-chip .reply-to-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.reply-to-chip .reply-to-cancel {
    background: transparent;
    border: 0;
    color: inherit;
    font-size: 18px;
    line-height: 1;
    padding: 0 6px;
    cursor: pointer;
    border-radius: 50%;
    transition: background .12s;
}
.reply-to-chip .reply-to-cancel:hover { background: rgba(124, 58, 237, 0.2); }

/* Compose form is a flex row by default. We only allow wrapping so the
   reply-to chip can claim its own line ABOVE the input — the chip itself
   forces the wrap via flex-basis:100%. The input wrap + Post button keep
   their default row layout when there's no chip. */
.modal-post-compose,
#comment-form { flex-wrap: wrap; }
.modal-post-compose .reply-to-chip,
#comment-form .reply-to-chip { flex: 0 0 100%; max-width: 100%; }

/* ── Comments sort toolbar — small select that lives above the comments list. */
.comments-toolbar {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: 4px 0 8px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 6px;
}
.comments-sort-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--dim);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.comments-sort {
    background: var(--surface-2);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 4px 8px;
    font-size: 13px;
    font-family: inherit;
    cursor: pointer;
}
.comments-sort:focus { outline: none; border-color: var(--accent); }

/* ── Mobile readability bumps ──────────────────────────────────────────────
   At ≤600px the link-card tile text was bottoming out at the clamp() floors
   (13/11/11 px) which read as too small thumb-distance. Pin to safe minimums
   and bump the modal body text so a long post is comfortable on a phone. */
@media (max-width: 600px) {
    .card-link-headline { font-size: 15px; line-height: 1.28; }
    .card-link-desc     { font-size: 13px; line-height: 1.4; }
    .card-link-host     { font-size: 12px; }
    .card-title         { font-size: 15px; }

    /* Modal body text on mobile — bigger reading size + slightly taller line
       height for the post commentary; title stays heavier as the visual anchor. */
    .modal-post-body p  { font-size: 16px; line-height: 1.55; }
    .modal-post-body h2 { font-size: 19px; }
    /* Match the Read-more clip height to the bigger line height so the
       collapsed preview keeps showing about the same number of lines. */
    .modal-post-text-clip.is-collapsed { max-height: 8em; }
}

/* ---------- Forms / Auth ---------- */
.auth-container { max-width: 400px; margin: 48px auto; padding: 32px; background: var(--surface); border: 1px solid var(--border); border-radius: 16px; }
.auth-container h1 { margin: 0 0 8px; font-size: 24px; }
.auth-container .subtitle { color: var(--dim); margin: 0 0 24px; font-size: 14px; }
.auth-form, .compose-form, .settings-form { display: flex; flex-direction: column; gap: 14px; }
.auth-form label, .compose-form label, .settings-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--dim); }
.auth-form label > span, .compose-form label > span, .settings-form label > span { color: var(--text); font-size: 13px; font-weight: 500; }
/* ── Global form-field style ───────────────────────────────────────────────
   Single source of truth: every text-like input on the site looks like the
   comment composer's input — pill, surface-2 fill, subtle border. Textareas
   get the same fill but stay rounded-square so they read as multi-line. */
.auth-form input, .compose-form input, .settings-form input, .settings-form select,
.auth-form select {
    background: var(--surface-2); color: var(--text);
    border: 1px solid var(--border); border-radius: 999px;
    padding: 9px 16px; font-size: 14px; font-family: inherit;
}
.compose-form textarea, .settings-form textarea {
    background: var(--surface-2); color: var(--text);
    border: 1px solid var(--border); border-radius: 14px;
    padding: 10px 14px; font-size: 14px; font-family: inherit;
}
.auth-form input:focus, .compose-form input:focus, .compose-form textarea:focus, .settings-form input:focus, .settings-form textarea:focus, .settings-form select:focus {
    outline: none; border-color: var(--accent);
}

/* ── Compose modal fields — subtle frosted inputs (dial-in pass) ────────────
   Lighter + more translucent than the default solid --surface-2 so the fields
   sit cleanly on the glass dialog instead of reading as heavy dark boxes.
   Elegant/minimal: a faint fill, a hairline edge (so they're still clearly
   visible), and a clean purple focus ring. Scoped to .compose-form for now —
   we'll roll this same look out to the other modal forms once it's approved. */
.compose-form input,
.compose-form textarea {
    background: rgba(255,255,255,0.045);
    border: 1px solid rgba(255,255,255,0.11);
    color: var(--text);
    transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.compose-form input::placeholder,
.compose-form textarea::placeholder { color: rgba(255,255,255,0.42); }
.compose-form input:hover,
.compose-form textarea:hover { border-color: rgba(255,255,255,0.18); }
.compose-form input:focus,
.compose-form textarea:focus {
    background: rgba(255,255,255,0.07);
    border-color: rgba(124,58,237,0.60);
    box-shadow: 0 0 0 3px rgba(124,58,237,0.14);
    outline: none;
}
/* ── Global button style ───────────────────────────────────────────────────
   "Shaded, shadow, flat" — flat accent fill, subtle drop shadow for depth,
   inset bottom-darkening for shading. Same look as the comment-Post button.
   Size varies by context (.btn ≈ form action, .btn-sm ≈ chip/inline) but the
   finish is the same everywhere. */
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    padding: 9px 18px; border-radius: 999px; border: none;
    font-size: 14px; font-weight: 600; cursor: pointer; text-decoration: none;
    transition: background .15s ease, box-shadow .15s ease, transform .05s ease;
}
.btn-primary {
    background: var(--btn-soft-bg);
    color: var(--btn-soft-color);
    border: 1px solid var(--btn-soft-border);
    box-shadow: var(--btn-soft-shadow);
}
.btn-primary:hover {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
    color: var(--btn-soft-color-hover);
}
.btn-primary:active { transform: translateY(1px); }
.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }
.btn-ghost:hover { border-color: rgba(255,255,255,0.25); }
.btn-danger {
    background: rgba(255,59,92,0.18);
    color: #fca5b5;
    border: 1px solid rgba(255,59,92,0.35);
    box-shadow: var(--btn-soft-shadow);
}
.btn-danger:hover {
    background: rgba(255,59,92,0.28);
    border-color: rgba(255,59,92,0.55);
    color: #ffffff;
}
.form-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
.auth-footer { text-align: center; margin-top: 18px; color: var(--dim); font-size: 13px; }

/* Password strength meter */
.pw-meter {
    height: 4px; border-radius: 2px; background: rgba(255,255,255,0.08);
    overflow: hidden; margin-top: 6px;
}
.pw-meter > span {
    display: block; height: 100%; width: 0%;
    background: #ff4e6b; transition: width 0.2s, background 0.2s;
}
.pw-meter[data-strength="1"] > span { width: 25%; background: #ff4e6b; }
.pw-meter[data-strength="2"] > span { width: 50%; background: #ffa54a; }
.pw-meter[data-strength="3"] > span { width: 75%; background: #f5d543; }
.pw-meter[data-strength="4"] > span { width: 100%; background: #4aff8c; }
.pw-match-ok { color: #4aff8c; }
.pw-match-bad { color: #ff8098; }

/* ---------- Compose link preview card ---------- */
.compose-link-preview {
    position: relative;
    border: 1px solid var(--border);
    border-radius: 12px;
    overflow: hidden;
    background: var(--panel, rgba(255,255,255,0.04));
}
.compose-link-preview .clp-image {
    width: 100%;
    aspect-ratio: 1.91 / 1;
    background-size: cover;
    background-position: center;
    background-color: rgba(255,255,255,0.05);
}
.compose-link-preview .clp-body {
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.compose-link-preview .clp-host {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--dim);
}
.compose-link-preview .clp-title {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.compose-link-preview .clp-desc {
    margin: 0;
    font-size: 13px;
    color: var(--dim);
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.compose-link-preview .clp-close {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: none;
    background: rgba(0,0,0,0.65);
    color: #fff;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
}
.compose-link-preview .clp-close:hover { background: rgba(0,0,0,0.85); }

/* ---------- Gallery carousel (modal multi-image swipe) -------------------
   Horizontal scroll-snap track. One slide per child; each slide centers its
   image inside a square aspect frame so portrait + landscape mix cleanly.
   Prev/next buttons sit centered on the sides; dot indicators below show
   position. Touch gets free-swipe behavior; mouse gets click navigation. */
.gallery-carousel {
    position: relative;
    width: 100%;
    background: #000;
}
.gallery-track {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
}
.gallery-track::-webkit-scrollbar { display: none; }
.gallery-slide {
    flex: 0 0 100%;
    scroll-snap-align: center;
    display: flex; align-items: center; justify-content: center;
    background: #000;
    aspect-ratio: 1 / 1;
    max-height: 90vh;
}
.gallery-slide img {
    max-width: 100%;
    max-height: 100%;
    width: auto; height: auto;
    object-fit: contain;
    display: block;
}
.gallery-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px; height: 40px;
    border-radius: 50%;
    border: none;
    background: rgba(0,0,0,0.55);
    -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
    color: #fff;
    font-size: 18px;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: background .15s ease, opacity .15s ease;
    z-index: 3;
    opacity: 0.6;
}
.gallery-nav:hover { background: rgba(0,0,0,0.78); opacity: 1; }
.gallery-prev { left: 12px; }
.gallery-next { right: 12px; }
.gallery-dots {
    position: absolute;
    left: 0; right: 0; bottom: 12px;
    display: flex; justify-content: center; gap: 6px;
    z-index: 3;
    pointer-events: none;
}
.gallery-dot {
    width: 7px; height: 7px;
    border-radius: 50%;
    background: rgba(255,255,255,0.4);
    transition: background .15s ease, transform .15s ease;
    pointer-events: auto;
    cursor: pointer;
}
.gallery-dot.is-active { background: #fff; transform: scale(1.2); }
@media (hover: none) {
    /* Touch devices — buttons hide so swipe is the affordance. */
    .gallery-nav { opacity: 0; pointer-events: none; }
}

/* ---------- Compose upload grid (multi-image) ----------------------------
   Each picked image gets its own square tile in a responsive grid. Tiles
   show a faint progress bar at the bottom while their XHR is in flight,
   plus an × button in the top-right to abort + remove. The legacy single-
   image preview block (.compose-upload-preview) is kept below as a fallback
   for any caller that still uses the old DOM. */
.compose-upload-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 8px;
    margin: 12px 0;
}
.compose-upload-grid[hidden] { display: none; }
.compose-upload-tile {
    position: relative;
    aspect-ratio: 1 / 1;
    border-radius: 10px;
    overflow: hidden;
    background: var(--surface-2);
    border: 1px solid var(--border);
}
.compose-upload-tile img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: opacity 0.25s ease;
}
.compose-upload-tile.uploading img { opacity: 0.55; }
/* Video tile in the composer (a single pre-uploading clip). */
.compose-upload-tile video {
    width: 100%; height: 100%;
    object-fit: cover; display: block;
    transition: opacity 0.25s ease;
}
.compose-upload-tile.uploading video { opacity: 0.55; }
.compose-video-tile { grid-column: span 2; aspect-ratio: 16 / 10; }
/* Title field nudged as "suggested" when a video is attached. */
.compose-form input.compose-title-suggested {
    border-color: rgba(124,58,237,0.5);
    box-shadow: 0 0 0 3px rgba(124,58,237,0.12);
}
.compose-upload-tile .compose-upload-progress {
    position: absolute;
    left: 0; bottom: 0;
    height: 3px; width: 0%;
    background: var(--accent);
    transition: width 0.18s ease-out;
    pointer-events: none;
}
.compose-upload-tile:not(.uploading) .compose-upload-progress {
    opacity: 0;
    transition: opacity 0.4s 0.15s;
}
.compose-upload-tile .compose-upload-remove {
    position: absolute;
    top: 4px; right: 4px;
    width: 22px; height: 22px;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.65);
    color: #fff;
    font-size: 16px; line-height: 1;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    z-index: 2;
}
.compose-upload-tile .compose-upload-remove:hover { background: rgba(0, 0, 0, 0.85); }

/* ── Post Edit Images modal grid ──────────────────────────────────────────
   Same shape as the compose grid (square tiles, × in the corner) so the
   user immediately recognizes the editing affordance. */
.post-edit-image-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 8px;
}
.post-edit-image-tile {
    position: relative;
    aspect-ratio: 1 / 1;
    border-radius: 10px;
    overflow: hidden;
    background: var(--surface-2);
    border: 1px solid var(--border);
}
.post-edit-image-tile img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.post-edit-image-tile .compose-upload-remove {
    position: absolute;
    top: 4px; right: 4px;
    width: 22px; height: 22px;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.65);
    color: #fff;
    font-size: 16px; line-height: 1;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    z-index: 2;
}
.post-edit-image-tile .compose-upload-remove:hover { background: rgba(0, 0, 0, 0.85); }
#post-edit-status.is-ok  { color: #4caf50; }
#post-edit-status.is-err { color: var(--like, #e74c3c); }

/* ── Drag-to-reorder visual state ─────────────────────────────────────────
   The lifted tile floats above the grid with a soft drop shadow and a
   slight tilt — the surrounding tiles physically reflow around an invisible
   placeholder (handled by drag-reorder.js with FLIP animation), so there
   is no static "drop indicator line." The gap that opens up IS the
   indicator. The lifted tile is pointer-events:none while dragging so the
   pointer hit-test sees through to the siblings underneath. */
.compose-upload-tile, .post-edit-image-tile {
    cursor: grab;
}
.compose-upload-tile.is-dragging, .post-edit-image-tile.is-dragging {
    cursor: grabbing;
    opacity: 0.96;
    transform: scale(1.05) rotate(-1.5deg);
    box-shadow: 0 16px 32px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.1);
    transition: transform .12s ease, box-shadow .12s ease;
}
/* Placeholder is the invisible same-size block that takes the lifted tile's
   slot. drag-reorder.js sets visibility:hidden inline; this rule provides
   a fallback so any styles inherited from .compose-upload-tile etc. don't
   leak background or border into the (visibly-empty) placeholder. */
.drag-placeholder {
    background: transparent !important;
    border: none !important;
    box-shadow: none !important;
}

/* ---------- Compose upload preview (legacy single-image fallback) ---------- */
.compose-upload-preview {
    position: relative;
    margin: 12px 0;
    border-radius: 12px;
    overflow: hidden;
    background: var(--surface-2);
    max-height: 320px;
}
.compose-upload-preview img {
    display: block;
    width: 100%;
    height: auto;
    max-height: 320px;
    object-fit: contain;
    transition: opacity 0.25s ease;
}
.compose-upload-preview.uploading img { opacity: 0.55; }
.compose-upload-progress {
    position: absolute;
    left: 0; bottom: 0;
    height: 2px;
    width: 0%;
    background: var(--accent);
    box-shadow: 0 0 8px rgba(74, 124, 255, 0.55);
    transition: width 0.18s ease-out;
    pointer-events: none;
}
.compose-upload-preview:not(.uploading) .compose-upload-progress {
    opacity: 0;
    transition: opacity 0.4s 0.15s;
}
.compose-upload-remove {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: none;
    background: rgba(0, 0, 0, 0.65);
    color: #fff;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
}
.compose-upload-remove:hover { background: rgba(0, 0, 0, 0.85); }

/* ---------- Follows list ---------- */
.follows-container { max-width: 720px; margin: 0 auto; padding: 20px; }
.profile-stat-link { color: inherit; text-decoration: none; cursor: pointer; }
.profile-stat-link:hover strong { text-decoration: underline; }
.follows-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.follows-tab {
    padding: 10px 18px; color: var(--dim); text-decoration: none;
    border-bottom: 2px solid transparent; font-weight: 500; font-size: 14px;
}
.follows-tab:hover { color: var(--text); }
.follows-tab.is-active { color: var(--text); border-bottom-color: var(--accent, #4a9eff); }
.follows-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.follows-item {
    display: flex; align-items: center; gap: 12px; padding: 12px;
    border-radius: 10px; transition: background 0.15s;
}
.follows-item:hover { background: rgba(255,255,255,0.03); }
.follows-user { display: flex; align-items: center; gap: 12px; flex: 1; text-decoration: none; color: inherit; min-width: 0; }
.follows-avatar { width: 48px; height: 48px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.follows-meta { display: flex; flex-direction: column; min-width: 0; }
.follows-name { font-weight: 600; color: var(--text); font-size: 15px; }
.follows-handle { color: var(--dim); font-size: 13px; }
.follows-bio { color: var(--dim); font-size: 13px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.follows-item .follow-btn { flex-shrink: 0; }
.follows-empty { color: var(--dim); text-align: center; padding: 40px 20px; }

.alert { padding: 10px 14px; border-radius: 8px; margin: 12px 0; font-size: 13px; }
.alert-error { background: rgba(255,59,92,0.15); color: #ff8098; border: 1px solid rgba(255,59,92,0.3); }
.alert-success { background: rgba(74,255,145,0.1); color: #6eff9e; border: 1px solid rgba(74,255,145,0.3); }

small { color: var(--dim); font-weight: 400; }

/* ---------- Pages ---------- */
.page-title { font-size: 22px; margin: 24px 20px 16px; }
.compose-container, .settings-container { max-width: 720px; margin: 0 auto; padding: 24px 20px; }

/* Profile */
.profile-head-wrap {
    max-width: 1300px;
    margin: 0 auto;
    padding: 0 20px;
}
/* Pages: verified check + page-type badge + creation notice on the page view. */
.profile-verified { color: var(--accent-link, #a855f7); font-size: 0.66em; vertical-align: middle; }
/* Purple Star — the paid verified badge (✦). Cosmic gradient on the glyph. */
.aion-star {
    display: inline-block; margin-left: 2px; font-size: 1.15em; line-height: 1;
    vertical-align: middle; position: relative; top: -0.12em;
    background: linear-gradient(135deg, #c084fc, #7c3aed);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
    filter: drop-shadow(0 0 3px rgba(168,85,247,0.6));
}
.profile-page-badge {
    display: inline-block; margin-left: 8px; vertical-align: middle;
    font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    color: var(--btn-soft-color, #c4b5fd);
    background: var(--btn-soft-bg, rgba(124,58,237,0.18));
    border: 1px solid var(--btn-soft-border, rgba(124,58,237,0.35));
    box-shadow: var(--btn-soft-shadow);
    border-radius: 999px; padding: 2px 9px;
}
.profile-page-created {
    margin: 0 0 12px; padding: 10px 14px; border-radius: 10px; font-size: 13px; line-height: 1.5;
    color: #a7f3d0; background: rgba(16,185,129,0.12); border: 1px solid rgba(16,185,129,0.30);
}

/* ── Profile creator-console tabs ─────────────────────────────────────── */
.profile-tabs {
    display: flex; gap: 4px; max-width: 1300px; margin: 8px auto 0; padding: 0 20px;
    overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
    border-bottom: 1px solid var(--border, #2a2a2a);
}
.profile-tabs::-webkit-scrollbar { display: none; }
.ptab {
    flex: 0 0 auto; padding: 10px 16px; font-size: 14px; font-weight: 600; white-space: nowrap;
    color: var(--dim, #9aa); text-decoration: none; border-bottom: 2px solid transparent;
    border-radius: 8px 8px 0 0; transition: color .15s ease, background .15s ease, border-color .15s ease;
}
.ptab:hover { color: var(--text, #eee); background: rgba(255,255,255,0.03); }
.ptab.active {
    color: var(--btn-soft-color, #c4b5fd);
    background: var(--btn-soft-bg, rgba(124,58,237,0.14));
    border-bottom-color: var(--accent, #7c3aed);
}
.ptab-live { position: relative; }
.ptab-live:hover, .ptab-live.active { color: #f0a5e0; }
.profile-tab-pane { max-width: 1300px; margin: 0 auto; padding: 18px 20px 60px; }
/* Tab content fills the full profile container (matches the cover + tab strip).
   Each tab template sets its own narrower max-width in an inline <style> that
   loads after this file, so we override at the pane so EVERY tab's content
   column is full width — no narrowing under the tabs. */
/* Tab content fills the container — including wrappers nested inside a hub
   (Monetize stacks several -wrap sections) and embedded modules (.ad-wrap,
   .pb-wrap, .studio-page). */
.profile-tab-pane > *,
.profile-tab-pane [class$="-wrap"],
.profile-tab-pane .studio-page { max-width: none !important; }
/* ALIGNMENT: cover, tab strip, tab underline, and ALL tab content share the
   SAME 20px container inset. Inner wrappers/grids must not add extra horizontal
   padding (that's what pushed the Feed grid to 32px and offset other panes). */
.profile-tab-pane [class$="-wrap"],
.profile-tab-pane .studio-page,
.profile-feed .grid { padding-left: 0 !important; padding-right: 0 !important; }
/* Tab-strip underline inset to 20px (matches the cover's bottom edge + the
   content), instead of spanning the full 1300px box edge-to-edge. */
.profile-tabs {
    border-bottom: 0 !important;
    background-image: linear-gradient(var(--border, #2a2a2a), var(--border, #2a2a2a));
    background-repeat: no-repeat;
    background-position: 20px 100%;
    background-size: calc(100% - 40px) 1px;
}
/* Stacked monetize sections — one page, no sub-tabs, clear dividers. */
.monetize-page > section { border-top: 1px solid var(--border,#2a2a2a); margin-top: 30px; padding-top: 26px; }
.monetize-page > section:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
.manage-page > section { border-top: 1px solid var(--border,#2a2a2a); margin-top: 30px; padding-top: 26px; }
.manage-page > section:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
/* Subtle loading state while a tab's content streams in via AJAX. */
.tab-loading { padding: 48px 0; text-align: center; color: var(--dim,#888); font-size: 14px; opacity: .8; }
/* Module back-bar — every standalone module (Studio / Ads / Publisher) shows
   this so you can always get back to your profile hub. */
.module-backbar { display:inline-flex; align-items:center; gap:6px; margin:0 0 14px; font-size:13px; font-weight:600;
    color:var(--btn-soft-color,#c4b5fd); text-decoration:none; padding:6px 12px; border-radius:999px;
    background:var(--btn-soft-bg,rgba(124,58,237,0.14)); border:1px solid var(--btn-soft-border,rgba(124,58,237,0.3)); }
.module-backbar:hover { background:var(--btn-soft-bg-hover,rgba(124,58,237,0.28)); color:#fff; }
.ptab-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.ptab-head h2 { margin: 0; font-size: 20px; }
.ptab-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
.ptab-card { display: block; padding: 16px; background: var(--surface, #16161a); border: 1px solid var(--border, #2a2a2a);
    border-radius: 12px; text-decoration: none; color: var(--text, #eee); transition: border-color .15s ease, transform .12s ease; }
.ptab-card:hover { border-color: rgba(124,58,237,0.4); transform: translateY(-1px); }
.ptab-card-name { font-weight: 700; font-size: 15px; }
.ptab-card-meta { font-size: 12px; color: var(--dim, #999); margin-top: 4px; }
.ptab-verif { color: var(--accent-link, #a855f7); }

/* Streaming "coming soon" */
.stream-soon { max-width: 560px; margin: 10px auto; text-align: center; padding: 44px 24px 28px;
    background: var(--surface, #16161a); border: 1px solid rgba(124,58,237,0.30); border-radius: 18px; position: relative; overflow: hidden; }
.stream-orb { position: absolute; top: -70px; left: 50%; transform: translateX(-50%); width: 260px; height: 260px; border-radius: 50%;
    background: radial-gradient(circle, rgba(168,85,247,0.35), rgba(124,58,237,0.05) 60%, transparent 70%); filter: blur(10px); pointer-events: none; }
.stream-badge { position: relative; display: inline-block; font-size: 11px; font-weight: 800; letter-spacing: .12em;
    color: #fff; background: linear-gradient(135deg, #a855f7, #7c3aed); padding: 4px 12px; border-radius: 999px; box-shadow: 0 0 18px rgba(168,85,247,0.6); }
.stream-soon h2 { position: relative; margin: 14px 0 8px; font-size: 22px; }
.stream-soon > p { position: relative; color: var(--dim, #aaa); font-size: 14px; line-height: 1.6; margin: 0 auto 20px; max-width: 420px; }
.stream-model { position: relative; text-align: left; max-width: 380px; margin: 0 auto 22px; background: rgba(255,255,255,0.03);
    border: 1px solid var(--border, #2a2a2a); border-radius: 12px; padding: 14px 16px; }
.sm-row { display: flex; justify-content: space-between; gap: 10px; padding: 7px 0; font-size: 14px; border-bottom: 1px solid rgba(255,255,255,0.05); }
.sm-row:last-of-type { border-bottom: 0; }
.sm-row span { color: var(--dim, #aaa); }
.sm-row strong { color: var(--text, #eee); }
.sm-foot { font-size: 11px; color: var(--dim, #888); margin: 10px 0 0; line-height: 1.5; }
.profile-cover {
    width: 100%;
    height: 400px;
    overflow: hidden;
    background: var(--surface-2);
    border-radius: 0 0 12px 12px;
}
.profile-cover img {
    width: 100%; height: 100%;
    object-fit: cover;
    object-position: 50% 50%;
    display: block;
}
.avatar-lg {
    width: 160px; height: 160px;
    border-radius: 50%;
    object-fit: cover;
    object-position: 50% 50%;
    background: var(--surface-2);
    flex-shrink: 0;
}
.profile-header {
    display: flex; align-items: center; gap: 24px;
    padding: 24px 0;
    border-bottom: 1px solid var(--border);
}
.profile-header.has-cover {
    margin-top: -60px;
    padding-top: 0;
    align-items: flex-end;
    position: relative;
}
.profile-header.has-cover .avatar-lg {
    border: 4px solid var(--bg);
    background: var(--bg);
}

/* Settings — profile image pickers */
.settings-media-row { display: flex; gap: 16px; flex-wrap: wrap; }
.settings-media-item { display: flex; flex-direction: column; gap: 8px; flex: 0 0 auto; }
.settings-media-item .settings-label { font-size: 13px; font-weight: 500; color: var(--text); }
.settings-media-cover { flex: 1; min-width: 220px; }
.settings-avatar-preview {
    width: 140px; height: 140px; border-radius: 50%; overflow: hidden;
    background: var(--surface-2); border: 1px solid var(--border);
}
.settings-avatar-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.settings-cover-preview {
    width: 100%; max-width: 540px; aspect-ratio: 2.7 / 1;
    border-radius: 10px; overflow: hidden;
    background: var(--surface-2); border: 1px solid var(--border);
    display: flex; align-items: center; justify-content: center;
}
.settings-cover-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.settings-cover-empty { color: var(--dim); font-size: 13px; }
.profile-avatar { width: 120px; height: 120px; border-radius: 50%; object-fit: cover; border: 2px solid var(--border); }
.profile-info { flex: 1; }
.profile-info h1 { margin: 0 0 4px; font-size: 26px; }
.profile-handle { color: var(--dim); font-size: 14px; margin: 0 0 12px; }
.profile-bio { margin: 0 0 16px; color: var(--text); line-height: 1.4; max-width: 600px; }
.profile-stats { display: flex; gap: 24px; margin-bottom: 12px; }
.profile-stats .stat { font-size: 14px; }
.profile-stats .stat strong { font-size: 18px; display: block; }
.profile-stats .stat span { color: var(--dim); font-size: 12px; }
.profile-actions { display: flex; gap: 8px; }

@media (max-width: 640px) {
    .profile-head-wrap { padding: 0 12px; }
    .profile-cover { height: 180px; }
    .profile-header,
    .profile-header.has-cover {
        flex-direction: column;
        align-items: center;
        text-align: center;
        padding: 16px 0 20px;
        gap: 12px;
    }
    .profile-header.has-cover { margin-top: -55px; }
    .avatar-lg { width: 110px; height: 110px; }
    .profile-avatar { width: 80px; height: 80px; }
    .profile-info { width: 100%; }
    .profile-info h1 { font-size: 22px; margin: 0 0 2px; }
    .profile-handle { margin: 0 0 10px; }
    .profile-bio {
        margin: 0 auto 14px;
        max-width: 92%;
        font-size: 14px;
    }
    .profile-links {
        justify-content: center;
        gap: 12px 16px;
        margin: 4px auto 10px;
    }
    .profile-stats {
        justify-content: center;
        gap: 18px;
        margin: 0 auto 14px;
        font-size: 13.5px;
    }
    .profile-actions {
        justify-content: center;
        flex-wrap: wrap;
        gap: 10px;
    }
    .profile-sponsored-url { max-width: 140px; }
}

/* Reposition widget (settings photo previews) */
.photo-reposition {
    position: relative;
    cursor: grab;
    user-select: none;
    touch-action: none;
}
.photo-reposition.dragging { cursor: grabbing; }
.photo-reposition img { pointer-events: none; }
.photo-reposition-hint {
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    background: rgba(0,0,0,0.55);
    color: #fff;
    font-size: 12px;
    padding: 4px 10px;
    border-radius: 999px;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s;
}
.photo-reposition:hover .photo-reposition-hint,
.photo-reposition.show-hint .photo-reposition-hint { opacity: 1; }
.photo-reposition.dragging .photo-reposition-hint { opacity: 0; }

/* Notifications dropdown (topbar) — width is set on the parent #dropdown-notifications
   (see selector below) so the inner content never has to fight the wrapper's
   width. The original min-width:340 vs .dropdown's 320px caused the "Mark all
   read" button to clip on the right. */
#dropdown-notifications { width: 400px; padding: 0; }
.notif-dropdown-inner { padding: 0; }
.notif-dropdown-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
    font-size: 15px;
    gap: 12px;
}
.notif-dropdown-head strong { font-size: 15px; }
.link-btn { background: none; border: none; color: var(--accent-link); cursor: pointer; font-size: 13px; padding: 0; white-space: nowrap; flex-shrink: 0; }
.link-btn:hover { text-decoration: underline; }
.notif-dropdown-list { max-height: 480px; overflow-y: auto; }
.notif-loading, .notif-empty { padding: 28px 20px; text-align: center; color: var(--dim); font-size: 13px; }
.notif-dropdown-list .notif-item {
    display: flex; gap: 12px; align-items: center;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    font-size: 14px; color: var(--text); text-decoration: none;
    line-height: 1.4;
    transition: background .12s ease;
}
.notif-dropdown-list .notif-item:hover { background: rgba(255,255,255,0.03); }
.notif-dropdown-list .notif-item:last-child { border-bottom: 0; }
.notif-dropdown-list .notif-item.unread { background: rgba(124,58,237,0.07); }
.notif-dropdown-list .notif-item.unread::after {
    /* Unread accent — soft purple bar on the right edge instead of the old
       leading dot. Reads as a status indicator without competing with the
       actor avatar for the leading-glyph slot. */
    content: '';
    position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
    width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
}
.notif-dropdown-list .notif-item { position: relative; padding-right: 22px; }
.notif-dropdown-list .avatar-sm { width: 36px; height: 36px; flex-shrink: 0; border-radius: 50%; object-fit: cover; }
.notif-text { flex: 1; min-width: 0; line-height: 1.4; }
.notif-name { font-weight: 600; }
.notif-time { color: var(--dim); font-size: 12px; }
.notif-dropdown-foot { padding: 10px 14px; text-align: center; border-top: 1px solid var(--border); }
.notif-dropdown-foot a { color: var(--accent-link); font-size: 13px; text-decoration: none; font-weight: 600; }
.notif-dropdown-foot a:hover { text-decoration: underline; }

/* ── Avatar with type-icon badge ──────────────────────────────────────────
   The actor avatar gets a small colored circle on its bottom-right showing
   the action type (heart for like, speech-bubble for comment, etc) — same
   pattern as Twitter / Instagram notifications. Clear at a glance whether
   "AION" reacted, replied, followed, or commented. */
.notif-avatar { position: relative; flex-shrink: 0; line-height: 0; }
.notif-type-icon {
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid var(--surface);   /* punches through the dropdown surface for clarity */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.notif-type-icon svg { width: 10px; height: 10px; display: block; }
.notif-avatar-md .notif-type-icon { width: 22px; height: 22px; right: -4px; bottom: -4px; }
.notif-avatar-md .notif-type-icon svg { width: 12px; height: 12px; }

/* Type-specific colors — semantic, low-saturation so the badges don't
   overpower the row content. Keeps the list scannable without screaming. */
.notif-type-like               { background: var(--like, #e34c4c); }
.notif-type-comment            { background: #4a7cff; }
.notif-type-reply              { background: var(--accent); }
.notif-type-follow             { background: #22c55e; }
.notif-type-mention            { background: var(--accent); }
.notif-type-share              { background: #ec8a3a; }
.notif-type-group_post,
.notif-type-group_join_request { background: #14b8a6; }

/* Notifications modal — same row pattern as the topbar dropdown but with
   roomier padding, larger avatars, and the type-icon badge scaled up to
   match the avatar-md size. */
.notifications-modal-dialog { max-width: 560px; width: 90%; max-height: 80vh; }
.notifications-modal-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 18px 22px;
    border-bottom: 1px solid var(--border);
    font-size: 16px;
    flex: 0 0 auto;
    gap: 12px;
}
.notifications-modal-head strong { font-size: 17px; }
.notifications-modal-body {
    padding: 4px 0;
    /* inherits flex:1 + overflow-y:auto from .modal-body */
}
.notif-modal-item {
    display: flex; gap: 14px; align-items: center;
    padding: 14px 22px 14px 22px;
    border-bottom: 1px solid var(--border);
    color: var(--text); text-decoration: none;
    transition: background .12s ease;
    position: relative;
}
.notif-modal-item:hover { background: rgba(255,255,255,0.03); }
.notif-modal-item:last-child { border-bottom: 0; }
.notif-modal-item.unread { background: rgba(124,58,237,0.07); }
.notif-modal-item.unread::after {
    content: '';
    position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
    width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
}
.notif-modal-item .avatar-md {
    width: 44px; height: 44px; flex-shrink: 0;
    border-radius: 50%; object-fit: cover;
}
.notif-modal-text { flex: 1; min-width: 0; line-height: 1.4; padding-right: 18px; }
.notif-modal-line { font-size: 14.5px; color: var(--text); word-wrap: break-word; }
.notif-modal-line .notif-name { font-weight: 600; }
.notif-modal-time {
    margin-top: 3px;
    font-size: 12px;
    color: var(--dim);
}
@media (max-width: 768px) {
    .notifications-modal-dialog { max-width: 100%; }
    .notifications-modal-head { padding: 16px 18px; }
    .notif-modal-item { padding: 14px 18px; gap: 12px; }
    .notif-modal-item .avatar-md { width: 40px; height: 40px; }
    .notif-modal-line { font-size: 14px; }
}

/* Search / user list */
.user-list { display: flex; flex-direction: column; gap: 4px; padding: 0 20px; max-width: 720px; margin: 16px auto; }

/* Search-result user rows — compact, scannable, one tap. */
.user-row {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px;
    border-radius: 10px;
    color: var(--text);
    text-decoration: none;
    transition: background .15s ease, transform .15s ease;
}
.user-row:hover {
    background: rgba(255,255,255,0.05);
}
.user-row .avatar-md {
    width: 44px; height: 44px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 44px;
    background: var(--surface-2);
}
.user-row > div {
    min-width: 0;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 1px;
    line-height: 1.3;
}
.user-row .u-name {
    font-size: 14.5px;
    font-weight: 600;
    color: var(--text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.user-row .u-handle {
    font-size: 12.5px;
    color: var(--dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.user-row .u-bio {
    font-size: 12.5px;
    color: var(--dim);
    margin-top: 2px;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media (max-width: 480px) {
    .user-row { padding: 8px 10px; gap: 10px; }
    .user-row .avatar-md { width: 40px; height: 40px; flex-basis: 40px; }
}

.user-list-item {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 12px;
    border-radius: 10px;
    background: var(--surface);
    border: 1px solid var(--border);
}
.user-list-item .avatar-sm { width: 40px; height: 40px; }
.user-list-item .meta { flex: 1; }
.user-list-item .meta .name { font-weight: 600; }
.user-list-item .meta .handle { font-size: 12px; color: var(--dim); }

.section-title { font-size: 13px; color: var(--dim); text-transform: uppercase; letter-spacing: 1px; margin: 24px 20px 8px; }

/* Empty states */
.empty-state { text-align: center; padding: 60px 20px; color: var(--dim); }
.empty-state h2 { color: var(--text); margin: 0 0 8px; }
.empty-state p { margin: 0 0 16px; }
.empty-state .empty-icon { font-size: 40px; line-height: 1; margin-bottom: 12px; opacity: 0.6; }

/* Compose form polish — hide the raw file-input label (the image-icon button
   in .compose-form-actions triggers it) and let the action row host the
   image button + emoji trigger + Post button on one right-justified line. */
.compose-form .compose-media-hidden { display: none; }
.compose-form label { position: relative; }
.compose-form-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 8px;
}
/* Match emoji-trigger size to .cmt-img-btn when sitting in the action row,
   so the two icons read as a matched pair flanking the Post button. */
.compose-form-actions .emoji-trigger {
    width: 36px;
    height: 36px;
    padding: 0;
    margin: 0;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Char-counter overlay — when char-counter.js renders inside a label and we
   tag it .char-counter-overlay, it floats in the bottom-right of the field
   instead of taking its own line below. Quiet, dim, normal-weight — meant to
   read as a passing reference, not compete with the field's content. */
.char-counter-overlay {
    position: absolute;
    bottom: 6px;
    right: 8px;
    padding: 0 4px;
    pointer-events: none;
    color: rgba(255,255,255,0.28);
    font-size: 10px;
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.2px;
}
/* Reset the .near / .over warning weights inherited from the base .char-counter
   rule so the soft styling holds until we actually need to shout about a limit. */
.char-counter-overlay.near { color: rgba(216, 155, 45, 0.7); font-weight: 400; }
.char-counter-overlay.over { color: rgba(227, 76, 76, 0.85); font-weight: 600; }

/* The body textarea scrolls, so a corner overlay counter unavoidably sits on
   the text at some scroll positions. For textarea fields, drop the counter onto
   its own line BELOW the field instead of floating over it. Single-line inputs
   (Title) keep the tucked-in corner overlay, with right padding so a long value
   clears it. */
.compose-form .char-counter-overlay {
    position: static;
    align-self: flex-end;
    padding: 0;
    color: #4a4560;            /* muted dark purple-gray — a quiet reference */
}
.compose-form .char-counter-overlay.near { color: rgba(216,155,45,0.8); }
.compose-form .char-counter-overlay.over { color: rgba(227,76,76,0.9); }

.char-counter {
    display: block;
    text-align: right;
    font-size: 12px;
    color: var(--dim);
    margin-top: 4px;
    font-variant-numeric: tabular-nums;
}
.char-counter.near { color: #d89b2d; }
.char-counter.over { color: #e34c4c; font-weight: 600; }

#feed-sentinel {
    padding: 24px 0 48px;
    text-align: center;
}
.feed-status {
    color: var(--dim);
    font-size: 13px;
    letter-spacing: 0.3px;
    opacity: 0.65;
}

/* ---------- Admin ---------- */
.admin-layout { display: flex; min-height: calc(100vh - var(--topbar-h)); }
.admin-sidebar {
    width: 220px;
    background: var(--surface);
    border-right: 1px solid var(--border);
    padding: 20px 0;
    flex-shrink: 0;
}
.admin-sidebar h2 { margin: 0 20px 16px; font-size: 13px; color: var(--dim); text-transform: uppercase; letter-spacing: 1px; }
.admin-sidebar a {
    display: block;
    padding: 10px 20px;
    font-size: 14px;
    color: var(--text);
    border-left: 3px solid transparent;
}
.admin-sidebar a:hover { background: rgba(255,255,255,0.04); }
.admin-sidebar a.active { border-left-color: var(--accent); background: rgba(74,124,255,0.08); color: var(--accent); }
.admin-content { flex: 1; padding: 24px; overflow-x: auto; }
.admin-content h1 { margin: 0 0 20px; font-size: 22px; }

.admin-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; }
.admin-stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 20px; }
.admin-stat-card .label { font-size: 12px; color: var(--dim); text-transform: uppercase; letter-spacing: 1px; }
.admin-stat-card .value { font-size: 28px; font-weight: 700; margin-top: 8px; }

.admin-table { width: 100%; border-collapse: collapse; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }
.admin-table th, .admin-table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--border); font-size: 14px; }
.admin-table th { background: var(--surface-2); color: var(--dim); font-size: 12px; text-transform: uppercase; letter-spacing: 1px; }
.admin-table tr:last-child td { border-bottom: none; }

@media (max-width: 768px) {
    .admin-layout { flex-direction: column; }
    .admin-sidebar { width: 100%; border-right: none; border-bottom: 1px solid var(--border); display: flex; flex-wrap: wrap; padding: 8px; }
    .admin-sidebar h2 { display: none; }
    .admin-sidebar a { padding: 8px 12px; border-left: none; border-bottom: 2px solid transparent; }
    .admin-sidebar a.active { border-left: none; border-bottom-color: var(--accent); }
}

/* ---------- Inline links in content ---------- */
.hashtag, .mention { color: var(--accent-link); }
.hashtag:hover, .mention:hover { text-decoration: underline; }

/* ---------- Scroll lock ---------- */
body.modal-open { overflow: hidden; }

/* ---------- Groups ---------- */
.groups-page { max-width: 1200px; margin: 0 auto; padding: 24px; }
.groups-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.groups-search { display: flex; gap: 8px; margin-bottom: 24px; }
.groups-search .search-input { flex: 1; padding: 10px 14px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--fg); }
.groups-section { margin-bottom: 32px; }
.groups-feed-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.groups-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }

.group-card { display: block; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; text-decoration: none; color: var(--fg); transition: transform .12s ease, border-color .12s ease; }
.group-card:hover { transform: translateY(-2px); border-color: var(--accent); }
.group-card-cover { height: 100px; background-size: cover; background-position: center; background-color: #222; }
.group-card-cover-empty { background-image: linear-gradient(135deg, #7c3aed, #06b6d4); }
.group-card-body { padding: 14px; }
.group-card-name { font-weight: 700; font-size: 16px; margin-bottom: 4px; display: flex; align-items: center; gap: 6px; }
.group-card-desc { color: var(--dim); font-size: 13px; line-height: 1.4; margin-bottom: 10px; max-height: 3.2em; overflow: hidden; }
.group-card-meta { color: var(--dim); font-size: 12px; }
.group-badge { font-size: 12px; color: var(--dim); font-weight: 400; }

.group-view { max-width: 1080px; margin: 0 auto; }
.group-header { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; overflow: hidden; margin: 16px; }
.group-cover { height: 400px; background-size: cover; background-position: center; background-color: #222; }
.group-cover-compact { height: 400px; } /* Uniform: topic page cover matches main group page */
.group-cover-empty { background-image: linear-gradient(135deg, #7c3aed, #06b6d4); }
.group-header-body { padding: 20px; }
.group-name { margin: 0 0 8px; font-size: 24px; display: flex; align-items: center; gap: 10px; }
.group-desc { color: var(--dim); margin: 0 0 12px; line-height: 1.5; }
.group-meta { color: var(--dim); font-size: 13px; margin-bottom: 14px; display: flex; gap: 8px; }
.group-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---------- In-context thread view (post rendered inside group shell) ----------
   Same width as profile/group pages (1080px) so page widths feel uniform
   across the whole site. Header and article share one unified card surface. */
.group-thread-view { max-width: 1080px; margin: 0 auto; padding: 0 20px; }
.group-header-compact {
    margin: 16px 0 0; border-radius: 14px 14px 0 0;
    border-bottom: none;
}
.group-header-body-compact { padding: 16px 24px; display: flex; flex-direction: column; gap: 4px; }
.group-thread-breadcrumb { font-size: 13px; color: var(--dim); display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.group-thread-breadcrumb a { color: var(--accent); text-decoration: none; }
.group-thread-breadcrumb a:hover { text-decoration: underline; }
.group-thread-breadcrumb .sep { color: var(--dim); }
.compose-container.in-group {
    max-width: none;
    margin: 0; padding: 28px 32px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-top: 0;
    border-radius: 0 0 14px 14px;
}
.group-thread-footer { margin: 16px 0 32px; display: flex; justify-content: flex-start; }

/* Standalone post page (index.php?page=post&id=N) — the canonical shareable URL
   + where external Publisher-widget clicks land. Give it a clean, centered
   article card so first-time visitors see polished AION, not bare text. */
.post-article:not(.in-group) {
    max-width: 680px;
    margin: 24px auto 48px;
    padding: 28px 32px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
}
.post-article.post-locked-page { text-align: left; }
@media (max-width: 720px) {
    .post-article:not(.in-group) { max-width: none; margin: 0; padding: 20px 16px; border-left: 0; border-right: 0; border-radius: 0; }
}

/* ---------- Post page media: small thumbnail, text wraps around it, click for lightbox ----------
   Small by design (max 300px desktop, 160px narrow mobile) so it never dominates
   a short post. Floats left so body text flows neatly around it. Click opens
   the full-resolution image in a lightbox. */
.post-media-btn {
    background: none; border: 0; padding: 0; margin: 4px 20px 12px 0;
    cursor: zoom-in; display: block; float: left;
    max-width: 300px; width: 100%;
}
.post-media-image, .post-media-video {
    max-width: 300px; width: 100%; border-radius: 10px; display: block;
}
.post-media-video { margin: 4px 20px 12px 0; float: left; }
.post-media-btn .post-media-image { margin: 0; transition: transform 0.15s ease, box-shadow 0.15s ease; }
.post-media-btn:hover .post-media-image { transform: scale(1.02); box-shadow: 0 6px 24px rgba(0,0,0,0.5); }
/* Float class kept for backward compat; its behavior is now the default for .post-media-btn */
.post-media-float { float: left; margin: 4px 20px 12px 0; }
.post-body-text { line-height: 1.65; margin-bottom: 16px; word-wrap: break-word; }
.post-body-text::after { content: ''; display: block; clear: both; }
@media (max-width: 640px) {
    .post-media-btn, .post-media-video { max-width: 160px; margin: 2px 14px 8px 0; }
    .post-media-image, .post-media-video { max-width: 160px; }
}

/* ---------- Image lightbox (used from post page; reusable elsewhere) ---------- */
.img-lightbox {
    position: fixed; inset: 0; z-index: 10000;
    background: rgba(0,0,0,0.92); backdrop-filter: blur(6px);
    display: flex; align-items: center; justify-content: center;
    padding: 40px; cursor: zoom-out;
    animation: img-lightbox-in 0.15s ease-out;
}
.img-lightbox img {
    max-width: 100%; max-height: 100%; border-radius: 8px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.6);
    cursor: default;
}
.img-lightbox-close {
    position: absolute; top: 20px; right: 20px;
    background: rgba(255,255,255,0.1); border: 0; color: #fff;
    font-size: 24px; line-height: 1; width: 40px; height: 40px;
    border-radius: 50%; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.img-lightbox-close:hover { background: rgba(255,255,255,0.2); }
@keyframes img-lightbox-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* ---------- Post page (full) comments — same look & feel as modal, sized for full width ---------- */
.post-comments-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 16px 0 8px; flex-wrap: wrap; }
.post-comments-head-left { display: flex; align-items: center; gap: 12px; }
.post-compose-cancel { background: none; border: 0; color: var(--dim); font-size: 20px; line-height: 1; padding: 0 6px; cursor: pointer; }
.post-compose-cancel:hover { color: var(--text); }
/* Inline reply form sits neatly beneath the comment it replies to */
.comment-body > .post-page-compose { margin-top: 8px; border-top: 0; padding: 0; }
.post-page-comments { padding: 0; overflow: visible; }
.post-page-comments .comment { padding: 12px 0; gap: 12px; }
.post-page-comments .comment .avatar-sm { width: 36px; height: 36px; }
.post-page-comments .comment-head { font-size: 15px; }
.post-page-comments .comment-head .time { font-size: 12px; }
.post-page-comments .comment-text { font-size: 15px; line-height: 1.5; margin: 4px 0 8px; }
.post-page-comments .comment-actions { font-size: 13px; gap: 14px; }
/* Post-page threading: same per-reply L pattern, tuned for the 36px avatar
   (no horizontal padding on .comment here, so avatar center is at x=18,
   y=30 — line at margin-left 17, L bottoms out at y=30). */
.post-page-comments .comment-replies { margin-left: 17px; padding-left: 26px; }
.post-page-comments .comment-replies > .comment::after {
    left: -26px;
    top: 0;
    width: 26px;
    height: 30px;            /* y=0 → y=30 (post-page avatar center) */
    border-bottom-left-radius: 14px;
}
.post-page-comments .comment-replies > .comment:not(:last-child)::before {
    left: -26px;
    top: 30px;
}

.post-page-compose { padding: 12px 0; border-top: 1px solid var(--border); margin-top: 8px; }
.post-page-compose input { padding: 10px 16px; font-size: 14px; }
.post-page-compose button { padding: 10px 20px; font-size: 14px; }

.post-reply-to {
    display: inline-flex; align-items: center; gap: 8px;
    background: rgba(124, 58, 237, 0.12); color: var(--accent);
    padding: 5px 12px; border-radius: 999px; font-size: 12px; font-weight: 600;
    margin: 12px 0 -4px;
}
.post-reply-to .post-reply-cancel {
    background: none; border: 0; color: inherit; font-size: 16px; line-height: 1;
    cursor: pointer; padding: 0 2px;
}
@media (max-width: 640px) {
    .group-thread-view { padding: 0 8px; }
    .group-cover, .group-cover-compact { height: 220px; } /* mobile-only: cover scales down so it doesn't eat the whole screen */
    .group-header-body, .group-header-body-compact { padding: 14px 16px; }
    .compose-container.in-group { padding: 20px 16px; }
    .group-header-compact { margin-top: 8px; }
    .group-thread-footer { margin: 12px 0 20px; }
}

.compose-group-chip {
    display: inline-block;
    background: var(--btn-soft-bg);
    color: var(--btn-soft-color);
    border: 1px solid var(--btn-soft-border);
    padding: 6px 12px; border-radius: 999px; font-size: 12px; font-weight: 600;
    margin-bottom: 8px;
    align-self: flex-start;
}
.compose-group-chip[hidden] { display: none; }
.btn-sm { padding: 4px 10px; font-size: 12px; }
.muted-badge { font-size: 11px; color: var(--dim); padding: 2px 8px; border-radius: 999px; background: rgba(255,255,255,0.05); margin-right: 4px; }

/* ---------- Legal stub pages (Terms / Privacy) ---------- */
.legal-page { max-width: 760px; line-height: 1.6; }
.legal-page h1 { margin: 0 0 4px; }
.legal-page h2 { margin-top: 28px; margin-bottom: 8px; font-size: 18px; }
.legal-page p, .legal-page ul { margin: 8px 0; }
.legal-page ul { padding-left: 22px; }
.legal-page .legal-meta { color: var(--dim); font-size: 13px; margin-bottom: 24px; }
.legal-page a { color: var(--accent); }

/* ---------- Site footer ---------- */
.site-footer {
    max-width: 1200px; margin: 32px auto 24px; padding: 16px 20px;
    color: var(--dim); font-size: 12px;
    display: flex; gap: 10px; align-items: center; justify-content: center; flex-wrap: wrap;
    border-top: 1px solid var(--border);
}
.site-footer a { color: var(--dim); text-decoration: none; }
.site-footer a:hover { color: var(--accent-link); }
.site-footer .sep { opacity: .5; }

.auth-legal { margin-top: 14px; font-size: 12px; color: var(--dim); text-align: center; }
.auth-legal a { color: var(--accent); }

/* ── Pretty checkboxes (signup: required Terms + optional newsletter) ────────
   The native checkbox looks crude on the glass card, so we replace it with a
   soft translucent purple box + white tick, matching the button design system
   (--btn-soft-*). One rule covers the standalone signup page (.auth-check) and
   the landing-card modal (.landing-auth-check). */
.auth-check input[type="checkbox"],
.landing-auth-check input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    width: 18px; height: 18px;
    margin: 1px 0 0;
    flex: 0 0 auto;
    border: 1px solid var(--btn-soft-border);
    border-radius: 6px;
    background: var(--btn-soft-bg);
    box-shadow: var(--btn-soft-shadow);
    cursor: pointer;
    position: relative;
    transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.auth-check input[type="checkbox"]:hover,
.landing-auth-check input[type="checkbox"]:hover {
    border-color: var(--btn-soft-border-hover);
    background: var(--btn-soft-bg-hover);
}
.auth-check input[type="checkbox"]:focus-visible,
.landing-auth-check input[type="checkbox"]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(124,58,237,0.30);
}
.auth-check input[type="checkbox"]:checked,
.landing-auth-check input[type="checkbox"]:checked {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
}
.auth-check input[type="checkbox"]:checked::after,
.landing-auth-check input[type="checkbox"]:checked::after {
    content: "";
    position: absolute;
    left: 5px; top: 1px;
    width: 5px; height: 10px;
    border: solid #ffffff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

/* ---------- Error page ---------- */
.error-page {
    max-width: 520px;
    margin: 64px auto;
    padding: 48px 24px;
    text-align: center;
}
.error-code {
    font-size: 96px;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -2px;
    color: var(--accent);
    margin-bottom: 8px;
}
.error-title {
    font-size: 24px;
    margin: 0 0 12px;
}
.error-message {
    color: var(--dim);
    line-height: 1.6;
    margin: 0 0 28px;
}
.error-actions {
    display: inline-flex;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
}
@media (max-width: 640px) {
    .error-page { margin: 32px auto; padding: 32px 16px; }
    .error-code { font-size: 72px; }
}

/* ──────────────────────────────────────────────────────────────────────
   FORUM / GROUP VIEW — dense rows, upvote rail, inline expand, wires
   ────────────────────────────────────────────────────────────────────── */

/* Inline composer stub at top of feed */
.forum-composer-stub {
    display: flex; align-items: center; gap: 12px;
    margin: 12px 16px;
    padding: 10px 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    transition: border-color .12s, background .12s;
}
.forum-composer-stub:hover { border-color: var(--accent); background: rgba(124, 58, 237, 0.05); }
.fc-avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.fc-prompt { color: var(--dim); font-size: 14px; }

/* Toolbar: sort pills left, view toggle right */
.forum-toolbar {
    display: flex; align-items: center; justify-content: space-between;
    margin: 8px 16px 10px;
    gap: 12px;
}
.forum-sort { display: inline-flex; gap: 2px; }
.forum-sort-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px;
    color: var(--dim); text-decoration: none;
    font-size: 13px; font-weight: 600;
    border-bottom: 2px solid transparent;
    transition: color .12s, border-color .12s;
}
.forum-sort-btn:hover { color: var(--text); }
.forum-sort-btn.active { color: var(--text); border-bottom-color: var(--accent); }
.forum-sort-btn.disabled { opacity: 0.35; pointer-events: none; }
.forum-sort-btn svg { opacity: 0.8; }

.forum-view-toggle { display: inline-flex; gap: 2px; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 2px; }
.forum-view-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 26px; border-radius: 6px;
    color: var(--dim); text-decoration: none;
}
.forum-view-btn:hover { color: var(--text); }
.forum-view-btn.active { background: var(--btn-soft-bg); color: var(--btn-soft-color); border: 1px solid var(--btn-soft-border); box-shadow: var(--btn-soft-shadow); }

/* Feed container in forum view: no extra padding */
.feed-container.forum-feed { padding: 0; }

/* ── Thread list ────────────────────────────────────────────────────── */
.thread-list {
    display: flex; flex-direction: column; gap: 1px;
    margin: 0 16px;
    background: var(--border);
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
}
.thread-row {
    display: flex; gap: 0;
    background: var(--surface);
    transition: background-color .12s ease;
    position: relative;
    min-height: 64px;
    padding: 0; /* children own their own padding */
    align-items: stretch;
    cursor: pointer;
}
.thread-row:hover { background: rgba(124, 58, 237, 0.08); }
.thread-row:hover .tr-title { color: var(--accent); }
.thread-row.expanded { background: rgba(124, 58, 237, 0.06); cursor: default; }
/* Interactive children override the row's pointer cursor */
.thread-row a, .thread-row button, .thread-row input, .thread-row form { cursor: pointer; }
.thread-row input[type="text"] { cursor: text; }

/* Up/Down vote rail */
.tr-vote {
    flex-shrink: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
    padding: 8px 6px 8px 10px;
    min-width: 42px;
    gap: 1px;
}
.tr-vote-btn {
    width: 28px; height: 26px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: none; border-radius: 6px;
    color: var(--dim); cursor: pointer;
    transition: color .12s, background .12s, transform .08s;
}
.tr-vote-btn:hover { background: rgba(124, 58, 237, 0.1); }
.tr-vote-btn:active { transform: scale(0.9); }
.tr-up:hover { color: var(--accent); }
.tr-down:hover { color: #ef4444; }
.tr-up.voted { color: var(--accent); }
.tr-down.voted { color: #ef4444; }
.tr-vote-btn.voted svg { fill: currentColor; stroke: currentColor; }
.tr-vote-score {
    font-size: 12px; font-weight: 700; color: var(--dim);
    min-height: 14px; line-height: 14px;
}
.tr-vote[data-my-vote="1"] .tr-vote-score { color: var(--accent); }
.tr-vote[data-my-vote="-1"] .tr-vote-score { color: #ef4444; }

/* Main column */
.tr-main {
    flex: 1; min-width: 0;
    padding: 10px 12px 10px 4px;
    display: flex; flex-direction: column; gap: 4px;
}
.tr-head { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--dim); flex-wrap: wrap; }
.tr-user { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; color: var(--text); }
.tr-user:hover { color: var(--accent); }
.tr-avatar { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; }
.tr-name { font-weight: 600; }
.tr-handle { color: var(--dim); font-weight: 400; }
.tr-dot { color: var(--dim); }
.tr-time { color: var(--dim); }
.tr-menu {
    margin-left: auto;
    background: transparent; border: none;
    width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;
    color: var(--dim); cursor: pointer; border-radius: 6px;
}
.tr-menu:hover { color: var(--text); background: rgba(255,255,255,0.05); }

.tr-content { min-width: 0; }
.tr-title {
    display: block; width: 100%; text-align: left;
    background: transparent; border: none; padding: 0;
    color: var(--text); font-size: 15px; font-weight: 600; line-height: 1.3;
    cursor: pointer;
    overflow: hidden; text-overflow: ellipsis;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.tr-title:hover { color: var(--accent); }
.tr-excerpt {
    color: var(--dim); font-size: 13px; line-height: 1.4;
    overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    margin-top: 2px;
}

.tr-meta { display: flex; align-items: center; gap: 4px; margin-top: 4px; }
.tr-meta-btn {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 4px 8px; border-radius: 6px;
    background: transparent; border: none;
    color: var(--dim); font-size: 12px; font-weight: 600;
    cursor: pointer; text-decoration: none;
    transition: color .12s, background .12s;
}
.tr-meta-btn:hover { color: var(--text); background: rgba(255,255,255,0.05); }
.tr-meta-btn.liked { color: #ff5c8a; }
.tr-meta-btn .emoji-img { width: 13px; height: 13px; vertical-align: middle; }
.tr-permalink { font-size: 14px; }

/* Media thumb */
.tr-thumb {
    flex-shrink: 0;
    width: 72px; height: auto;
    align-self: stretch;
    margin: 10px 10px 10px 0;
    border-radius: 6px;
    background-size: cover; background-position: center;
    background-color: #1a0a2e;
    border: none; padding: 0; cursor: pointer;
}

/* Expanded area */
.tr-expand {
    flex-basis: 100%; width: 100%;
    order: 99;
    background: var(--bg);
    border-top: 1px solid var(--border);
}
.thread-row.expanded { display: flex; flex-wrap: wrap; }
.tr-loading { padding: 20px; text-align: center; color: var(--dim); font-size: 13px; }
.tr-ex { padding: 16px 18px 12px; }
.tr-ex-media-wrap { margin-bottom: 12px; background: #000; border-radius: 8px; overflow: hidden; display: flex; justify-content: center; }
.tr-ex-media { max-width: 100%; max-height: 500px; display: block; }
.tr-ex-body { color: var(--text); font-size: 14px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; margin-bottom: 14px; }
.tr-ex-body .body-url, .tr-ex-body .hashtag, .tr-ex-body .mention { color: #4ea3ff; text-decoration: none; }
.tr-ex-body .body-url:hover, .tr-ex-body .hashtag:hover, .tr-ex-body .mention:hover { text-decoration: underline; }

.tr-ex-link {
    display: flex; gap: 12px; align-items: stretch;
    padding: 10px; margin-bottom: 14px;
    border: 1px solid var(--border); border-radius: 10px;
    text-decoration: none; color: inherit;
}
.tr-ex-link:hover { border-color: var(--accent); }
.tr-ex-link-img { width: 120px; height: 80px; flex-shrink: 0; background-size: cover; background-position: center; border-radius: 6px; background-color: #1a0a2e; }
.tr-ex-link-body { min-width: 0; flex: 1; font-size: 13px; }
.tr-ex-link-host { display: block; color: var(--dim); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 3px; }
.tr-ex-link-body strong { display: block; color: var(--text); font-size: 14px; margin-bottom: 2px; }
.tr-ex-link-body p { color: var(--dim); margin: 0; font-size: 12px;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* Comments with threaded wires */
.tr-ex-comments { margin-bottom: 10px; }
.tr-ex-empty { color: var(--dim); font-size: 13px; padding: 8px 0; font-style: italic; }
/* Forum/group comment row — sized + spaced to match the post-modal .comment
   row exactly (32px round avatar, 14px body text, hover tint, 12px time-ago).
   The forum keeps its own classes (.tc-*) because it has voting + Reddit-style
   tree that the modal doesn't, but visual rhythm matches across surfaces. */
.tc {
    margin-bottom: 4px;
    padding: 12px 6px;
    border-radius: 6px;
    transition: background-color .12s ease;
}
.tc:hover { background: rgba(255,255,255,0.02); }
.tc-row { display: flex; gap: 10px; }
.tc-avatar-link { flex-shrink: 0; line-height: 0; }
.tc-avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; display: block; flex-shrink: 0; }
.tc-body { flex: 1; min-width: 0; }
.tc-head { font-size: 14px; margin-bottom: 3px; display: flex; align-items: baseline; gap: 6px; }
.tc-name { color: var(--text); font-weight: 600; text-decoration: none; }
.tc-name:hover { color: var(--accent); }
.tc-time { color: var(--dim); font-size: 12px; margin-left: 0; }
.tc-text { color: var(--text); font-size: 14px; line-height: 1.45; margin: 3px 0 8px; white-space: pre-wrap; word-break: break-word; }
.tc-text .body-url, .tc-text .hashtag, .tc-text .mention { color: #4ea3ff; text-decoration: none; }
.tc-actions { margin-top: 6px; display: flex; align-items: center; gap: 12px; }
.tc-act {
    background: transparent; border: none; padding: 0;
    display: inline-flex; align-items: center; gap: 4px;
    color: var(--dim); font-size: 12px; font-weight: 600; cursor: pointer;
    transition: color .12s ease;
}
.tc-act:hover { color: var(--accent); }
.tc-act .count:empty { display: none; }
.tc-act.like-btn.liked { color: var(--accent); }
.tc-act.like-btn .react-glyph { display: inline-flex; align-items: center; }
.tc-act.like-btn .emoji { font-size: 13px; line-height: 1; }
.tc-share.copied { color: #22c55e; }
.tc-share.copied::after { content: 'copied'; font-size: 10px; margin-left: 4px; }

/* Comment up/down vote widget — horizontal, inline with actions */
.tc-vote {
    display: inline-flex; align-items: center; gap: 2px;
}
.tc-vote-btn {
    background: transparent; border: none; padding: 2px 4px; border-radius: 4px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--dim); cursor: pointer;
    transition: color .12s, background .12s, transform .08s;
}
.tc-vote-btn:hover { background: rgba(124, 58, 237, 0.1); }
.tc-vote-btn:active { transform: scale(0.9); }
.tc-up:hover { color: var(--accent); }
.tc-down:hover { color: #ef4444; }
.tc-up.voted { color: var(--accent); }
.tc-down.voted { color: #ef4444; }
.tc-vote-btn.voted svg { fill: currentColor; stroke: currentColor; }
.tc-vote-score { font-size: 11px; font-weight: 700; color: var(--dim); min-width: 12px; text-align: center; }
.tc-vote-score.pos { color: var(--accent); }
.tc-vote-score.neg { color: #ef4444; }

/* ── Forum reply threading (.tc-kids) — same per-reply L pattern as the
   post-modal .comment-replies. One vertical+arc per reply via ::after,
   below-curve extension via ::before (skipped on last reply so no dangling
   tail). Positioning targets the 32px avatar's center column. */
.tc-kids {
    margin-left: 21px;
    padding-left: 22px;
}
.tc-kids > .tc {
    position: relative;
}
.tc-kids > .tc::after {
    content: '';
    position: absolute;
    left: -22px;
    top: 0;
    width: 22px;
    height: 28px;             /* y=0 → y=28 (avatar center inside .tc-row) */
    border-left: 2px solid rgba(255,255,255,0.12);
    border-bottom: 2px solid rgba(255,255,255,0.12);
    border-bottom-left-radius: 12px;
    pointer-events: none;
}
.tc-kids > .tc:not(:last-child)::before {
    content: '';
    position: absolute;
    left: -22px;
    top: 28px;
    bottom: 0;
    width: 2px;
    background: rgba(255,255,255,0.12);
    pointer-events: none;
}
.tc-kids > .tc:hover::after {
    border-left-color: rgba(255,255,255,0.20);
    border-bottom-color: rgba(255,255,255,0.20);
}
.tc-kids > .tc:not(:last-child):hover::before {
    background: rgba(255,255,255,0.20);
}
/* Defensive flatten: nested .tc-kids inside .tc-kids gets no indent or lines —
   keeps the visual nesting capped at one level even if the API ever returns
   deeper trees in this surface. */
.tc-kids .tc-kids {
    margin-left: 0;
    padding-left: 0;
}
.tc-kids .tc-kids > .tc::after,
.tc-kids .tc-kids > .tc::before { display: none; }

/* ─────────────────────────────────────────────────────────────────────────
   Global comment / reply form polish. Subtle, blended with the dark surface.
   Applies to:
     .tr-ex-compose  — bottom composer inside an expanded forum thread
     .tc-inline-form — inline reply-to-comment form in the forum view
     .modal-post-compose, .post-page-compose — main post page form
     #comment-form   — postcard modal comment form
   ───────────────────────────────────────────────────────────────────────── */
.tr-ex-compose,
.tc-inline-form,
.modal-post-compose,
#comment-form {
    display: flex; align-items: center; gap: 8px;
    margin: 6px 0 2px;
    padding: 10px 14px;
    flex-wrap: wrap;
}
/* The post modal's bottom composer lives inside a bordered side pane — keep it
   breathing on all four sides and tuck neatly to the border. */
.modal-post-compose,
#comment-form {
    padding: 12px 16px !important;
    border-top: 1px solid rgba(255,255,255,0.06);
    background: transparent;
}
/* Inline forum composers are inside a row, a tighter padding is fine. */
.tr-ex-compose,
.tc-inline-form {
    padding: 6px 10px;
}
.tr-ex-compose input[type="text"],
.tc-inline-form input[type="text"],
.modal-post-compose input[type="text"],
#comment-form input[type="text"] {
    flex: 1; min-width: 0;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
    padding: 8px 12px;
    color: var(--text, #e6e6ea);
    font-size: 13px;
    line-height: 1.4;
    transition: border-color .15s, background .15s;
}
.tr-ex-compose input[type="text"]::placeholder,
.tc-inline-form input[type="text"]::placeholder,
.modal-post-compose input[type="text"]::placeholder,
#comment-form input[type="text"]::placeholder {
    color: rgba(230,230,234,0.35);
}
.tr-ex-compose input[type="text"]:focus,
.tc-inline-form input[type="text"]:focus,
.modal-post-compose input[type="text"]:focus,
#comment-form input[type="text"]:focus {
    outline: none;
    background: rgba(255,255,255,0.05);
    border-color: rgba(124,58,237,0.45);
}
.tr-ex-compose button[type="submit"],
.tc-inline-form button[type="submit"],
.modal-post-compose button[type="submit"],
#comment-form button[type="submit"] {
    background: rgba(124,58,237,0.18);
    color: var(--accent, #7c3aed);
    border: 1px solid rgba(124,58,237,0.3);
    border-radius: 8px;
    padding: 7px 14px;
    font-size: 12px; font-weight: 600;
    cursor: pointer;
    transition: background .15s, border-color .15s;
}
.tr-ex-compose button[type="submit"]:hover,
.tc-inline-form button[type="submit"]:hover,
.modal-post-compose button[type="submit"]:hover,
#comment-form button[type="submit"]:hover {
    background: rgba(124,58,237,0.28);
    border-color: rgba(124,58,237,0.5);
}
.tr-ex-compose button[type="submit"]:disabled,
.tc-inline-form button[type="submit"]:disabled { opacity: .5; cursor: default; }

.tc-inline-cancel,
.post-compose-cancel {
    background: transparent; border: none;
    color: rgba(230,230,234,0.4);
    font-size: 13px; font-weight: 600; cursor: pointer;
    padding: 6px 8px;
    transition: color .15s;
}
.tc-inline-cancel:hover,
.post-compose-cancel:hover { color: var(--text, #e6e6ea); }

/* Attach-image button (inline with submit). Selectors bumped to beat the
   legacy `.modal-post-compose button { background: accent; border-radius:999px }`
   and any other form-wide button defaults. */
.tr-ex-compose .cmt-img-btn,
.tc-inline-form .cmt-img-btn,
.modal-post-compose .cmt-img-btn,
#comment-form .cmt-img-btn,
.cmt-img-btn {
    background: transparent !important;
    border: 1px solid rgba(255,255,255,0.1) !important;
    border-radius: 8px !important;
    width: 34px; height: 34px;
    padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    color: rgba(230,230,234,0.6) !important;
    cursor: pointer;
    flex: none;
    transition: color .15s, border-color .15s, background .15s;
}
.cmt-img-btn svg { display: block; width: 18px; height: 18px; }
.cmt-img-btn:hover {
    color: var(--accent, #7c3aed) !important;
    border-color: rgba(124,58,237,0.4) !important;
    background: rgba(124,58,237,0.08) !important;
}
.cmt-img-btn:disabled { opacity: .4; cursor: default; }

/* Kill browser autofill's bright background override on all comment fields */
.tr-ex-compose input:-webkit-autofill,
.tc-inline-form input:-webkit-autofill,
.modal-post-compose input:-webkit-autofill,
#comment-form input:-webkit-autofill,
.tr-ex-compose input:-webkit-autofill:focus,
.tc-inline-form input:-webkit-autofill:focus,
.modal-post-compose input:-webkit-autofill:focus,
#comment-form input:-webkit-autofill:focus {
    -webkit-box-shadow: 0 0 0 1000px #1c1c1f inset !important;
    box-shadow: 0 0 0 1000px #1c1c1f inset !important;
    -webkit-text-fill-color: #e6e6ea !important;
    caret-color: #e6e6ea;
    transition: background-color 9999s ease-in-out 0s;
}

/* ── GLOBAL: kill browser autofill's bright override on EVERY field ──────────
   Chrome/Edge/Safari paint autofilled inputs with an opaque off-white
   background + dark text via a UA style that plain `background` can't touch —
   that's why the login / signup / settings fields flashed bright white the
   moment they were filled. The UA background cannot be made transparent, so we
   MASK it with a dark inset box-shadow tinted to match our glass fields, force
   the text light, and freeze the background transition so the browser can't
   repaint it white on blur. One authoritative rule so no form is ever missed. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:focus {
    -webkit-box-shadow: 0 0 0 1000px #1c1a28 inset !important;
    box-shadow: 0 0 0 1000px #1c1a28 inset !important;
    -webkit-text-fill-color: var(--text) !important;
    caret-color: var(--text) !important;
    transition: background-color 9999s ease-in-out 0s !important;
}

/* Also bump specificity so legacy .modal-post-compose input (line ~1091) cannot
   override the subtle translucent look we want. */
.tr-ex-compose input[type="text"],
.tc-inline-form input[type="text"],
.modal-post-compose input[type="text"],
.post-page-compose input[type="text"],
#comment-form input[type="text"] {
    background: rgba(255,255,255,0.04) !important;
    border: 1px solid rgba(255,255,255,0.1) !important;
    color: #e6e6ea !important;
    border-radius: 8px !important;
}
.tr-ex-compose input[type="text"]:focus,
.tc-inline-form input[type="text"]:focus,
.modal-post-compose input[type="text"]:focus,
.post-page-compose input[type="text"]:focus,
#comment-form input[type="text"]:focus {
    background: rgba(255,255,255,0.06) !important;
    border-color: rgba(124,58,237,0.5) !important;
}

/* Thumbnail preview next to the form while composing */
.cmt-img-preview {
    flex-basis: 100%;
    display: inline-flex; align-items: center; gap: 8px;
    padding: 4px 0 0;
}
.cmt-img-preview img {
    max-height: 60px; max-width: 120px;
    border-radius: 6px; object-fit: cover;
    border: 1px solid rgba(255,255,255,0.08);
}
.cmt-img-remove {
    background: rgba(0,0,0,0.5);
    border: 1px solid rgba(255,255,255,0.15);
    border-radius: 50%;
    width: 22px; height: 22px;
    color: #fff; font-size: 14px; line-height: 1; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
}
.cmt-img-remove:hover { background: rgba(239,68,68,0.7); border-color: rgba(239,68,68,0.9); }
.cmt-img-loading { font-size: 11px; color: rgba(230,230,234,0.45); }
.cmt-img-error   { font-size: 11px; color: #ef4444; }

/* Attached image shown inside a posted comment. Clicking opens lightbox. */
.tc-image,
.comment-image {
    display: block;
    max-width: 200px;
    max-height: 200px;
    margin: 6px 0;
    border-radius: 8px;
    object-fit: cover;
    cursor: zoom-in;
    border: 1px solid rgba(255,255,255,0.06);
    transition: transform .15s, border-color .15s;
}
.tc-image:hover,
.comment-image:hover {
    border-color: rgba(124,58,237,0.35);
    transform: scale(1.01);
}

/* Lightbox overlay (shared — also used by post-thread.js for post images) */
.img-lightbox {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.9);
    display: flex; align-items: center; justify-content: center;
    z-index: 9999;
    padding: 40px;
    cursor: zoom-out;
}
.img-lightbox img {
    max-width: 100%; max-height: 100%;
    object-fit: contain;
    border-radius: 6px;
    box-shadow: 0 8px 40px rgba(0,0,0,0.6);
}
.img-lightbox-close {
    position: absolute; top: 16px; right: 20px;
    background: rgba(255,255,255,0.08);
    border: 1px solid rgba(255,255,255,0.15);
    color: #fff;
    width: 36px; height: 36px; border-radius: 50%;
    font-size: 22px; line-height: 1; cursor: pointer;
}
.img-lightbox-close:hover { background: rgba(255,255,255,0.15); }

/* Replaced by the curved L-connectors block above (.tc-kids ::after / ::before).
   The old straight 1px border-left used to live here — kept this margin-top
   stub so there's a small breathing gap before the first reply. */
.tc-kids { margin-top: 6px; }

.tr-ex-compose {
    display: flex; gap: 8px; margin-top: 8px;
}
.tr-ex-compose input {
    flex: 1; padding: 8px 12px;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border); border-radius: 8px;
    font-size: 13px; font-family: inherit;
}
.tr-ex-compose input:focus { outline: none; border-color: var(--accent); }
.tr-ex-compose button {
    padding: 8px 16px; border-radius: 999px;
    background: var(--btn-soft-bg);
    color: var(--btn-soft-color);
    border: 1px solid var(--btn-soft-border);
    cursor: pointer; font-weight: 600; font-size: 13px;
    box-shadow: var(--btn-soft-shadow);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.tr-ex-compose button:hover {
    background: var(--btn-soft-bg-hover);
    border-color: var(--btn-soft-border-hover);
    color: var(--btn-soft-color-hover);
}
.tr-ex-foot {
    display: flex; gap: 8px; justify-content: flex-end;
    margin-top: 10px; padding-top: 10px;
    border-top: 1px solid var(--border);
}
.tr-ex-foot .btn { padding: 6px 12px; font-size: 12px; }

@media (max-width: 640px) {
    .thread-list { margin: 0 8px; border-radius: 8px; }
    .tr-thumb { width: 56px; margin: 8px 8px 8px 0; }
    .tr-main { padding: 8px 10px 8px 4px; }
    .tr-title { font-size: 14px; }
    .tr-excerpt { -webkit-line-clamp: 1; }
    .forum-toolbar { margin: 8px 8px 10px; }
    .forum-composer-stub { margin: 10px 8px; }
}

/* ── Hovercard (Bluesky-style profile preview) ─────────────────────────── */
.hovercard {
    position: absolute;
    z-index: 9999;
    width: 320px;
    max-width: calc(100vw - 16px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 16px 40px rgba(0,0,0,0.55), 0 2px 6px rgba(0,0,0,0.3);
    padding: 14px;
    color: var(--text);
    opacity: 0;
    transform: translateY(4px);
    pointer-events: none;
    transition: opacity 140ms ease, transform 140ms ease;
    font-size: 13px;
    line-height: 1.4;
}
.hovercard.open { opacity: 1; transform: translateY(0); pointer-events: auto; }
.hc-loading, .hc-error { color: var(--dim); padding: 10px 4px; text-align: center; }

.hc-head {
    display: flex; gap: 10px; align-items: center;
    text-decoration: none; color: inherit;
}
.hc-avatar {
    width: 52px; height: 52px; border-radius: 50%;
    object-fit: cover; flex-shrink: 0; background: var(--surface-2);
}
.hc-name-wrap { min-width: 0; flex: 1; }
.hc-name {
    font-weight: 700; font-size: 15px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hc-handle { color: var(--dim); font-size: 13px; }

.hc-actions {
    display: flex; gap: 8px; margin-top: 10px;
}
.hc-actions .btn { flex: 1; padding: 6px 10px; font-size: 13px; }
.hc-msg[disabled] { opacity: 0.55; cursor: not-allowed; }

.hc-bio {
    margin: 10px 0 0; color: var(--text);
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
}

.hc-links {
    margin-top: 8px;
    display: flex; flex-direction: column; gap: 4px;
}
.hc-link {
    display: inline-flex; align-items: center; gap: 6px;
    color: #4ea3ff; text-decoration: none;
    font-size: 13px; max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hc-link:hover { text-decoration: underline; }
.hc-link svg { flex-shrink: 0; opacity: 0.85; }
.hc-sponsored { color: #c8a8ff; font-weight: 600; }
.hc-star { color: #b07aff; flex-shrink: 0; filter: drop-shadow(0 0 3px rgba(176,122,255,0.5)); }

.hc-stats {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    display: flex; gap: 14px;
    font-size: 12px;
}
.hc-stats span, .hc-stats a { color: var(--dim); text-decoration: none; }
.hc-stats strong { color: var(--text); margin-right: 3px; }
.hc-stats a:hover { color: var(--text); }

/* Profile header: website + sponsored links row (mirrors hovercard styling) */
.profile-links {
    display: flex; flex-wrap: wrap; gap: 14px;
    margin: 6px 0 4px;
    font-size: 13px;
}
.profile-link {
    display: inline-flex; align-items: center; gap: 6px;
    color: #4ea3ff; text-decoration: none;
}
.profile-link:hover { text-decoration: underline; }
.profile-link.profile-sponsored { color: #c8a8ff; font-weight: 600; }
.profile-link .hc-star { color: #b07aff; filter: drop-shadow(0 0 3px rgba(176,122,255,0.55)); }

/* Sponsored row: ★ [blue link]  plain white label */
.hc-sponsored-row,
.profile-sponsored-row {
    display: inline-flex; align-items: center; gap: 6px;
    flex-wrap: wrap;
}
.hc-sponsored-url,
.profile-sponsored-url {
    color: #4ea3ff; text-decoration: none;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    max-width: 180px;
}
.hc-sponsored-url:hover,
.profile-sponsored-url:hover { text-decoration: underline; }
.hc-sponsored-label,
.profile-sponsored-label {
    color: var(--text);
    font-weight: 600;
}

/* ── New 3-layout card system (overrides legacy card-footer scrim) ────── */

/* Hide the legacy card-footer scrim entirely on visual cards — they now use
   .card-caption + .card-actions-bar instead. card-footer still renders for
   any caller that doesn't go through the new card.php (legacy compat). */
.postcard .card-footer { display: none; }

/* Mobile: hide the 3-dot owner menu in grid cards. Owners can still manage
   their posts via the modal action row, which keeps the grid uncluttered. */
@media (max-width: 768px) {
    .postcard .card-menu { display: none !important; }
}

/* Caption strip on visual cards. Sits at the TOP under a gradient scrim by
   default. On desktop hover, animates to fill the card — larger, bold,
   centered both axes — for a magazine-style preview. */
.card-caption {
    position: absolute;
    left: 0; right: 0; top: 0; bottom: auto;
    /* padding-top clears the avatar/username pill; gradient starts at top edge. */
    padding: 56px 24px 24px;
    background: linear-gradient(to bottom,
        rgba(0,0,0,0.42) 0%,
        rgba(0,0,0,0.32) 55%,
        rgba(0,0,0,0) 100%);
    color: #fff;
    z-index: 4;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    gap: 4px;
    overflow: hidden;
    letter-spacing: 0.1px;
    text-shadow: 0 1px 3px rgba(0,0,0,0.55);
    transition: padding .25s ease, background .25s ease, gap .25s ease;
}
.card-caption-title {
    font-size: 17px;
    font-weight: 700;
    line-height: 1.28;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}
.card-caption-body {
    font-size: 14px;
    font-weight: 500;
    line-height: 1.35;
    opacity: 0.92;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: -webkit-line-clamp .25s ease;
}
@media (max-width: 768px) {
    .card-caption { padding: 44px 16px 18px; gap: 4px; }
    /* Mobile bump: caption text was shrinking too aggressively on small tiles
       (15/13). Push back up to 17/14 — readable thumb-distance without busting
       the 2-line clamp on standard headlines. */
    .card-caption-title { font-size: 17px; line-height: 1.25; }
    .card-caption-body  { font-size: 14px; line-height: 1.4; }
}

/* Desktop hover: caption blooms — title + body grow, body unclamps to fit
   the card frame. The scrim deepens slightly so longer text stays legible. */
@media (hover: hover) {
    .postcard:hover .card-caption {
        bottom: 36px; /* leave action bar visible */
        padding: 56px 28px 24px;
        background: linear-gradient(to bottom,
            rgba(0,0,0,0.55) 0%,
            rgba(0,0,0,0.42) 55%,
            rgba(0,0,0,0.20) 100%);
        gap: 8px;
        justify-content: flex-start;
    }
    .postcard:hover .card-caption-title {
        font-size: clamp(18px, 2.4cqw, 22px);
        -webkit-line-clamp: 3;
    }
    .postcard:hover .card-caption-body {
        font-size: clamp(13px, 1.6cqw, 15px);
        -webkit-line-clamp: 8;
    }
}

/* ── Blog tiles with a portrait/vertical featured image ──────────────────
   Many blog heroes are memes / infographics / posters that already carry
   their own baked-in text. Overlaying the title + excerpt on top at full
   opacity clutters them. So for blog tiles we keep the image CLEAN by
   default — only the small BLOG pill + author show. On desktop HOVER, a full
   darkening scrim drops in and the title/excerpt fade up so they read clearly
   over any image; the click still opens the post. On touch (no hover) the
   image just stays clean and the tap opens the modal as normal. This solves
   the clutter for every blog image without trying to OCR text out of them. */
.postcard.card-blog .card-caption {
    background: none;            /* no top scrim — image reads clean */
    justify-content: flex-start; /* keep the BLOG pill pinned at the top */
}
.postcard.card-blog .card-caption-title,
.postcard.card-blog .card-caption-body {
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .22s ease, transform .22s ease;
}
@media (hover: hover) {
    .postcard.card-blog:hover .card-caption {
        /* Full-card darken so the revealed text is legible over a busy image. */
        background: linear-gradient(to bottom,
            rgba(0,0,0,0.50) 0%,
            rgba(0,0,0,0.58) 55%,
            rgba(0,0,0,0.66) 100%);
        bottom: 36px;            /* leave the action bar clear */
        justify-content: flex-start;
    }
    .postcard.card-blog:hover .card-caption-title,
    .postcard.card-blog:hover .card-caption-body {
        opacity: 1;
        transform: none;
    }
}

/* Action bar at the very bottom of visual + text cards. 50% by default,
   full on hover/tap. Centered icons, no scrim of its own (caption supplies
   the gradient when present; otherwise a tiny shadow handles legibility). */
.card-actions-bar {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 8px 10px 10px;
    background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%);
    z-index: 5;
    opacity: 0.7;
    transition: opacity .18s ease;
}
.postcard:hover .card-actions-bar { opacity: 1; }
@media (hover: none) { .card-actions-bar { opacity: 0.95; } }

/* ── Locked card — fan-subscription paywall teaser ──────────────────────── */
.card-locked-media {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center; text-align: center;
}
.card-locked-media::before {
    content: ''; position: absolute; inset: 0;
    background: rgba(0,0,0,0.34);
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.card-locked-inner {
    position: relative; z-index: 1;
    display: flex; flex-direction: column; align-items: center; gap: 7px;
    padding: 20px; color: #fff; max-width: 100%;
}
.card-locked-ico { opacity: 0.92; }
.card-locked-tier {
    font-size: 11px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
    color: #fff; background: rgba(0,0,0,0.30); border: 1px solid rgba(255,255,255,0.30);
    border-radius: 999px; padding: 3px 11px;
}
.card-locked-label { font-size: 13px; opacity: 0.9; }
.card-locked-cta {
    margin-top: 6px; font-size: 13px; font-weight: 700; text-decoration: none;
    color: var(--btn-soft-color, #c4b5fd);
    background: var(--btn-soft-bg, rgba(124,58,237,0.20));
    border: 1px solid var(--btn-soft-border, rgba(124,58,237,0.5));
    box-shadow: var(--btn-soft-shadow);
    border-radius: 999px; padding: 8px 16px;
}
.card-locked-cta:hover { background: var(--btn-soft-bg-hover, rgba(124,58,237,0.34)); color: #fff; }

/* Locked full-post views — standalone page + modal side column. */
.post-locked-box {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    gap: 10px; padding: 40px 24px; color: var(--text);
}
.post-locked-box svg { color: var(--accent, #a855f7); opacity: 0.9; }
.post-locked-box h2 { margin: 6px 0 0; font-size: 19px; }
.post-locked-box p  { margin: 0 0 8px; color: var(--dim); font-size: 14px; max-width: 360px; line-height: 1.5; }
.modal-post-locked .modal-post-side { padding-bottom: 16px; }

.card-actions-bar .card-actions {
    display: flex; align-items: center; justify-content: center;
    gap: clamp(8px, 7cqw, 28px);
}
.card-actions-bar .action-btn {
    background: transparent; border: none;
    color: #fff;
    font-size: 12px; font-weight: 600;
    display: inline-flex; align-items: center; gap: 4px;
    cursor: pointer;
    padding: 4px 0;
    text-decoration: none;
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
}
.card-actions-bar .action-btn svg,
.card-actions-bar .action-btn .emoji {
    width: clamp(16px, 8cqw, 22px);
    height: clamp(16px, 8cqw, 22px);
}
.card-actions-bar .action-btn:hover { transform: scale(1.08); }
.card-actions-bar .action-btn.liked { color: var(--like); }
.card-actions-bar .action-btn .count {
    font-size: clamp(10px, 3.2cqw, 12px);
    min-width: 0;
    font-variant-numeric: tabular-nums;
}
/* Empty count spans render nothing */
.card-actions-bar .action-btn .count:empty { display: none; }

/* ── Link card stack: header → image → meta ────────────────────────────── */
.card-link .card-link-stack {
    position: absolute; inset: 0;
    display: flex; flex-direction: column;
    background: var(--surface);
}

/* Top: author + user commentary */
.card-link-header {
    flex-shrink: 0;
    padding: 10px 14px 8px;
    display: flex; flex-direction: column;
    gap: 6px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}
.card-link-author {
    display: inline-flex; align-items: center; gap: 7px;
    color: #fff;
    text-decoration: none;
    font-size: 12px;
    font-weight: 600;
    align-self: flex-start;
    line-height: 1;
    background: rgba(0,0,0,0.5);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    padding: 3px 11px 3px 3px;
    border-radius: 999px;
}
.card-link-author:hover { color: var(--accent-link); }
.card-link-author-avatar {
    width: 22px; height: 22px;
    border-radius: 50%;
    object-fit: cover;
    background: rgba(255,255,255,0.1);
    flex-shrink: 0;
}
.card-link-author-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
.card-link-commentary {
    margin: 0;
    font-size: 12px;
    line-height: 1.35;
    color: var(--dim);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Middle: link hero image (or gradient fallback). Grows to fill any height
   that header + meta don't claim, so the 2:3 frame is always exactly filled
   no matter the device. min-height enforces a respectable image presence. */
.card-link-image {
    flex: 1 1 auto;
    min-height: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    background-color: rgba(255,255,255,0.04);
}
.card-link-image-fallback { /* gradient classes paint the background */ }

/* ── AION News feed tiles: image-first, author chip overlaid on the cover ──
   Matches the /news/ page tile (news/_card.php). ONLY @AIONNews link cards get
   this (.card-link-stack-news) — regular users' link shares keep the header
   bar + their commentary. The header floats over the top of the image instead
   of sitting in a bar that pushes the image down. */
.card-link-stack-news .card-link-header {
    position: absolute; top: 0; left: 0; right: 0; z-index: 3;
    background: transparent;
    border-bottom: 0;
    padding: 10px;
    pointer-events: none;            /* clicks fall through to the card… */
}
.card-link-stack-news .card-link-author { pointer-events: auto; } /* …except the chip */
/* Soft top scrim so the white author chip always reads over a bright cover. */
.card-link-stack-news .card-link-image::before,
.card-link-stack-news .card-link-image-blog::before,
.card-link-stack-news .card-link-image-video::before {
    content: ''; position: absolute; top: 0; left: 0; right: 0; height: 56px;
    background: linear-gradient(to bottom, rgba(0,0,0,0.45), rgba(0,0,0,0));
    z-index: 1; pointer-events: none;
}

/* Bottom: title → desc → source → actions. Content-sized — never grows past
   what its content needs, leaving the rest of the frame for the image. */
.card-link-meta {
    flex: 0 0 auto;
    position: relative;
    background: var(--surface);
    padding: 12px 14px 38px; /* bottom padding for action bar overlay */
    display: flex; flex-direction: column;
    gap: 6px;
    overflow: hidden;
    color: var(--text);
    border-top: 1px solid var(--border);
}
.card-link-headline {
    margin: 0;
    font-size: clamp(13px, 2.0cqw, 16px);
    font-weight: 700;
    line-height: 1.25;
    color: var(--text);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.card-link-desc {
    margin: 0;
    font-size: clamp(11px, 1.5cqw, 13px);
    line-height: 1.4;
    color: var(--dim);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.card-link-host {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--dim);
    text-decoration: none;
    align-self: flex-start;
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    transition: color .15s ease;
}
.card-link-host:hover { color: var(--accent-link); text-decoration: none; }
.card-link-actions {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 6px 10px 8px;
    opacity: 0.75;
    transition: opacity .18s ease;
}
.postcard:hover .card-link-actions { opacity: 1; }
@media (hover: none) { .card-link-actions { opacity: 0.9; } }

.card-link-actions .card-actions {
    display: flex; align-items: center; justify-content: center;
    gap: clamp(8px, 7cqw, 28px);
}
.card-link-actions .action-btn {
    background: transparent; border: none;
    color: var(--text);
    font-size: 12px; font-weight: 600;
    display: inline-flex; align-items: center; gap: 4px;
    cursor: pointer; padding: 2px 0;
}
.card-link-actions .action-btn svg,
.card-link-actions .action-btn .emoji {
    width: clamp(15px, 7cqw, 20px);
    height: clamp(15px, 7cqw, 20px);
}
.card-link-actions .action-btn .count {
    font-size: clamp(10px, 3cqw, 12px);
    color: var(--dim);
}
.card-link-actions .action-btn .count:empty { display: none; }
.card-link-actions .action-btn.liked { color: var(--like); }

/* ── AION inline search: tab strip above topbar ──────────────────────── */
.search-tabs {
    position: sticky; top: 0; z-index: 101;
    background: var(--bg, #0a0a0b);
    border-bottom: 1px solid var(--border, #2a2a2a);
}
.search-tabs[hidden] { display: none !important; }
body.has-search-tabs .topbar { top: 46px; }

.search-tabs-inner {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 4px;
    min-height: 46px;
    height: 46px;
    padding: 0 12px;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
}
.search-tabs-inner::-webkit-scrollbar { display: none; }
@media (max-width: 600px) {
    .search-tabs-inner { justify-content: flex-start; padding: 0 8px; }
}

/* Topbar bookmarks button: active state */
#topbar-bookmarks.is-active {
    background: rgba(124,58,237,0.12);
    color: var(--text);
}

/* Individual tab — a real browser tab anchored to the TOP of the topbar:
   rounded top, open bottom (no bottom border), fused into the topbar below.
   AION purple, 1px border, dialed-back glow, STUDIO-style label. */
.search-tab {
    display: inline-flex;
    align-items: center;
    gap: 1px;
    min-width: 116px;
    max-width: 240px;
    padding: 0 5px 0 13px;
    height: 36px;
    margin-bottom: -1px;            /* dip over the strip's bottom border → fused to topbar */
    background: transparent;
    border: 1px solid var(--btn-soft-border, rgba(124,58,237,0.35));
    border-bottom: none;
    border-radius: 13px 13px 0 0;
    color: var(--btn-soft-color, #c4b5fd);
    box-shadow: 0 -3px 9px -7px var(--btn-soft-border, rgba(124,58,237,0.35));
    cursor: pointer;
    transition: background .16s ease, color .16s ease, border-color .16s ease, box-shadow .16s ease;
    flex-shrink: 0;
}
.search-tab:hover {
    background: var(--btn-soft-bg, rgba(124,58,237,0.16));
    border-color: var(--btn-soft-border-hover, rgba(124,58,237,0.5));
    color: var(--btn-soft-color-hover, #fff);
}
.search-tab.is-active {
    background: var(--btn-soft-bg-hover, rgba(124,58,237,0.30));
    border-color: var(--btn-soft-border-hover, rgba(124,58,237,0.5));
    color: var(--btn-soft-color-hover, #fff);
    box-shadow: 0 -4px 11px -7px var(--btn-soft-border-hover, rgba(124,58,237,0.55));
}

/* Inline + New button (sibling of .search-tab, appended after last tab) */
.search-tab-new {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 34px; height: 32px;
    margin-left: 2px; margin-bottom: -1px;
    background: transparent;
    border: 1px solid var(--btn-soft-border, rgba(124,58,237,0.30));
    border-bottom: none;
    border-radius: 11px 11px 0 0;
    color: var(--btn-soft-color, #c4b5fd);
    cursor: pointer;
    transition: background .16s ease, color .16s ease, border-color .16s ease;
}
.search-tab-new:hover {
    background: var(--btn-soft-bg, rgba(124,58,237,0.16));
    color: var(--btn-soft-color-hover, #fff);
    border-color: var(--btn-soft-border-hover, rgba(124,58,237,0.5));
}
.search-tab-new .icon { width: 18px; height: 18px; }

.search-tab-label {
    flex: 1;
    min-width: 0;
    background: none; border: none; padding: 0;
    color: inherit;
    font-family: inherit;
    font-size: 12px;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.11em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: left;
    cursor: pointer;
}
.search-tab-close {
    width: 22px; height: 22px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: none;
    color: currentColor; opacity: 0.5;
    font-size: 18px; line-height: 1;
    border-radius: 50%;
    cursor: pointer;
    transition: background .15s ease, opacity .15s ease, color .15s ease;
}
.search-tab-close:hover { background: rgba(255,255,255,0.12); opacity: 1; }

/* Bookmark+plus icon on tab — larger + easier to tap, between label and × */
.search-tab-bm {
    flex-shrink: 0;
    width: 30px; height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: none;
    color: currentColor; opacity: 0.6;
    border-radius: 8px;
    cursor: pointer;
    transition: background .15s ease, opacity .15s ease, color .15s ease;
}
.search-tab-bm .icon { width: 21px; height: 21px; }
.search-tab-bm:hover { background: rgba(255,255,255,0.08); opacity: 1; color: #ffd166; }
.search-tab-bm.is-saved { opacity: 1; color: #ffd166; }
.search-tab-bm.is-saved .bm-shape { fill: rgba(255,209,102,0.25); }
.search-tab-bm.is-saved .bm-plus  { display: none; }

/* Bookmark+plus icon on postcard action row */
.action-btn.bookmark-btn { color: var(--dim); }
.action-btn.bookmark-btn:hover { color: #ffd166; }
.action-btn.bookmark-btn.is-saved { color: #ffd166; }
.action-btn.bookmark-btn.is-saved .bm-shape { fill: rgba(255,209,102,0.25); }
.action-btn.bookmark-btn.is-saved .bm-plus  { display: none; }

/* Drag indicators for tab reordering */
.search-tab.is-dragging { opacity: 0.5; }
.search-tab.drop-before { box-shadow: -2px 0 0 0 var(--accent); }
.search-tab.drop-after  { box-shadow:  2px 0 0 0 var(--accent); }

/* ── In-app browser omnibar (native shells only; js/aion-browser-chrome.js).
   A persistent, sticky address/search bar below the topbar: it sits in normal
   flow (pushing content down — no overlap), and while browsing it pins below the
   topbar with the native WebView rendering the site beneath it. ── */
.aion-bbar {
    position: sticky; top: 0;
    z-index: 95;
    display: flex; align-items: center; gap: 6px;
    height: 44px;
    padding: 0 10px;
    background: var(--surface, #141416);
    border-bottom: 1px solid var(--border, #2a2a2a);
    box-shadow: 0 6px 18px -10px rgba(0,0,0,0.7);
}
.aion-bbar[hidden] { display: none !important; }
/* Back/forward/reload only matter while a page is loaded. */
.aion-bbar-nav { display: inline-flex; align-items: center; gap: 6px; }
body:not(.aion-browsing) .aion-bbar-nav { display: none; }
.aion-bbar-btn {
    flex-shrink: 0;
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 9px;
    color: var(--dim, #aaa);
    cursor: pointer;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.aion-bbar-btn:hover {
    background: var(--btn-soft-bg, rgba(124,58,237,0.16));
    border-color: var(--btn-soft-border, rgba(124,58,237,0.35));
    color: var(--btn-soft-color, #c4b5fd);
}
.aion-bbar-btn .ic { width: 19px; height: 19px; }
.aion-bbar-close { margin-left: 2px; }
.aion-bbar-close:hover {
    color: #ff6b6b; border-color: rgba(255,107,107,0.4); background: rgba(255,107,107,0.12);
}
.aion-bbar-addr {
    flex: 1; min-width: 0;
    height: 32px;
    padding: 0 14px;
    background: var(--bg, #0a0a0b);
    border: 1px solid var(--border, #2a2a2a);
    border-radius: 999px;
    color: var(--text, #eee);
    font-size: 13.5px;
    outline: none;
    transition: border-color .15s ease, box-shadow .15s ease;
}
.aion-bbar-addr:focus {
    border-color: var(--btn-soft-border-hover, rgba(124,58,237,0.55));
    box-shadow: 0 0 0 3px var(--btn-soft-bg, rgba(124,58,237,0.16));
}

/* ── Omnibox autocomplete dropdown (js/aion-suggest.js) ──────────────── */
.aion-suggest {
    position: fixed; z-index: 200;
    max-height: 320px; overflow-y: auto;
    background: var(--surface, #141416);
    border: 1px solid var(--btn-soft-border, rgba(124,58,237,0.35));
    border-radius: 12px;
    box-shadow: 0 14px 40px rgba(0,0,0,0.55);
    padding: 4px;
}
.aion-suggest[hidden] { display: none; }
.aion-sug {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 12px; border-radius: 9px;
    cursor: pointer; color: var(--text, #eee);
    font-size: 13.5px; white-space: nowrap; overflow: hidden;
}
.aion-sug:hover, .aion-sug.is-sel { background: var(--btn-soft-bg, rgba(124,58,237,0.18)); }
.aion-sug-ic { flex: 0 0 auto; font-size: 14px; opacity: 0.9; }
.aion-sug-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.aion-sug-hint { flex: 0 0 auto; font-size: 11px; color: var(--dim, #888); text-transform: uppercase; letter-spacing: 0.04em; }

/* Bookmarks pane */
.bookmarks-pane .page-title { margin: 0 0 16px; }
.bookmarks-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 12px;
}
.bookmark-card {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    overflow: hidden;
    transition: border-color .15s ease, transform .15s ease;
}
.bookmark-card:hover { border-color: rgba(124,58,237,0.4); transform: translateY(-1px); }
.bookmark-link {
    display: flex; gap: 12px;
    padding: 12px;
    color: var(--text);
    text-decoration: none;
}
.bookmark-thumb {
    flex-shrink: 0;
    width: 64px; height: 64px;
    border-radius: 8px;
    object-fit: cover;
    background: rgba(255,255,255,0.04);
}
.bookmark-thumb-blank {
    display: flex; align-items: center; justify-content: center;
    font-size: 24px; font-weight: 700; color: var(--dim);
    background: linear-gradient(135deg, rgba(124,58,237,0.18), rgba(124,58,237,0.04));
}
.bookmark-body { flex: 1; min-width: 0; }
.bookmark-title {
    font-weight: 400; font-size: 13.5px; line-height: 1.35;
    text-transform: uppercase; letter-spacing: 0.09em;
    color: var(--btn-soft-color, #c4b5fd);
    overflow: hidden; text-overflow: ellipsis;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.bookmark-source {
    font-size: 11px; color: var(--dim);
    margin-top: 4px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bookmark-desc {
    font-size: 12px; color: var(--dim);
    margin: 6px 0 0;
    overflow: hidden; text-overflow: ellipsis;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.bookmark-del {
    position: absolute; top: 6px; right: 6px;
    width: 22px; height: 22px;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0,0,0,0.5);
    border: none;
    color: var(--dim);
    font-size: 16px; line-height: 1;
    border-radius: 50%;
    cursor: pointer;
    opacity: 0;
    transition: opacity .15s ease, color .15s ease, background .15s ease;
}
.bookmark-card:hover .bookmark-del { opacity: 1; }
.bookmark-del:hover { background: rgba(220,38,38,0.6); color: #fff; }

/* Toast */
.aion-flash {
    position: fixed; bottom: 24px; left: 50%;
    transform: translateX(-50%);
    background: rgba(20,20,24,0.95);
    color: var(--text);
    border: 1px solid rgba(124,58,237,0.35);
    border-radius: 999px;
    padding: 10px 18px;
    font-size: 13px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.5);
    z-index: 9999;
    opacity: 1;
    transition: opacity .35s ease, transform .35s ease;
}
.aion-flash.is-out { opacity: 0; transform: translate(-50%, 8px); }

/* ── Search dropdown refinement: wider, elegant, pill input ──────────── */
#dropdown-search {
    width: 560px;
    max-width: calc(100vw - 32px);
    padding: 14px;
    border-radius: 0 0 16px 16px;
}
#dropdown-search .search-form { gap: 10px; }
#dropdown-search .search-input {
    padding: 13px 18px;
    font-size: 15px;
    border-radius: 999px;
    background: rgba(255,255,255,0.04);
}
#dropdown-search .search-input:focus {
    background: rgba(255,255,255,0.06);
    border-color: rgba(124,58,237,0.5);
    box-shadow: 0 0 0 3px rgba(124,58,237,0.12);
}
#dropdown-search .search-submit {
    padding: 0 22px;
    font-size: 14px;
    border-radius: 999px;
}
@media (max-width: 640px) {
    #dropdown-search { width: 100%; border-radius: 0 0 12px 12px; }
    body.has-search-tabs .topbar { top: 46px; }
    .search-tab { min-width: 116px; max-width: 180px; }
}

/* ─────────────────────────────────────────────────────────────────────────
   Comment-form polish — keep all controls on a single tight row.
   The auto-injected .emoji-trigger and the .post-compose-cancel button must
   not inherit the legacy "big purple pill" look from .modal-post-compose
   button. Submit stays as the only filled button.
   ───────────────────────────────────────────────────────────────────────── */
.modal-post-compose,
.post-page-compose,
#comment-form {
    align-items: center;
    gap: 8px !important;
    flex-wrap: wrap;
}
.modal-post-compose .emoji-trigger,
.post-page-compose .emoji-trigger,
#comment-form .emoji-trigger,
.tc-inline-form .emoji-trigger,
.tr-ex-compose .emoji-trigger {
    background: transparent !important;
    color: rgba(230,230,234,0.55) !important;
    border: 1px solid rgba(255,255,255,0.10) !important;
    border-radius: 999px !important;
    width: 32px !important; height: 32px !important;
    padding: 0 !important;
    margin: 0 !important;
    font-size: 0 !important;
    font-weight: normal !important;
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
}
.modal-post-compose .emoji-trigger:hover,
.post-page-compose .emoji-trigger:hover,
#comment-form .emoji-trigger:hover,
.tc-inline-form .emoji-trigger:hover,
.tr-ex-compose .emoji-trigger:hover,
.modal-post-compose .emoji-trigger.is-active,
.post-page-compose .emoji-trigger.is-active,
#comment-form .emoji-trigger.is-active {
    background: rgba(124,58,237,0.12) !important;
    border-color: rgba(124,58,237,0.4) !important;
    color: #e6e6ea !important;
}
.modal-post-compose .emoji-trigger svg,
.post-page-compose .emoji-trigger svg,
#comment-form .emoji-trigger svg {
    width: 18px; height: 18px; display: block;
}
/* Cancel ✕ — small, transparent. Beats .modal-post-compose button. */
.modal-post-compose .post-compose-cancel,
.post-page-compose .post-compose-cancel,
#comment-form .post-compose-cancel {
    background: transparent !important;
    color: rgba(230,230,234,0.45) !important;
    border: 0 !important;
    border-radius: 6px !important;
    padding: 0 8px !important;
    font-size: 20px !important;
    line-height: 1 !important;
    font-weight: normal !important;
    width: auto !important; height: auto !important;
    flex: 0 0 auto;
}
.modal-post-compose .post-compose-cancel:hover,
.post-page-compose .post-compose-cancel:hover,
#comment-form .post-compose-cancel:hover { color: #e6e6ea !important; }
/* Submit — compact subtle purple, not a giant pill. */
.post-page-compose > button[type="submit"],
#comment-form > button[type="submit"] {
    background: rgba(124,58,237,0.18) !important;
    color: #c4b5fd !important;
    border: 1px solid rgba(124,58,237,0.35) !important;
    border-radius: 8px !important;
    padding: 7px 14px !important;
    font-size: 13px !important;
    font-weight: 600;
    flex: 0 0 auto;
}
.post-page-compose > button[type="submit"]:hover,
#comment-form > button[type="submit"]:hover {
    background: rgba(124,58,237,0.28) !important;
    border-color: rgba(124,58,237,0.55) !important;
    color: #ffffff !important;
}
/* The text input must keep grabbing remaining space so the row stays one line */
.modal-post-compose input[type="text"],
.post-page-compose input[type="text"],
#comment-form input[type="text"] {
    flex: 1 1 0 !important;
    min-width: 0 !important;
}
/* Hide the inline char-counter inside compose forms unless the user is
   actually approaching the limit. Reclaims ~50px of input width. */
.modal-post-compose .char-counter,
.post-page-compose .char-counter,
#comment-form .char-counter,
.tc-inline-form .char-counter,
.tr-ex-compose .char-counter {
    display: none;
    flex: 0 0 auto;
    margin: 0;
    font-size: 11px;
    padding: 0 4px;
}
.modal-post-compose .char-counter.near,
.modal-post-compose .char-counter.over,
.post-page-compose .char-counter.near,
.post-page-compose .char-counter.over,
#comment-form .char-counter.near,
#comment-form .char-counter.over,
.tc-inline-form .char-counter.near,
.tc-inline-form .char-counter.over,
.tr-ex-compose .char-counter.near,
.tr-ex-compose .char-counter.over {
    display: block;
    flex-basis: 100%;
    text-align: right;
    order: 100;
}

/* ─────────────────────────────────────────────────────────────────────────
   Site-wide scrollbar — thin, dim, matches dark theme. Applies to body and
   any overflow container that doesn't already opt out.
   ───────────────────────────────────────────────────────────────────────── */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,0.14) transparent;
}
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,0.14);
    border-radius: 999px;
    border: 2px solid transparent;
    background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.28); background-clip: padding-box; }
*::-webkit-scrollbar-corner { background: transparent; }

/* ─────────────────────────────────────────────────────────────────────────
   Video-embed cards (YouTube / Vimeo / TikTok / Instagram / Facebook / X)

   Two layouts driven by media.orientation:
     - horizontal → goes through the link-stack path (image on top, meta
                    panel below). Play-button overlay sits on the image.
     - vertical   → goes through the visual path (full-bleed thumbnail).
                    Play-button overlay floats centered, same look-and-feel
                    as image cards.
   Modal mirrors this with proper iframe aspect ratios.
   ───────────────────────────────────────────────────────────────────────── */

/* Play-button overlay used on BOTH orientations + on link-stack videos. */
.cm-yt-play,
.card-video-play {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    z-index: 3;
    pointer-events: none;
    filter: drop-shadow(0 4px 16px rgba(0,0,0,0.55));
    opacity: 0.92;
    transition: opacity .18s ease, transform .18s ease;
}
.cm-yt-play svg,
.card-video-play svg { display: block; }
.postcard:hover .cm-yt-play,
.postcard:hover .card-video-play {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.08);
}
@media (max-width: 600px) {
    .cm-yt-play svg          { width: 48px; height: 36px; }
    .card-video-play svg     { width: 44px; height: 32px; }
}

/* The card-link-image must be a positioning context for the play overlay. */
.postcard.card-link .card-link-image { position: relative; }

/* Video-embed thumbnails are 16:9, audio-embed album art is square — never
   crop either. Use the same dual-image pattern as image cards: blurred copy
   fills the area, sharp copy sits contained over it. No black bars, no
   clipping, full thumbnail visible. */
.postcard.card-video-embed.card-link .card-link-image-video,
.postcard.card-audio-embed.card-link .card-link-image-video {
    position: relative;
    overflow: hidden;
    background: #000;
}
.postcard.card-video-embed.card-link .card-link-image-video .cm-image-bg,
.postcard.card-audio-embed.card-link .card-link-image-video .cm-image-bg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    /* Soft natural extension — keep the original luminance so the letterbox
       area reads as a continuation of the thumbnail, not a black bar. */
    filter: blur(22px) saturate(1.15);
    transform: scale(1.2);
    z-index: 0;
}
.postcard.card-video-embed.card-link .card-link-image-video .cm-image-fg,
.postcard.card-audio-embed.card-link .card-link-image-video .cm-image-fg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: contain;
    z-index: 1;
}
.postcard.card-video-embed.card-link .card-link-image-video .card-video-play,
.postcard.card-audio-embed.card-link .card-link-image-video .card-video-play {
    z-index: 2;
}

/* Tighter header on video-embed cards — username sits compact at top, video
   gets to claim more vertical space. Title + desc + host stay in the meta
   panel below the video, line-clamped per the existing link-card rules. */
.postcard.card-video-embed.card-link .card-link-header {
    padding: 6px 10px 4px;
    gap: 2px;
}
/* Tighten the bottom panel too — video gets max vertical real estate. */
.postcard.card-video-embed.card-link .card-link-meta {
    padding: 8px 12px 32px;
    gap: 3px;
}

/* Brand-color fallbacks for hostile / non-embeddable video sources when
   we don't have a scraped thumbnail. Applied by data-hostile on the
   article + data-hostile-logo on the image div (set in card.php). */
.postcard.card-link[data-hostile] .card-link-image:not([style*="background-image"]) {
    background-image: none !important;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 auto;
}
.postcard.card-link[data-hostile="X"]         .card-link-image:not([style*="background-image"]) { background: #000; }
.postcard.card-link[data-hostile="Instagram"] .card-link-image:not([style*="background-image"]) { background: linear-gradient(135deg, #833ab4 0%, #fd1d1d 50%, #fcb045 100%); }
.postcard.card-link[data-hostile="TikTok"]    .card-link-image:not([style*="background-image"]) { background: #010101; }
.postcard.card-link[data-hostile="YouTube"]   .card-link-image:not([style*="background-image"]) { background: #0f0f0f; }
.postcard.card-link[data-hostile="Vimeo"]     .card-link-image:not([style*="background-image"]) { background: #00adef; }
.postcard.card-link[data-hostile="Facebook"]  .card-link-image:not([style*="background-image"]) { background: #1877f2; }
.postcard.card-link[data-hostile] .card-link-image[data-hostile-logo]::after {
    content: attr(data-hostile-logo);
    color: #fff;
    font-size: 56px;
    font-weight: 900;
    letter-spacing: -2px;
    opacity: 0.9;
}

/* Vertical video-embed brand fallback (no thumbnail) — uses cm-vertical-brand
   block instead of the image-bg/fg pattern. */
.cm-vertical-brand {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    background: #0f0f0f;
}
.cm-vertical-brand[data-brand="X"]         { background: #000; }
.cm-vertical-brand[data-brand="Instagram"] { background: linear-gradient(135deg, #833ab4 0%, #fd1d1d 50%, #fcb045 100%); }
.cm-vertical-brand[data-brand="TikTok"]    { background: #010101; }
.cm-vertical-brand[data-brand="YouTube"]   { background: #0f0f0f; }
.cm-vertical-brand[data-brand="Vimeo"]     { background: #00adef; }
.cm-vertical-brand[data-brand="Facebook"]  { background: #1877f2; }
.cm-vertical-brand::after {
    content: attr(data-brand-glyph);
    color: #fff;
    font-size: 96px;
    font-weight: 900;
    opacity: 0.9;
}

/* Modal-side iframe — orientation-aware aspect ratio. Horizontal stays
   16:9, vertical 9:16, both centered with black letterbox where needed. */
.modal-embed-frame {
    position: relative;
    width: 100%;
    background: #000;
    margin: 0 auto;
}
.modal-embed-frame.is-horizontal { aspect-ratio: 16 / 9; }
.modal-embed-frame.is-vertical {
    aspect-ratio: 9 / 16;
    max-width: 420px;          /* don't let portrait videos take half the screen */
}
.modal-embed-frame iframe {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    border: 0;
    display: block;
}

/* Modal-side branded click-out card — used when iframe embed isn't available
   (TikTok / Instagram / Facebook / X). Big thumbnail with CTA. */
.modal-embed-fallback {
    position: relative;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    width: 100%;
    background-size: cover;
    background-position: center;
    background-color: #0f0f0f;
    color: #fff;
    text-decoration: none;
    transition: filter .18s ease;
}
.modal-embed-fallback.is-horizontal { aspect-ratio: 16 / 9; }
.modal-embed-fallback.is-vertical   { aspect-ratio: 9 / 16; max-width: 420px; margin: 0 auto; }
.modal-embed-fallback[data-brand="X"]         { background-color: #000; }
.modal-embed-fallback[data-brand="Instagram"] { background-image: linear-gradient(135deg, #833ab4 0%, #fd1d1d 50%, #fcb045 100%); }
.modal-embed-fallback[data-brand="TikTok"]    { background-color: #010101; }
.modal-embed-fallback[data-brand="Facebook"]  { background-color: #1877f2; }
.modal-embed-fallback:hover { filter: brightness(1.1); }
.modal-embed-cta {
    background: rgba(0,0,0,0.7);
    padding: 12px 20px;
    border-radius: 999px;
    font-weight: 600;
    font-size: 14px;
    margin: 24px;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

/* Modal-side audio player — Spotify / SoundCloud iframe. Unlike video,
   audio players are wide horizontal blocks at fixed heights, so we let
   the iframe declare its own height (set via the height attribute) and
   just give the wrapper a rounded border-radius to match the link tile. */
.modal-audio-frame {
    width: 100%;
    display: block;
    background: #181818;
    border-radius: 12px;
    overflow: hidden;
}
.modal-audio-frame iframe {
    display: block;
    width: 100%;
    border: 0;
}
.modal-audio-frame[data-provider="soundcloud"] { background: #f2f2f2; }

/* Card-side album-art tile — square art reuses .card-link-image-video's
   dual-image pattern (rules above already cover .card-audio-embed). The
   brand-tinted fallback below handles posts where the scrape didn't return
   any artwork — fills the area with the brand color + a music glyph. */
.card-link-image-audio[data-audio-logo] {
    position: relative;
}
.card-link-image-audio[data-audio-logo][data-audio-brand="Spotify"]    { background: #1db954; }
.card-link-image-audio[data-audio-logo][data-audio-brand="SoundCloud"] { background: #ff5500; }
.card-link-image-audio[data-audio-logo]::after {
    content: attr(data-audio-logo);
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 96px;
    line-height: 1;
    color: rgba(255,255,255,0.92);
    pointer-events: none;
    z-index: 1;
}

/* Emoji-picker pane — inherits the global thin style but tightens further
   so the scrollbar doesn't visually compete with the emoji grid. */
.emoji-panes::-webkit-scrollbar { width: 6px; }
.emoji-panes::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,0.18);
    border: 1px solid transparent;
}
.emoji-panes::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.35); }
.emoji-panes { scrollbar-gutter: stable; }

/* ─────────────────────────────────────────────────────────────────────────
   Compose-input-wrap — wrapper around the comment <input> that hosts the
   emoji-trigger and cmt-img-btn floating over the input's right edge.
   The input fills 100% of the wrap; the icons sit absolutely on top.
   ───────────────────────────────────────────────────────────────────────── */
.compose-input-wrap {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: block;
}
.compose-input-wrap input[type="text"] {
    width: 100% !important;
    padding-right: 76px !important;
    box-sizing: border-box;
}
.compose-input-wrap .emoji-trigger,
.compose-input-wrap .cmt-img-btn {
    position: absolute !important;
    top: 50% !important;
    transform: translateY(-50%) !important;
    width: 28px !important;
    height: 28px !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    color: rgba(230,230,234,0.55) !important;
    border-radius: 999px !important;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    flex: none;
}
.compose-input-wrap .emoji-trigger { right: 38px !important; }
.compose-input-wrap .cmt-img-btn   { right: 6px  !important; }
.compose-input-wrap .emoji-trigger:hover,
.compose-input-wrap .cmt-img-btn:hover,
.compose-input-wrap .emoji-trigger.is-active {
    background: rgba(124,58,237,0.14) !important;
    color: #e6e6ea !important;
    border-color: transparent !important;
}
.compose-input-wrap .emoji-trigger svg,
.compose-input-wrap .cmt-img-btn svg {
    width: 18px; height: 18px; display: block;
}
/* The wrap takes the full input row, but the cmt-img-preview should still
   live below as a wrapped flex item. Force it onto its own line. */
.cmt-img-preview { flex-basis: 100%; order: 99; }

/* ─────────────────────────────────────────────────────────────────────────
   Settings-page save overlay — full-screen "Saving…" card shown on submit
   so users don't think the page hung when image processing takes a moment.
   ───────────────────────────────────────────────────────────────────────── */
.settings-saver {
    position: fixed; inset: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0,0,0,0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    animation: settings-saver-fade .15s ease;
}
.settings-saver[hidden] { display: none; }
.settings-saver-card {
    background: var(--surface, #18181b);
    border: 1px solid var(--border, rgba(255,255,255,0.08));
    border-radius: 14px;
    padding: 28px 36px;
    min-width: 280px;
    max-width: 380px;
    text-align: center;
    box-shadow: 0 30px 80px rgba(0,0,0,0.6);
}
.settings-saver-spinner {
    width: 44px; height: 44px;
    margin: 0 auto 16px;
    border: 3px solid rgba(255,255,255,0.1);
    border-top-color: var(--accent, #7c3aed);
    border-radius: 50%;
    animation: settings-saver-spin 0.85s linear infinite;
}
.settings-saver-text {
    font-size: 15px;
    font-weight: 600;
    color: var(--text, #e6e6ea);
    margin-bottom: 6px;
}
.settings-saver-sub {
    font-size: 13px;
    color: var(--dim, rgba(230,230,234,0.55));
    line-height: 1.4;
}
@keyframes settings-saver-spin { to { transform: rotate(360deg); } }
@keyframes settings-saver-fade { from { opacity: 0; } to { opacity: 1; } }

/* ---------- Landing gate (alpha landing) ----------------------------------
 * Standalone page — no topbar, no header. Purpose-built to feel like a HUD
 * overlay in a high-end game: heavily translucent card, medium purple border,
 * radial purple glow behind it, subtle drop-shadow for depth. Form controls
 * inside reuse the comment-composer's tokens so the language stays consistent
 * across the product.
 */
.landing-body {
    min-height: 100vh; min-height: 100dvh;
    margin: 0;
    /* The body has a solid bg-color set inline. We need a stacking context
       on body itself so that .landing-bg-image (a position:fixed sibling of
       .landing-shell) actually paints ABOVE the body's color fill. Without
       this, the image would render behind the solid color and stay invisible. */
    position: relative;
    isolation: isolate;
}

/* Full-viewport background image. position:fixed + inset:0 means it always
   covers the whole screen regardless of scroll position or viewport size,
   on every device. `background-size: cover` scales the image to fill — it
   may crop edges on extreme aspect ratios, which is the right tradeoff for
   a hero shot (no letterboxing, no tiling). z-index:0 keeps it above the
   body color but below all positioned content (which uses z-index >= 1). */
.landing-bg-image {
    position: fixed;
    inset: 0;
    z-index: 0;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
    pointer-events: none;
}

.landing-shell {
    position: relative;
    z-index: 1;
    min-height: 100vh; min-height: 100dvh;
    display: flex; align-items: center; justify-content: center;
    padding: 32px 20px;
    overflow: hidden;
}
.landing-bg-veil {
    position: absolute; inset: 0;
    background: radial-gradient(ellipse at center, rgba(10,10,11,0.35) 0%, rgba(10,10,11,0.85) 70%, rgba(10,10,11,0.95) 100%);
    pointer-events: none;
}
.landing-bg-glow {
    position: absolute;
    width: 900px; height: 900px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(124,58,237,0.32) 0%, rgba(124,58,237,0.14) 35%, rgba(124,58,237,0) 70%);
    filter: blur(50px);
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
    animation: landing-glow-pulse 8s ease-in-out infinite;
}
@keyframes landing-glow-pulse {
    0%, 100% { opacity: 0.7; transform: translate(-50%, -50%) scale(1); }
    50%      { opacity: 1;   transform: translate(-50%, -50%) scale(1.08); }
}

/* The card. Almost-transparent dark surface with a soft purple frame,
   blurred backdrop, and an outer halo so it lifts off the page. */
.landing-card {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 520px;
    background: rgba(20, 20, 24, 0.38);
    backdrop-filter: blur(24px) saturate(140%);
    -webkit-backdrop-filter: blur(24px) saturate(140%);
    border: 1px solid rgba(124, 58, 237, 0.38);
    border-radius: 18px;
    padding: 48px 44px 28px;
    box-shadow:
        0 40px 100px rgba(0,0,0,0.7),
        0 0 0 1px rgba(124,58,237,0.08),
        inset 0 1px 0 rgba(255,255,255,0.04);
    text-align: center;
    animation: landing-card-in 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}
@keyframes landing-card-in {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
}

.landing-logo-wrap { margin-bottom: 24px; }
.landing-logo {
    max-width: 200px;
    max-height: 80px;
    margin: 0 auto;
    display: block;
    filter: drop-shadow(0 4px 24px rgba(124,58,237,0.35));
}
.landing-wordmark {
    font-size: 38px;
    font-weight: 800;
    letter-spacing: -0.02em;
    background: linear-gradient(135deg, #fff 0%, #c4a8ff 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.landing-headline {
    margin: 0 0 10px;
    font-size: 30px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: #fff;
    line-height: 1.15;
}
.landing-subheadline {
    margin: 0 0 28px;
    font-size: 15px;
    color: rgba(255, 255, 255, 0.7);
    line-height: 1.5;
    max-width: 420px;
    margin-left: auto; margin-right: auto;
}

.landing-email-cta {
    margin: 0 0 12px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: rgba(255, 255, 255, 0.7);
}

/* Form layout — inline input + button, mirroring the comment composer.
   Mobile collapses to stacked. */
.landing-form { margin: 0 0 18px; }
.landing-form .landing-input-row,
.landing-alpha-form .landing-input-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Input — exact tokens from #comment-form input[type="text"]: subtle
   translucent fill, hairline border, purple focus. Sized up slightly so
   email entry feels deliberate at hero scale. */
.landing-form input[type="email"],
.landing-form input[type="password"],
.landing-alpha-form input[type="password"] {
    flex: 1; min-width: 0;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 8px;
    padding: 11px 14px;
    color: var(--text, #e6e6ea);
    font-size: 14px;
    line-height: 1.4;
    outline: none;
    transition: border-color .15s, background .15s;
}
.landing-form input[type="email"]::placeholder,
.landing-form input[type="password"]::placeholder,
.landing-alpha-form input[type="password"]::placeholder {
    color: rgba(230,230,234,0.35);
}
.landing-form input[type="email"]:focus,
.landing-form input[type="password"]:focus,
.landing-alpha-form input[type="password"]:focus {
    background: rgba(255,255,255,0.05);
    border-color: rgba(124,58,237,0.55);
}
/* Kill autofill yellow */
.landing-form input:-webkit-autofill,
.landing-form input:-webkit-autofill:focus,
.landing-alpha-form input:-webkit-autofill,
.landing-alpha-form input:-webkit-autofill:focus {
    -webkit-box-shadow: 0 0 0 1000px #1c1c1f inset !important;
    box-shadow: 0 0 0 1000px #1c1c1f inset !important;
    -webkit-text-fill-color: #e6e6ea !important;
    caret-color: #e6e6ea;
    transition: background-color 9999s ease-in-out 0s;
}

/* JOIN / UNLOCK button — exact tokens from #comment-form button[type=submit]:
   translucent purple fill, purple border, accent text. Sized up slightly
   to match the bigger input. */
.landing-form button[type="submit"],
.landing-alpha-form button[type="submit"] {
    background: rgba(124,58,237,0.18);
    color: var(--accent, #7c3aed);
    border: 1px solid rgba(124,58,237,0.3);
    border-radius: 8px;
    padding: 10px 18px;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.05em;
    cursor: pointer;
    transition: background .15s, border-color .15s;
    white-space: nowrap;
}
.landing-form button[type="submit"]:hover,
.landing-alpha-form button[type="submit"]:hover {
    background: rgba(124,58,237,0.28);
    border-color: rgba(124,58,237,0.5);
}
.landing-form button[type="submit"]:active,
.landing-alpha-form button[type="submit"]:active {
    transform: translateY(1px);
}

.landing-form-msg {
    min-height: 22px;
    margin-top: 12px;
    font-size: 13px;
    line-height: 1.4;
}
.landing-form-msg.is-loading::before {
    content: '';
    display: inline-block;
    width: 12px; height: 12px;
    border: 2px solid rgba(255,255,255,0.2);
    border-top-color: var(--accent, #7c3aed);
    border-radius: 50%;
    animation: landing-spin 0.7s linear infinite;
    vertical-align: middle;
}
@keyframes landing-spin { to { transform: rotate(360deg); } }
.landing-msg-ok  { color: #4ade80; }
.landing-msg-err { color: #f87171; }

.landing-footer-note {
    margin: 14px 0 0;
    font-size: 12px;
    color: rgba(255,255,255,0.4);
    line-height: 1.5;
}

/* Alpha-code disclosure — quietly tucked under the email form. */
.landing-alpha {
    margin: 0 auto 18px;
    max-width: 420px;
    text-align: left;
}
.landing-alpha summary {
    font-size: 12px;
    color: rgba(255,255,255,0.5);
    cursor: pointer;
    text-align: center;
    padding: 6px;
    list-style: none;
    transition: color 0.15s;
    user-select: none;
}
.landing-alpha summary::-webkit-details-marker { display: none; }
.landing-alpha summary:hover { color: rgba(255,255,255,0.85); }
.landing-alpha[open] summary { color: rgba(255,255,255,0.85); margin-bottom: 8px; }
.landing-alpha-form { margin-top: 4px; }

/* "Already have an account? Log in →" link — small, centered, low contrast
   so it doesn't compete with the email CTA but is clearly findable for
   testers + admins coming back after a logout. */
.landing-login-link {
    margin: 6px 0 0;
    font-size: 12px;
    color: rgba(255,255,255,0.45);
    letter-spacing: 0.02em;
}
.landing-login-link a {
    color: rgba(255,255,255,0.85);
    text-decoration: none;
    font-weight: 600;
    transition: color 0.15s;
}
.landing-login-link a:hover { color: var(--accent-link, #a855f7); }

.landing-version {
    margin-top: 22px;
    padding-top: 20px;
    border-top: 1px solid rgba(255,255,255,0.06);
    font-family: "SF Mono", "JetBrains Mono", "Consolas", monospace;
    font-size: 10px;
    font-weight: 500;
    color: rgba(255,255,255,0.32);
    letter-spacing: 0.4em;
    text-transform: uppercase;
}

@media (max-width: 560px) {
    .landing-card { padding: 36px 22px 22px; border-radius: 14px; }
    .landing-headline { font-size: 25px; }
    .landing-subheadline { font-size: 14px; margin-bottom: 22px; }
    .landing-form .landing-input-row,
    .landing-alpha-form .landing-input-row {
        flex-direction: column;
        align-items: stretch;
    }
    .landing-form button[type="submit"],
    .landing-alpha-form button[type="submit"] { padding: 12px 18px; }
    .landing-version { letter-spacing: 0.3em; }
}

/* ---------- Theme picker (Settings → Theme) -------------------------------
 * Two visual cards, each with a tiny preview swatch of how the theme paints.
 * Selected card gets a purple ring + accent border. Clicking instantly
 * swaps the theme on the page (data-theme on <html>) so users see the
 * change before saving — saves on form submit like every other setting.
 */
.theme-picker {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
    margin: 8px 0;
}
.theme-card {
    position: relative;
    display: flex !important;
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 10px;
    padding: 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    cursor: pointer;
    transition: border-color .15s, background .15s, box-shadow .15s;
}
.theme-card:hover { border-color: rgba(124,58,237,0.35); }
.theme-card input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.theme-card.is-selected {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(124,58,237,0.15);
    background: rgba(124,58,237,0.05);
}
.theme-swatch {
    aspect-ratio: 16 / 9;
    border-radius: 8px;
    border: 1px solid rgba(255,255,255,0.06);
    position: relative;
    overflow: hidden;
}
/* Each swatch shows: bg fill + a faux card + an accent dot, so users see
   the actual surface color, the actual border color, and the actual accent
   working together — not just a flat color block. */
.theme-swatch::before {
    content: '';
    position: absolute;
    left: 14%; top: 22%; right: 14%; bottom: 22%;
    border-radius: 4px;
}
.theme-swatch::after {
    content: '';
    position: absolute;
    right: 12%; bottom: 14%;
    width: 12px; height: 12px;
    border-radius: 50%;
    background: #7c3aed;
    box-shadow: 0 0 12px rgba(124,58,237,0.5);
}
.theme-swatch-dark    { background: #0a0a0b; }
.theme-swatch-dark::before    { background: #1c1c1f; border: 1px solid rgba(255,255,255,0.08); }
.theme-swatch-cosmos  { background: linear-gradient(135deg, #0a0913 0%, #15121f 100%); }
.theme-swatch-cosmos::before  { background: #1c1a28; border: 1px solid rgba(168,140,255,0.12); }

/* Astro swatch — show the actual atmosphere: deep cosmic gradient + a tiny
   sprinkle of stars + a glowing accent dot. Mini-preview of what users get. */
.theme-swatch-astro {
    background:
        radial-gradient(ellipse at top, rgba(124,58,237,0.35) 0%, transparent 60%),
        radial-gradient(1px 1px at 14px 8px,  rgba(255,255,255,0.9),  transparent 50%),
        radial-gradient(1px 1px at 36px 22px, rgba(196,168,255,0.7),  transparent 50%),
        radial-gradient(0.5px 0.5px at 50px 14px, rgba(255,255,255,0.6), transparent 50%),
        radial-gradient(1px 1px at 70px 30px, rgba(168,200,255,0.7),  transparent 50%),
        radial-gradient(0.5px 0.5px at 24px 32px, rgba(255,255,255,0.5), transparent 50%),
        linear-gradient(135deg, #07061a 0%, #16142a 100%);
}
.theme-swatch-astro::before {
    background: #1f1c34;
    border: 1px solid rgba(168,140,255,0.2);
    box-shadow: 0 0 10px rgba(124,58,237,0.25);
}
.theme-swatch-astro::after {
    box-shadow: 0 0 16px rgba(124,58,237,0.7), 0 0 4px rgba(255,255,255,0.6);
}

/* Dark Matter swatch — pure black, scattered tiny white stars, the inner
   "card" is just a hairline outline (no fill) — the wireframe statement
   in miniature. The accent dot stays purple to communicate that the
   accent color is preserved as the lone color in the theme. */
.theme-swatch-dark-matter {
    background:
        radial-gradient(0.5px 0.5px at 14px 8px,  rgba(255,255,255,0.85), transparent 50%),
        radial-gradient(0.5px 0.5px at 36px 22px, rgba(255,255,255,0.7),  transparent 50%),
        radial-gradient(0.5px 0.5px at 50px 14px, rgba(255,255,255,0.5),  transparent 50%),
        radial-gradient(1px 1px at 70px 30px,     rgba(255,255,255,0.75), transparent 50%),
        radial-gradient(0.5px 0.5px at 24px 32px, rgba(255,255,255,0.4),  transparent 50%),
        radial-gradient(1px 1px at 88px 16px,     rgba(255,255,255,0.65), transparent 50%),
        radial-gradient(0.5px 0.5px at 110px 38px, rgba(255,255,255,0.55), transparent 50%),
        #000;
}
.theme-swatch-dark-matter::before {
    background: transparent;
    border: 1px solid rgba(255,255,255,0.18);
}

/* Plasma swatch — violet-black with a pink/violet aurora at the corners, a
   glowing gradient card, and a charged accent dot. */
.theme-swatch-plasma {
    background:
        radial-gradient(ellipse at top left,     rgba(255,92,213,0.40) 0%, transparent 55%),
        radial-gradient(ellipse at bottom right,  rgba(124,80,255,0.40) 0%, transparent 55%),
        linear-gradient(135deg, #120814 0%, #220f2c 100%);
}
.theme-swatch-plasma::before {
    background: linear-gradient(135deg, rgba(255,92,213,0.28), rgba(168,85,247,0.28));
    border: 1px solid rgba(255,106,213,0.40);
    box-shadow: 0 0 10px rgba(232,80,255,0.35);
}
.theme-swatch-plasma::after {
    background: linear-gradient(135deg, #ff5cd5, #a855f7);
    box-shadow: 0 0 16px rgba(232,80,255,0.85), 0 0 4px rgba(255,180,240,0.7);
}

.theme-card-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.theme-card-name { font-size: 14px; font-weight: 600; color: var(--text); }
.theme-card-tag {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.08em;
    padding: 2px 6px;
    border-radius: 999px;
    background: rgba(124,58,237,0.15);
    color: var(--accent-link, #a855f7);
    text-transform: uppercase;
}
.theme-card-desc { font-size: 12px; color: var(--dim); line-height: 1.4; }

/* ── Share icon (solid share glyph) ───────────────────────────────────
   Solid white share arrow at rest. On hover/focus of the owning button it
   tints to the cosmic #aionCosmicShare gradient (fill, since it's a solid
   glyph) with a soft purple glow. overflow:visible lets the glow paint
   outside the viewBox. */
.aion-share-ico { overflow: visible; transition: filter 0.2s ease; }
button:hover .aion-share-ico path,
button:focus-visible .aion-share-ico path { fill: url(#aionCosmicShare); }
button:hover .aion-share-ico,
button:focus-visible .aion-share-ico {
    filter: drop-shadow(0 0 5px rgba(167,139,250,0.9));
}
@media (prefers-reduced-motion: reduce) {
    .aion-share-ico { transition: none; }
}

/* ── Blog cards ───────────────────────────────────────────────────────
   Two shapes: WITH a featured image → reuses the link-stack layout (image +
   meta), gets a small "Blog" tag in the meta. WITHOUT an image → a colored
   full-bleed typography tile (.cb-tile) on a ctbg-* background, so a text-only
   blog looks rich and never leaves a blank image block in the feed. */
/* "BLOG" pill — identical finish to the Post button (.btn-primary): same
   --btn-soft-* tokens + shadow. Just smaller. No bespoke colors. */
.card-blog-tag {
    display:inline-block; align-self:flex-start;
    font-size:10px; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
    color:var(--btn-soft-color, #c4b5fd);
    background:var(--btn-soft-bg, rgba(124,58,237,0.18));
    border:1px solid var(--btn-soft-border, rgba(124,58,237,0.35));
    box-shadow:var(--btn-soft-shadow);
    border-radius:999px; padding:3px 10px; margin-bottom:6px;
}
.postcard.card-blog .cb-tile {
    display:flex; flex-direction:column; height:100%;
    padding:18px 18px 16px; box-sizing:border-box; color:#fff;
}
.cb-tile .cb-tag {
    align-self:flex-start;
    font-size:10px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
    color:rgba(255,255,255,0.9);
    background:rgba(0,0,0,0.22); border:1px solid rgba(255,255,255,0.28);
    border-radius:999px; padding:2px 8px; margin-bottom:10px;
}
.cb-tile .cb-title {
    margin:0; font-weight:800; line-height:1.2;
    font-size:clamp(17px, 4.2cqi, 26px);
    text-shadow:0 1px 10px rgba(0,0,0,0.35);
    display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden;
}
.cb-tile .cb-excerpt {
    margin:0.5em 0 0; line-height:1.5; color:rgba(255,255,255,0.92);
    font-size:clamp(12px, 2.6cqi, 15px);
    display:-webkit-box; -webkit-line-clamp:6; -webkit-box-orient:vertical; overflow:hidden;
    flex:1 1 auto; min-height:0;
}
.cb-tile .cb-readmore {
    margin-top:10px; align-self:flex-start;
    font-size:12px; font-weight:700; color:#fff;
    border-bottom:1px solid rgba(255,255,255,0.6); padding-bottom:1px;
}
/* Blog WITH a featured image → link-stack, refined: shorter image (so the
   excerpt panel gets more room), BLOG pill overlaid on the image's top-left
   (not a meta row), bigger 5-line excerpt below the title. */
/* Landscape image-blog → default link-card layout: image (flex:1) fills, meta
   is content-height → NO empty gap below the text. Bigger 5-line excerpt; the
   BLOG pill overlays the image (keeps its Post-button finish). */
.postcard.card-blog.card-link .card-link-meta { flex: 1 1 auto; }
.postcard.card-blog.card-link .card-link-headline { flex: 0 0 auto; }
/* Bigger, more readable excerpt that GROWS to fill the panel (no empty gap to
   the bottom). Clipped by height with a soft bottom fade instead of a hard cut. */
.postcard.card-blog.card-link .card-link-desc {
    display: block; -webkit-line-clamp: none;
    font-size: clamp(13px, 3cqw, 16px); line-height: 1.5;
    flex: 1 1 auto; min-height: 0; overflow: hidden;
    -webkit-mask-image: linear-gradient(180deg, #000 82%, transparent 100%);
            mask-image: linear-gradient(180deg, #000 82%, transparent 100%);
}
.postcard.card-blog.card-link .card-link-image-blog .card-blog-tag {
    position:absolute; top:8px; left:8px; z-index:3; margin:0;
}
/* SMART HYBRID image fit — cover fills the frame (no blur, no bars); only the
   FRAME SHAPE differs by orientation:
     • Portrait/poster → fills the whole tile (text overlaid), minimal crop.
     • Wide/square → a clean landscape banner at the top, meta fills below.
   A small crop is the accepted trade for a clean, bar-free look. */
.card-blog .cm-image-bg { display: none; }
.card-blog .cm-image-fg {
    position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:1;
}
.postcard.card-blog.card-link .card-link-image-blog {
    position:relative; overflow:hidden; flex:0 0 auto;
    aspect-ratio:3 / 2; background:#0b0b0d;
}
/* Compose soft-suggest hint for long (blog-length) posts. */
.compose-blog-hint {
    margin:8px 0 2px; padding:8px 12px; border-radius:10px;
    font-size:12.5px; line-height:1.4;
    color:var(--btn-soft-color, #c4b5fd);
    background:var(--btn-soft-bg, rgba(124,58,237,0.14));
    border:1px solid var(--btn-soft-border, rgba(124,58,237,0.30));
}
.compose-blog-hint[hidden] { display:none; }

