:root {
    /* Flat colors — used for small elements (tags, borders) where a gradient would look busy */
    --color-primary: #3d6ff2;
    --color-primary-hover: #2c56c9;
    --color-danger: #dc2626;
    --color-danger-hover: #b91c1c;
    --color-success: #059669;
    --color-success-hover: #047857;
    --color-warning: #d97706;
    --color-warning-hover: #b45309;

    /* Gradients — reserved for buttons and other contained, clickable surfaces */
    --gradient-primary: linear-gradient(135deg, #5b8def 0%, #7c5cf5 100%);
    --gradient-success: linear-gradient(135deg, #34d399 0%, #059669 100%);
    --gradient-warning: linear-gradient(135deg, #fbbf24 0%, #d97706 100%);
    --gradient-danger: linear-gradient(135deg, #f87171 0%, #dc2626 100%);

    --shadow-primary: 0 4px 14px rgba(61, 111, 242, 0.3);
    --shadow-primary-hover: 0 6px 20px rgba(61, 111, 242, 0.4);
    --shadow-success: 0 4px 14px rgba(5, 150, 105, 0.35);
    --shadow-success-hover: 0 6px 20px rgba(5, 150, 105, 0.45);
    --shadow-warning: 0 4px 14px rgba(217, 119, 6, 0.35);
    --shadow-warning-hover: 0 6px 20px rgba(217, 119, 6, 0.45);
    --shadow-danger: 0 4px 14px rgba(220, 38, 38, 0.35);
    --shadow-danger-hover: 0 6px 20px rgba(220, 38, 38, 0.45);

    --color-bg: #f6f8fd;
    --color-surface: #ffffff;
    --color-surface-tint: #eef3ff;
    --color-border: #dbe4f5;
    --color-text: #1f2937;
    --color-text-muted: #5b6472;

    --radius-sm: 4px;
    --radius-md: 8px;
    --shadow-sm: 0 4px 8px rgba(61, 111, 242, 0.08);

    --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

    /* Type scale — one standard set of sizes, used across every stylesheet instead of
       ad-hoc rem/px values, so text reads consistently from page to page. */
    --text-xs: 0.75rem;    /* 12px — badges, timestamps, fine print */
    --text-sm: 0.875rem;   /* 14px — secondary text, labels, form hints */
    --text-base: 1rem;     /* 16px — body copy */
    --text-lg: 1.125rem;   /* 18px — lead paragraphs, emphasized body */
    --text-xl: 1.375rem;   /* 22px — card/section titles */
    --text-2xl: 1.75rem;   /* 28px — page headings */
    --text-3xl: 2.25rem;   /* 36px — hero/brand text */

    /* Draft badge/icon tint (draftsPage.html, composeModal's "Save draft" affordance) - reuses
       --color-warning's own hue rather than a new color, just a lightly-tinted background
       version of it, same "semantic color + tint" pattern already used elsewhere
       (.share-option-danger:hover's rgba(220, 38, 38, 0.1), for example). */
    --color-draft-bg: rgba(217, 119, 6, 0.12);

    /* Bookmark accent - deliberately its own hue (violet), distinct from primary/danger/warning/
       success, so a saved post reads as its own kind of state rather than borrowing a color
       that already means something else (Like's blue, a warning's amber). */
    --color-bookmark: #7c5cf5;
    --color-bookmark-hover: #6a45e8;
    --color-bookmark-bg: rgba(124, 92, 245, 0.1);

    /* Repost accent - same reasoning as bookmark's own comment above: its own hue (green),
       distinct from Like's blue and Save's violet, so "I reposted this" reads as its own state
       rather than borrowing a color that already means something else. */
    --color-repost: #1a9c6b;
    --color-repost-hover: #158058;
    --color-repost-bg: rgba(26, 156, 107, 0.1);

    --header-glass: rgba(255, 255, 255, 0.14);
    --header-glass-border: rgba(255, 255, 255, 0.25);
    --color-surface-translucent: rgba(255, 255, 255, 0.92);
    --ocean-leaves-bg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='600'%3E%3Cg fill='white'%3E%3Cpath fill-opacity='0.34' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(30,550) rotate(15) scale(0.9)'/%3E%3Cpath fill-opacity='0.28' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(90,500) rotate(-40) scale(1.15)'/%3E%3Cpath fill-opacity='0.32' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(200,470) rotate(60) scale(0.7)'/%3E%3Cpath fill-opacity='0.24' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(150,400) rotate(10) scale(1.0)'/%3E%3Cpath fill-opacity='0.32' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(260,350) rotate(-25) scale(0.85)'/%3E%3Cpath fill-opacity='0.3' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(50,300) rotate(45) scale(0.95)'/%3E%3Cpath fill-opacity='0.26' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(120,230) rotate(-15) scale(1.2)'/%3E%3Cpath fill-opacity='0.32' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(230,180) rotate(30) scale(0.75)'/%3E%3Cpath fill-opacity='0.28' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(40,110) rotate(-50) scale(1.05)'/%3E%3Cpath fill-opacity='0.24' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(180,50) rotate(20) scale(0.9)'/%3E%3Cpath fill-opacity='0.32' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(270,10) rotate(-10) scale(0.95)'/%3E%3C/g%3E%3C/svg%3E");
    --ocean-gradient: linear-gradient(180deg, #1a4d8f 0%, #2f66d1 45%, #5b8def 85%, #7ba8ef 100%);
}

/* Dark mode - same token names, redefined values, so every stylesheet that already reads
   var(--color-*) picks this up automatically with no other changes needed. Toggled by
   js/theme.js setting/removing data-theme="dark" on <html>, persisted in localStorage.
   Accents are brightened slightly (not just inverted) so they don't look muddy on dark
   surfaces - a standard dark-mode adjustment. */
:root[data-theme="dark"] {
    --color-primary: #6d93ff;
    --color-primary-hover: #8aa9ff;
    --color-danger: #f87171;
    --color-danger-hover: #fca5a5;
    --color-success: #34d399;
    --color-success-hover: #6ee7b7;
    --color-warning: #fbbf24;
    --color-warning-hover: #fcd34d;

    --gradient-primary: linear-gradient(135deg, #4d7ef0 0%, #8a6ef5 100%);
    --gradient-success: linear-gradient(135deg, #2fbf8a 0%, #059669 100%);
    --gradient-warning: linear-gradient(135deg, #f5b43e 0%, #c2760a 100%);
    --gradient-danger: linear-gradient(135deg, #f0645f 0%, #b91c1c 100%);

    --shadow-primary: 0 4px 14px rgba(109, 147, 255, 0.35);
    --shadow-primary-hover: 0 6px 20px rgba(109, 147, 255, 0.45);
    --shadow-success: 0 4px 14px rgba(52, 211, 153, 0.3);
    --shadow-success-hover: 0 6px 20px rgba(52, 211, 153, 0.4);
    --shadow-warning: 0 4px 14px rgba(251, 191, 36, 0.3);
    --shadow-warning-hover: 0 6px 20px rgba(251, 191, 36, 0.4);
    --shadow-danger: 0 4px 14px rgba(248, 113, 113, 0.3);
    --shadow-danger-hover: 0 6px 20px rgba(248, 113, 113, 0.4);

    --color-bg: #0b101b;
    --color-surface: #161d2c;
    --color-surface-tint: #1d2740;
    --color-border: #2a3550;
    --color-text: #e7ecf7;
    --color-text-muted: #92a0bd;

    --shadow-sm: 0 4px 10px rgba(0, 0, 0, 0.35);

    --color-draft-bg: rgba(251, 191, 36, 0.14);

    --color-bookmark: #a78bfa;
    --color-bookmark-hover: #c4b5fd;
    --color-bookmark-bg: rgba(167, 139, 250, 0.16);

    --color-repost: #4fd39a;
    --color-repost-hover: #6ee0ac;
    --color-repost-bg: rgba(79, 211, 154, 0.16);

    --header-glass: rgba(255, 255, 255, 0.05);
    --header-glass-border: rgba(255, 255, 255, 0.1);
    --color-surface-translucent: rgba(22, 29, 44, 0.92);

    /* Same leaves-over-ocean motif, deepened to a midnight palette rather than inverted or
       blacked out - "same scene, nighttime" instead of a different design. Leaf opacity is
       halved via a dedicated dark SVG (fill-opacity can't be scaled with a CSS variable) so
       they read as a quiet ambient detail instead of competing with the darker cards. */
    --ocean-leaves-bg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='600'%3E%3Cg fill='white'%3E%3Cpath fill-opacity='0.17' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(30,550) rotate(15) scale(0.9)'/%3E%3Cpath fill-opacity='0.14' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(90,500) rotate(-40) scale(1.15)'/%3E%3Cpath fill-opacity='0.16' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(200,470) rotate(60) scale(0.7)'/%3E%3Cpath fill-opacity='0.12' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(150,400) rotate(10) scale(1.0)'/%3E%3Cpath fill-opacity='0.16' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(260,350) rotate(-25) scale(0.85)'/%3E%3Cpath fill-opacity='0.15' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(50,300) rotate(45) scale(0.95)'/%3E%3Cpath fill-opacity='0.13' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(120,230) rotate(-15) scale(1.2)'/%3E%3Cpath fill-opacity='0.16' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(230,180) rotate(30) scale(0.75)'/%3E%3Cpath fill-opacity='0.14' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(40,110) rotate(-50) scale(1.05)'/%3E%3Cpath fill-opacity='0.12' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(180,50) rotate(20) scale(0.9)'/%3E%3Cpath fill-opacity='0.16' d='M0,-11 C6,-8 8,0 6,7 C4,11 -4,11 -6,7 C-8,0 -6,-8 0,-11 Z' transform='translate(270,10) rotate(-10) scale(0.95)'/%3E%3C/g%3E%3C/svg%3E");
    --ocean-gradient: linear-gradient(180deg, #050914 0%, #0b1830 45%, #142544 85%, #1c3358 100%);
}

* {
    box-sizing: border-box;
}

/* Ambient background: falling leaves over the same blue gradient family as the login/register
   hero - "Bodh" (tree) motif, "Sea" (blue) color, tying both halves of the name together.
   Built entirely as layered CSS backgrounds (a repeating leaf tile that falls forever, plus
   the gradient) rather than separate DOM elements, so it's guaranteed to always render behind
   every page's actual content - no z-index/stacking risk, no markup changes. */
body {
    font-family: var(--font-family);
    margin: 0;
    color: #ffffff;
    background-image: var(--ocean-leaves-bg), var(--ocean-gradient);
    background-repeat: repeat, no-repeat;
    background-position: 0 0, center top;
    background-size: 300px 600px, cover;
    background-attachment: fixed, fixed;
    animation: leaves-fall 16s linear infinite;
}

@keyframes leaves-fall {
    from { background-position: 0 0,     center top; }
    to   { background-position: 0 600px, center top; }
}

/* Shared app header, used by fragments/nav.html on every app page (not the standalone auth pages).
   Frosted glass over the blue background - same padding/layout as before, only the fill changes.
   position+z-index here (not just on the profile dropdown inside it) matters: .filter-sort-container
   below explicitly promotes itself to z-index 10 (and its own dropdown panel to 20) since its
   backdrop-filter creates a stacking context - without the header ALSO being explicitly
   stacked, the nav profile dropdown's z-index:30 is trapped inside an unstacked parent and
   loses to that sibling regardless of its own z-index. */
.app-header {
    position: relative;
    z-index: 50;
}

.app-header .header-container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding: 20px;
    background: var(--header-glass);
    border: 1px solid var(--header-glass-border);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-radius: var(--radius-md);
    transition: background 0.3s ease, border-color 0.3s ease;
}

.app-header .app-title {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: inherit;
    flex: 0 0 auto;
    white-space: nowrap;
}

.brand-mark {
    width: 30px;
    height: 30px;
    color: #ffffff;
    flex: 0 0 auto;
}

/* Avatar base — an initial-letter circle, sized/colored per use-site (comment thread vs.
   post-list byline). Shared here since both viewPostByID.css and dashboardStyle.css use it. */
.avatar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    overflow: hidden;
    color: #fff;
    font-weight: bold;
}

.avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Bookmark toggle base - shared here (not dashboardStyle.css) since viewPostByID.html renders
   standalone and never loads that stylesheet, but does need this for its own reaction-bar pill.
   Icon-only by default (postRows.html's feed-row usage); .bookmark-pill in viewPostByID.css
   layers the bordered-pill look on top for the full post page / modal. .saved fills the outline
   icon and tints it violet - its own accent, not borrowed from Like's blue or a warning's amber,
   so a saved post reads as its own kind of state. The .pop pseudo-element gives the click a
   brief radial flash (js/bookmark.js toggles .pulse to restart it) - same "immediate feedback"
   principle the reaction buttons already follow, just without a page reload. */
.bookmark-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: none;
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    position: relative;
    flex: 0 0 auto;
    font-family: inherit;
}

.bookmark-toggle .icon {
    width: 16px;
    height: 16px;
    position: relative;
    z-index: 1;
}

.bookmark-toggle:hover {
    background: var(--color-bookmark-bg);
    color: var(--color-bookmark);
}

.bookmark-toggle.saved {
    color: var(--color-bookmark);
}

.bookmark-toggle.saved .icon {
    fill: currentColor;
}

.bookmark-toggle .pop {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: var(--color-bookmark-bg);
    transform: scale(0);
    opacity: 0;
    pointer-events: none;
}

.bookmark-toggle.pulse .pop {
    animation: bookmarkPop 0.42s ease-out;
}

@keyframes bookmarkPop {
    0% { transform: scale(0.3); opacity: 0.9; }
    100% { transform: scale(2.1); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .bookmark-toggle.pulse .pop {
        animation: none;
    }
}

/* Preset avatar icon (fragments/avatarContent.html) - sized as a share of whatever circle it's
   in rather than the fixed 16px .icon default, since that circle ranges from a 20px dashboard
   byline up to a 108px profile header. */
svg.avatar-icon {
    width: 55%;
    height: 55%;
    flex: 0 0 auto;
    vertical-align: 0;
}

.app-header nav {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    flex: 1 1 auto;
    justify-content: flex-end;
    min-width: 0;
}

/* ---- Nav profile dropdown (fragments/nav.html) - avatar trigger + panel replacing the old
   separate logout button and standalone theme-toggle circle. ---- */
.nav-profile {
    position: relative;
    flex: 0 0 auto;
}

.nav-avatar-trigger {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 2px solid var(--header-glass-border);
    background: transparent;
    padding: 0;
    cursor: pointer;
    display: block;
}

.nav-avatar-trigger .avatar {
    width: 100%;
    height: 100%;
    font-size: var(--text-sm);
    background: var(--gradient-primary);
}

.nav-profile-dropdown {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    z-index: 30;
    width: 250px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 14px;
    box-shadow: 0 16px 40px rgba(10, 16, 30, 0.25);
    overflow: hidden;
}

.nav-profile-dropdown[hidden] {
    display: none;
}

.nav-dd-header {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 16px;
}

.nav-dd-avatar {
    width: 42px;
    height: 42px;
    font-size: var(--text-base);
    background: var(--gradient-primary);
    flex: 0 0 auto;
}

.nav-dd-name {
    font-weight: 700;
    font-size: var(--text-sm);
    color: var(--color-text);
}

.nav-dd-handle {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

.nav-dd-divider {
    height: 1px;
    background: var(--color-border);
}

.nav-dd-items {
    padding: 6px;
}

.nav-dd-item {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    padding: 10px;
    border: none;
    background: none;
    border-radius: 8px;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--text-sm);
    color: var(--color-text);
    text-align: left;
    text-decoration: none;
    box-sizing: border-box;
}

.nav-dd-item:hover {
    background: var(--color-surface-tint);
}

.nav-dd-item svg {
    width: 17px;
    height: 17px;
    color: var(--color-text-muted);
    flex: 0 0 auto;
}

.nav-dd-item.nav-dd-danger {
    color: var(--color-danger);
}

.nav-dd-item.nav-dd-danger svg {
    color: var(--color-danger);
}

.nav-dd-grow {
    flex: 1 1 auto;
}

/* Toggle switch for the dark-mode row - reflects state via [data-theme="dark"] on <html>,
   same source of truth js/theme.js already reads/writes. */
.nav-dd-switch {
    width: 34px;
    height: 19px;
    border-radius: 999px;
    background: var(--color-border);
    position: relative;
    flex: 0 0 auto;
    transition: background 0.2s ease;
}

.nav-dd-switch::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.2s ease;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

:root[data-theme="dark"] .nav-dd-switch {
    background: var(--gradient-primary);
}

:root[data-theme="dark"] .nav-dd-switch::after {
    transform: translateX(15px);
}

/* Icons: inherit currentColor from whatever button/link/text contains them */
.icon {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    vertical-align: -3px;
}

/* Buttons: gradient fill + a soft glow in the same hue, small lift on hover.
   One system, one accent per action - primary/success/warning/danger. */
.login-button,
.logout-button,
.pagination-button,
.filter-apply-button {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    color: white;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    text-decoration: none;
    font-family: inherit;
    font-weight: 600;
}

.login-button:hover,
.logout-button:hover,
.pagination-button:hover,
.filter-apply-button:hover {
    transform: translateY(-1px);
}

.login-button,
.pagination-button,
.filter-apply-button {
    background: var(--gradient-primary);
    box-shadow: var(--shadow-primary);
}

.login-button:hover,
.pagination-button:hover,
.filter-apply-button:hover {
    box-shadow: var(--shadow-primary-hover);
}

.logout-button {
    background: var(--gradient-danger);
    box-shadow: var(--shadow-danger);
}

.logout-button:hover {
    box-shadow: var(--shadow-danger-hover);
}

/* Follow/unfollow toggle (profile.html's header + postDashboard.html's Active-writers widget,
   js/follow.js drives both). Not-following state mirrors .login-button's filled pill; the
   .following state instead mirrors .edit-profile-btn's own neutral outline look (profileStyle.css)
   since it visually replaces that exact button on someone else's profile - hovering while
   following shifts to a danger tint as the "about to unfollow" cue, color/border only, no text
   swap to "Unfollow". */
.follow-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 9px 16px;
    font-family: inherit;
    font-weight: 700;
    font-size: var(--text-sm);
    border: none;
    border-radius: 999px;
    cursor: pointer;
    background: var(--gradient-primary);
    color: #fff;
    box-shadow: var(--shadow-primary);
    transition: box-shadow 0.15s ease, background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.follow-btn:hover {
    box-shadow: var(--shadow-primary-hover);
}

.follow-btn.following {
    background: transparent;
    color: var(--color-text);
    border: 1px solid var(--color-border);
    box-shadow: none;
}

.follow-btn.following:hover {
    border-color: var(--color-danger);
    background: rgba(220, 38, 38, 0.1);
    color: var(--color-danger);
}

.follow-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.follow-btn.compact {
    padding: 5px 12px;
    font-size: var(--text-xs);
    flex-shrink: 0;
}

/* The follower/following list modal's own row for the viewer themselves - not a real toggle
   (js/follow.js skips it since it has no data-username), just a neutral badge in the same slot
   a real button would occupy. */
.follow-btn.self {
    background: transparent;
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
    box-shadow: none;
    cursor: default;
}

/* Dark mode toggle - lives in the header (fragments/nav.html) so it's on every page that
   shares that header, for logged-in and logged-out visitors alike. Both icons are always in
   the DOM; only one is shown at a time based on the current theme, so there's no layout
   shift or icon swap flicker on click. */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border: 1px solid var(--header-glass-border);
    border-radius: 50%;
    background: var(--header-glass);
    color: #ffffff;
    cursor: pointer;
    flex: 0 0 auto;
    transition: background 0.2s ease, border-color 0.2s ease;
}

.theme-toggle:hover {
    background: rgba(255, 255, 255, 0.22);
}

.theme-toggle .icon {
    width: 18px;
    height: 18px;
}

.theme-toggle .icon-moon {
    display: none;
}

:root[data-theme="dark"] .theme-toggle .icon-sun {
    display: none;
}

:root[data-theme="dark"] .theme-toggle .icon-moon {
    display: block;
}

/* Share/download kebab menu (fragments/shareMenu.html) - shared by the dashboard post-row
   and the post page's reaction bar, so it lives here in common.css rather than either
   page-specific stylesheet. js/share.js drives all the open/close and copy behavior. */
.options-menu {
    position: relative;
    display: inline-block;
}

.options-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
    font-family: inherit;
    padding: 0;
}

.options-trigger:hover,
.options-trigger[aria-expanded="true"] {
    border-color: var(--color-primary);
    color: var(--color-primary);
}

.options-trigger .icon {
    width: 13px;
    height: 13px;
}

.reaction-bar .options-trigger {
    width: 34px;
    height: 34px;
}

.reaction-bar .options-trigger .icon {
    width: 16px;
    height: 16px;
}

.share-panel {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 30;
    width: 260px;
    max-width: calc(100vw - 16px);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(61, 111, 242, 0.16);
    padding: 6px;
    text-align: left;
}

/* On a dense dashboard row, the excerpt/tags text runs almost the full card width, so a
   floating panel opened anywhere on the row will sit right beside some of that text - it
   isn't actually a rendering bug (the panel does correctly paint and receive clicks above
   everything else), but it reads as messy. Dimming the rest of the card while the menu is
   open - the same trick YouTube/Gmail use for their card kebab menus - makes the panel read
   unambiguously as a floating layer instead of "overlapping" the row's own content.
   Toggled by js/share.js on the closest .post-row when a kebab inside it opens/closes. */
.post-row.menu-open {
    z-index: 40;
}

.post-row.menu-open::after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(255, 255, 255, 0.72);
    border-radius: inherit;
    z-index: 25;
    pointer-events: none;
}

.share-option {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 11px;
    border: none;
    background: none;
    border-radius: 8px;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--color-text);
    text-align: left;
    text-decoration: none;
    box-sizing: border-box;
}

/* Delete Post, consolidated into this menu from its old standalone red button (modal post
   view only - see fragments/shareMenu.html's canEdit param) - red text on the same row layout
   instead of a solid button, matching how a destructive action reads in a dropdown menu. */
.share-option-danger,
.share-option-danger .icon {
    color: var(--color-danger);
}

.share-option-danger:hover {
    background: rgba(220, 38, 38, 0.1);
}

/* Shared "are you sure?" dialog (fragments/confirmDialog.html), replacing the browser's
   native confirm() for delete forms. */
.confirm-dialog-backdrop {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(5, 8, 15, 0.65);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
}

.confirm-dialog-backdrop[hidden] {
    display: none;
}

.confirm-dialog-card {
    width: 100%;
    max-width: 380px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 16px;
    box-shadow: 0 30px 60px -20px rgba(0, 0, 0, 0.6);
    padding: 24px 24px 20px;
    text-align: center;
}

.confirm-dialog-icon {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    margin: 0 auto 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(220, 38, 38, 0.14);
    color: var(--color-danger);
}

.confirm-dialog-icon .icon {
    width: 24px;
    height: 24px;
}

.confirm-dialog-title {
    font-size: var(--text-lg);
    font-weight: 800;
    margin: 0 0 8px;
    color: var(--color-text);
}

.confirm-dialog-message {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    line-height: 1.55;
    margin: 0 0 22px;
}

.confirm-dialog-actions {
    display: flex;
    gap: 10px;
}

.confirm-dialog-btn {
    flex: 1;
    border: none;
    border-radius: 999px;
    padding: 11px 0;
    font-size: var(--text-sm);
    font-weight: 700;
    cursor: pointer;
    font-family: inherit;
}

.confirm-dialog-cancel {
    background: var(--color-surface-tint);
    color: var(--color-text);
}

.confirm-dialog-cancel:hover {
    background: var(--color-border);
}

.confirm-dialog-danger {
    background: var(--gradient-danger);
    color: #fff;
    box-shadow: var(--shadow-danger);
}

.confirm-dialog-danger:hover {
    box-shadow: var(--shadow-danger-hover);
}

.share-option:hover {
    background: var(--color-surface-tint);
}

/* Scoped to .share-panel (not just .share-option) so this always wins over the dashboard's
   generic ".post-reactions .icon" rule (same specificity) regardless of stylesheet order -
   the share menu should render identically on the dashboard row and the post page. */
.share-panel .share-option .icon {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: var(--color-text-muted);
}

.share-option .chevron {
    margin-left: auto;
    width: 13px;
    height: 13px;
    transition: transform 0.2s ease;
}

.share-option[aria-expanded="true"] .chevron {
    transform: rotate(90deg);
}

/* The share menu is built from <span>s rather than <div>s (see fragments/shareMenu.html for
   why), so anything relying on block-level sizing/stacking needs an explicit display here -
   a bare <span> defaults to inline, where "height" and "width" have no effect. */
.share-divider {
    display: block;
    height: 1px;
    background: var(--color-border);
    margin: 4px 6px;
}

.accordion-body {
    display: block;
}

.accordion-body[hidden] {
    display: none;
}

.share-expanded {
    display: block;
    padding: 4px 8px 10px;
}

.platform-row {
    display: flex;
    gap: 8px;
    margin: 6px 0 12px;
}

.platform-btn {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    padding: 10px 4px 8px;
    border: 1px solid var(--color-border);
    border-radius: 10px;
    background: var(--color-surface);
    cursor: pointer;
    font-family: inherit;
    font-size: 10.5px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.platform-btn:hover {
    border-color: var(--color-primary);
    background: var(--color-surface-tint);
    color: var(--color-primary);
}

.platform-btn .icon-circle {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-surface-tint);
}

.platform-btn .icon-circle .icon {
    width: 16px;
    height: 16px;
    color: inherit;
}

.platform-btn.whatsapp .icon-circle {
    background: #e7f9ee;
    color: #25d366;
}

.platform-btn.x .icon-circle {
    background: #eceef0;
    color: #14181c;
}

.platform-btn.linkedin .icon-circle {
    background: #e7f1fb;
    color: #0a66c2;
}

.link-field {
    display: flex;
    align-items: stretch;
    border: 1px solid var(--color-border);
    border-radius: 20px;
    overflow: hidden;
    background: var(--color-bg);
}

.link-field input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: transparent;
    padding: 9px 12px;
    font-size: 12px;
    color: var(--color-text-muted);
    font-family: inherit;
    outline: none;
    text-overflow: ellipsis;
}

.copy-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border: none;
    padding: 0 14px;
    background: var(--gradient-primary);
    color: #fff;
    font-family: inherit;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
}

.copy-btn.copied {
    background: var(--gradient-success);
}

/* Download accordion's format checkboxes (fragments/shareMenu.html) - real <input>/<button>
   elements, not spans like the rest of this menu's options: an empirically-confirmed headless-
   Chrome DOM dump shows plain <button>/<input> nested inside the dashboard's post-row <a> stay
   correctly nested (no card-splitting), unlike a real nested <a> or <form> - those still need
   the span/role="button" treatment used elsewhere in this file. */
.format-option {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 11px 9px 21px;
    border-radius: 8px;
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--color-text);
}

.format-option:hover {
    background: var(--color-surface-tint);
}

.format-option input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--color-primary);
    flex: 0 0 auto;
}

