/* =============================================================
   CYBERPUNK / FUTURISTIC THEME — Our Dark Home
   ============================================================= */

/* ---- CSS Variables ---- */
:root {
    --cy-bg:          #05050f;
    --cy-surface-1:   #09091e;
    --cy-surface-2:   #0d0d2b;
    --cy-surface-3:   #111135;
    --cy-border:      #1c1c50;
    --cy-border-glow: rgba(0, 245, 255, 0.25);
    --cy-cyan:        #00f5ff;
    --cy-cyan-dim:    rgba(0, 245, 255, 0.15);
    --cy-cyan-glow:   rgba(0, 245, 255, 0.6);
    --cy-magenta:     #ff2d78;
    --cy-magenta-dim: rgba(255, 45, 120, 0.15);
    --cy-green:       #39ff14;
    --cy-green-dim:   rgba(57, 255, 20, 0.15);
    --cy-yellow:      #ffd700;
    /* Softer companions to the gold above, matching the -dim pattern used by
       cyan, magenta and green. The soft tone is for text and icons: #ffd700 at
       small sizes on a near-black background reads as an alert, which is wrong
       for a state that just means "waiting". */
    --cy-yellow-soft: #ffe98a;
    --cy-yellow-dim:  rgba(255, 215, 0, 0.12);

    /* Restricted or closed, as distinct from both gold and red. Gold means
       elevated (pinned); red means destructive (delete). A lock is neither —
       it is a door that is shut, and it needed its own tone rather than
       borrowing one that already means something else. */
    --cy-amber:       #ffa64d;
    --cy-amber-dim:   rgba(255, 166, 77, 0.14);
    --cy-text:        #c8d8f8;
    /* Secondary text. Was #7080b0, which cleared AA on paper (5.05:1) and still
       read as washed-out grey-blue at the small sizes it is mostly used at —
       timestamps, meta lines, hints, half the chrome on the site. Lifted to
       8.17:1, which is the single change that does most for legibility here,
       because this is the colour nearly all the hard-to-read text was in. */
    --cy-text-dim:    #a6b2da;

    /* Secondary CONTENT, as opposed to secondary chrome.
       A forum description, a "last posted by" line, a relative time: things a
       reader actually reads, that simply are not the headline. --cy-text-dim
       is for chrome that should recede — hints, placeholders, separators. The
       forum and PM modules had no middle tier available and reached for -dim
       for both, which is how ordinary content ended up painted as furniture. */
    --cy-text-soft:   #b9c4e6;
    --cy-error:       #ff3a3a;
    --cy-font-hud:    'Orbitron', 'Share Tech Mono', monospace;
    --cy-font-body:   'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

/* ====================== BASE ====================== */
body {
    background-color: var(--cy-bg);
    background-image:
        repeating-linear-gradient(
            0deg, transparent, transparent 2px,
            rgba(0, 245, 255, 0.013) 2px,
            rgba(0, 245, 255, 0.013) 4px
        ),
        repeating-linear-gradient(
            90deg, transparent, transparent 40px,
            rgba(28, 28, 80, 0.18) 40px,
            rgba(28, 28, 80, 0.18) 41px
        ),
        repeating-linear-gradient(
            180deg, transparent, transparent 40px,
            rgba(28, 28, 80, 0.18) 40px,
            rgba(28, 28, 80, 0.18) 41px
        );
    color: var(--cy-text);
    font-family: var(--cy-font-body);
    font-size: 1rem;
    /* There was no line-height at all, so everything inherited the browser
       default of roughly 1.2 — tight enough that paragraphs of any length read
       as a block. 1.6 is the usual comfortable value for body copy and costs
       only vertical space. */
    line-height: 1.6;
    padding-top: 64px;
}

/* Ambient glow overlay */
body::before {
    content: "";
    position: fixed;
    top: 0; left: 0;
    width: 100%; height: 100%;
    z-index: -2;
    background:
        radial-gradient(ellipse at 20% 20%, rgba(0, 245, 255, 0.05) 0%, transparent 55%),
        radial-gradient(ellipse at 80% 80%, rgba(255, 45, 120, 0.05) 0%, transparent 55%),
        radial-gradient(ellipse at 50% 0%,  rgba(0, 245, 255, 0.06) 0%, transparent 50%);
    pointer-events: none;
    will-change: transform;
    transform: translateZ(0);
}

/* Scanline overlay — static, no animation */
body::after {
    content: "";
    position: fixed;
    top: 0; left: 0;
    width: 100%; height: 100%;
    z-index: -1;
    background: repeating-linear-gradient(
        0deg,
        rgba(0,0,0,0.06) 0px,
        rgba(0,0,0,0.06) 1px,
        transparent 1px,
        transparent 3px
    );
    will-change: transform;
    transform: translateZ(0);
    pointer-events: none;
    opacity: 0.45;
}

/* ====================== TYPOGRAPHY ====================== */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--cy-font-hud);
    letter-spacing: 0.04em;
}

.gothic-title {
    font-family: var(--cy-font-hud);
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--cy-cyan);
}

.gothic-title-enhanced {
    font-family: var(--cy-font-hud);
    letter-spacing: 0.1em;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--cy-cyan);
    text-shadow:
        0 0 8px  var(--cy-cyan),
        0 0 18px rgba(0, 245, 255, 0.5),
        0 0 35px rgba(0, 245, 255, 0.25);
    position: relative;
}

/* Glitch animation for title hover */
.gothic-title:hover {
    animation: glitch 0.4s ease-in-out;
}

@keyframes glitch {
    0%, 100% { transform: none; }
    20%  { transform: translateX(-2px) skewX(-2deg); clip-path: polygon(0 20%, 100% 20%, 100% 45%, 0 45%); }
    40%  { transform: translateX(2px)  skewX(2deg);  clip-path: polygon(0 55%, 100% 55%, 100% 80%, 0 80%); }
    60%  { transform: translateX(-1px); }
    80%  { transform: translateX(1px); }
}

/* ====================== LAYOUT ====================== */
.gothic-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 1rem;
}

.gothic-navbar {
    border-bottom: 1px solid var(--cy-border);
    background-color: rgba(5, 5, 15, 0.95);
}

/* ====================== CARDS ====================== */
.gothic-card {
    background-color: var(--cy-surface-1);
    border: 1px solid var(--cy-border);
    border-radius: 0;
    clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 12px 100%, 0 calc(100% - 12px));
    box-shadow: 0 4px 20px rgba(0,0,0,0.6);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    position: relative;
}

.gothic-card::before {
    content: '';
    position: absolute;
    inset: 0;
    border: 1px solid rgba(0, 245, 255, 0.08);
    pointer-events: none;
    transition: border-color 0.3s ease;
}

.gothic-card-hover:hover,
.gothic-card:hover {
    box-shadow:
        0 8px 30px rgba(0,0,0,0.7),
        0 0 20px rgba(0, 245, 255, 0.12);
    transform: translateY(-3px);
}

.gothic-card:hover::before {
    border-color: rgba(0, 245, 255, 0.3);
}

/* ====================== ENHANCED BORDER ====================== */
.gothic-enhanced-border {
    position: relative;
    border: 1px solid var(--cy-border);
    box-shadow:
        0 0 15px rgba(0, 245, 255, 0.08),
        inset 0 0 15px rgba(0, 245, 255, 0.03);
}

.gothic-enhanced-border::before {
    content: '';
    position: absolute;
    top: 4px; right: 4px; bottom: 4px; left: 4px;
    border: 1px solid rgba(0, 245, 255, 0.12);
    pointer-events: none;
}

/* Animated corner brackets */
.gothic-enhanced-border::after {
    content: '';
    position: absolute;
    top: -1px; left: -1px;
    width: 16px; height: 16px;
    border-top: 2px solid var(--cy-cyan);
    border-left: 2px solid var(--cy-cyan);
    box-shadow: -2px -2px 8px rgba(0, 245, 255, 0.4);
    pointer-events: none;
}

/* Bottom-right corner accent via wrapper trick */
.gothic-enhanced-border-wrap {
    position: relative;
}
.gothic-enhanced-border-wrap::after {
    content: '';
    position: absolute;
    bottom: -1px; right: -1px;
    width: 16px; height: 16px;
    border-bottom: 2px solid var(--cy-magenta);
    border-right: 2px solid var(--cy-magenta);
    box-shadow: 2px 2px 8px rgba(255, 45, 120, 0.4);
    pointer-events: none;
}

/* ====================== SURFACE COLORS ====================== */
.bg-gothic-charcoal { background-color: var(--cy-surface-2); }
.bg-gothic-black    { background-color: var(--cy-bg); }
.bg-gothic-darkgray { background-color: var(--cy-surface-1); }
.bg-gothic-accent   { background-color: var(--cy-cyan); }

/* ====================== TEXT COLORS ======================
   Prefixed with `body` deliberately. The compiled Tailwind bundle is loaded
   AFTER this file and defines .text-gothic-muted itself, at the same
   specificity — so an unprefixed rule here loses, and the lifted --cy-text-dim
   above would never reach the hundreds of views that use the utility class.
   `body .x` is (0,1,1) against Tailwind's (0,1,0) and wins without !important. */
body .text-gothic-text      { color: var(--cy-text); }
body .text-gothic-muted     { color: var(--cy-text-dim); }
body .text-gothic-highlight { color: var(--cy-cyan); }

/* Error states */
.text-error,
.text-red-300,
.text-red-400,
.text-red-500,
.text-red-600 {
    color: var(--cy-error) !important;
    text-shadow: 0 0 8px rgba(255, 58, 58, 0.5);
}

form p.text-error,
form p[class*="text-red"] {
    color: var(--cy-error) !important;
}

/* ====================== BORDERS ====================== */
.border-gothic-border    { border-color: var(--cy-border); }
.border-gothic-highlight { border-color: var(--cy-cyan); }

/* ====================== FORMS ====================== */
.gothic-input {
    color: var(--cy-text) !important;
    background-color: var(--cy-surface-2) !important;
    border-color: var(--cy-border) !important;
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
    padding: 0.5rem 0.75rem;
    box-shadow: inset 0 2px 8px rgba(0,0,0,0.4);
    font-family: var(--cy-font-body);
    letter-spacing: 0.03em;
}

.gothic-input:focus {
    border-color: var(--cy-cyan) !important;
    box-shadow:
        0 0 0 2px rgba(0, 245, 255, 0.2),
        0 0 12px rgba(0, 245, 255, 0.15),
        inset 0 2px 8px rgba(0,0,0,0.4) !important;
    outline: none;
}

.gothic-input::placeholder { color: var(--cy-text-dim) !important; }

/* Global form overrides */
input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="reset"]),
textarea,
select {
    color: var(--cy-text) !important;
    background-color: var(--cy-surface-2) !important;
    border-color: var(--cy-border) !important;
    font-family: var(--cy-font-body);
}

input:not([type="checkbox"]):not([type="radio"]):focus,
textarea:focus,
select:focus {
    border-color: var(--cy-cyan) !important;
    box-shadow: 0 0 0 2px rgba(0, 245, 255, 0.2), 0 0 10px rgba(0, 245, 255, 0.12) !important;
    outline: none !important;
}

input[type="checkbox"],
input[type="radio"] {
    background-color: var(--cy-surface-2) !important;
    border-color: var(--cy-border) !important;
    accent-color: var(--cy-cyan);
}

input[type="checkbox"]:checked,
input[type="radio"]:checked {
    background-color: var(--cy-cyan) !important;
    border-color: var(--cy-cyan) !important;
}

/* Select arrow */
select {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2300f5ff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e") !important;
    background-position: right 0.5rem center !important;
}

/* Autofill override */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--cy-text);
    -webkit-box-shadow: 0 0 0px 1000px #0d0d2b inset;
    transition: background-color 5000s ease-in-out 0s;
}

/* ====================== BUTTONS ====================== */
.gothic-btn {
    background-color: transparent;
    color: var(--cy-cyan);
    border: 1px solid var(--cy-cyan);
    padding: 0.4rem 0.9rem;
    transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease;
    font-family: var(--cy-font-hud);
    letter-spacing: 0.06em;
    font-size: 0.8rem;
    text-transform: uppercase;
    clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
    box-shadow: 0 0 8px rgba(0, 245, 255, 0.2), inset 0 0 8px rgba(0, 245, 255, 0.04);
    cursor: pointer;
}

.gothic-btn:hover {
    background-color: var(--cy-cyan-dim);
    color: #ffffff;
    box-shadow: 0 0 16px rgba(0, 245, 255, 0.5), inset 0 0 12px rgba(0, 245, 255, 0.1);
    transform: translateY(-1px);
    text-shadow: 0 0 6px var(--cy-cyan);
}

.gothic-btn:active { transform: translateY(1px); }

/* Destructive variant — same silhouette, red instead of cyan, so Clear All
   never reads as just another action next to Mark All as Read. */
.gothic-btn-danger {
    color: var(--cy-error);
    border-color: var(--cy-error);
    box-shadow: 0 0 8px rgba(255, 58, 58, 0.2), inset 0 0 8px rgba(255, 58, 58, 0.04);
}

.gothic-btn-danger:hover {
    background-color: rgba(255, 58, 58, 0.15);
    color: #ffffff;
    box-shadow: 0 0 16px rgba(255, 58, 58, 0.5), inset 0 0 12px rgba(255, 58, 58, 0.1);
    text-shadow: 0 0 6px rgba(255, 58, 58, 0.8);
}

/* Cancel variant — same silhouette, muted instead of cyan.
   Cancel buttons in the forum and PM screens were plain .gothic-btn, which made
   them glow exactly as brightly as the action they back out of: two equally lit
   buttons side by side, and no way to tell at a glance which one posts. Dimming
   it is the whole point, so the primary action is the one the eye lands on.
   Not red — backing out is not destructive, and .gothic-btn-danger is spoken
   for by things that actually delete. */
.gothic-btn-cancel {
    color: var(--cy-text-dim);
    border-color: var(--cy-border);
    box-shadow: none;
}

.gothic-btn-cancel:hover {
    background-color: rgba(255, 45, 120, 0.1);
    border-color: var(--cy-magenta);
    color: var(--cy-magenta);
    box-shadow: 0 0 10px rgba(255, 45, 120, 0.25);
    text-shadow: none;
}

.gothic-button {
    background-color: transparent;
    color: var(--cy-cyan);
    padding: 0.55rem 1.15rem;
    border: 1px solid var(--cy-cyan);
    font-family: var(--cy-font-hud);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, transform 0.2s ease;
    position: relative;
    overflow: hidden;
    clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
    box-shadow: 0 0 10px rgba(0, 245, 255, 0.2), inset 0 0 10px rgba(0, 245, 255, 0.04);
}

/* Sweep animation on hover */
.gothic-button::before {
    content: '';
    position: absolute;
    top: 0; left: 0;
    width: 100%; height: 100%;
    background: linear-gradient(90deg, transparent 0%, rgba(0,245,255,0.12) 50%, transparent 100%);
    transform: translateX(-200%);
    transition: transform 0.4s ease;
}

.gothic-button:hover::before { transform: translateX(200%); }

.gothic-button:hover {
    background-color: var(--cy-cyan-dim);
    color: #ffffff;
    box-shadow: 0 0 18px rgba(0, 245, 255, 0.5), inset 0 0 14px rgba(0, 245, 255, 0.08);
    transform: translateY(-2px);
    text-shadow: 0 0 8px var(--cy-cyan);
}

.gothic-button:active {
    transform: translateY(1px);
    box-shadow: 0 0 8px rgba(0, 245, 255, 0.3);
}

/* Small variant */
.gothic-button-sm {
    padding: 0.3rem 0.75rem;
    font-size: 0.75rem;
    clip-path: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 7px 100%, 0 calc(100% - 7px));
}

