/* ==========================================================================
   nostr-bbs Forum — Design System Foundation
   Tailwind utility classes are handled by the Play CDN in dev
   and by Tailwind CLI tree-shaking in production.

   Shared design tokens (glass-card, mesh-bg, aurora-shimmer, ambient-orb,
   candy-gradient, skeleton, gradient-text, hover-lift, neon-pulse,
   gradient-border, scrollbar-none, fade-in) are loaded from:
     design-tokens.css  (co-located copy of src/styles/design-tokens.css)
   ========================================================================== */

/* design-tokens.css is loaded via <link data-trunk> in index.html.
   Trunk content-hashes CSS filenames, so @import cannot resolve the
   original name at runtime. Do NOT re-add an @import here. */

/* ---------------------------------------------------------------------------
   1. CSS Custom Properties (forum-specific aliases)
   --------------------------------------------------------------------------- */

:root {
    /* Legacy aliases — kept for forum components that reference these directly */
    --amber-glow: var(--dl-glow-ambient);
    --amber-glow-strong: var(--dl-glow-strong);
    --card-bg: var(--dl-card-bg);
    --card-border: var(--dl-card-border);
    --glass-bg: var(--dl-glass-bg);
    --glass-border: var(--dl-glass-border);
}

/* ---------------------------------------------------------------------------
   2. Base Reset & Typography
   --------------------------------------------------------------------------- */

body {
    margin: 0;
    padding: 0;
    background-color: #111827;
    color: #f9fafb;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    line-height: 1.6;
}

/* Selection */
::selection {
    background: rgba(245, 158, 11, 0.3);
    color: #fbbf24;
}

/* Focus rings */
*:focus-visible {
    outline: 2px solid #f59e0b;
    outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   3. Loading Screen
   --------------------------------------------------------------------------- */

.loading-screen {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: #111827;
    transition: opacity 0.4s ease, visibility 0.4s ease;
}

.loading-screen.fade-out {
    opacity: 0;
    visibility: hidden;
}

.loading-logo {
    font-size: 2.5rem;
    font-weight: 800;
    letter-spacing: -0.025em;
    margin: 0;
    animation: gradient-shift 3s ease-in-out infinite;
    background-size: 200% 200%;
}

.loading-status {
    margin-top: 1rem;
    font-size: 0.875rem;
    color: #9ca3af;
    letter-spacing: 0.05em;
    animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

.loading-ring {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    border: 3px solid rgba(245, 158, 11, 0.1);
    border-top-color: #f59e0b;
    animation: spin 1s linear infinite;
    margin-bottom: 1.5rem;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

@keyframes gradient-shift {
    0%, 100% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
}

/* ---------------------------------------------------------------------------
   4. Animations
   --------------------------------------------------------------------------- */

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

@keyframes shimmer {
    0% { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

@keyframes fade-in {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}

/* ---------------------------------------------------------------------------
   5. Utility Classes
   NOTE: glass-card, gradient-text, skeleton, hover-lift, fade-in
         are now provided by design-tokens.css
   --------------------------------------------------------------------------- */

/* Glassmorphism — heavier blur variant (forum-specific) */
.glass {
    background: var(--glass-bg);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid var(--glass-border);
}

/* Fade in for DM bubbles */
.animate-fadeIn {
    animation: dl-fade-in 0.3s ease-out;
}

/* Toast slide-in */
@keyframes slide-in-down {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: translateY(0); }
}

.animate-slide-in-down {
    animation: slide-in-down 0.25s ease-out;
}

/* Subtle float for empty states */
@keyframes gentle-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-6px); }
}

.animate-gentle-float {
    animation: gentle-float 3s ease-in-out infinite;
}

/* Amber glow on hover */
.glow-hover {
    transition: box-shadow 0.2s ease;
}

.glow-hover:hover {
    box-shadow: var(--amber-glow);
}

/* ---------------------------------------------------------------------------
   6. Form Elements
   --------------------------------------------------------------------------- */

input:focus,
textarea:focus,
select:focus {
    box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.5);
    border-color: rgba(245, 158, 11, 0.5);
}

/* ---------------------------------------------------------------------------
   7. Scrollbar
   --------------------------------------------------------------------------- */

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: #111827;
}

::-webkit-scrollbar-thumb {
    background: #374151;
    border-radius: 9999px;
    border: 2px solid #111827;
}

::-webkit-scrollbar-thumb:hover {
    background: #4b5563;
    box-shadow: 0 0 6px rgba(245, 158, 11, 0.15);
}

/* Firefox scrollbar */
* {
    scrollbar-width: thin;
    scrollbar-color: #374151 #111827;
}

/* scrollbar-none is provided by design-tokens.css */

/* ---------------------------------------------------------------------------
   8–14. Shared visual effects
   NOTE: ambient-orb, glass-card-interactive, gradient-border, aurora-shimmer,
         neon-pulse, candy-gradient, mesh-bg, animate-ambient-breathe
         are now provided by design-tokens.css
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   15. Toast Notifications
   --------------------------------------------------------------------------- */

.toast-container {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    pointer-events: none;
    max-width: 400px;
}