.format-option .icon {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: var(--color-text-muted);
}

.format-label-grow {
    flex: 1 1 auto;
}

.download-confirm {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: calc(100% - 10px);
    margin: 6px 5px 2px;
    border: none;
    border-radius: 999px;
    padding: 9px 0;
    background: var(--gradient-primary);
    color: #fff;
    font-family: inherit;
    font-weight: 700;
    font-size: var(--text-sm);
    cursor: pointer;
}

.download-confirm:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.download-confirm .icon {
    width: 15px;
    height: 15px;
}

/* Shared responsive rules - the header/nav is used on every page via fragments/nav.html,
   so fixing it here covers the dashboard, post page, and every other page that includes it. */
@media (max-width: 600px) {
    .app-header {
        padding: 12px;
    }

    .app-header .header-container {
        padding: 14px;
        gap: 10px;
    }

    .app-header .app-title h1 {
        font-size: var(--text-lg);
    }

    .app-header nav {
        width: 100%;
    }

    .login-button,
    .logout-button {
        flex: 1 1 auto;
        justify-content: center;
    }

    /* Safety net independent of the anchor fix above: never let the dropdown's own width
       exceed the viewport on very narrow phones, regardless of where the trigger sits. */
    .nav-profile-dropdown {
        max-width: calc(100vw - 24px);
    }

    /* Media lightbox's fixed side arrows leave little headroom at narrow widths - shrink them
       so the media itself keeps most of the screen rather than the chrome around it. */
    .media-lightbox-arrow { width: 38px; height: 38px; }
    .media-lightbox-arrow .icon { width: 16px; height: 16px; }
    .media-lightbox-prev { left: 6px; }
    .media-lightbox-next { right: 6px; }
    .media-lightbox-stage { width: min(94vw, 900px); }
}