/* Secondary button (magenta) */
.gothic-button-secondary {
    border-color: var(--cy-magenta);
    color: var(--cy-magenta);
    box-shadow: 0 0 10px rgba(255, 45, 120, 0.2), inset 0 0 10px rgba(255, 45, 120, 0.03);
}

.gothic-button-secondary::before {
    background: linear-gradient(90deg, transparent 0%, rgba(255,45,120,0.12) 50%, transparent 100%);
}

.gothic-button-secondary:hover {
    background-color: var(--cy-magenta-dim);
    color: #ffffff;
    box-shadow: 0 0 18px rgba(255, 45, 120, 0.5), inset 0 0 14px rgba(255, 45, 120, 0.08);
    text-shadow: 0 0 8px var(--cy-magenta);
    border-color: var(--cy-magenta);
    transform: translateY(-2px);
}

/* Danger button */
.gothic-button-danger {
    border-color: var(--cy-error);
    color: var(--cy-error);
    box-shadow: 0 0 10px rgba(255, 58, 58, 0.2);
}

.gothic-button-danger:hover {
    background-color: rgba(255, 58, 58, 0.15);
    color: #ff8888;
    box-shadow: 0 0 18px rgba(255, 58, 58, 0.5);
    text-shadow: 0 0 8px var(--cy-error);
    border-color: var(--cy-error);
}

/* Ornate corners via pseudo-elements */
.gothic-button-ornate {
    position: relative;
}
.gothic-button-ornate::after {
    content: '';
    position: absolute;
    top: -2px; left: -2px;
    width: 8px; height: 8px;
    border-top: 1px solid rgba(0,245,255,0.6);
    border-left: 1px solid rgba(0,245,255,0.6);
    pointer-events: none;
}

/* Pulse for primary CTA */
.gothic-button-pulse {
    /* box-shadow pulse removed for scroll performance */
}

/* ====================== LINKS ====================== */
.gothic-link {
    position: relative;
    color: var(--cy-cyan);
    transition: color 0.3s ease;
    text-decoration: none;
}

.gothic-link::after {
    content: '';
    position: absolute;
    bottom: -2px; left: 50%;
    width: 0; height: 1px;
    background: var(--cy-cyan);
    transition: width 0.3s ease;
    transform: translateX(-50%);
    box-shadow: 0 0 4px var(--cy-cyan);
}

.gothic-link:hover {
    color: #ffffff;
    text-shadow: 0 0 8px var(--cy-cyan);
}

.gothic-link:hover::after { width: 100%; }

/* ====================== DIVIDERS ====================== */
.gothic-divider {
    position: relative;
    height: 30px;
    margin: 2rem 0;
}

.gothic-divider::before {
    content: '';
    position: absolute;
    top: 50%; left: 0; right: 0;
    height: 1px;
    background: linear-gradient(to right, transparent, var(--cy-cyan), transparent);
    box-shadow: 0 0 6px var(--cy-cyan);
}

.gothic-divider::after {
    content: '◈';
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    background-color: var(--cy-surface-1);
    color: var(--cy-cyan);
    padding: 0 1rem;
    font-size: 0.9rem;
    text-shadow: 0 0 8px var(--cy-cyan);
}

/* ====================== NAVIGATION ====================== */
.sticky-nav {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 1000;
    background: linear-gradient(to bottom, rgba(5,5,18,0.98), rgba(9,9,28,0.97));
    box-shadow: 0 4px 20px rgba(0,0,0,0.6), 0 1px 0 rgba(0,245,255,0.15);
    border-bottom: 1px solid rgba(0, 245, 255, 0.25);
    transition: background 0.3s ease;
    will-change: transform;
    transform: translateZ(0);
}

/* Static bottom glow line */
.sticky-nav::after {
    content: '';
    position: absolute;
    bottom: -1px; left: 0; right: 0;
    height: 1px;
    background: linear-gradient(to right, transparent, var(--cy-cyan) 30%, var(--cy-magenta) 60%, var(--cy-cyan) 80%, transparent);
}

/* Logo */
.nav-logo {
    position: relative;
    display: inline-block;
    transition: transform 0.3s ease;
    text-decoration: none;
    letter-spacing: 0.08em;
    pointer-events: auto;
}

.nav-logo:hover {
    text-shadow: 0 0 12px var(--cy-cyan), 0 0 24px rgba(0,245,255,0.5);
    transform: scale(1.04);
}

/* Override glitch animation for the nav logo link:
   the standard glitch uses clip-path which trims the pointer-event hit area,
   making the link unclickable mid-animation. This version keeps the visual
   effect without clip-path so the link is always clickable. */
.nav-logo.gothic-title:hover {
    animation: nav-logo-glitch 0.4s ease-in-out;
}

@keyframes nav-logo-glitch {
    0%, 100% { transform: scale(1.04); }
    20%  { transform: scale(1.04) translateX(-2px) skewX(-2deg); }
    40%  { transform: scale(1.04) translateX(2px)  skewX(2deg); }
    60%  { transform: scale(1.04) translateX(-1px); }
    80%  { transform: scale(1.04) translateX(1px); }
}

/* Nav links */
.nav-link {
    position: relative;
    transition: color 0.3s ease;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.78rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-decoration: none;
}

.nav-link::after {
    content: '';
    position: absolute;
    bottom: -2px; left: 50%;
    width: 0; height: 1px;
    background: var(--cy-cyan);
    box-shadow: 0 0 6px var(--cy-cyan);
    transition: width 0.3s ease;
    transform: translateX(-50%);
}

.nav-link:hover {
    color: var(--cy-cyan);
    text-shadow: 0 0 8px rgba(0, 245, 255, 0.5);
}

.nav-link:hover::after { width: 100%; }

/* Mobile menu button */
.mobile-menu-button {
    transition: background-color 0.3s ease, transform 0.3s ease;
}

/* Home icon in nav bar */
.nav-home-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    width: 2rem; height: 2rem;
    color: var(--cy-text-dim);
    font-size: 1rem;
    border: 1px solid rgba(0, 245, 255, 0.15);
    border-radius: 6px;
    text-decoration: none;
    transition: color 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease, background-color 0.25s ease;
}

.nav-home-icon:hover {
    color: var(--cy-cyan);
    border-color: rgba(0, 245, 255, 0.45);
    background-color: rgba(0, 245, 255, 0.06);
    box-shadow: 0 0 10px rgba(0, 245, 255, 0.18);
}

/* Generic tooltip */
.gothic-tooltip-trigger {
    position: relative;
}

.gothic-tooltip {
    pointer-events: none;
    position: absolute;
    top: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    white-space: nowrap;
    background: rgba(5, 5, 18, 0.92);
    color: var(--cy-cyan);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 0.3rem 0.6rem;
    border: 1px solid rgba(0, 245, 255, 0.3);
    border-radius: 4px;
    box-shadow: 0 0 8px rgba(0, 245, 255, 0.15);
    opacity: 0;
    transition: opacity 0.2s ease;
    z-index: 9999;
}

/* Small arrow */
.gothic-tooltip::after {
    content: '';
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 4px solid transparent;
    border-bottom-color: rgba(0, 245, 255, 0.3);
}

.gothic-tooltip-trigger:hover .gothic-tooltip,
.gothic-tooltip-trigger:focus-within .gothic-tooltip {
    opacity: 1;
}

/* Wrapper form of the trigger: used when the tooltip cannot be a CHILD of the
   control it describes. .gothic-button has overflow:hidden and a clip-path for
   its shine sweep, which would clip an absolutely-positioned tooltip away to
   nothing — so the button and the tooltip become siblings inside this, and the
   wrapper owns the positioning context. inline-flex so it still behaves as one
   item inside the flex rows these buttons sit in. */
.gothic-tooltip-wrap { display: inline-flex; }


.mobile-menu-button:hover {
    background-color: var(--cy-cyan-dim);
    transform: scale(1.05);
}

/* Dropdown menu */
.dropdown-menu {
    background: rgb(9, 9, 28);
    border: 1px solid rgba(0, 245, 255, 0.2);
    box-shadow: 0 12px 30px rgba(0,0,0,0.7), 0 0 20px rgba(0,245,255,0.08);
}

.dropdown-item {
    transition: color 0.2s ease, background-color 0.2s ease, transform 0.2s ease;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.78rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
}

.dropdown-item:hover {
    background-color: var(--cy-cyan-dim);
    color: var(--cy-cyan);
    transform: translateX(4px);
    text-shadow: 0 0 6px rgba(0,245,255,0.4);
}

/* ====================== ACCOUNT MENU GROUPS ======================
   The account dropdown had grown to fourteen flat entries. It is now an
   accordion: four collapsible groups plus a few top-level links.

   Written by hand rather than with Tailwind utilities because the prebuilt
   bundle is from 2026-06-17 and cannot be rebuilt without npm — a utility that
   is not already compiled into it renders as nothing at all, silently. Semantic
   classes here always apply. */

.nav-dropdown-body {
    /* Fixed floor so the menu does not resize as groups open and close —
       a panel that changes width under the cursor is hard to aim at. */
    min-width: 15rem;
}

/* Shared row geometry for every entry, link or button. .dropdown-item supplies
   the colour, font and hover; this supplies the layout. */
.nav-group-item,
.nav-group-toggle {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0.5rem 1rem;
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
}

.nav-group-toggle {
    justify-content: space-between;
    color: var(--cy-text);
}

.nav-group-toggle > span {
    display: flex;
    align-items: center;
}

/* Fixed width so labels line up in a column regardless of glyph width. */
.nav-group-icon {
    width: 1.1rem;
    margin-right: 0.55rem;
    text-align: center;
    flex-shrink: 0;
}

.nav-group-caret {
    font-size: 0.75rem;
    margin-left: 0.75rem;
    opacity: 0.6;
    transition: transform 0.2s ease, opacity 0.2s ease;
}

.nav-group-toggle.is-open .nav-group-caret {
    transform: rotate(180deg);
    opacity: 1;
}

.nav-group-toggle.is-open {
    color: var(--cy-cyan);
}

/* The rail is what makes an expanded group read as belonging to its header
   rather than as more top-level entries. */
.nav-group-items {
    border-left: 1px solid rgba(0, 245, 255, 0.18);
    margin-left: 1.15rem;
}

.nav-group-items .nav-group-item {
    padding-left: 0.9rem;
    font-size: 0.75rem;
}

.nav-group-logout {
    color: var(--cy-text-dim);
}

/* Replaces `border-t border-gothic-border my-1`. The utilities were doing half
   a job: my-1 is not in the prebuilt bundle, so the old divider sat flush
   against the rows above and below it with no separating space at all. */
.nav-divider {
    height: 1px;
    margin: 0.35rem 0;
    background: rgba(0, 245, 255, 0.18);
}

/* ====================== FOLDED FORUM FEED ENTRIES ======================
   A burst of forum posts collapses into one feed entry listing up to four of
   them. The list is indented under the phrase with a rail, so it reads as
   "these are the posts" rather than as four unrelated feed rows. */

.gothic-forum-refs {
    list-style: none;
    margin: 0.45rem 0 0;
    padding: 0 0 0 0.85rem;
    border-left: 1px solid rgba(0, 245, 255, 0.18);
}

.gothic-forum-refs li {
    margin: 0.2rem 0;
    font-size: 0.85rem;
    line-height: 1.4;
}

/* The forum name is context, not the link — keeping it out of the anchor stops
   it competing with the thread title for the click. */
.gothic-forum-ref-forum {
    color: var(--cy-text-dim);
    font-size: 0.75rem;
    opacity: 0.75;
}

.gothic-forum-ref-forum::before {
    content: "· ";
}

/* ====================== PROFILE WALL ======================
   Posts left on a member's profile, and replies to them. Hand-written rather
   than Tailwind utilities: the prebuilt bundle is stale and cannot be rebuilt,
   so anything not already compiled into it renders as nothing at all. */

.profile-wall-form {
    margin-bottom: 1.1rem;
}

.profile-wall-input {
    width: 100%;
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid var(--cy-border, rgba(0, 245, 255, 0.2));
    border-radius: 5px;
    padding: 0.6rem 0.75rem;
    color: var(--cy-text);
    font-family: inherit;
    font-size: 0.9rem;
    resize: vertical;
}

.profile-wall-input:focus {
    outline: none;
    border-color: var(--cy-cyan);
    box-shadow: 0 0 8px rgba(0, 245, 255, 0.25);
}

.profile-wall-form-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.5rem;
}

.profile-wall-flash {
    margin-bottom: 0.85rem;
    padding: 0.5rem 0.75rem;
    border-radius: 4px;
    font-size: 0.82rem;
    border: 1px solid rgba(0, 245, 255, 0.3);
    background: rgba(0, 245, 255, 0.08);
    color: var(--cy-cyan);
}

.profile-wall-flash.is-error {
    border-color: rgba(255, 70, 90, 0.4);
    background: rgba(255, 70, 90, 0.1);
    color: #ff8d9c;
}

.profile-wall-post {
    padding: 0.75rem 0;
    border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.profile-wall-post:first-child {
    border-top: 0;
}

.profile-wall-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.3rem;
}

.profile-wall-author {
    font-weight: 600;
    font-size: 0.88rem;
}

.profile-wall-time {
    color: var(--cy-text-dim);
    font-size: 0.75rem;
}

/* Pushed to the far end so it never sits next to the name, where it would be
   easy to hit while aiming for the profile link. */
.profile-wall-delete {
    margin-left: auto;
}

.profile-wall-delete button {
    background: none;
    border: 0;
    padding: 0.15rem 0.3rem;
    cursor: pointer;
    color: var(--cy-text-dim);
    font-size: 0.75rem;
    transition: color 0.2s ease;
}

.profile-wall-delete button:hover {
    color: #ff6b7d;
}

.profile-wall-body {
    font-size: 0.9rem;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
}

/* The rail is what makes replies read as belonging to the post above rather
   than as more wall posts. */
.profile-wall-replies {
    margin: 0.6rem 0 0 0.6rem;
    padding-left: 0.85rem;
    border-left: 1px solid rgba(0, 245, 255, 0.18);
}

.profile-wall-reply {
    margin-bottom: 0.55rem;
}

.profile-wall-reply:last-child {
    margin-bottom: 0;
}

.profile-wall-reply-toggle {
    margin-top: 0.5rem;
}

.profile-wall-reply-toggle > summary {
    cursor: pointer;
    display: inline-block;
    color: var(--cy-text-dim);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    transition: color 0.2s ease;
}

.profile-wall-reply-toggle > summary:hover {
    color: var(--cy-cyan);
}

.profile-wall-reply-toggle[open] > summary {
    margin-bottom: 0.45rem;
}

/* Decorative nav dots */
.nav-decoration {
    position: absolute;
    width: 6px; height: 6px;
    border-radius: 50%;
    background-color: var(--cy-cyan);
    box-shadow: 0 0 8px var(--cy-cyan), 0 0 16px rgba(0,245,255,0.4);
    opacity: 0;
    transition: opacity 0.5s ease;
}

.sticky-nav:hover .nav-decoration { opacity: 1; }

.nav-decoration-1 { top: 8px; left: 12px; }
.nav-decoration-2 { top: 8px; right: 12px; }

/* Content container */
.content-container {
    padding-top: 4rem;
    width: 100%;
}

/* ====================== UTILITY ====================== */
.rounded-md { border-radius: 0.375rem; }

.transition-colors {
    transition-property: color, background-color, border-color;
}
.duration-300 { transition-duration: 300ms; }

/* Focus states */
.focus\:border-gothic-highlight:focus  { border-color: var(--cy-cyan); }
.focus\:ring:focus {
    --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
    --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);
    box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000);
}
.focus\:ring-gothic-highlight:focus { --tw-ring-color: rgba(0,245,255,0.4); }
.focus\:ring-opacity-25:focus { --tw-ring-opacity: 0.25; }