.toast-item {
    pointer-events: auto;
    background: rgba(31, 41, 55, 0.95);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(55, 65, 81, 0.5);
    border-radius: 12px;
    padding: 0.75rem 1rem;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.3),
                0 0 40px rgba(0, 0, 0, 0.1);
    animation: toast-enter 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.toast-item.toast-success {
    border-color: rgba(34, 197, 94, 0.3);
    box-shadow: 0 0 20px rgba(34, 197, 94, 0.08);
}

.toast-item.toast-error {
    border-color: rgba(239, 68, 68, 0.3);
    box-shadow: 0 0 20px rgba(239, 68, 68, 0.08);
}

.toast-item.toast-warning {
    border-color: rgba(245, 158, 11, 0.3);
    box-shadow: 0 0 20px rgba(245, 158, 11, 0.08);
}

.toast-item.toast-exit {
    animation: toast-exit 0.2s ease-in forwards;
}

@keyframes toast-enter {
    from { opacity: 0; transform: translateX(100%) scale(0.95); }
    to { opacity: 1; transform: translateX(0) scale(1); }
}

@keyframes toast-exit {
    from { opacity: 1; transform: translateX(0) scale(1); }
    to { opacity: 0; transform: translateX(100%) scale(0.95); }
}

/* ---------------------------------------------------------------------------
   16. Modal Overlay
   --------------------------------------------------------------------------- */

.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 9000;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    animation: modal-fade-in 0.2s ease-out;
    display: flex;
    align-items: center;
    justify-content: center;
}

.modal-panel {
    background: rgba(17, 24, 39, 0.95);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border: 1px solid rgba(55, 65, 81, 0.5);
    border-radius: 16px;
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.4),
                0 0 80px rgba(245, 158, 11, 0.05);
    animation: modal-scale-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
    max-height: 90vh;
    overflow-y: auto;
    width: min(90vw, 480px);
}

@keyframes modal-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes modal-scale-in {
    from { opacity: 0; transform: scale(0.93) translateY(10px); }
    to { opacity: 1; transform: scale(1) translateY(0); }
}

/* ---------------------------------------------------------------------------
   17. Reaction Particle Burst
   --------------------------------------------------------------------------- */

.reaction-burst {
    position: relative;
    display: inline-flex;
    transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.reaction-burst:active {
    transform: scale(1.3);
}

.reaction-burst.is-active {
    color: #fbbf24;
}

.reaction-particle {
    position: absolute;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: #fbbf24;
    animation: reaction-fly 0.5s ease-out forwards;
    pointer-events: none;
}

@keyframes reaction-fly {
    0% { opacity: 1; transform: translate(0, 0) scale(1); }
    100% { opacity: 0; transform: translate(var(--dx, 20px), var(--dy, -20px)) scale(0); }
}

/* ---------------------------------------------------------------------------
   18. Typing Indicator
   --------------------------------------------------------------------------- */

.typing-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #9ca3af;
    animation: typing-bounce 1.4s ease-in-out infinite;
}

.typing-dot:nth-child(2) { animation-delay: 0.2s; }
.typing-dot:nth-child(3) { animation-delay: 0.4s; }

@keyframes typing-bounce {
    0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
    30% { transform: translateY(-6px); opacity: 1; }
}

/* ---------------------------------------------------------------------------
   19. Mobile Bottom Navigation
   --------------------------------------------------------------------------- */

.mobile-bottom-nav {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 50;
    background: rgba(17, 24, 39, 0.95);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-top: 1px solid rgba(55, 65, 81, 0.3);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    display: none;
}

@media (max-width: 639px) {
    .mobile-bottom-nav {
        display: flex;
    }

    /* Height of the fixed bottom nav, published as ONE custom property so every
       mobile surface compensates from the same number instead of each
       hand-copying a `4rem` that nothing keeps in sync (that drift is what let
       the chat composer slide under the nav — see §31a).

       3.5rem is the nav's real box: a 20px icon + a 2px gap + a 10px label at
       the body line-height of 1.6, inside 0.5rem of vertical padding, plus the
       1px top border = 55px. The home-indicator inset is the nav's OWN
       padding-bottom, so it is always added alongside this token and never
       folded into it — folding it in would double-count on iOS. */
    :root {
        --forum-mobile-nav-h: 3.5rem;
    }

    /* The nav renders only for signed-in readers (`<Show when=is_authed>` in
       mobile_bottom_nav.rs). Reserving its band for a signed-out reader leaves
       a dead 56px strip at the foot of every page, so zero the token when the
       nav is genuinely absent from the DOM. Progressive enhancement: a browser
       without `:has()` keeps the reserved band, which is the safe way to fail
       (dead space, never covered content). */
    :root:not(:has(.mobile-bottom-nav)) {
        --forum-mobile-nav-h: 0px;
    }

    /* Account for bottom nav height on mobile. Now derived from the token
       above, so it collapses to just the safe-area inset when signed out. */
    main {
        padding-bottom: calc(var(--forum-mobile-nav-h, 3.5rem) + env(safe-area-inset-bottom, 0px));
    }

    /* Anything scrolled into view (the thread composer's `scroll_into_view` in
       thread.rs, in-page anchors) must stop above the nav rather than behind
       it. */
    html {
        scroll-padding-bottom: calc(var(--forum-mobile-nav-h, 3.5rem) + env(safe-area-inset-bottom, 0px) + 0.5rem);
    }

    /* Tap-target floor. The nav item already computes to ~54px, so this is a
       defensive minimum that keeps it at/above the 44px guideline if the label
       font ever shrinks — it must never be traded away for compactness. */
    .mobile-nav-item {
        min-height: 44px;
    }
}