/* Flash-message toast (fragments/toast.html + js/toast.js) - "Saved as draft"/"Post
   published"/"Post deleted successfully" etc. Bottom-center rather than a corner: the FAB
   already claims bottom-right on dashboard-shell pages, and center keeps this safe on any
   page regardless of what else is anchored to an edge. */
.toast {
    position: fixed;
    left: 50%;
    bottom: 28px;
    transform: translate(-50%, 16px);
    z-index: 300;
    max-width: calc(100vw - 32px);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-text);
    font-size: var(--text-sm);
    font-weight: 600;
    padding: 12px 20px;
    border-radius: 999px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.3);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease, transform 0.2s ease;
    cursor: pointer;
}

.toast.show {
    opacity: 1;
    transform: translate(-50%, 0);
    pointer-events: auto;
}

.toast.toast-error {
    background: var(--color-danger);
    border-color: var(--color-danger);
    color: #fff;
}

@media (prefers-reduced-motion: reduce) {
    .toast {
        transition: opacity 0.01s linear;
        transform: translate(-50%, 0);
    }
}

/* ---------- Post media collage (shared by fragments/postRows.html's feed cards AND
   fragments/postMediaGallery.html's post-detail view) - moved here from dashboardStyle.css
   because viewPostByID.html (the standalone post page) never loads that stylesheet, only
   common.css + viewPostByID.css. One set of rules, both callers, no drift between them. ---------- */