/* Hover helpers */
.hover\:bg-gothic-highlight:hover { background-color: var(--cy-cyan-dim); }
.hover\:text-gothic-text:hover    { color: var(--cy-text); }

/* ====================== PROSE ====================== */
.prose { color: var(--cy-text); font-family: var(--cy-font-body); }

.prose h1, .prose h2, .prose h3, .prose h4 {
    font-family: var(--cy-font-hud);
    color: var(--cy-cyan);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.prose p { margin-bottom: 1.25em; line-height: 1.75; }

.prose a {
    color: var(--cy-cyan);
    text-decoration: none;
    border-bottom: 1px solid rgba(0,245,255,0.3);
    transition: color 0.3s ease, border-bottom-color 0.3s ease;
}
.prose a:hover {
    color: #ffffff;
    border-bottom-color: var(--cy-cyan);
    text-shadow: 0 0 6px var(--cy-cyan);
}

.prose ul { list-style-type: none; padding-left: 0; }
.prose ul li::before { content: '▸ '; color: var(--cy-cyan); }

/* Big first letter */
.prose p:first-of-type::first-letter {
    font-size: 2em;
    font-weight: 800;
    color: var(--cy-cyan);
    float: left;
    line-height: 0.85;
    margin-right: 0.1em;
    font-family: var(--cy-font-hud);
    text-shadow: 0 0 12px var(--cy-cyan);
}

/* ====================== ACTIVITY FEED ====================== */
.gothic-activity-item {
    position: relative;
    margin-bottom: 1.5rem;
    padding: 1.25rem 1.25rem 1.25rem 1.5rem;
    background-color: var(--cy-surface-1);
    border: 1px solid var(--cy-border);
    border-left: 2px solid rgba(0, 245, 255, 0.15);
    box-shadow: 0 4px 12px rgba(0,0,0,0.4);
    transition: box-shadow 0.3s ease, border-color 0.3s ease;
    overflow: hidden;
}


.gothic-activity-item:hover { 
    border-color: rgba(0, 245, 255, 0.35);
    box-shadow: 0 6px 20px rgba(0,0,0,0.5), 0 0 15px rgba(0,245,255,0.06);
}


.gothic-activity-time {
    color: var(--cy-text-dim);
    font-size: 0.78rem;
    margin-bottom: 0.5rem;
    font-family: var(--cy-font-hud);
    letter-spacing: 0.04em;
}

.gothic-activity-content { position: relative; }

.gothic-activity-user {
    font-weight: 700;
    color: var(--cy-cyan);
    font-family: var(--cy-font-hud);
    font-size: 0.85rem;
    letter-spacing: 0.05em;
    text-shadow: 0 0 6px rgba(0,245,255,0.4);
    text-decoration: none;
}

.gothic-activity-action { color: var(--cy-magenta); }

.gothic-activity-detail {
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid rgba(0, 245, 255, 0.1);
    font-size: 0.95rem;
}

/* Inline label directly before the username, not a corner watermark.
   Fixed width so the usernames stay vertically aligned down the feed
   regardless of how wide each type's glyph is. */
.gothic-activity-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    margin-right: 0.4rem;
    opacity: 0.85;
    font-size: 0.9rem;
    line-height: 1;
    transition: opacity 0.3s ease, text-shadow 0.3s ease;
    color: var(--cy-cyan);
}

/* No scale() here — this sits in the text flow now, and scaling would
   nudge the username sideways on hover. */
.gothic-activity-item:hover .gothic-activity-icon {
    opacity: 1;
    text-shadow: 0 0 8px currentColor;
}

/* Per-type icon colors */
.gothic-activity-registration .gothic-activity-icon { color: var(--cy-green); }
.gothic-activity-post         .gothic-activity-icon { color: var(--cy-cyan); }
.gothic-activity-comment      .gothic-activity-icon { color: var(--cy-yellow); }
.gothic-activity-image        .gothic-activity-icon { color: var(--cy-magenta); }
.gothic-activity-music        .gothic-activity-icon { color: #a78bfa; }

/* ====================== FEED HEADER ====================== */
.gothic-feed-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 2rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--cy-border);
}

.gothic-feed-title {
    font-family: var(--cy-font-hud);
    font-size: 1.15rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--cy-cyan);
    text-shadow: 0 0 10px rgba(0,245,255,0.4);
    position: relative;
}

.gothic-feed-title::after {
    content: '';
    position: absolute;
    bottom: -0.6rem; left: 0;
    width: 60%;
    height: 1px;
    background: linear-gradient(to right, var(--cy-cyan), transparent);
    box-shadow: 0 0 6px var(--cy-cyan);
}

.gothic-feed-filter { display: flex; gap: 0.4rem; flex-wrap: wrap; }

.gothic-feed-filter-btn {
    background-color: transparent;
    color: var(--cy-text-dim);
    border: 1px solid var(--cy-border);
    padding: 0.2rem 0.65rem;
    font-size: 0.75rem;
    font-family: var(--cy-font-hud);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
    cursor: pointer;
    clip-path: polygon(0 0, calc(100% - 5px) 0, 100% 5px, 100% 100%, 5px 100%, 0 calc(100% - 5px));
}

.gothic-feed-filter-btn:hover,
.gothic-feed-filter-btn.active {
    background-color: var(--cy-cyan-dim);
    color: var(--cy-cyan);
    border-color: var(--cy-cyan);
    text-shadow: 0 0 6px rgba(0,245,255,0.4);
    box-shadow: 0 0 8px rgba(0,245,255,0.15);
}

/* Empty feed */
.gothic-feed-empty {
    text-align: center;
    padding: 3rem 1rem;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.85rem;
    letter-spacing: 0.06em;
    border: 1px dashed var(--cy-border);
    margin: 2rem 0;
}

.gothic-feed-empty-icon {
    font-size: 2rem;
    margin-bottom: 1rem;
    opacity: 0.3;
    color: var(--cy-cyan);
    display: block;
}


/* ====================== NOTIFICATIONS ====================== */
.gothic-notifications-dropdown { position: relative; }

/* cursor: pointer because two of these are <button> elements (the bell, logout)
   and browsers default buttons to the arrow cursor — without this they feel
   dead next to the anchors beside them. */
.gothic-notifications-btn { transition: transform 0.3s ease, color 0.3s ease; cursor: pointer; }

.gothic-notifications-btn:hover {
    transform: scale(1.12);
    color: var(--cy-cyan);
    text-shadow: 0 0 8px rgba(0,245,255,0.5);
}

.gothic-notification-badge {
    color: #ffffff !important;
}

.gothic-notifications-menu {
    background-color: var(--cy-surface-1);
    border: 1px solid rgba(0, 245, 255, 0.25);
    box-shadow: 0 12px 30px rgba(0,0,0,0.7), 0 0 20px rgba(0,245,255,0.06);
    transform-origin: top right;
    transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    width: 350px !important;
}

.gothic-notifications-menu:not(.hidden) {
    animation: menu-appear 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes menu-appear {
    0%   { opacity: 0; transform: scale(0.95) translateY(-6px); }
    100% { opacity: 1; transform: scale(1)    translateY(0); }
}

.gothic-notifications-menu .p-3.border-b.border-gothic-dark {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.gothic-notifications-menu h3 {
    font-family: var(--cy-font-hud);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-size: 0.85rem;
    color: var(--cy-cyan);
    margin: 0;
    text-shadow: 0 0 6px rgba(0,245,255,0.4);
}

.gothic-notifications-menu a.gothic-link {
    font-family: var(--cy-font-hud);
    letter-spacing: 0.06em;
    white-space: nowrap;
    margin-left: 1rem;
    font-size: 0.75rem;
    text-transform: uppercase;
}

/* Duplicate-empty fixes */
#notifications-empty           { display: none; }
#notifications-empty.show      { display: block; }
#notifications-content .gothic-notification-empty { display: none !important; }
#notifications-content:empty + #notifications-empty { display: block !important; }

/* Scrollbar */
.gothic-notifications-list {
    scrollbar-width: thin;
    scrollbar-color: var(--cy-cyan) var(--cy-surface-1);
}
.gothic-notifications-list::-webkit-scrollbar       { width: 4px; }
.gothic-notifications-list::-webkit-scrollbar-track  { background: var(--cy-surface-1); }
.gothic-notifications-list::-webkit-scrollbar-thumb  {
    background-color: var(--cy-cyan);
    border-radius: 4px;
    box-shadow: 0 0 4px var(--cy-cyan);
}

/* Individual notification */
.gothic-notification-item {
    position: relative;
    transition: background-color 0.3s ease;
    overflow: hidden;
    border-bottom: 1px solid var(--cy-border);
}

.gothic-notification-item:hover { background-color: var(--cy-cyan-dim); }

.gothic-notification-item::after {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 2px;
    background-color: transparent;
    transition: background-color 0.3s ease, box-shadow 0.3s ease;
}

.gothic-notification-item:hover::after {
    background-color: var(--cy-cyan);
    box-shadow: 0 0 8px var(--cy-cyan);
}

.gothic-notification-unread { background-color: rgba(0, 245, 255, 0.05); }

.gothic-notification-unread::before {
    content: '';
    position: absolute;
    top: 50%; right: 10px;
    transform: translateY(-50%);
    width: 5px; height: 5px;
    border-radius: 50%;
    background-color: var(--cy-cyan);
    box-shadow: 0 0 6px var(--cy-cyan);
}

/* Avatar */
.gothic-avatar { position: relative; }
.gothic-avatar::after {
    content: '';
    position: absolute;
    top: -2px; left: -2px; right: -2px; bottom: -2px;
    border: 1px solid rgba(0,245,255,0.3);
    border-radius: 50%;
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
}
.gothic-notification-item:hover .gothic-avatar::after {
    opacity: 1;
    transform: scale(1.1);
    box-shadow: 0 0 8px rgba(0,245,255,0.4);
}

/* Empty / loading states */
.gothic-notification-empty,
.gothic-notification-loading {
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.8rem;
    letter-spacing: 0.06em;
    text-align: center;
    padding: 1.5rem 1rem;
}

.gothic-notification-loading i {
    color: var(--cy-cyan);
    text-shadow: 0 0 6px var(--cy-cyan);
    animation: spin 1.5s linear infinite;
}

/* Mark-all button */
.gothic-mark-all-btn {
    width: 100%;
    background-color: transparent;
    color: var(--cy-text-dim);
    border: 1px solid var(--cy-border);
    padding: 0.35rem 0.75rem;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
    cursor: pointer;
}
.gothic-mark-all-btn:hover {
    color: var(--cy-cyan);
    border-color: var(--cy-cyan);
    background-color: var(--cy-cyan-dim);
    text-shadow: 0 0 6px rgba(0,245,255,0.4);
}

/* Clear-all button — matches the mark-all button's footprint so the two sit
   level, but red on hover to signal that it destroys rather than dismisses. */
.gothic-clear-all-btn {
    width: 100%;
    background-color: transparent;
    color: var(--cy-text-dim);
    border: 1px solid var(--cy-border);
    padding: 0.35rem 0.75rem;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
    cursor: pointer;
}
.gothic-clear-all-btn:hover {
    color: var(--cy-error);
    border-color: var(--cy-error);
    background-color: rgba(255, 58, 58, 0.15);
    text-shadow: 0 0 6px rgba(255, 58, 58, 0.4);
}

/* ====================== PAGINATION ====================== */
nav[aria-label="pagination"] span,
nav[aria-label="pagination"] a {
    background-color: var(--cy-surface-2) !important;
    border-color: var(--cy-border) !important;
    color: var(--cy-text-dim) !important;
    font-family: var(--cy-font-hud);
    font-size: 0.78rem;
    letter-spacing: 0.05em;
}

nav[aria-label="pagination"] a:hover {
    background-color: var(--cy-cyan-dim) !important;
    border-color: var(--cy-cyan) !important;
    color: var(--cy-cyan) !important;
}

nav[aria-label="pagination"] span[aria-current="page"] span {
    background-color: var(--cy-cyan-dim) !important;
    border-color: var(--cy-cyan) !important;
    color: var(--cy-cyan) !important;
    box-shadow: 0 0 8px rgba(0,245,255,0.2);
}

/* ====================== ALERTS ====================== */
.gothic-alert {
    padding: 0.75rem 1rem;
    border: 1px solid var(--cy-border);
    font-family: var(--cy-font-hud);
    font-size: 0.82rem;
    letter-spacing: 0.04em;
}

.gothic-alert-success {
    border-color: var(--cy-green);
    background-color: var(--cy-green-dim);
    color: var(--cy-green);
    text-shadow: 0 0 6px rgba(57,255,20,0.4);
}

.gothic-alert-error {
    border-color: var(--cy-error);
    background-color: rgba(255,58,58,0.1);
    color: var(--cy-error);
}

/* ====================== KEYFRAMES ====================== */
@keyframes pulse {
    0%   { transform: scale(1);   opacity: 0.4; }
    100% { transform: scale(1.6); opacity: 0.9; }
}

@keyframes spin {
    0%   { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

@keyframes data-stream {
    0%   { transform: translateY(-100%); opacity: 0; }
    10%  { opacity: 0.6; }
    90%  { opacity: 0.6; }
    100% { transform: translateY(100vh); opacity: 0; }
}

/* ====================== RESPONSIVE ====================== */
@media (max-width: 640px) {
    .prose h1 { font-size: 1.4rem; }
    .prose h2 { font-size: 1.2rem; }
    .gothic-title { font-size: 0.9em; }
}

/* ====================== MISC HELPERS ====================== */
/* Scrollbar global */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--cy-border) var(--cy-surface-1);
}
*::-webkit-scrollbar       { width: 6px; height: 6px; }
*::-webkit-scrollbar-track  { background: var(--cy-surface-1); }
*::-webkit-scrollbar-thumb  { background-color: var(--cy-border); border-radius: 4px; }
*::-webkit-scrollbar-thumb:hover { background-color: var(--cy-cyan); }

/* Selection */
::selection {
    background-color: rgba(0, 245, 255, 0.25);
    color: #ffffff;
}

/* HUD corners for sections */
.cy-hud-box {
    position: relative;
    border: 1px solid var(--cy-border);
    padding: 1.5rem;
}
.cy-hud-box::before {
    content: '';
    position: absolute;
    top: -1px; left: -1px;
    width: 14px; height: 14px;
    border-top: 2px solid var(--cy-cyan);
    border-left: 2px solid var(--cy-cyan);
    box-shadow: -2px -2px 6px rgba(0,245,255,0.3);
}
.cy-hud-box::after {
    content: '';
    position: absolute;
    bottom: -1px; right: -1px;
    width: 14px; height: 14px;
    border-bottom: 2px solid var(--cy-magenta);
    border-right: 2px solid var(--cy-magenta);
    box-shadow: 2px 2px 6px rgba(255,45,120,0.3);
}

/* Feed post textarea override */
#feed-content {
    background-color: var(--cy-surface-2) !important;
    border: 1px solid var(--cy-border) !important;
    color: var(--cy-text) !important;
    font-family: var(--cy-font-body) !important;
    resize: vertical;
    transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
#feed-content:focus {
    border-color: var(--cy-cyan) !important;
    box-shadow: 0 0 12px rgba(0,245,255,0.12) !important;
}

/* Image gallery grid hover */
.gallery-item:hover img {
    filter: brightness(1.1) saturate(1.3);
    box-shadow: 0 0 20px rgba(0,245,255,0.25);
}

/* Profile avatar border */
.gothic-avatar-ring {
    border: 2px solid var(--cy-cyan);
    box-shadow: 0 0 12px rgba(0,245,255,0.4);
}

/* Status indicators */
.text-green-500 { color: var(--cy-green) !important; text-shadow: 0 0 6px rgba(57,255,20,0.4); }
.text-blue-500  { color: #60a5fa !important; }


/* ====================== ANIMATED EMOJI ======================
   Unicode glyphs animated with CSS — see App\Support\Emoji. Messages store the
   :shortcode:, so restyling here restyles every message ever sent, and a glyph
   that never animates is still perfectly readable. */

.ae {
    display: inline-block;
    font-style: normal;
    line-height: 1;
    /* Keeps a glyph from clipping mid-transform inside a tight line box. */
    will-change: transform, opacity, filter;
}

.ae-pulse   { animation: ae-pulse   1.8s ease-in-out infinite; }
.ae-float   { animation: ae-float   3s   ease-in-out infinite; }
.ae-flit    { animation: ae-flit    2.4s ease-in-out infinite; }
.ae-crawl   { animation: ae-crawl   3.5s linear         infinite; }
.ae-sway    { animation: ae-sway    3s   ease-in-out infinite; }
.ae-flicker { animation: ae-flicker 2.2s steps(2, end) infinite; }
.ae-glow    { animation: ae-glow    2.6s ease-in-out infinite; }
.ae-beat    { animation: ae-beat    1.2s ease-in-out infinite; }
.ae-twinkle { animation: ae-twinkle 1.6s ease-in-out infinite; }
.ae-spin    { animation: ae-spin    3s   linear      infinite; }
.ae-glitch  { animation: ae-glitch  2s   steps(3, end) infinite; }
.ae-shake   { animation: ae-shake   1.6s ease-in-out infinite; }
.ae-bounce  { animation: ae-bounce  1.6s ease-in-out infinite; }
.ae-tilt    { animation: ae-tilt    2.4s ease-in-out infinite; }

@keyframes ae-pulse   { 0%,100% { transform: scale(1);      } 50% { transform: scale(1.22); } }
@keyframes ae-float   { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes ae-flit    { 0%,100% { transform: translate(0,0) rotate(0); }
                        25%     { transform: translate(3px,-3px) rotate(8deg); }
                        75%     { transform: translate(-3px,-2px) rotate(-8deg); } }
@keyframes ae-crawl   { 0%,100% { transform: translateX(0)    rotate(0); }
                        25%     { transform: translateX(3px)  rotate(6deg); }
                        50%     { transform: translateX(0)    rotate(0); }
                        75%     { transform: translateX(-3px) rotate(-6deg); } }
@keyframes ae-sway    { 0%,100% { transform: rotate(-7deg); } 50% { transform: rotate(7deg); } }
@keyframes ae-flicker { 0%,100% { opacity: 1; filter: brightness(1.15); }
                        45%     { opacity: .55; filter: brightness(.8); }
                        60%     { opacity: 1; filter: brightness(1.3); } }
@keyframes ae-glow    { 0%,100% { filter: drop-shadow(0 0 1px rgba(0,245,255,.5)); }
                        50%     { filter: drop-shadow(0 0 7px rgba(0,245,255,.95)); } }
@keyframes ae-beat    { 0%,100% { transform: scale(1); } 15% { transform: scale(1.3); } 30% { transform: scale(1); } }
@keyframes ae-twinkle { 0%,100% { transform: scale(1)   rotate(0);     opacity: 1; }
                        50%     { transform: scale(.72) rotate(22deg); opacity: .6; } }
@keyframes ae-spin    { from    { transform: rotate(0); } to { transform: rotate(360deg); } }
@keyframes ae-glitch  { 0%,100% { transform: translate(0,0);   filter: hue-rotate(0); }
                        33%     { transform: translate(-2px,1px); filter: hue-rotate(70deg); }
                        66%     { transform: translate(2px,-1px); filter: hue-rotate(-70deg); } }
@keyframes ae-shake   { 0%,100% { transform: translateX(0); }
                        20%     { transform: translateX(-3px); }
                        40%     { transform: translateX(3px); }
                        60%     { transform: translateX(-2px); }
                        80%     { transform: translateX(2px); } }
@keyframes ae-bounce  { 0%,100% { transform: translateY(0)    scale(1); }
                        40%     { transform: translateY(-5px) scale(1.06); } }
@keyframes ae-tilt    { 0%,100% { transform: rotate(0); } 30% { transform: rotate(-16deg); } 60% { transform: rotate(10deg); } }

/* Anyone who has asked the OS for less motion gets the glyph, standing still. */
@media (prefers-reduced-motion: reduce) {
    .ae, .ae[class*="ae-"] { animation: none !important; }
}

/* ====================== PRIVATE MESSAGES ======================
   Built as named classes rather than Tailwind utilities: the compiled Tailwind
   bundle is prebuilt and cannot be rebuilt on this server, so any utility not
   already in it renders as nothing. These are safe to add and to change. */

/* ---- Inbox list ---- */
.pm-thread {
    display: flex;
    align-items: flex-start;
    gap: 0.9rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
    text-decoration: none;
    transition: border-color 0.25s ease, background-color 0.25s ease, transform 0.25s ease;
}
.pm-thread:hover {
    border-color: var(--cy-cyan);
    background-color: var(--cy-surface-2);
    transform: translateX(2px);
}
.pm-thread + .pm-thread { margin-top: 0.5rem; }

/* An unread thread gets the cyan edge, so the inbox scans at a glance. */
.pm-thread-unread {
    border-left: 3px solid var(--cy-cyan);
    background-color: var(--cy-surface-2);
}

.pm-thread-avatar {
    width: 44px; height: 44px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--cy-border);
    flex-shrink: 0;
}
.pm-thread-avatar-fallback {
    width: 44px; height: 44px;
    border-radius: 50%;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-bg);
    display: flex; align-items: center; justify-content: center;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    flex-shrink: 0;
}
.pm-thread-body { flex: 1; min-width: 0; }
.pm-thread-name {
    font-family: var(--cy-font-hud);
    letter-spacing: 0.05em;
    color: var(--cy-text);
    font-size: 0.9rem;
}
.pm-thread-preview {
    color: var(--cy-text-dim);
    font-size: 0.82rem;
    margin-top: 0.15rem;
    /* One line only — a long message must not stretch the row. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pm-thread-time {
    color: var(--cy-text-dim);
    font-size: 0.75rem;
    font-family: var(--cy-font-hud);
    flex-shrink: 0;
    white-space: nowrap;
}
.pm-thread-count {
    display: inline-block;
    min-width: 1.25rem;
    padding: 0.05rem 0.35rem;
    border-radius: 999px;
    background: linear-gradient(135deg, #003344 0%, #006688 100%);
    color: #ffffff;
    font-size: 0.75rem;
    text-align: center;
    font-family: var(--cy-font-hud);
}

/* ---- Thread view ---- */
.pm-log {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    padding: 1rem;
    max-height: 60vh;
    overflow-y: auto;
    background-color: var(--cy-bg);
    border: 1px solid var(--cy-border);
}

.pm-msg { display: flex; flex-direction: column; max-width: 78%; }
/* Sent messages sit right, received left — the only cue needed for who spoke. */
.pm-msg-out { align-self: flex-end;   align-items: flex-end; }
.pm-msg-in  { align-self: flex-start; align-items: flex-start; }

.pm-bubble {
    padding: 0.55rem 0.85rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-2);
    color: var(--cy-text);
    font-size: 0.9rem;
    line-height: 1.5;
    /* Long unbroken strings (URLs) must wrap, not widen the column. */
    overflow-wrap: anywhere;
    word-break: break-word;
}
.pm-msg-out .pm-bubble {
    border-color: rgba(0, 245, 255, 0.4);
    background-color: rgba(0, 245, 255, 0.07);
}
.pm-meta {
    margin-top: 0.2rem;
    font-size: 0.75rem;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    letter-spacing: 0.04em;
}

/* ---- Composer ---- */
.pm-composer {
    margin-top: 0.75rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
    padding: 0.75rem;
}
.pm-composer textarea {
    width: 100%;
    min-height: 84px;
    resize: vertical;
    background-color: var(--cy-surface-2);
    border: 1px solid var(--cy-border);
    color: var(--cy-text);
    padding: 0.6rem 0.75rem;
    font-family: var(--cy-font-body);
    font-size: 0.9rem;
}
.pm-composer textarea:focus {
    outline: none;
    border-color: var(--cy-cyan);
    box-shadow: 0 0 10px rgba(0, 245, 255, 0.25);
}
.pm-composer-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-top: 0.6rem;
}
.pm-composer-left { display: flex; align-items: center; gap: 0.5rem; position: relative; }
.pm-charcount { font-size: 0.75rem; color: var(--cy-text-dim); font-family: var(--cy-font-hud); }
.pm-charcount-over { color: var(--cy-error); }