.mobile-nav-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 0.5rem 0;
    gap: 0.125rem;
    color: #6b7280;
    transition: color 0.15s ease;
    position: relative;
    font-size: 0.625rem;
    letter-spacing: 0.025em;
}

.mobile-nav-item.active {
    color: #f59e0b;
}

.mobile-nav-item.active::before {
    content: '';
    position: absolute;
    top: 0;
    left: 25%;
    right: 25%;
    height: 2px;
    background: #f59e0b;
    border-radius: 0 0 2px 2px;
}

/* ---------------------------------------------------------------------------
   20. Link Preview Cards
   --------------------------------------------------------------------------- */

.link-preview-card {
    border: 1px solid rgba(55, 65, 81, 0.4);
    border-radius: 10px;
    overflow: hidden;
    background: rgba(31, 41, 55, 0.4);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
    max-width: 480px;
}

.link-preview-card:hover {
    border-color: rgba(245, 158, 11, 0.2);
    box-shadow: 0 0 16px rgba(245, 158, 11, 0.06);
}

/* ---------------------------------------------------------------------------
   21. Virtual Scroll Container
   --------------------------------------------------------------------------- */

.virtual-scroll {
    overflow-y: auto;
    will-change: transform;
    -webkit-overflow-scrolling: touch;
    contain: layout style;
}

/* ---------------------------------------------------------------------------
   22. Notification Badge
   --------------------------------------------------------------------------- */

.notification-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 16px;
    height: 16px;
    border-radius: 9999px;
    background: #ef4444;
    color: white;
    font-size: 0.625rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
    border: 2px solid #111827;
    animation: badge-pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes badge-pop {
    0% { transform: scale(0); }
    100% { transform: scale(1); }
}

/* ---------------------------------------------------------------------------
   23. Identicon Avatar
   --------------------------------------------------------------------------- */