/* height:auto + object-fit:contain (not cover) - a single image/video shows its own natural
   framing instead of being force-cropped to fill a fixed-height box, which on a wide/landscape
   image read as "stretched" and cut real content out of frame (see the approved design
   artifact). max-height is a safety cap for genuine outliers (an unusually tall portrait
   image), not the box's normal driving dimension anymore - most media just shows in full. */
.gallery-single img, .gallery-single video { width: 100%; height: auto; max-height: 300px; object-fit: contain; border-radius: 12px; display: block; }
.gallery-video-wrap { position: relative; display: block; }
/* Centered play badge over a video's own poster/first frame - no autoplay in-feed or in the
   collage, matches this project's existing "no motion unless the user asks for it" stance
   (see the leaf loader's own prefers-reduced-motion handling). */
.g-play {
    position: absolute; inset: 0; margin: auto; width: 44px; height: 44px; border-radius: 50%;
    background: rgba(15, 20, 35, 0.6); display: flex; align-items: center; justify-content: center;
}
.g-play .icon { width: 18px; height: 18px; color: #fff; }

.gallery-grid { display: grid; gap: 4px; border-radius: 12px; overflow: hidden; }
.gallery-grid.count-2 { grid-template-columns: 1fr 1fr; }
.gallery-grid.count-3 { grid-template-columns: 2fr 1fr; grid-template-rows: 1fr 1fr; }
.gallery-grid.count-3 .g-item:first-child { grid-row: 1 / 3; }
.gallery-grid.count-4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.g-item { position: relative; overflow: hidden; background: var(--color-surface-tint); border: 0; padding: 0; display: block; cursor: pointer; }
/* Still object-fit:cover, not contain - a multi-image grid needs matched cell heights to line
   up cleanly, and there's no single "natural ratio" that works for several different photos
   side by side the way there is for one image alone. min-height dropped 130px -> 90px instead,
   a smaller footprint overall without changing how the grid itself works (see the approved
   design artifact). */
.g-item img, .g-item video { width: 100%; height: 100%; object-fit: cover; display: block; min-height: 90px; }
/* The 5th+ image never gets its own cell - the 4th tile gets a "+N" scrim instead, so a big
   gallery still reads clearly at feed-card size rather than shrinking every cell to nothing.
   Items past the 4th are still rendered in the DOM (postMediaGallery.html renders the full
   list, not just the first 4) so the lightbox below can page through all of them - this rule
   is what keeps them out of the visible grid without deleting them. */
.g-item.more::after {
    content: attr(data-more); position: absolute; inset: 0; background: rgba(10, 15, 30, 0.55);
    color: #fff; font-size: var(--text-xl); font-weight: 700; display: flex; align-items: center; justify-content: center;
}
.gallery-grid .g-item:nth-child(n+5) { display: none; }
/* .gallery-single and .g-item are real buttons (js/mediaGallery.js opens the lightbox on
   click) - reset the button chrome a plain <img>/<video> tile shouldn't visibly have. */
.gallery-single, .g-item { font: inherit; text-align: left; }
.gallery-single:focus-visible, .g-item:focus-visible { outline: 3px solid var(--color-primary); outline-offset: -3px; }

/* ---------- Media lightbox (js/mediaGallery.js) - one overlay, built once and reused, opened
   by clicking any tile in a .post-view-media collage (post-detail view only, not the feed-card
   grid - that still opens the post itself, unchanged). Fixed/full-viewport by design so it
   can't be pushed off-screen by whatever layout it was opened from, same reasoning this
   project's other fixed-position UI (modals, toasts) already follows. ---------- */
.media-lightbox {
    position: fixed; inset: 0; background: rgba(4, 6, 12, 0.92); z-index: 1000;
    display: flex; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none; transition: opacity 0.15s ease;
}
.media-lightbox.open { opacity: 1; pointer-events: auto; }
.media-lightbox-close {
    position: absolute; top: 18px; right: 18px; width: 40px; height: 40px; border-radius: 50%;
    background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.16); color: #fff;
    display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.media-lightbox-close:hover { background: rgba(255, 255, 255, 0.16); }
.media-lightbox-close .icon { width: 18px; height: 18px; }
.media-lightbox-counter {
    position: absolute; top: 22px; left: 50%; transform: translateX(-50%);
    color: rgba(255, 255, 255, 0.85); font-size: var(--text-sm); font-weight: 700;
    font-variant-numeric: tabular-nums; letter-spacing: 0.02em;
}
.media-lightbox-arrow {
    position: absolute; top: 50%; transform: translateY(-50%); width: 48px; height: 48px; border-radius: 50%;
    background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.16); color: #fff;
    display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.media-lightbox-arrow:hover { background: rgba(255, 255, 255, 0.18); }
.media-lightbox-arrow .icon { width: 20px; height: 20px; }
.media-lightbox-prev { left: 18px; }
.media-lightbox-next { right: 18px; }
.media-lightbox-arrow[hidden] { display: none; }
.media-lightbox-stage { width: min(92vw, 900px); max-height: 84vh; display: flex; align-items: center; justify-content: center; }
.media-lightbox-stage img, .media-lightbox-stage video { max-width: 100%; max-height: 84vh; border-radius: 10px; display: block; object-fit: contain; }
.media-lightbox-dots { position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%); display: flex; gap: 8px; }
.media-lightbox-dots button {
    width: 7px; height: 7px; border-radius: 50%; border: 0; background: rgba(255, 255, 255, 0.3); cursor: pointer; padding: 0;
}
.media-lightbox-dots button.active { background: #fff; width: 18px; border-radius: 4px; }
.media-lightbox-dots[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
    .media-lightbox { transition: none; }
}