/* ---- Emoji picker ---- */
.pm-emoji-btn {
    background: transparent;
    border: 1px solid var(--cy-border);
    color: var(--cy-text-dim);
    padding: 0.3rem 0.6rem;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1.2;
    transition: border-color 0.25s ease, color 0.25s ease;
}
.pm-emoji-btn:hover { border-color: var(--cy-cyan); color: var(--cy-cyan); }

.pm-emoji-menu {
    position: absolute;
    bottom: calc(100% + 0.5rem);
    left: 0;
    z-index: 60;
    width: 296px;
    max-height: 280px;
    overflow-y: auto;
    background-color: var(--cy-surface-2);
    border: 1px solid var(--cy-border);
    box-shadow: 0 10px 30px rgba(0,0,0,0.75), 0 0 18px rgba(0,245,255,0.12);
    padding: 0.6rem;
}
.pm-emoji-menu[hidden] { display: none; }
.pm-emoji-group + .pm-emoji-group { margin-top: 0.6rem; }
.pm-emoji-group-label {
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--cy-text-dim);
    margin-bottom: 0.35rem;
}
.pm-emoji-grid {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 0.2rem;
}
.pm-emoji-pick {
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    font-size: 1.25rem;
    line-height: 1;
    padding: 0.3rem 0;
    transition: border-color 0.2s ease, background-color 0.2s ease;
}
.pm-emoji-pick:hover {
    border-color: var(--cy-cyan);
    background-color: rgba(0, 245, 255, 0.1);
}

/* ---- Notices ---- */
.pm-notice {
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--cy-border);
    border-left: 3px solid var(--cy-error);
    background-color: rgba(255, 58, 58, 0.07);
    color: var(--cy-text-dim);
    font-size: 0.84rem;
}

/* ---- Live thread: new-message pill ----
   Anchors to the transcript, so it can sit over the bottom of the log without
   the log itself needing to change size when it appears. */
.pm-log-wrap { position: relative; }

.pm-new-pill {
    position: absolute;
    left: 50%;
    bottom: 0.75rem;
    transform: translateX(-50%);
    z-index: 5;
    padding: 0.35rem 0.9rem;
    border: 1px solid var(--cy-cyan);
    background-color: rgba(9, 9, 30, 0.95);
    color: var(--cy-cyan);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
    box-shadow: 0 0 14px rgba(0, 245, 255, 0.35);
    transition: background-color 0.25s ease, box-shadow 0.25s ease;
}
.pm-new-pill:hover {
    background-color: rgba(0, 245, 255, 0.15);
    box-shadow: 0 0 20px rgba(0, 245, 255, 0.55);
}
.pm-new-pill[hidden] { display: none; }

/* Newly-arrived messages fade in, so a live append reads as an arrival rather
   than as content that was always there. */
.pm-log .pm-msg { animation: pm-msg-in 0.28s ease-out; }

@keyframes pm-msg-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .pm-log .pm-msg { animation: none; }
}

/* The composer's inline error, when a send is refused mid-conversation. */
#pm-composer-error { margin-bottom: 0.6rem; }

/* A form wrapping a nav icon (logout) must not introduce a block-level box, or
   it breaks the row's alignment and the gap spacing around it. */
.nav-icon-form {
    display: inline-flex;
    align-items: center;
    margin: 0;
}

/* ---- Top-right nav cluster ----
   The row is now eleven icons — dashboard, profile, feed, forums, galleries,
   music, members, friends, messages, notifications, log out — plus the account
   menu. Every one added pushes the width at which the bar starts to overrun, so
   the ceiling here rose from 820px to 1100px when galleries and music went in,
   and to 1200px when friends did.

   Handled here rather than with a `md:` Tailwind variant because the prebuilt
   bundle ships only six md: utilities and none of the layout ones — and there
   is no local npm to rebuild it. Above 1200px nothing below applies. */
@media (min-width: 640px) and (max-width: 1200px) {
    .nav-icon-cluster { gap: 0.2rem; }

    .nav-icon-cluster .gothic-notifications-btn { padding: 0.28rem; }

    .nav-icon-cluster .nav-link {
        padding-left: 0.45rem;
        padding-right: 0.45rem;
    }

    /* A long display name is the single biggest consumer of what is left. */
    .nav-icon-cluster .nav-link > div:first-child {
        max-width: 8rem;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* ====================== PRESENCE ======================
   Online / offline indicator. Green reads as "here" without needing a legend;
   offline is a hollow, muted dot rather than red, because being away is not an
   error state and a wall of red dots on the members list would read as alarm. */

.presence-dot {
    display: inline-block;
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    flex-shrink: 0;
}

.presence-online {
    background-color: var(--cy-green);
    box-shadow: 0 0 6px rgba(57, 255, 20, 0.85);
    animation: presence-pulse 2.4s ease-in-out infinite;
}

.presence-offline {
    background-color: transparent;
    border: 1px solid var(--cy-text-dim);
    box-shadow: none;
}

/* Sits over the corner of an avatar. The ring matches the page background so
   the dot reads as floating above the image rather than painted onto it. */
.presence-dot-overlay {
    position: absolute;
    right: -1px;
    bottom: -1px;
    width: 0.7rem;
    height: 0.7rem;
    border: 2px solid var(--cy-surface-1);
    box-sizing: content-box;
}
.presence-dot-overlay.presence-offline {
    background-color: var(--cy-surface-3);
    border-color: var(--cy-surface-1);
}

/* Any avatar that hosts an overlay dot needs to be a positioning context.
   inline-flex rather than inline-block: it collapses the descender gap under
   the image without setting line-height: 0, which would inherit into initial
   letters in the no-avatar fallback and squash them. flex-shrink guards the
   cases where this wrapper is itself a flex child (the PM rows). */
.avatar-with-presence {
    position: relative;
    display: inline-flex;
    flex-shrink: 0;
}

.presence-inline {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.presence-label {
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--cy-text-dim);
}
.presence-inline .presence-online + .presence-label { color: var(--cy-green); }

@keyframes presence-pulse {
    0%, 100% { opacity: 1;   box-shadow: 0 0 6px  rgba(57, 255, 20, 0.85); }
    50%      { opacity: .72; box-shadow: 0 0 11px rgba(57, 255, 20, 1); }
}

@media (prefers-reduced-motion: reduce) {
    .presence-online { animation: none; }
}

/* ====================== FORUM ======================
   Named classes throughout rather than Tailwind utilities: the compiled bundle
   is prebuilt and cannot be regenerated on this server, so anything not already
   in it renders as nothing. These are safe to add and to change. */

.fm-crumbs {
    display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--cy-text-dim);
    margin-bottom: 1.25rem;
}

.fm-tag {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--cy-border);
    border-radius: 3px;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--cy-text-dim);
    vertical-align: middle;
}
.fm-tag-staff { color: var(--cy-magenta); border-color: rgba(255,45,120,0.45); }

/* ---- Index: categories and forums ---- */
.fm-category { margin-bottom: 1.75rem; }

.fm-category-head {
    padding: 0.65rem 1rem;
    background: linear-gradient(90deg, rgba(0,245,255,0.09), transparent);
    border-left: 3px solid var(--cy-cyan);
}
.fm-category-title {
    font-family: var(--cy-font-hud);
    font-size: 0.85rem; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--cy-cyan);
    margin: 0;
}
.fm-category-desc { font-size: 0.75rem; color: var(--cy-text-dim); margin: 0.2rem 0 0; }