.avatar-identicon {
    border-radius: 50%;
    overflow: hidden;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    text-transform: uppercase;
    flex-shrink: 0;
    border: 2px solid rgba(245, 158, 11, 0.15);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.avatar-identicon:hover {
    border-color: rgba(245, 158, 11, 0.4);
    box-shadow: 0 0 12px rgba(245, 158, 11, 0.1);
}

/* ---------------------------------------------------------------------------
   24. Emoji Picker Grid
   --------------------------------------------------------------------------- */

.emoji-grid {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 2px;
    max-height: 200px;
    overflow-y: auto;
}

.emoji-btn {
    padding: 4px;
    border-radius: 6px;
    font-size: 1.25rem;
    line-height: 1;
    text-align: center;
    cursor: pointer;
    transition: background 0.1s ease, transform 0.1s ease;
    border: none;
    background: transparent;
}

.emoji-btn:hover {
    background: rgba(245, 158, 11, 0.15);
    transform: scale(1.15);
}

/* ---------------------------------------------------------------------------
   25. Breadcrumb Navigation
   --------------------------------------------------------------------------- */

.breadcrumb-nav {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.8125rem;
    color: #9ca3af;
    overflow-x: auto;
    white-space: nowrap;
    padding: 0.5rem 0;
}

.breadcrumb-nav a {
    color: #9ca3af;
    transition: color 0.15s ease;
    text-decoration: none;
}

.breadcrumb-nav a:hover {
    /* Zone-scoped crumbs (drill-down pages set --zone-accent) follow the zone
       accent; the index/events crumbs fall back to brand amber (issue #43). */
    color: var(--zone-accent, #fbbf24);
}

.breadcrumb-separator {
    color: #4b5563;
    font-size: 0.75rem;
}

/* ---------------------------------------------------------------------------
   26. Global Search Overlay
   --------------------------------------------------------------------------- */

.search-overlay {
    position: fixed;
    inset: 0;
    z-index: 9500;
    background: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 15vh;
    animation: modal-fade-in 0.15s ease-out;
}

.search-panel {
    width: min(90vw, 600px);
    background: rgba(17, 24, 39, 0.98);
    border: 1px solid rgba(55, 65, 81, 0.5);
    border-radius: 16px;
    box-shadow: 0 0 60px rgba(0, 0, 0, 0.4),
                0 0 120px rgba(245, 158, 11, 0.03);
    animation: modal-scale-in 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
    overflow: hidden;
}

/* ---------------------------------------------------------------------------
   27. Confirm Dialog
   --------------------------------------------------------------------------- */

.confirm-actions {
    display: flex;
    gap: 0.75rem;
    justify-content: flex-end;
}

/* ---------------------------------------------------------------------------
   27a. Zone-accent utilities (issue #43)

   Zone-scoped surfaces follow the operator's configured accent, exposed as the
   `--zone-accent` custom property by `zone_tile_style` (index tiles) and
   `zone_accent_style_cfg` (drill-down page roots). Every utility references
   `var(--zone-accent, <brand-amber>)` DIRECTLY in its property (never via an
   intermediate alias, which would freeze the fallback at :root) so it resolves
   per element against the nearest `--zone-accent`, and falls back to the brand
   amber when none is set — making each class a visual no-op outside any zone
   context (header, login, home, admin keep brand amber). `color-mix` is fine:
   the deployment targets evergreen browsers (already used inline in channel.rs).
   --------------------------------------------------------------------------- */

/* Static accent text (chips/links that were text-amber-400). */
.za-text {
    color: var(--zone-accent, #fbbf24);
}

/* Accent text on hover (was hover:text-amber-400). */
.za-hover-text {
    transition: color 0.15s ease;
}
.za-hover-text:hover {
    color: var(--zone-accent, #fbbf24);
}

/* Accent text when the enclosing `.group` is hovered (was group-hover:text-amber-400). */
.za-group-hover-text {
    transition: color 0.15s ease;
}
.group:hover .za-group-hover-text {
    color: var(--zone-accent, #fbbf24);
}

/* Soft accent chip: tinted fill + accent text (was text-amber-400 bg-amber-500/10). */
.za-chip {
    color: var(--zone-accent, #fbbf24);
    background-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 12%, transparent);
}

/* Small accent icon tile (was bg-amber-500/10 text-amber-400). */
.za-icon-tile {
    color: var(--zone-accent, #fbbf24);
    background-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 12%, transparent);
}

/* Ghost accent button: tinted fill + border, darker on hover
   (was bg-amber-500/10 hover:bg-amber-500/20 text-amber-400 border-amber-500/20). */
.za-chip-btn {
    color: var(--zone-accent, #fbbf24);
    background-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 10%, transparent);
    border-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 20%, transparent);
    transition: background-color 0.2s ease;
}
.za-chip-btn:hover {
    background-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 20%, transparent);
}

/* Solid accent fill with dark ink for high-emphasis chips/buttons
   (was bg-amber-400/bg-amber-500). `:not(:disabled)` lets Tailwind's
   `disabled:bg-gray-600` win when the button is disabled. */
.za-solid {
    background-color: var(--zone-accent, #f59e0b);
}
.za-solid:not(:disabled):hover {
    background-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 85%, #ffffff);
}

/* Accent border tint on hover (was hover:border-amber-500/30). */
.za-hover-border {
    transition: border-color 0.2s ease, background 0.2s ease,
                box-shadow 0.2s ease, transform 0.2s ease;
}
.za-hover-border:hover {
    border-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 30%, transparent);
}

/* Accent focus ring for zone-scoped form fields (was focus:border/ring-amber-500).
   Higher specificity than the global `input:focus` amber rule, so it wins. */
.za-focus:focus {
    outline: none;
    border-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 55%, transparent);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--zone-accent, #f59e0b) 55%, transparent);
}

/* Spinner in the zone accent (was border-amber-400 border-t-transparent). */
.za-spinner {
    border-color: var(--zone-accent, #f59e0b);
    border-top-color: transparent;
}

/* ---------------------------------------------------------------------------
   28. Category / Section Cards (Forum Index)
   --------------------------------------------------------------------------- */

.category-hero-card {
    position: relative;
    border-radius: 16px;
    overflow: hidden;
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    min-height: 160px;
}

.category-hero-card:hover {
    transform: translateY(-2px);
    /* Zone-accent glow on the index cards (issue #43): color-mix with the
       brand-amber fallback is pixel-identical to the old rgba(245,158,11,0.1)
       when no --zone-accent is set, and follows the tile's accent when it is. */
    box-shadow: 0 0 28px color-mix(in srgb, var(--zone-accent, #f59e0b) 10%, transparent),
                0 12px 40px rgba(0, 0, 0, 0.3);
}

.section-list-card {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 10px;
    padding: 0.875rem 1rem;
    transition: border-color 0.2s ease, background 0.2s ease;
}

.section-list-card:hover {
    border-color: color-mix(in srgb, var(--zone-accent, #f59e0b) 25%, transparent);
    background: rgba(31, 41, 55, 0.7);
}

/* ---------------------------------------------------------------------------
   29. Event Cards
   --------------------------------------------------------------------------- */

.event-card {
    background: var(--card-bg);
    border: 1px solid var(--card-border);
    border-radius: 12px;
    overflow: hidden;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.event-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 0 20px rgba(245, 158, 11, 0.08);
}

.event-date-badge {
    width: 3.5rem;
    text-align: center;
    background: rgba(245, 158, 11, 0.1);
    border-radius: 8px;
    padding: 0.375rem;
    flex-shrink: 0;
}

/* ---------------------------------------------------------------------------
   30. Profile Modal
   --------------------------------------------------------------------------- */

.profile-avatar-ring {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    padding: 3px;
    background: linear-gradient(135deg, #fbbf24, #f97316, #a855f7);
    display: flex;
    align-items: center;
    justify-content: center;
}

.profile-avatar-ring > div {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    overflow: hidden;
}

/* ---------------------------------------------------------------------------
   Reduced-motion: disable all ambient animations
   --------------------------------------------------------------------------- */

/* Reduced-motion for forum-specific animations
   (shared tokens handle their own reduced-motion rules) */
@media (prefers-reduced-motion: reduce) {
    .typing-dot,
    .reaction-particle {
        animation: none !important;
    }
}

/* ---------------------------------------------------------------------------
   BBS switch sash — a thin, glitchy amber/orange terminal ribbon that invites
   the reader into the retro ASCII BBS. Monospace + phosphor glow + scanlines +
   an occasional RGB-split glitch, deliberately at odds with the soft forum
   aesthetic so it reads as "another interface lives here".
   --------------------------------------------------------------------------- */

.bbs-sash {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    width: 100%;
    padding: 0.4rem 1rem;
    margin: -0.5rem 0 1.75rem;   /* pull up snug under the hero card */
    border-top: 1px solid rgba(255, 176, 0, 0.35);
    border-bottom: 1px solid rgba(255, 176, 0, 0.35);
    background:
        repeating-linear-gradient(
            to bottom,
            rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0) 2px,
            rgba(0, 0, 0, 0.28) 3px, rgba(0, 0, 0, 0.28) 4px),   /* scanlines */
        linear-gradient(90deg,
            rgba(120, 60, 0, 0.35), rgba(255, 140, 0, 0.14), rgba(120, 60, 0, 0.35));
    color: #ffb000;
    font-family: "Cascadia Mono", "JetBrains Mono", "DejaVu Sans Mono", Consolas, monospace;
    font-size: 0.78rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-decoration: none;
    text-shadow: 0 0 6px rgba(255, 176, 0, 0.55);
    cursor: pointer;
    overflow: hidden;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.bbs-sash:hover {
    color: #0a0a0a;
    background: #ffb000;
    text-shadow: none;
}
.bbs-sash:focus-visible {
    outline: 2px solid #ffd47a;
    outline-offset: 2px;
}

/* Blinking terminal caret bracketing the label. */
.bbs-sash .caret { animation: bbs-sash-blink 1.05s step-end infinite; }
@keyframes bbs-sash-blink { 50% { opacity: 0; } }

/* The label glitches: a periodic RGB-split judder over a steady phosphor flicker. */
.bbs-sash .label {
    position: relative;
    animation: bbs-sash-flicker 3.5s infinite steps(60);
}
.bbs-sash .label::before,
.bbs-sash .label::after {
    content: attr(data-text);
    position: absolute;
    inset: 0;
    clip-path: inset(0 0 0 0);
    opacity: 0.75;
    pointer-events: none;
}
.bbs-sash .label::before { color: #ff3b3b; animation: bbs-sash-glitch-r 3.2s infinite steps(40); }
.bbs-sash .label::after  { color: #35d0ff; animation: bbs-sash-glitch-c 2.7s infinite steps(40); }

@keyframes bbs-sash-flicker {
    0%, 92%, 100% { opacity: 1; }
    93% { opacity: 0.72; }
    97% { opacity: 0.9; }
}
@keyframes bbs-sash-glitch-r {
    0%, 88%, 100% { transform: translate(0, 0); opacity: 0; }
    90% { transform: translate(-2px, -1px); opacity: 0.8; }
    94% { transform: translate(2px, 1px); opacity: 0.8; }
}
@keyframes bbs-sash-glitch-c {
    0%, 85%, 100% { transform: translate(0, 0); opacity: 0; }
    88% { transform: translate(2px, 0); opacity: 0.75; }
    92% { transform: translate(-2px, 1px); opacity: 0.75; }
}

@media (prefers-reduced-motion: reduce) {
    .bbs-sash .caret,
    .bbs-sash .label,
    .bbs-sash .label::before,
    .bbs-sash .label::after { animation: none !important; }
    .bbs-sash .label::before,
    .bbs-sash .label::after { opacity: 0 !important; }
}

/* ---------------------------------------------------------------------------
   31. Narrow-viewport thread reading pass          (user feedback, 2026-09)

   Reported: "Lots of space and overlap making it hard to follow a thread" on
   mobile. Two separate defects, fixed separately below:

     (a) a genuine z-overlap — the fixed bottom nav sitting on top of the chat
         composer and the last message; and
     (b) vertical rhythm — the thread stack burning ~25-35% more height than it
         needs, so a phone screen shows two or three posts instead of four.

   Scope discipline: this section changes spacing and box geometry only. No
   colours are introduced, so the runtime-injected `[data-theme="light"]`
   palette in stores/preferences.rs stays authoritative and untouched. The
   compact-density overrides injected from the same place are all `!important`
   and target `.glass-card` / `.space-y-6` / `.space-y-4` / `.py-3`, none of
   which this section touches, so the two passes compose rather than fight.

   Selector note: several rules below have to reach Leptos markup that carries
   only Tailwind utilities (the .rs files are owned elsewhere and were read but
   not edited). Each such selector is qualified with a `:has()` or a multi-class
   signature that was verified unique across `src/` — the qualifier is named in
   the comment so a future rename fails loudly rather than silently widening the
   rule. `style.css` is linked AFTER `tailwind.dist.css` in index.html, so
   equal-specificity overrides of utilities win on order alone.

   Breakpoint: `max-width: 639px` throughout, matching §19 and the underside of
   Tailwind's `sm:` (min-width 640px). Using 640px here would double-apply in
   the 1px overlap with every `sm:` utility on these surfaces.
   --------------------------------------------------------------------------- */

/* -- 31a. THE OVERLAP ------------------------------------------------------
   Root cause: `src/pages/channel.rs:816` and `src/pages/dm_chat.rs:166` size
   the chat column with `h-[calc(100vh-64px)]` — the viewport minus the 64px
   sticky header. That reserves nothing for the *fixed* `.mobile-bottom-nav`,
   so the compose bar pinned to the bottom of that flex column, its send
   button, and the tail of the message list all sat underneath the nav.

   §19's `main { padding-bottom }` cannot rescue this: the column's height is
   absolute, not content-driven, so padding on the ancestor pushes the page
   taller without moving the column's bottom edge off the nav.

   Also re-based on `100dvh` (the *dynamic* viewport) so iOS Safari's
   collapsing URL bar stops claiming the same band a second time — `100vh`
   there is the *large* viewport and is always too tall.

   Same specificity as Tailwind's arbitrary-value utility; wins on link order.
   `100vh` is kept as a first declaration so a browser without `dvh` still gets
   the nav compensation rather than dropping the whole rule. */
@media (max-width: 639px) {
    .h-\[calc\(100vh-64px\)\] {
        height: calc(
            100vh - 64px
            - var(--forum-mobile-nav-h, 3.5rem)
            - env(safe-area-inset-bottom, 0px)
        );
        height: calc(
            100dvh - 64px
            - var(--forum-mobile-nav-h, 3.5rem)
            - env(safe-area-inset-bottom, 0px)
        );
    }

    /* Same defect, same fix, different surface: the notification drawer
       (notification_center.rs:114) is `fixed top-16` with
       `h-[calc(100dvh-4rem)]`, so its bottom edge lands at the viewport floor
       and the nav — which shares its `z-50` — covers the last notification row
       and its tap target. Subtract the same band. */
    .h-\[calc\(100dvh-4rem\)\] {
        height: calc(
            100dvh - 4rem
            - var(--forum-mobile-nav-h, 3.5rem)
            - env(safe-area-inset-bottom, 0px)
        );
    }
}

/* -- 31b. Composer keyboard / safe-area clearance --------------------------
   The thread page's single bottom composer (`#thread-reply-composer`,
   thread.rs:958) is scrolled into view by `el.scroll_into_view()` when the
   reader taps Reply on a post (thread.rs:673-679). The default alignment can
   land it flush with the viewport bottom, i.e. behind the nav. `scroll-margin`
   is the correct lever — it changes where the scroll stops, not the layout.

   Deliberately NOT inside the media query: on desktop the token is undeclared
   and the fallback resolves to 0px, making this a no-op there. */
#thread-reply-composer {
    scroll-margin-bottom: calc(
        var(--forum-mobile-nav-h, 0px) + env(safe-area-inset-bottom, 0px) + 0.5rem
    );
}

@media (max-width: 639px) {
    /* -- 31c. Thread page gutter -------------------------------------------
       thread.rs:841 opens the page with `p-4 sm:p-6` (16px all round at this
       width). On a 360px screen that is 32px — nearly a tenth of the width —
       spent on empty margin before the post text starts. 12px still separates
       the cards from the screen edge without the letterbox feel.

       Qualified with `:has(article.rounded-xl.p-5)` because section.rs:357
       shares the same `max-w-4xl mx-auto p-4 sm:p-6` root; the topic-root
       `<article>` (thread.rs:1176-1177) exists only on the thread page. */
    .max-w-4xl.mx-auto.p-4:has(article.rounded-xl.p-5) {
        padding: 0.75rem;
    }

    /* -- 31d. Topic-root card ----------------------------------------------
       `bg-gray-800/70 border rounded-xl p-5 mt-4` (thread.rs:1177): 20px of
       padding and 16px of top margin. The root post is the one card that
       deserves emphasis, so it stays the roomiest of the three — 14px padding
       (-30%) and 12px top margin (-25%), which is still a visible step above
       the reply cards below it. */
    article.bg-gray-800\/70.rounded-xl.p-5 {
        padding: 0.875rem;
        margin-top: 0.75rem;
    }

    /* -- 31e. Reply cards --------------------------------------------------
       `bg-gray-800/40 border border-gray-700/50 rounded-lg p-4`
       (thread.rs:1292) — verified unique to the thread ReplyCard. 16px → 12px
       vertical / 13px horizontal (-25%). Horizontal stays marginally larger
       than vertical so the text column keeps a comfortable inset while the
       stack tightens, which is where the reclaimed height actually matters. */
    div.bg-gray-800\/40.border-gray-700\/50.rounded-lg.p-4 {
        padding: 0.75rem 0.8125rem;
    }

    /* -- 31f. Gap between consecutive replies -------------------------------
       `space-y-3` (thread.rs:936) puts 12px between reply cards. Combined with
       31e that is 12 + 12 + 12 = 36px of dead band between one author's last
       line and the next author's name. 8px (-33%) still reads as a separator
       because each card carries its own border and fill.

       Tailwind's `space-y-*` selector is `.space-y-3 > :not([hidden]) ~
       :not([hidden])` (specificity 0,3,0), so the override is deliberately
       qualified up past it rather than relying on order alone. The `:has()`
       pins this to the thread reply stack; other `space-y-3` lists elsewhere
       in the app keep their 12px. */
    div.space-y-3:has(> div.bg-gray-800\/40.border-gray-700\/50.rounded-lg.p-4) > * + * {
        margin-top: 0.5rem;
    }

    /* -- 31g. "N replies" heading ------------------------------------------
       The replies section opens with `mt-6` (24px) and its heading carries
       `mb-3` (12px) — 36px of chrome to announce a label that is already
       obvious from the cards beneath it. 16px / 8px (-33%).

       `h3.uppercase.tracking-wide.mb-3` is unique to thread.rs:924 (board.rs's
       uppercase h3 is `text-white` with no `mb-3`). */
    div.mt-6:has(> h3.uppercase.tracking-wide.mb-3) {
        margin-top: 1rem;
    }

    h3.uppercase.tracking-wide.mb-3 {
        margin-bottom: 0.5rem;
    }

    /* -- 31h. Composer offset ----------------------------------------------
       `mt-6` (24px) between the last reply and the composer (thread.rs:958).
       16px keeps the composer visibly detached from the stack without the gulf.
       The composer's own `p-3` is already tight and is left alone. */
    #thread-reply-composer {
        margin-top: 1rem;
    }

    /* -- 31i. Avatar / meta row --------------------------------------------
       Both thread cards lay out as `flex items-start gap-3` (thread.rs:1180,
       1293) — a 12px channel between the avatar and the text column. At this
       width the avatar is small enough that 10px reads identically while
       returning width to the message body. Scoped to direct children of the
       two thread cards so no other `gap-3` row in the app is touched. */
    article.bg-gray-800\/70.rounded-xl.p-5 > div.gap-3,
    div.bg-gray-800\/40.border-gray-700\/50.rounded-lg.p-4 > div.gap-3 {
        gap: 0.625rem;
    }

    /* -- 31j. Nested-reply indentation -------------------------------------
       thread_view.rs:187 and :228 indent a nested reply with
       `ml-4 + border-l-2 + pl-3` = 16 + 2 + 12 = 30px. On a 360px screen with
       the page gutter that is ~12% of the line width gone to whitespace, and
       the 16px margin is pure void — the reader cannot see it, so it buys no
       legibility.

       Keep the 2px rule (that IS the "this is nested" signal), shrink the void
       around it: 8 + 2 + 10 = 20px (-33%). The rule now sits close to the text
       it brackets, which reads as nesting more clearly than a wide gap did.

       Verified unique: only thread_view.rs carries this class trio. */
    .ml-4.border-l-2.pl-3 {
        margin-left: 0.5rem;
        padding-left: 0.625rem;
    }

    /* Indentation cap. Reply threading is architecturally one level deep today
       (thread_view.rs gates the second-level affordance behind `allow_reply`,
       and thread.rs quote-and-appends rather than nesting), so this never
       fires as written. It exists so that if nesting is ever deepened, the
       second level and everything under it collapse to a 4+2+8 = 14px step
       instead of compounding 20px at a time until the text column is a sliver. */
    .ml-4.border-l-2.pl-3 .ml-4.border-l-2.pl-3 {
        margin-left: 0.25rem;
        padding-left: 0.5rem;
    }

    /* -- 31k. Send-button tap target ---------------------------------------
       message_input.rs:969 renders the send button at `w-8 h-8` — a 32px box,
       below the 44px guideline, and it is the single most-tapped control in a
       thread. Compactness must not make this worse, so rather than shrink
       anything nearby, expand the *hit area* to 44px with a transparent
       pseudo-element centred on the button. The visible 32px amber circle is
       unchanged, so the composer's density is untouched.

       `bg-amber-500` + `rounded-full` + `w-8 h-8` is unique to the send button;
       the sibling emoji/attach controls are `p-1.5 rounded-lg`. */
    button.w-8.h-8.rounded-full.bg-amber-500 {
        position: relative;
    }

    button.w-8.h-8.rounded-full.bg-amber-500::after {
        content: '';
        position: absolute;
        top: 50%;
        left: 50%;
        width: 44px;
        height: 44px;
        transform: translate(-50%, -50%);
        border-radius: 50%;
    }
}

/* -- 32. The 3D poker table -------------------------------------------------
   src/components/table3d.rs: a canvas with DOM labels pinned over it by
   js/table3d/overlay.js, which writes each label's transform and the numbers
   in its [data-t3d-field] spans, and marks seat labels with
   data-t3d-active (to act), -winner, -allin, -folded, -button and -check (a
   brief flash for a check). Wide screens get a 16:10 stage, phones a square
   one; the scene reframes its camera to whichever it is given.
   --------------------------------------------------------------------------- */

.t3d-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border-radius: 0.75rem;
    background: #0b0f14;
    isolation: isolate;
}

/* square on phones: a stadium table seen from above is wider than it is
   deep, so a taller stage would only add empty floor */
@media (max-width: 639px) {
    .t3d-stage { aspect-ratio: 1 / 1; }
}

.t3d-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    outline: none;
}

.t3d-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 1;
}

.t3d-overlay [data-t3d-anchor] {
    position: absolute;
    left: 0;
    top: 0;
    white-space: nowrap;
    will-change: transform;
}

.t3d-tag {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.25rem 0.625rem;
    border-radius: 9999px;
    background: rgba(10, 12, 16, 0.72);
    border: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
    backdrop-filter: blur(4px);
    font-size: 0.8125rem;
    transition: box-shadow 0.2s ease, border-color 0.2s ease, opacity 0.2s ease;
}

.t3d-name { color: #f3f4f6; font-weight: 600; max-width: 12rem; overflow: hidden; text-overflow: ellipsis; }
.t3d-num { color: #fcd34d; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.75rem; }

.t3d-tag[data-t3d-active] {
    border-color: rgba(251, 191, 36, 0.9);
    box-shadow: 0 0 0 2px rgba(251, 191, 36, 0.35), 0 2px 12px rgba(0, 0, 0, 0.4);
}

.t3d-tag[data-t3d-winner] {
    border-color: rgba(52, 211, 153, 0.95);
    box-shadow: 0 0 0 2px rgba(52, 211, 153, 0.35), 0 0 18px rgba(52, 211, 153, 0.35);
}

.t3d-tag[data-t3d-folded] { opacity: 0.55; }

.t3d-tag[data-t3d-allin] .t3d-num::after {
    content: " · ALL IN";
    color: #f59e0b;
    font-weight: 700;
}

.t3d-tag[data-t3d-check] { animation: t3d-check 0.42s ease-out; }

@keyframes t3d-check {
    0%, 100% { transform-origin: center; filter: none; }
    30% { filter: brightness(1.6); }
}

.t3d-amount {
    padding: 0.125rem 0.5rem;
    border-radius: 0.375rem;
    background: rgba(10, 12, 16, 0.66);
    color: #fde68a;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.75rem;
}

.t3d-pot { color: #fef3c7; font-weight: 600; }

.t3d-veil {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    background: radial-gradient(ellipse at center, #13281f 0%, #0b0f14 70%);
}

.t3d-veil .loading-ring {
    width: 40px;
    height: 40px;
    margin-bottom: 0.5rem;
}

/* inspection (js/table3d/inspect.js): while a group of cards is lifted to
   the camera, the bet and pot tags under it step aside; the seat labels
   stay, and the lifted cards are laid out between them */
.t3d-amount { transition: opacity 0.2s ease; }
.t3d-overlay[data-t3d-inspecting] .t3d-amount { opacity: 0; }

/* one tab stop per inspectable group, laid over its cards by
   js/table3d/peek-keys.js: invisible until focused from the keyboard, when
   its ring frames the cards; the pointer goes through to the canvas */
.t3d-peek {
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;
    padding: 0;
    border: 0;
    border-radius: 0.5rem;
    background: transparent;
    pointer-events: none;
    will-change: transform;
}

.t3d-peek:focus-visible {
    outline: 2px solid rgba(251, 191, 36, 0.95);
    outline-offset: 2px;
    box-shadow: 0 0 0 6px rgba(251, 191, 36, 0.18);
}

@media (prefers-reduced-motion: reduce) {
    .t3d-tag { transition: none; }
    .t3d-tag[data-t3d-check] { animation: none; }
    .t3d-amount { transition: none; }
}

/* -- 33. Card inspection on the flat table ------------------------------------
   src/components/flat_peek.rs: hovering (or tapping, or Enter on) a group
   of cards lays a fixed clone over it in .peek2d-layer (a child of <body>)
   and transitions it to the viewport's centre, enlarged; the slot keeps its
   place (transparent while the clone is up), so nothing reflows. Above the page
   and the sticky header, below every modal (.modal-backdrop is 9000).
   --------------------------------------------------------------------------- */

.peek2d-slot {
    border-radius: 0.5rem;
    outline: none;
    /* tapping to lift and again to drop must not read as a double-tap zoom */
    touch-action: manipulation;
}

.peek2d-slot[role="button"] { cursor: zoom-in; }

.peek2d-slot:focus-visible {
    outline: 2px solid rgba(251, 191, 36, 0.95);
    outline-offset: 3px;
}

.peek2d-layer {
    position: fixed;
    inset: 0;
    z-index: 8000;
    pointer-events: none;
}

/* lifted by a tap: the enlarged cards cover the action buttons, so the tap
   that puts them down lands on the layer, never on a button underneath it
   (components/flat_peek.rs, `FlatPeek::capture_taps`) */
.peek2d-layer[data-peek-capture] {
    pointer-events: auto;
    touch-action: none;
}

.peek2d-clone {
    position: fixed;
    margin: 0;
    box-sizing: border-box;
    transform-origin: 50% 50%;
    transition: transform 450ms cubic-bezier(0.16, 1, 0.3, 1);
    pointer-events: none;
    filter: drop-shadow(0 18px 28px rgba(0, 0, 0, 0.55));
}

/* the flourish: a small tilt, either way, that settles as the cards arrive */
.peek2d-clone.peek2d-up { animation: peek2d-tilt 450ms ease-out; }
.peek2d-clone.peek2d-down { transition: transform 350ms cubic-bezier(0.55, 0, 1, 0.45); }

@keyframes peek2d-tilt {
    0% { rotate: 0deg; }
    40% { rotate: var(--peek-tilt, 2deg); }
    100% { rotate: 0deg; }
}

/* reduced motion: a short crossfade where the cards end up, no travel */
.peek2d-clone.peek2d-fade {
    transition: opacity 140ms linear;
    opacity: 0;
    animation: none;
}
.peek2d-clone.peek2d-fade.peek2d-up { opacity: 1; }

/* -- 34. Keyboard hints on touch screens ------------------------------------
   The poker table labels its buttons with their shortcut keys (1, 2, 3,
   Enter) and prints a key legend (src/pages/table.rs). A phone has no such
   keys, and the hints only crowd the buttons, so a device whose only pointer
   is coarse and cannot hover hides them.
   --------------------------------------------------------------------------- */
@media (hover: none) and (pointer: coarse) {
    .kbd-hint { display: none; }
}