.fm-forum {
    display: grid;
    grid-template-columns: 2.5rem 1fr auto auto;
    gap: 1rem;
    align-items: center;
    padding: 0.9rem 1rem;
    border: 1px solid var(--cy-border);
    border-top: none;
    background-color: var(--cy-surface-1);
    text-decoration: none;
    transition: background-color 0.25s ease, border-color 0.25s ease;
}
.fm-forum:hover { background-color: var(--cy-surface-2); border-color: rgba(0,245,255,0.35); }
.fm-forum-icon { color: var(--cy-cyan); font-size: 1.2rem; text-align: center; opacity: 0.75; }
.fm-forum-body { min-width: 0; display: flex; flex-direction: column; }
.fm-forum-title {
    font-family: var(--cy-font-hud);
    font-size: 0.9rem; letter-spacing: 0.04em;
    color: var(--cy-text);
}
.fm-forum-desc { font-size: 0.78rem; color: var(--cy-text-dim); margin-top: 0.2rem; }
.fm-forum-stats, .fm-thread-stats {
    display: flex; flex-direction: column; gap: 0.1rem;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem; letter-spacing: 0.05em;
    color: var(--cy-text-dim);
    text-align: right;
    white-space: nowrap;
}
.fm-forum-last, .fm-thread-last {
    display: flex; flex-direction: column; gap: 0.1rem;
    min-width: 11rem; text-align: right;
}
.fm-forum-last-title { font-size: 0.76rem; color: var(--cy-text); }
.fm-forum-last-meta, .fm-thread-last-meta {
    font-size: 0.75rem; color: var(--cy-text-dim); font-family: var(--cy-font-hud);
}

/* ---- Thread listing ---- */
.fm-thread {
    display: grid;
    grid-template-columns: 2rem 1fr auto auto;
    gap: 1rem;
    align-items: center;
    padding: 0.8rem 1rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
    text-decoration: none;
    transition: background-color 0.25s ease, border-color 0.25s ease;
}
.fm-thread + .fm-thread { border-top: none; }
.fm-thread:hover { background-color: var(--cy-surface-2); border-color: rgba(0,245,255,0.35); }

/* Unread threads carry the cyan edge and a brighter title — the listing has to
   be scannable at a glance, which a subtle weight change alone is not. */
.fm-thread-unread { border-left: 3px solid var(--cy-cyan); background-color: var(--cy-surface-2); }
.fm-thread-unread .fm-thread-title { color: var(--cy-cyan); font-weight: 600; }

.fm-thread-icon { color: var(--cy-text-dim); text-align: center; }
.fm-thread-unread .fm-thread-icon { color: var(--cy-cyan); }
.fm-thread-body { min-width: 0; display: flex; flex-direction: column; }
.fm-thread-title { font-size: 0.92rem; color: var(--cy-text); }
.fm-thread-meta { font-size: 0.75rem; color: var(--cy-text-dim); font-family: var(--cy-font-hud); margin-top: 0.15rem; }
.fm-thread-heading {
    font-family: var(--cy-font-hud);
    font-size: 1.05rem; letter-spacing: 0.04em;
    color: var(--cy-cyan); margin: 0;
}

/* ---- Posts ---- */
.fm-post {
    display: grid;
    grid-template-columns: 10rem 1fr;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
    margin-bottom: 0.75rem;
}
.fm-post-highlight {
    border-color: var(--cy-cyan);
    box-shadow: 0 0 18px rgba(0,245,255,0.35);
    transition: box-shadow 0.4s ease, border-color 0.4s ease;
}

.fm-post-author {
    display: flex; flex-direction: column; align-items: center; gap: 0.4rem;
    padding: 1rem 0.75rem;
    border-right: 1px solid var(--cy-border);
    background-color: rgba(0,0,0,0.18);
    text-align: center;
}
.fm-post-avatar { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; border: 1px solid var(--cy-border); }
.fm-post-avatar-fallback {
    width: 56px; height: 56px; border-radius: 50%;
    border: 1px solid var(--cy-border); background-color: var(--cy-bg);
    display: flex; align-items: center; justify-content: center;
    font-family: var(--cy-font-hud); font-size: 1.3rem; color: var(--cy-text-dim);
}
.fm-post-name { font-family: var(--cy-font-hud); font-size: 0.8rem; letter-spacing: 0.04em; }
.fm-post-joined { font-size: 0.75rem; color: var(--cy-text-dim); font-family: var(--cy-font-hud); }

.fm-post-main { padding: 0.9rem 1.1rem; min-width: 0; }
.fm-post-head {
    display: flex; justify-content: space-between; align-items: center;
    border-bottom: 1px solid var(--cy-border);
    padding-bottom: 0.5rem; margin-bottom: 0.75rem;
}
.fm-post-permalink { font-family: var(--cy-font-hud); font-size: 0.75rem; color: var(--cy-text-dim); text-decoration: none; }
.fm-post-permalink:hover { color: var(--cy-cyan); }
.fm-post-date { font-family: var(--cy-font-hud); font-size: 0.75rem; color: var(--cy-text-dim); }

.fm-post-body {
    color: var(--cy-text); font-size: 0.94rem; line-height: 1.7;
    /* Long unbroken strings must wrap, not widen the column. */
    overflow-wrap: anywhere; word-break: break-word;
}
.fm-post-edited { font-size: 0.75rem; color: var(--cy-text-dim); font-style: italic; margin-top: 0.6rem; }

.fm-post-actions {
    display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
    margin-top: 0.85rem; padding-top: 0.6rem;
    border-top: 1px solid var(--cy-border);
}
.fm-action {
    display: inline-flex; align-items: center; gap: 0.35rem;
    background: transparent; border: 1px solid transparent;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud); font-size: 0.75rem; letter-spacing: 0.05em;
    padding: 0.25rem 0.5rem; cursor: pointer;
    transition: color 0.2s ease, border-color 0.2s ease;
}
.fm-action:hover { color: var(--cy-cyan); border-color: rgba(0,245,255,0.35); }
.fm-action-danger:hover { color: var(--cy-error); border-color: rgba(255,58,58,0.4); }
.fm-like.is-liked { color: var(--cy-error); }
.fm-like.is-liked i { text-shadow: 0 0 8px rgba(255,58,58,0.6); }

/* ---- BBCode output ---- */
.bb-quote {
    margin: 0.7rem 0; padding: 0.6rem 0.9rem;
    border-left: 3px solid var(--cy-cyan);
    background-color: rgba(0,245,255,0.05);
}
.bb-quote-who {
    display: block; font-family: var(--cy-font-hud); font-size: 0.75rem;
    letter-spacing: 0.06em; text-transform: uppercase; color: var(--cy-cyan);
    margin-bottom: 0.3rem; font-style: normal;
}
.bb-quote-body { color: var(--cy-text-dim); font-size: 0.9rem; }
.bb-code {
    margin: 0.7rem 0; padding: 0.7rem 0.9rem;
    background-color: var(--cy-bg); border: 1px solid var(--cy-border);
    font-family: 'Share Tech Mono', monospace; font-size: 0.84rem;
    color: var(--cy-green);
    overflow-x: auto; white-space: pre;
}
.bb-u { text-decoration: underline; }
.bb-list { margin: 0.6rem 0 0.6rem 1.3rem; list-style: square; }
.bb-list li { margin: 0.2rem 0; }
.bb-img { max-width: 100%; height: auto; border: 1px solid var(--cy-border); margin: 0.5rem 0; }

/* Spoiler: blacked out until clicked. The JS-free fallback is :focus, so a
   keyboard user can still reveal it. */
.bb-spoiler {
    background-color: var(--cy-surface-3);
    color: transparent;
    border-radius: 3px; padding: 0 0.25rem; cursor: pointer;
    transition: color 0.2s ease, background-color 0.2s ease;
}
.bb-spoiler:hover, .bb-spoiler:focus, .bb-spoiler.revealed {
    background-color: rgba(0,245,255,0.08);
    color: var(--cy-text);
}

/* ---- Composer ---- */
.fm-composer { border: 1px solid var(--cy-border); background-color: var(--cy-surface-2); }
.fm-toolbar {
    display: flex; align-items: center; gap: 0.15rem; flex-wrap: wrap;
    padding: 0.4rem 0.5rem;
    border-bottom: 1px solid var(--cy-border);
    background-color: var(--cy-surface-3);
    position: relative;
}
.fm-toolbar-sep { width: 1px; height: 1.1rem; background-color: var(--cy-border); margin: 0 0.35rem; }
.fm-tool {
    background: transparent; border: 1px solid transparent;
    color: var(--cy-text-dim); cursor: pointer;
    padding: 0.3rem 0.5rem; font-size: 0.8rem; line-height: 1;
    transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}
.fm-tool:hover { color: var(--cy-cyan); border-color: rgba(0,245,255,0.35); background-color: rgba(0,245,255,0.07); }

.fm-emoji-menu {
    position: absolute; top: calc(100% + 0.35rem); left: 0.5rem; z-index: 60;
    width: 296px; max-height: 280px; overflow-y: auto;
    background-color: var(--cy-surface-2); border: 1px solid var(--cy-border);
    box-shadow: 0 10px 30px rgba(0,0,0,0.75), 0 0 18px rgba(0,245,255,0.12);
    padding: 0.6rem;
}
.fm-emoji-menu[hidden] { display: none; }

.fm-textarea {
    width: 100%; display: block; resize: vertical;
    background-color: var(--cy-surface-2); border: none;
    color: var(--cy-text); padding: 0.75rem 0.9rem;
    font-family: var(--cy-font-body); font-size: 0.94rem; line-height: 1.6;
}
.fm-textarea:focus { outline: none; box-shadow: inset 0 0 0 1px var(--cy-cyan); }

.fm-composer-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    flex-wrap: wrap;
    padding: 0.4rem 0.7rem;
    border-top: 1px solid var(--cy-border);
    background-color: var(--cy-surface-3);
}
.fm-hint { font-size: 0.75rem; color: var(--cy-text-dim); }
.fm-hint code { color: var(--cy-cyan); font-family: 'Share Tech Mono', monospace; }
.fm-charcount { font-family: var(--cy-font-hud); font-size: 0.75rem; color: var(--cy-text-dim); white-space: nowrap; }
.fm-charcount-over { color: var(--cy-error); }

/* ---- Forms ---- */
.fm-label {
    display: block;
    font-family: var(--cy-font-hud); font-size: 0.75rem;
    letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--cy-text-dim); margin-bottom: 0.4rem;
}
.fm-input {
    width: 100%;
    background-color: var(--cy-surface-2);
    border: 1px solid var(--cy-border);
    color: var(--cy-text);
    padding: 0.55rem 0.75rem;
    font-family: var(--cy-font-body); font-size: 0.92rem;
}
.fm-input:focus { outline: none; border-color: var(--cy-cyan); box-shadow: 0 0 10px rgba(0,245,255,0.25); }
.fm-check { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; color: var(--cy-text-dim); cursor: pointer; }

/* ---- Narrow screens ---- */
@media (max-width: 800px) {
    /* The stats and last-post columns are the first things to go: the title and
       who posted last are what people actually scan for. */
    .fm-forum, .fm-thread { grid-template-columns: 2rem 1fr; }
    .fm-forum-stats, .fm-thread-stats, .fm-forum-last, .fm-thread-last { display: none; }

    /* Author details move above the post rather than squeezing a 10rem rail. */
    .fm-post { grid-template-columns: 1fr; }
    .fm-post-author {
        flex-direction: row; justify-content: flex-start;
        border-right: none; border-bottom: 1px solid var(--cy-border);
        padding: 0.6rem 0.9rem;
    }
    .fm-post-avatar, .fm-post-avatar-fallback { width: 34px; height: 34px; font-size: 0.9rem; }
    .fm-post-joined { display: none; }
}

/* ---- Forum: sidebar layout, prefixes, attachments, search ---- */

.fm-layout { display: grid; grid-template-columns: 1fr 17rem; gap: 1.5rem; align-items: start; }
.fm-layout-main { min-width: 0; }

.fm-sidebar { display: flex; flex-direction: column; gap: 1rem; }
.fm-widget { border: 1px solid var(--cy-border); background-color: var(--cy-surface-1); padding: 0.9rem 1rem; }
.fm-widget-title {
    display: flex; align-items: center; gap: 0.45rem;
    font-family: var(--cy-font-hud); font-size: 0.75rem;
    letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--cy-cyan); margin: 0 0 0.7rem;
}
.fm-widget-empty, .fm-widget-foot {
    font-size: 0.75rem; color: var(--cy-text-dim); margin: 0.6rem 0 0;
}

.fm-stat-list { margin: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.fm-stat-list > div { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
.fm-stat-list dt { font-size: 0.75rem; color: var(--cy-text-dim); }
.fm-stat-list dd {
    margin: 0; font-family: var(--cy-font-hud); font-size: 0.82rem; color: var(--cy-text);
}
.fm-stat-online { color: var(--cy-green) !important; }

.fm-online-list { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.fm-online-member {
    display: inline-flex; align-items: center; gap: 0.35rem;
    padding: 0.2rem 0.45rem 0.2rem 0.2rem;
    border: 1px solid var(--cy-border); border-radius: 999px;
    text-decoration: none; transition: border-color 0.2s ease;
}
.fm-online-member:hover { border-color: var(--cy-cyan); }
.fm-online-member img, .fm-online-fallback {
    width: 20px; height: 20px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
}
.fm-online-fallback {
    background-color: var(--cy-bg); color: var(--cy-text-dim);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 0.75rem; font-family: var(--cy-font-hud);
}
.fm-online-name { font-size: 0.75rem; color: var(--cy-text-dim); }

.fm-widget-search { display: flex; gap: 0.35rem; }
.fm-widget-search .fm-input { font-size: 0.78rem; padding: 0.4rem 0.55rem; }

/* ---- Prefixes ---- */
.fm-prefix {
    display: inline-block; margin-right: 0.4rem;
    padding: 0.05rem 0.4rem;
    border: 1px solid currentColor; border-radius: 3px;
    font-family: var(--cy-font-hud); font-size: 0.75rem;
    letter-spacing: 0.08em; text-transform: uppercase;
    vertical-align: middle;
}

.fm-title-row { display: grid; grid-template-columns: 12rem 1fr; gap: 0.75rem; }
.fm-prefix-field, .fm-title-field { min-width: 0; }

/* ---- Postbit stats ---- */
.fm-post-stats {
    margin: 0.35rem 0 0; width: 100%;
    display: flex; flex-direction: column; gap: 0.15rem;
}
.fm-post-stats > div { display: flex; justify-content: space-between; gap: 0.5rem; }
.fm-post-stats dt { font-size: 0.75rem; color: var(--cy-text-dim); text-transform: uppercase; letter-spacing: 0.06em; }
.fm-post-stats dd { margin: 0; font-family: var(--cy-font-hud); font-size: 0.75rem; color: var(--cy-text); }

/* ---- Attachments ---- */
.bb-attach { display: inline-block; margin: 0.4rem 0.4rem 0.4rem 0; line-height: 0; }
.bb-attach img {
    max-width: 320px; max-height: 320px; height: auto;
    border: 1px solid var(--cy-border);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.bb-attach:hover img { border-color: var(--cy-cyan); box-shadow: 0 0 14px rgba(0,245,255,0.3); }

.fm-attachments { margin-top: 0.9rem; padding-top: 0.7rem; border-top: 1px dashed var(--cy-border); }
.fm-attachments-label {
    font-family: var(--cy-font-hud); font-size: 0.75rem;
    letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--cy-text-dim); margin: 0 0 0.4rem;
}
.fm-attachments-grid { display: flex; flex-wrap: wrap; gap: 0.4rem; }

/* Composer tray */
.fm-attach-tray { padding: 0.55rem 0.7rem; border-top: 1px solid var(--cy-border); background-color: var(--cy-surface-3); }
.fm-attach-list { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.fm-attach-item {
    position: relative; width: 108px;
    border: 1px solid var(--cy-border); background-color: var(--cy-surface-2);
    padding: 0.3rem; display: flex; flex-direction: column; gap: 0.2rem;
}
.fm-attach-item.is-uploading { align-items: center; justify-content: center; min-height: 96px; }
.fm-attach-spinner { color: var(--cy-cyan); }
.fm-attach-item img { width: 100%; height: 64px; object-fit: cover; }
.fm-attach-name {
    font-size: 0.75rem; color: var(--cy-text-dim);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fm-attach-size { font-size: 0.75rem; color: var(--cy-text-dim); font-family: var(--cy-font-hud); }
.fm-attach-buttons { display: flex; gap: 0.2rem; align-items: center; }
.fm-attach-insert, .fm-attach-remove {
    background: transparent; border: 1px solid var(--cy-border);
    color: var(--cy-text-dim); cursor: pointer;
    font-size: 0.75rem; font-family: var(--cy-font-hud);
    padding: 0.1rem 0.3rem; text-transform: uppercase; letter-spacing: 0.05em;
}
.fm-attach-insert { flex: 1; }
.fm-attach-insert:hover { color: var(--cy-cyan); border-color: var(--cy-cyan); }
.fm-attach-remove:hover { color: var(--cy-error); border-color: var(--cy-error); }
.fm-attach-hint { font-size: 0.75rem; color: var(--cy-text-dim); margin: 0.45rem 0 0; }
.fm-attach-error { font-size: 0.75rem; color: var(--cy-error); margin: 0.45rem 0 0; }

/* ---- Lightbox ---- */
/* Column, so the Close button can sit UNDER the picture rather than over it.
   The whole stack stays centred as one block. */
.fm-lightbox {
    position: fixed; inset: 0; z-index: 200;
    background-color: rgba(2, 2, 8, 0.93);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 1rem;
    padding: 2rem; cursor: zoom-out;
}
/*
   max-height budgets for the rest of the column explicitly rather than using
   100%: as a flex item, a percentage height resolves against the container
   including the button, so a tall image would push Close off the bottom of the
   screen — the one place it must never be. 9rem covers the 2rem padding top and
   bottom, the 1rem gap and the button itself, with room to spare.
*/
.fm-lightbox img {
    max-width: 100%; max-height: calc(100vh - 9rem);
    border: 1px solid var(--cy-border);
    box-shadow: 0 0 40px rgba(0,245,255,0.2);
    cursor: default;
}
.fm-lightbox-close {
    position: absolute; top: 1rem; right: 1.4rem;
    background: transparent; border: none;
    color: var(--cy-text-dim); font-size: 2.2rem; line-height: 1; cursor: pointer;
}
.fm-lightbox-close:hover { color: var(--cy-cyan); }

/* The overlay sets cursor: zoom-out for the backdrop; the button is a button. */
.fm-lightbox-dismiss { cursor: pointer; flex-shrink: 0; }

/* ---- Search ---- */
.fm-search-row { display: grid; grid-template-columns: 1fr 14rem auto; gap: 0.5rem; }
.fm-search-heading {
    font-family: var(--cy-font-hud); font-size: 0.75rem;
    letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--cy-cyan); margin: 0 0 0.7rem;
}
.fm-search-hit {
    display: flex; flex-direction: column; gap: 0.3rem;
    padding: 0.85rem 1rem; margin-bottom: 0.5rem;
    border: 1px solid var(--cy-border); background-color: var(--cy-surface-1);
    text-decoration: none; transition: border-color 0.2s ease, background-color 0.2s ease;
}
.fm-search-hit:hover { border-color: var(--cy-cyan); background-color: var(--cy-surface-2); }
.fm-search-hit-head { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; }
.fm-search-hit-title { font-size: 0.9rem; color: var(--cy-text); }
.fm-search-hit-body { font-size: 0.82rem; color: var(--cy-text-dim); line-height: 1.55; }
.fm-search-hit-meta { font-family: var(--cy-font-hud); font-size: 0.75rem; color: var(--cy-text-dim); white-space: nowrap; }

@media (max-width: 900px) {
    /* Sidebar drops below the board rather than squeezing both columns. */
    .fm-layout { grid-template-columns: 1fr; }
    .fm-title-row, .fm-search-row { grid-template-columns: 1fr; }
}

/* ====================== FRIENDS ======================
   Named classes throughout, not Tailwind utilities: the compiled bundle in
   public/build is prebuilt and cannot be regenerated on this server, so any
   utility not already in it renders as nothing. These are safe to add and to
   change. Same rule as the FORUM and MESSAGES blocks above. */

/* ---- Tabs (friends screens, and the members grid's Everyone/Friends toggle) ---- */
.friend-tabs {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.friend-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-decoration: none;
    transition: border-color 0.25s ease, color 0.25s ease, background-color 0.25s ease;
}
.friend-tab:hover {
    border-color: var(--cy-cyan);
    color: var(--cy-text);
    background-color: var(--cy-surface-2);
}
.friend-tab-active {
    border-color: var(--cy-cyan);
    color: var(--cy-cyan);
    background-color: var(--cy-surface-2);
    box-shadow: inset 0 -2px 0 var(--cy-cyan);
}
/* Pushed to the far end of the row: it leaves this screen rather than
   switching between views of it, and should not read as a fourth tab. */
.friend-tab-trailing { margin-left: auto; }

.friend-tab-count {
    display: inline-block;
    min-width: 1.2rem;
    padding: 0.05rem 0.35rem;
    border-radius: 999px;
    background: linear-gradient(135deg, #003344 0%, #006688 100%);
    color: #ffffff;
    font-size: 0.75rem;
    text-align: center;
}

.friend-search {
    display: flex;
    gap: 0.75rem;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
}
.friend-search-input {
    flex: 1;
    min-width: 12rem;
    padding: 0.5rem 1rem;
    border: 1px solid var(--cy-border);
    border-radius: 0.375rem;
    background-color: var(--cy-bg);
    color: var(--cy-text);
    font-size: 0.875rem;
}
.friend-search-input:focus {
    outline: none;
    border-color: var(--cy-cyan);
    box-shadow: 0 0 0 1px var(--cy-cyan-dim);
}

.friend-hint {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
    color: var(--cy-text-dim);
    font-size: 0.78rem;
}
.friend-hint i { color: var(--cy-cyan); opacity: 0.7; }

/* ---- Flash ---- */
.friend-flash {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.7rem 1rem;
    margin-bottom: 1.25rem;
    border-left: 3px solid var(--cy-cyan);
    background-color: var(--cy-surface-2);
    color: var(--cy-text);
    font-size: 0.85rem;
}
.friend-flash-ok    { border-left-color: var(--cy-green);   }
.friend-flash-ok i  { color: var(--cy-green);   }
.friend-flash-error { border-left-color: var(--cy-magenta); }
.friend-flash-error i { color: var(--cy-magenta); }

/* ---- Member grid ---- */
.friend-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1rem;
}

.friend-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 1rem 0.75rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
    text-align: center;
    transition: border-color 0.25s ease, transform 0.25s ease;
}
.friend-card:hover { border-color: var(--cy-cyan); transform: translateY(-2px); }

.friend-card-identity {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    width: 100%;
    text-decoration: none;
    min-width: 0;
}

.friend-card-avatar,
.friend-card-avatar-fallback {
    width: 64px; height: 64px;
    border-radius: 50%;
    border: 1px solid var(--cy-border);
    object-fit: cover;
}
.friend-card-avatar-fallback {
    display: flex; align-items: center; justify-content: center;
    background-color: var(--cy-bg);
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 1.5rem;
}
.friend-card:hover .friend-card-avatar,
.friend-card:hover .friend-card-avatar-fallback { border-color: var(--cy-cyan); }

.friend-card-name {
    font-family: var(--cy-font-hud);
    font-size: 0.82rem;
    letter-spacing: 0.04em;
    color: var(--cy-text);
    /* One line: a long display name must not stretch the card out of the grid. */
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.friend-card:hover .friend-card-name { color: var(--cy-cyan); }

.friend-card-meta {
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--cy-text-dim);
}

.friend-card-actions,
.member-card-actions {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    width: 100%;
    margin-top: 0.5rem;
}

/* ---- The friend button, in all five of its states ---- */
.friend-action { display: flex; gap: 0.35rem; flex-wrap: wrap; }
/* On a card the buttons stack and fill the width; in a profile header they sit
   side by side at their natural size. */
.friend-action-compact { flex-direction: column; width: 100%; }

.friend-form { display: contents; }

.friend-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-2);
    color: var(--cy-text);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease, opacity 0.2s ease;
}
.friend-btn:active { transform: translateY(1px); }

.friend-btn-compact {
    width: 100%;
    padding: 0.35rem 0.5rem;
    font-size: 0.75rem;
    gap: 0.3rem;
}

/* Card layout is driven by the CONTAINER as well as by the compact flag.
   The flag travels in the markup, and an AJAX re-render that dropped it once
   left a button sitting half-width and left-aligned until the next reload.
   Anchoring the same rules to the card containers means the layout cannot come
   apart that way again, whatever the swapped-in classes happen to say. */
.member-card-actions .friend-action,
.friend-card-actions .friend-action {
    flex-direction: column;
    width: 100%;
}
.member-card-actions .friend-btn,
.friend-card-actions .friend-btn {
    width: 100%;
    padding: 0.35rem 0.5rem;
    font-size: 0.75rem;
    gap: 0.3rem;
}

.friend-btn-add       { border-color: rgba(0, 245, 255, 0.4); color: var(--cy-cyan); }
.friend-btn-add:hover { background-color: var(--cy-cyan-dim); border-color: var(--cy-cyan); }

.friend-btn-accept       { border-color: rgba(57, 255, 20, 0.45); color: var(--cy-green); }
.friend-btn-accept:hover { background-color: var(--cy-green-dim); border-color: var(--cy-green); }

.friend-btn-decline       { color: var(--cy-text-dim); }
.friend-btn-decline:hover { border-color: var(--cy-magenta); color: var(--cy-magenta); }

/* Requested — a sent request still waiting on an answer. Structurally identical
   to Add Friend above; only the hue differs, so the three friend states read as
   one control in three colours rather than three different controls. */
.friend-btn-pending       { border-color: rgba(255, 215, 0, 0.45); color: var(--cy-yellow-soft); }
.friend-btn-pending:hover { background-color: var(--cy-yellow-dim); border-color: var(--cy-yellow); }

.friend-btn-message       { color: var(--cy-text-dim); }
.friend-btn-message:hover { border-color: var(--cy-cyan); color: var(--cy-cyan); }

/* Friends: reads as state at rest, as an action on hover. The two labels are
   both in the DOM and swapped by visibility rather than by JS, so the button
   works — and reads correctly — with scripting off. */
.friend-btn-friends       { border-color: rgba(57, 255, 20, 0.35); color: var(--cy-green); }
.friend-btn-friends:hover,
.friend-btn-friends:focus-visible {
    border-color: var(--cy-magenta);
    color: var(--cy-magenta);
    background-color: var(--cy-magenta-dim);
}
.friend-icon-hover,
.friend-label-hover   { display: none; }
.friend-btn-friends:hover .friend-icon-resting,
.friend-btn-friends:focus-visible .friend-icon-resting,
.friend-btn-friends:hover .friend-label-resting,
.friend-btn-friends:focus-visible .friend-label-resting { display: none; }
.friend-btn-friends:hover .friend-icon-hover,
.friend-btn-friends:focus-visible .friend-icon-hover,
.friend-btn-friends:hover .friend-label-hover,
.friend-btn-friends:focus-visible .friend-label-hover { display: inline; }

/* Mid-request. Pointer events off so a double click cannot fire the action
   twice while the first is still in flight. */
.friend-action-busy .friend-btn {
    opacity: 0.55;
    pointer-events: none;
}

/* ---- Faces: the profile box and mutual-friends line ---- */
.friend-face-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}
.friend-face-link { display: inline-flex; text-decoration: none; }

.friend-face {
    width: 32px; height: 32px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--cy-border);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background-color: var(--cy-bg);
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.8rem;
}
.friend-face-lg { width: 44px; height: 44px; font-size: 1rem; }
.friend-face-fallback { text-transform: uppercase; }
.friend-face-link:hover .friend-face { border-color: var(--cy-cyan); }

.friend-mutual-line {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    padding-bottom: 0.75rem;
    margin-bottom: 0.75rem;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
/* Overlapped faces, so a row of six reads as "a group you both know" rather
   than as six separate links competing with the list below it. */
.friend-mutual-faces { display: inline-flex; }
.friend-mutual-faces a + a { margin-left: -0.5rem; }
.friend-mutual-faces .friend-face { border-color: var(--cy-surface-1); border-width: 2px; }

.friend-mutual-label {
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--cy-text-dim);
}

/* ---- Dashboard panel ---- */
.friend-panel-empty {
    color: var(--cy-text-dim);
    font-size: 0.82rem;
}
.friend-panel-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0;
    text-decoration: none;
}
.friend-panel-row + .friend-panel-row { border-top: 1px solid rgba(255, 255, 255, 0.05); }
.friend-panel-row:hover .friend-panel-name { color: var(--cy-cyan); }
.friend-panel-name {
    flex: 1;
    min-width: 0;
    color: var(--cy-text);
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 640px) {
    /* Two columns rather than one: the cards are small, and a single column
       turns a short friends list into a long scroll. */
    .friend-grid { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); }
    .friend-tab-trailing { margin-left: 0; }
}

/* ---- Friend action toasts ----
   Fixed to the corner so an action taken halfway down the members grid is
   confirmed where the eye already is, without moving anything on the page. */
.friend-toasts {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: min(22rem, calc(100vw - 2rem));
    /* The container spans a corner of the viewport; only the toasts themselves
       should intercept the pointer. */
    pointer-events: none;
}
.friend-toasts .friend-flash {
    margin-bottom: 0;
    pointer-events: auto;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.6);
    animation: friend-toast-in 0.22s ease-out;
}
.friend-flash-leaving { opacity: 0; transition: opacity 0.3s ease; }

@keyframes friend-toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .friend-toasts .friend-flash { animation: none; }
}

/* ====================== FEED SCOPE ======================
   Everyone / Friends switch above the community feed, and the friends
   empty state. Hand-written like everything else here: the compiled
   Tailwind bundle is stale and cannot be rebuilt on this server. */

.feed-scope {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
    padding-bottom: 0.85rem;
    border-bottom: 1px solid var(--cy-border);
}

/* Segmented control. One bordered group rather than two loose buttons, so it
   reads as "pick one of these" instead of two unrelated actions. */
.feed-scope-switch {
    display: inline-flex;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
    overflow: hidden;
}

.feed-scope-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.95rem;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-decoration: none;
    transition: color 0.2s ease, background-color 0.2s ease;
}
.feed-scope-btn + .feed-scope-btn { border-left: 1px solid var(--cy-border); }
.feed-scope-btn:hover { color: var(--cy-text); background-color: var(--cy-surface-2); }

.feed-scope-btn.is-active {
    color: var(--cy-cyan);
    background-color: var(--cy-surface-2);
    box-shadow: inset 0 -2px 0 var(--cy-cyan);
}

/* Tells the reader what a refresh will do. The toggle is deliberately
   transient, and without this that reads as the setting failing to save. */
.feed-scope-note {
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--cy-text-dim);
}

.feed-scope-default-form { margin: 0; margin-left: auto; }

.feed-scope-default-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.8rem;
    border: 1px dashed var(--cy-border);
    background: transparent;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color 0.2s ease, color 0.2s ease;
}
.feed-scope-default-btn:hover {
    border-color: var(--cy-cyan);
    border-style: solid;
    color: var(--cy-cyan);
}
/* [hidden] needs help against the display above. */
.feed-scope-default-btn[hidden] { display: none; }


.gothic-feed-empty-hint {
    margin-top: 0.5rem;
    font-size: 0.82rem;
    color: var(--cy-text-dim);
}

@media (max-width: 640px) {
    /* The button drops onto its own line rather than squeezing the switch. */
    .feed-scope-default-form { margin-left: 0; }
    .feed-scope { gap: 0.5rem; }
}

/* Form action rows: primary action first, Cancel to its right, both pushed to
   the right edge. Declared once so every form that grows a Cancel lays it out
   the same way — the reply box, the new-thread form and the inline editors were
   each doing their own thing with text-right and ad-hoc gaps. */
.fm-form-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    flex-wrap: wrap;
}

/* ====================== READABILITY FLOOR ======================
   A last, deliberately small set of overrides for text that was still hard to
   read after the palette lift. Everything here is a colour or spacing change —
   nothing moves, nothing resizes — so it is safe to adjust or drop wholesale.

   Prefixed with `body` throughout: the compiled Tailwind bundle loads after
   this file and would otherwise win at equal specificity. */

/* Tailwind greys used in a few older views. gray-600 lands at 2.6:1 on a card
   and gray-500 at 4.06:1 — both below AA for body text, and both used for
   things people actually have to read (timestamps, helper lines). Mapped onto
   the theme's own secondary colour rather than picked afresh, so they track any
   future change to --cy-text-dim instead of drifting away from it. */
body .text-gray-600,
body .text-gray-500 { color: var(--cy-text-dim); }

/* Uppercase Orbitron is the house label style, but at label sizes the tracking
   that makes a heading look deliberate makes a sentence hard to scan. Capped
   only where the text is small; headings keep their spacing. */
body .gothic-title { letter-spacing: 0.08em; }

/* Body copy should not glow. A text-shadow on a heading reads as neon; the same
   shadow under a paragraph is a blur that costs legibility for nothing. */
body p,
body li,
body td,
body dd,
body .gothic-activity-action,
body .gothic-activity-post-content,
body .fm-post-body,
body .pm-bubble { text-shadow: none; }

/* Paragraph rhythm. `line-height` on body is inherited, but several components
   set their own tighter value for single-line labels and then get reused for
   sentences. */
body p                        { line-height: 1.6; }
body .fm-post-body            { line-height: 1.7; }
body .gothic-activity-content { line-height: 1.6; }

/* Placeholders sat at browser-default grey on a near-black field, which is the
   one piece of text in a form nobody can read. */
body ::placeholder { color: var(--cy-text-dim); opacity: 1; }

/* Disabled controls dim to near-invisible by UA default on dark backgrounds. */
body :disabled,
body [disabled] { opacity: 0.65; }

/* ====================== FORUM SUB-THREADS ======================
   A reply can hang off a specific post, up to ForumPost::MAX_DEPTH levels.
   Indent communicates the nesting; a left rail carries it when the indent
   itself has to give way on a narrow screen. */

.fm-post-group { margin-bottom: 0.75rem; }

.fm-subthread {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0.4rem 0 0 1.5rem;
    /* The rail is what actually says "these belong to the post above" once the
       indent is reduced on small screens. */
    border-left: 2px solid var(--cy-border);
    padding-left: 0.75rem;
}
.fm-subthread[hidden] { display: none; }

.fm-subpost {
    /* Overrides the two-column grid .fm-post uses — a reply has no author
       sidebar, so it stacks. */
    display: block;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--cy-border);
    background-color: var(--cy-surface-1);
}
.fm-subpost[hidden] { display: none; }

/* Depth is expressed as margin rather than nesting, because the replies are
   flat siblings in the DOM — see forum/partials/sub-post.blade.php. */
.fm-subpost[data-depth="2"] { margin-left: 1.25rem; }
.fm-subpost[data-depth="3"] { margin-left: 2.5rem; }

.fm-subpost-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 0.4rem;
}

.fm-subpost-avatar {
    width: 24px; height: 24px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--cy-border);
    flex-shrink: 0;
}
.fm-subpost-avatar-fallback {
    display: inline-flex; align-items: center; justify-content: center;
    background-color: var(--cy-bg);
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    text-transform: uppercase;
}

.fm-subpost-name {
    font-family: var(--cy-font-hud);
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    color: var(--cy-text);
}

.fm-subpost-date {
    margin-left: auto;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    color: var(--cy-text-dim);
    text-decoration: none;
    white-space: nowrap;
}
.fm-subpost-date:hover { color: var(--cy-cyan); }

.fm-subpost-body { font-size: 0.9rem; }

.fm-subpost-actions {
    margin-top: 0.4rem;
    padding-top: 0.4rem;
    border-top: 1px solid rgba(255, 255, 255, 0.05);
}

/* ---- "Show all N replies" ---- */
.fm-subthread-more {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.7rem;
    border: 1px dashed var(--cy-border);
    background: transparent;
    color: var(--cy-text-dim);
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color 0.2s ease, color 0.2s ease;
}
.fm-subthread-more:hover { border-color: var(--cy-cyan); color: var(--cy-cyan); border-style: solid; }
.fm-subthread-more[hidden] { display: none; }

/* ---- The shared inline composer, wherever it currently lives ---- */
.fm-inline-reply {
    padding: 0.85rem;
    border: 1px solid var(--cy-cyan);
    background-color: var(--cy-surface-2);
    box-shadow: 0 0 14px rgba(0, 245, 255, 0.12);
}
.fm-inline-reply[hidden] { display: none; }

.fm-inline-reply-target {
    margin: 0 0 0.6rem;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--cy-text-dim);
}
.fm-inline-reply-target i { color: var(--cy-cyan); margin-right: 0.35rem; }
.fm-inline-reply-target strong { color: var(--cy-cyan); }

@media (max-width: 640px) {
    /* Indent gives way before legibility does. The rail keeps saying what the
       margin no longer can — three levels of indent on a phone leaves a column
       of text a few words wide. */
    .fm-subthread { margin-left: 0.5rem; padding-left: 0.5rem; }
    .fm-subpost[data-depth="2"] { margin-left: 0.5rem; }
    .fm-subpost[data-depth="3"] { margin-left: 1rem; }
    .fm-subpost-date { margin-left: 0; width: 100%; }
}

/* ====================== COMMENT REPLY CONTROL ======================
   One look for the Reply control on every comment surface — image galleries,
   music, the story reader. It sits in the header row immediately after the
   author and timestamp, NOT floated to the right-hand edge of the card.

   Right-aligning it put the control as far as physically possible from the
   thing it acts on: on a wide comment you were pairing a name on the left with
   a button on the right and hoping they belonged together. Next to the name it
   needs no hoping. Destructive actions (Delete) still sit apart on the right,
   which is the separation actually worth having. */

.comment-reply-link {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
    font-family: var(--cy-font-hud);
    font-size: 0.75rem;
    letter-spacing: 0.03em;
    color: var(--cy-cyan);
    opacity: 0.85;
    transition: opacity 0.2s ease, color 0.2s ease;
    /* Baseline-aligned rows are the norm in these headers; without this the
       icon drags the button off the author's baseline. */
    align-self: baseline;
}
.comment-reply-link:hover,
.comment-reply-link:focus-visible { opacity: 1; text-decoration: underline; }

/* ====================== DASHBOARD SECTIONS ======================
   The dashboard is a stack of panels that all looked identical: same border,
   same background, and a header with no separation from the content under it.
   Scanning for "where does Recent Tracks end and Quick Actions begin" meant
   reading, not glancing.

   Nothing here changes a font or a size — the ask was prominence, not scale.
   Three cues do the work: a brighter edge, a rule under the header, and a
   coloured spine down the left that gives each panel an identity. */

.dash-section {
    /* Brighter than --cy-border so a panel edge is a deliberate line rather
       than a suggestion. */
    border-color: rgba(0, 245, 255, 0.32);
}

/* The header row is always the first child — the icon-plus-title bar, or on
   the alerts panel the partial whose root IS that bar. */
.dash-section > div:first-child {
    border-bottom: 1px solid rgba(0, 245, 255, 0.18);
    padding-bottom: 0.7rem;
}

/* A spine down the left edge. Starts below the clipped corner so it does not
   poke out of the card's cut, and fades out so it reads as an accent rather
   than a second border. */
.dash-section::after {
    content: '';
    position: absolute;
    left: 0;
    top: 14px;
    bottom: 0;
    width: 3px;
    background: linear-gradient(180deg, var(--cy-cyan) 0%, rgba(0, 245, 255, 0.12) 65%, transparent 100%);
    pointer-events: none;
}

/* Magenta spine for the panels whose headers are already magenta — the feed,
   tracks and alerts — so the stack is not a wall of identical cyan.
   An explicit class rather than :has() on the header icon: that would have
   tied the panel's colour to whichever icon someone picked next, and changed
   it silently the first time one was swapped. */
.dash-section-alt::after {
    background: linear-gradient(180deg, var(--cy-magenta) 0%, rgba(255, 45, 120, 0.12) 65%, transparent 100%);
}
.dash-section-alt { border-color: rgba(255, 45, 120, 0.3); }
.dash-section-alt > div:first-child { border-bottom-color: rgba(255, 45, 120, 0.18); }
.dash-section-alt:hover { border-color: rgba(255, 45, 120, 0.5); }

.dash-section:hover { border-color: rgba(0, 245, 255, 0.5); }

@media (max-width: 640px) {
    /* Panels stack on a phone, so the boundary between them is doing more
       work — keep the spine but tighten the header rule's breathing room. */
    .dash-section > div:first-child { padding-bottom: 0.55rem; }
}

/* ============== ACCOUNT MENU GROUP TONES (hover + colour) ==============
   Layered ON TOP of the ACCOUNT MENU GROUPS block earlier in this file, which
   still owns the geometry. Kept separate and later so these per-group colours
   win over the generic cyan open-state there without needing !important.

   The four accordion groups in the header's account menu. They open on hover
   (see partials/nav-dropdown-group.blade.php) and each carries its own colour,
   so the menu is scanned by hue rather than read top to bottom.

   Tones follow what the rest of the site already means by them: cyan for
   galleries, magenta for music — as everywhere music appears — green for
   community, yellow for account. */

.nav-group { position: relative; }

/* The group's icon is coloured whether it is open or shut. Colouring only the
   open state would mean the menu looks identical until you are already inside
   the group you were trying to find. */
.nav-group-cyan    > .nav-group-toggle .nav-group-icon { color: var(--cy-cyan); }
.nav-group-magenta > .nav-group-toggle .nav-group-icon { color: var(--cy-magenta); }
.nav-group-green   > .nav-group-toggle .nav-group-icon { color: var(--cy-green); }
.nav-group-yellow  > .nav-group-toggle .nav-group-icon { color: var(--cy-yellow); }

/* Open header, caret and the rail down the expanded items, all in the group's
   own colour. .nav-group-toggle.is-open sets cyan generically; these override
   it per group, and sit later in the file so they win without !important. */
.nav-group-magenta > .nav-group-toggle.is-open { color: var(--cy-magenta); }
.nav-group-green   > .nav-group-toggle.is-open { color: var(--cy-green); }
.nav-group-yellow  > .nav-group-toggle.is-open { color: var(--cy-yellow); }

.nav-group-magenta .nav-group-items { border-left-color: rgba(255, 45, 120, 0.4); }
.nav-group-green   .nav-group-items { border-left-color: rgba(57, 255, 20, 0.4); }
.nav-group-yellow  .nav-group-items { border-left-color: rgba(255, 215, 0, 0.4); }
.nav-group-cyan    .nav-group-items { border-left-color: rgba(0, 245, 255, 0.4); }

/* A tinted wash behind the whole group while it is open, which is what makes
   the boundary between one group and the next obvious at a glance. */
.nav-group-cyan:hover,
.nav-group-cyan:focus-within,
.nav-group-cyan.is-open          { background-color: rgba(0, 245, 255, 0.06); }
.nav-group-magenta:hover,
.nav-group-magenta:focus-within,
.nav-group-magenta.is-open       { background-color: rgba(255, 45, 120, 0.06); }
.nav-group-green:hover,
.nav-group-green:focus-within,
.nav-group-green.is-open         { background-color: rgba(57, 255, 20, 0.06); }
.nav-group-yellow:hover,
.nav-group-yellow:focus-within,
.nav-group-yellow.is-open        { background-color: rgba(255, 215, 0, 0.06); }

/* A spine on the left of the hovered group, matching the dashboard panels. */
.nav-group::before {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 2px;
    background: transparent;
    transition: background-color 0.18s ease;
}
.nav-group-cyan:hover::before,
.nav-group-cyan.is-open::before    { background: var(--cy-cyan); }
.nav-group-magenta:hover::before,
.nav-group-magenta.is-open::before { background: var(--cy-magenta); }
.nav-group-green:hover::before,
.nav-group-green.is-open::before   { background: var(--cy-green); }
.nav-group-yellow:hover::before,
.nav-group-yellow.is-open::before  { background: var(--cy-yellow); }

/* Items inside a group pick up the group's colour on hover rather than the
   global cyan, so a row always tells you which group you are inside. */
.nav-group-magenta .nav-group-item:hover { color: var(--cy-magenta); }
.nav-group-green   .nav-group-item:hover { color: var(--cy-green); }
.nav-group-yellow  .nav-group-item:hover { color: var(--cy-yellow); }

@media (hover: none) {
    /* No hover on a touchscreen — the wash and spine would either never appear
       or stick after a tap. The click handler is what opens groups there. */
    .nav-group:hover:not(.is-open) { background-color: transparent; }
    .nav-group:hover:not(.is-open)::before { background: transparent; }
}

/* ============== ACCOUNT MENU GROUP MOTION ==============
   The slide, the stagger and the hover polish for the account menu accordion.
   Timing lives here; the DELAYS before a group opens or closes are in the
   Alpine scope in layouts/navigation.blade.php — those are behaviour, these
   are presentation. */

.nav-group-items {
    /* max-height rather than height: the natural height is unknown to CSS, and
       a group only ever holds three or four rows so a generous ceiling costs
       nothing. The easing is the standard decelerate curve — it lands softly
       instead of stopping dead. */
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    /* Keeps collapsed links out of the tab order. Delayed on the way out so it
       does not blank the panel mid-collapse. */
    visibility: hidden;
    transform: translateY(-4px);
    transition:
        max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
        opacity    0.2s ease,
        transform  0.25s cubic-bezier(0.4, 0, 0.2, 1),
        visibility 0s linear 0.3s;
}

.nav-group-items.is-open {
    max-height: 22rem;
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition:
        max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
        opacity    0.22s ease 0.04s,
        transform  0.25s cubic-bezier(0.4, 0, 0.2, 1),
        visibility 0s;
}

/* Rows arrive one after another rather than as a block. Four steps only — the
   groups hold three or four items, and a longer ladder would just feel slow. */
.nav-group-items.is-open .nav-group-item {
    animation: nav-item-in 0.26s cubic-bezier(0.4, 0, 0.2, 1) both;
}
.nav-group-items.is-open .nav-group-item:nth-child(1) { animation-delay: 0.04s; }
.nav-group-items.is-open .nav-group-item:nth-child(2) { animation-delay: 0.09s; }
.nav-group-items.is-open .nav-group-item:nth-child(3) { animation-delay: 0.14s; }
.nav-group-items.is-open .nav-group-item:nth-child(n+4) { animation-delay: 0.19s; }

@keyframes nav-item-in {
    from { opacity: 0; transform: translateX(-6px); }
    to   { opacity: 1; transform: translateX(0); }
}

/* The header nudges right as its group opens, so the group reads as one moving
   piece rather than a static label with a panel appearing under it. */
.nav-group-toggle {
    transition: color 0.2s ease, background-color 0.2s ease, padding-left 0.2s ease;
}
.nav-group.is-open > .nav-group-toggle { padding-left: 1.2rem; }

/* The spine grows from the top rather than snapping in. */
.nav-group::before {
    transform: scaleY(0);
    transform-origin: top;
    transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.18s ease;
}
.nav-group:hover::before,
.nav-group.is-open::before { transform: scaleY(1); }

.nav-group { transition: background-color 0.22s ease; }

@media (prefers-reduced-motion: reduce) {
    /* Keep the state changes, drop the movement. The panel still opens and
       closes — it just does it instantly. */
    .nav-group-items,
    .nav-group-items.is-open,
    .nav-group-toggle,
    .nav-group,
    .nav-group::before { transition: none; }
    .nav-group-items.is-open .nav-group-item { animation: none; }
    .nav-group.is-open > .nav-group-toggle { padding-left: 1rem; }
}

/* Hover bridge for the account menu.
   The panel sits 8px below its button (mt-2). That gap belongs to neither, so
   travelling from one to the other fires mouseleave on the wrapper and starts
   the close timer — the delay covers it, but the state still flickers. This
   invisible strip spans the gap so the pointer never actually leaves.
   Only while open: an always-on strip would sit over whatever is underneath. */
.dropdown-menu::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: -0.55rem;
    height: 0.55rem;
}

/* Story panels on the dashboard take the Library's yellow spine, the same way
   .dash-section-alt takes magenta. Declared here beside that rule rather than
   in the page's local <style>, so all three panel tones live together. */
.dash-section-story::after {
    background: linear-gradient(180deg, var(--cy-yellow) 0%, rgba(255, 215, 0, 0.12) 65%, transparent 100%);
}
.dash-section-story { border-color: rgba(255, 215, 0, 0.3); }
.dash-section-story > div:first-child { border-bottom-color: rgba(255, 215, 0, 0.18); }
.dash-section-story:hover { border-color: rgba(255, 215, 0, 0.5); }

/* ============== PROSE THAT WAS WEARING A LABEL'S CLOTHES ==============
   Orbitron, uppercased, letter-spaced is the house style for LABELS — one or
   two words, or a button. It is the wrong dress for a sentence: all-caps
   removes the word-shape cues people actually read by, and tracking pushes the
   letters far enough apart that a phrase stops being one object.

   The earlier readability pass fixed size and contrast and left this alone,
   which is why these still read badly at a perfectly legible 12px.

   THE RULE, for anything added later:
     - one or two words, or a button        -> display font, uppercase, tracked
     - a sentence, a relative time, or ANY
       text containing a member's own words
       (a display name, a title)            -> body font, sentence case

   That last case matters most. A display name forced to uppercase is not the
   name that member chose. */

.feed-scope-note,
.presence-label,
.friend-mutual-label,
.friend-card-meta,
.fm-inline-reply-target,
.bb-quote-who {
    font-family: var(--cy-font-body);
    text-transform: none;
    letter-spacing: normal;
}

/* "Just for now — a refresh returns to your default." A whole sentence, and
   the one that prompted this. */
.feed-scope-note { font-size: 0.8rem; line-height: 1.5; }

/* "Last seen 3 hours ago" / "Online now" — a relative time reads as a phrase,
   not a tag. The green-when-online rule earlier still applies. */
.presence-label { font-size: 0.8rem; }

/* "3 friends in common". */
.friend-mutual-label { font-size: 0.8rem; }

/* Presence line under a member card. */
.friend-card-meta { font-size: 0.78rem; }

/* "Replying to <name>" — contains a display name, so it must not be uppercased.
   The name itself keeps the accent colour it already had. */
.fm-inline-reply-target { font-size: 0.82rem; }

/* The "X said:" citation on a quote. Same reason: it is somebody's name. */
.bb-quote-who { font-size: 0.82rem; font-weight: 600; }

/* The counter / meta line under a page heading — "28 members found, 3 online
   now", "12 conversations", "Blocked members cannot message you…".

   These were text-xs + tracking-widest + font-mono: 12px wide-tracked
   monospace, which is a fine costume for a two-word status tag and a poor one
   for a sentence. Body font at a readable size, normal tracking, and the muted
   colour that everything secondary already uses.

   Deliberately NOT applied to the auth and guest screens, which use the same
   utilities on purpose for their HUD-style status banners and were tuned
   separately. */
.meta-line {
    font-family: var(--cy-font-body);
    font-size: 0.85rem;
    letter-spacing: normal;
    line-height: 1.55;
    color: var(--cy-text-dim);
}

/* ============================================================
   SECONDARY CONTENT: OFF THE FLOOR
   ============================================================

   The readability pass set 0.75rem / 12px as the FLOOR — the smallest text
   allowed anywhere. The forum and PM modules then adopted it as their
   DEFAULT, so 57 rules landed on the smallest permitted size and the dimmest
   permitted colour at the same time, several of them also in Orbitron.

   Contrast alone was not the fault: --cy-text-dim measured 8.17:1, which
   passes AA comfortably. Three things were stacking:

     size    12px is a floor for a tag, not a target for a sentence
     weight  light-on-dark blooms — thin strokes erode in a way the same
             pairing inverted does not, so dark themes need more size or
             weight for equal legibility, and this had neither
     face    Orbitron is a geometric display face with a low x-height and
             uniform strokes; it is built for six characters at 20px, and
             at 12px it stops resolving

   So this is mostly a SIZE and FACE fix with a modest contrast lift, not a
   contrast fix. Appended last, so single-class selectors here beat their
   originals on source order without !important.

   Left deliberately alone: tags, form labels, tab strips, filter and
   mark-all buttons, character counters, avatar-fallback initials, the emoji
   picker's group headings. Those are real labels — small, dim and
   letter-spaced is correct for them, and lifting them would flatten the
   distinction that makes the content read as content. */

/* ---- Carries a name, a relative time, or a sentence: body face ---- */
.fm-forum-last-meta,
.fm-thread-last-meta,
.fm-thread-meta,
.fm-post-date,
.fm-subpost-date,
.fm-post-joined,
.fm-search-hit-meta,
.fm-online-name,
.pm-meta,
.pm-thread-time,
.gothic-activity-time {
    font-family: var(--cy-font-body);
    letter-spacing: normal;
    text-transform: none;
    font-size: 0.82rem;
    color: var(--cy-text-soft);
}

/* ---- Already the body face, just too small and too dim ---- */
.fm-forum-desc,
.fm-category-desc {
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--cy-text-soft);
}

.fm-hint,
.fm-attach-hint,
.friend-hint,
.fm-widget-empty,
.fm-widget-foot,
.fm-post-edited,
.profile-wall-time,
.fm-stat-list dt,
.gothic-notification-empty,
.gothic-notification-loading {
    font-size: 0.82rem;
    color: var(--cy-text-soft);
}

/* Dim AND translucent — the two compounded to roughly 5.5:1, the lowest
   contrast on the site. The forum reference is a navigational cue; it can be
   quiet without being faint. */
.gothic-forum-ref-forum {
    opacity: 1;
    font-size: 0.8rem;
    color: var(--cy-text-dim);
}

/* ---- Keeps the HUD face (stat readouts, controls) — size and tone only ---- */
.fm-forum-stats,
.fm-thread-stats,
.fm-crumbs,
.fm-post-permalink {
    font-size: 0.8rem;
    color: var(--cy-text-soft);
}

/* Post controls. These are the primary verbs on a post — Reply especially —
   and they were rendering fainter than the timestamp beside them. */
.fm-action {
    font-size: 0.8rem;
    color: var(--cy-text-soft);
}

/* Page numbers are targets as much as text. */
nav[aria-label="pagination"] span,
nav[aria-label="pagination"] a {
    font-size: 0.84rem;
}

/* Fixed in the prose pass for face; these bring size and tone into line.
   The green "online" state is set by a three-class selector and still wins. */
.presence-label,
.friend-card-meta,
.friend-mutual-label,
.fm-inline-reply-target {
    color: var(--cy-text-soft);
}

/* The value beside a lifted label. Left on the HUD face and the bright tier —
   "2", "Mar 2025" are readouts and that is what the face is for — but matched
   in size to the dt above, which would otherwise sit larger than its own
   value. */
.fm-post-stats dd { font-size: 0.82rem; }

/* ============================================================
   FORUM: COLOUR THAT MEANS SOMETHING
   ============================================================

   The forum was doing 26 of its 33 accent jobs in cyan — category heads,
   forum icons, unread rows, headings, focus rings, every hover. When one
   colour marks everything, it marks nothing, and the board reads as a single
   undifferentiated wall.

   Colour is assigned to STATE and to GROUPING here, never as decoration:

     cyan     the default / unread / interactive
     gold     pinned — elevated, deliberately placed above the rest
     amber    locked or read-only — a door that is shut
     magenta  staff, and the like heart
     green    presence

   The one rule: if two things look different they must BE different. Nothing
   below tints something merely to break up the grey. */

/* ---- Category zones ----
   Each category gets an accent from a four-step rotation, applied to its
   head bar, its title, and the icons of the forums inside it — so a category
   reads as a zone rather than as another run of rows. Four, not more,
   because the fifth tone would have to be a near-duplicate of one of these,
   and two categories that look almost-but-not-quite alike is worse than two
   that clearly repeat. */
.fm-category      { --fm-accent: var(--cy-cyan);    --fm-accent-wash: rgba(0,245,255,0.09); }
.fm-cat-1         { --fm-accent: var(--cy-cyan);    --fm-accent-wash: rgba(0,245,255,0.09); }
.fm-cat-2         { --fm-accent: var(--cy-magenta); --fm-accent-wash: rgba(255,45,120,0.09); }
.fm-cat-3         { --fm-accent: var(--cy-yellow-soft); --fm-accent-wash: rgba(255,215,0,0.08); }
.fm-cat-4         { --fm-accent: var(--cy-green);   --fm-accent-wash: rgba(57,255,20,0.07); }

.fm-category .fm-category-head {
    background: linear-gradient(90deg, var(--fm-accent-wash), transparent);
    border-left-color: var(--fm-accent);
}
.fm-category .fm-category-title { color: var(--fm-accent); }
.fm-category .fm-forum-icon     { color: var(--fm-accent); }

/* The icon carries the zone colour, so it no longer needs to be held back to
   0.75 opacity to avoid competing with a title in the identical colour. */
.fm-category .fm-forum:hover .fm-forum-icon { opacity: 1; }

/* ---- Thread state ----
   Pinned and locked were both rendering in the same neutral as an ordinary
   thread, which meant the icon was the only signal and the tag beside it was
   decoration. */
.fm-thread-pinned .fm-thread-icon { color: var(--cy-yellow-soft); }
.fm-thread-pinned { border-left: 3px solid rgba(255,215,0,0.55); }

.fm-thread-locked .fm-thread-icon { color: var(--cy-amber); }

/* Unread outranks pinned: what you have not read is the more useful thing to
   spot, and a pinned thread is pinned every time you look at it. Declared
   after so it wins on source order at equal specificity. */
.fm-thread-unread { border-left-color: var(--cy-cyan); }

/* ---- Tags ---- */
.fm-tag-pinned {
    color: var(--cy-yellow-soft);
    border-color: rgba(255,215,0,0.45);
    background-color: var(--cy-yellow-dim);
}
.fm-tag-locked {
    color: var(--cy-amber);
    border-color: rgba(255,166,77,0.45);
    background-color: var(--cy-amber-dim);
}
/* Read-only is a restriction, but a mild one — you can still read everything.
   Outlined rather than filled, so it sits below Locked in loudness. */
.fm-tag-readonly {
    color: var(--cy-text-soft);
    border-color: var(--cy-border);
}
.fm-tag-staff { background-color: var(--cy-magenta-dim); }

/* ---- Post controls ----
   Reply / Edit / Delete now separate on hover instead of all going cyan:
   the safe one, the one that changes your own words, and the one that
   destroys. Colour arrives on hover only — a resting toolbar of three
   different colours would shout. */
.fm-edit-toggle:hover { color: var(--cy-yellow-soft); }

/* A like is affection, not a fault. It was painted in the error red used by
   Delete and the attachment failures, which put "somebody liked this" in the
   same visual language as "this went wrong". */
.fm-like.is-liked   { color: var(--cy-magenta); }
.fm-like.is-liked i { text-shadow: 0 0 8px rgba(255,45,120,0.6); }
.fm-like:hover      { color: var(--cy-magenta); }

/* ---- Sidebar widgets ----
   Three stacked panels in one colour read as one panel. A tinted title marks
   where each begins; the bodies stay neutral. */
.fm-widget-title-stats  { color: var(--cy-yellow-soft); }
.fm-widget-title-search { color: var(--cy-magenta); }

/* ============================================================
   PRESENCE: THREE STATES
   ============================================================

   Offline was a transparent circle with a thin dim ring — which reads as an
   empty slot, or as a dot that failed to load, rather than as information.
   Red says it.

     online   green   solid, pulsing
     away     yellow  solid, still — no pulse, because the pulse is what
                      signals "here right now" and away is precisely not that
     offline  red     solid, no pulse

   Declared after the originals so these win on source order.

   Colour is not the only channel: every dot carries the state as text in its
   title and aria-label ("Online now" / "Away" / "Last seen 2 hours ago"), and
   the inline variant prints it beside the dot. That matters for the red/green
   pairing in particular, which is the one a colour-blind reader is most
   likely to lose. */

.presence-away {
    background-color: var(--cy-yellow);
    border: none;
    box-shadow: 0 0 6px rgba(255, 215, 0, 0.7);
    animation: none;
}

.presence-offline {
    background-color: var(--cy-error);
    border: none;
    /* Dimmer halo than the other two. Offline is the resting state for most
       of the member list most of the time, and a page of glowing red dots
       would read as a page of alarms. */
    box-shadow: 0 0 5px rgba(255, 58, 58, 0.45);
    animation: none;
}

/* The overlay variant previously repainted offline to a flat surface colour,
   which is what produced the empty circle over the avatar. */
.presence-dot-overlay.presence-offline {
    background-color: var(--cy-error);
    border-color: var(--cy-surface-1);
}
.presence-dot-overlay.presence-away {
    background-color: var(--cy-yellow);
    border-color: var(--cy-surface-1);
}

/* Label tone follows the dot for the two live states. Offline is left in the
   ordinary secondary colour on purpose — the text there is "Last seen 3 hours
   ago", and colouring an ordinary fact red would read as a warning about the
   member rather than a statement about their presence. */
.presence-inline .presence-away + .presence-label {
    color: var(--cy-yellow-soft);
}

/* ---- Presence tooltip ----
   Same .gothic-tooltip component the header icon buttons use.

   IT CANNOT LIVE INSIDE THE CARD. .gothic-card carries the cut-corner
   clip-path that gives the site its shape, and a clip-path clips every
   descendant — unconditionally, with no z-index able to escape it. A tooltip
   anchored to an avatar near a card's edge was therefore being sliced off by
   the card's own bevel. (.gothic-card:hover also sets a transform, which
   creates a stacking context and would have trapped it a second way even
   without the clip.)

   So presence.js keeps ONE tooltip element as a direct child of <body>,
   positioned to the hovered avatar. Outside every card, so outside every
   clip-path, overflow and stacking context on the site.

   It is revealed by hovering the AVATAR, not the dot: the dot is about 13px
   across, which is a fiddly target, and enlarging its hit area would mean it
   swallowed clicks meant for the profile link underneath. */
.presence-floating-tip {
    position: fixed;
    top: 0;
    left: 0;
    /* .gothic-tooltip supplies transform: translateX(-50%), so the `left` the
       script sets is read as the avatar's CENTRE. */
}

.presence-floating-tip.is-visible {
    opacity: 1;
}

/* Flipped above the avatar when there is no room below — the arrow has to
   turn over with it. */
.presence-floating-tip.is-above::after {
    bottom: auto;
    top: 100%;
    border-bottom-color: transparent;
    border-top-color: rgba(0, 245, 255, 0.3);
}
