/* ============================================================================
   The forum's own design system.

   WHY THIS FILE EXISTS AT ALL. The directory is strict monochrome: a thirteen-step ink ramp
   carries every surface, and colour appears in exactly three places. That restraint is right for
   a directory, where the product is other people's businesses and anything decorative competes
   with them. A forum is the opposite - it is a room full of people talking, and the things a
   reader needs to pick out at a glance (who is a moderator, which thread is busy, where their own
   post is) are precisely what a monochrome system has no vocabulary for.

   IT SHARES NOTHING WITH brandligo.css AND MUST NOT. The layout loads this file alone: no
   Bootstrap, no bootstrap-bridge, no ink ramp. Two token systems on one page is how you get a
   button that is half one theme and half the other, and the bridge exists specifically to repaint
   Bootstrap's components - which nothing here uses.

   EVERY COLOUR IS A TOKEN, and DesignSystemTest is extended to enforce that on forum views. The
   point of a second palette is that it is a NAMED second system, not permission to write hex
   wherever it is convenient - which is what "an entirely different theme" would otherwise decay
   into by the third screen.

   PREFIX: fx- (forum experience). Deliberately not bl-, so a class from one system pasted into the
   other renders unstyled and obviously wrong rather than subtly wrong.
   ========================================================================== */

/* ============================================================================
   1. Tokens
   ========================================================================== */

:root {
    /* --- ink ------------------------------------------------------------- */
    --fx-ink-0: #ffffff;
    --fx-ink-50: #f7f8fa;
    --fx-ink-100: #eef0f4;
    --fx-ink-200: #dfe3ea;
    --fx-ink-300: #c6ccd8;
    --fx-ink-400: #99a2b3;
    --fx-ink-500: #6b7488;
    --fx-ink-600: #4d5566;
    --fx-ink-700: #363d4c;
    --fx-ink-800: #232935;
    --fx-ink-900: #151a23;

    /* --- the accent, and it is the whole reason for a second palette -----
       A deep teal: distinct from anything in the directory at a glance, and dark enough at 600 to
       carry white text at AA on the buttons and links that use it. */
    --fx-accent-50: #e8f5f4;
    --fx-accent-100: #c9e7e4;
    --fx-accent-300: #6fbdb6;
    --fx-accent-500: #2b8f86;
    --fx-accent-600: #217a72;
    --fx-accent-700: #1a615b;

    /* --- a warm second, for the "busy" and "new" signals a forum needs --- */
    --fx-warm-100: #fdeee0;
    --fx-warm-500: #d97b28;
    --fx-warm-700: #a45a17;

    /* --- roles. Four states, four colours, and they are the one place this
       theme spends colour on identity. A moderator has to be recognisable in a
       thread of twenty replies without reading the word. ------------------ */
    --fx-role-moderator: #6d3fc4;
    --fx-role-moderator-bg: #f0eafc;
    --fx-role-leader: var(--fx-accent-600);
    --fx-role-leader-bg: var(--fx-accent-50);
    --fx-role-member: var(--fx-ink-600);
    --fx-role-member-bg: var(--fx-ink-100);
    --fx-role-new: var(--fx-warm-700);
    --fx-role-new-bg: var(--fx-warm-100);

    /* --- status --------------------------------------------------------- */
    --fx-danger: #b3261e;
    --fx-danger-bg: #fdecea;
    --fx-success: #1e7a4d;
    --fx-success-bg: #e7f5ee;

    /* --- semantic surfaces ---------------------------------------------- */
    --fx-page: var(--fx-ink-50);
    --fx-surface: var(--fx-ink-0);
    --fx-surface-sunken: var(--fx-ink-100);
    --fx-border: var(--fx-ink-200);
    --fx-border-strong: var(--fx-ink-300);
    --fx-text: var(--fx-ink-800);
    --fx-text-muted: var(--fx-ink-500);
    --fx-text-subtle: var(--fx-ink-400);
    --fx-link: var(--fx-accent-600);

    /* --- type. A larger base than the directory: this is a reading surface
       and 16px body text on a page of prose is the floor, not the target. -- */
    --fx-font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --fx-text-xs: 0.75rem;
    --fx-text-sm: 0.875rem;
    --fx-text-base: 1rem;
    --fx-text-md: 1.0625rem;
    --fx-text-lg: 1.25rem;
    --fx-text-xl: 1.5rem;
    --fx-text-2xl: 1.875rem;

    /* --- space ---------------------------------------------------------- */
    --fx-space-1: 0.25rem;
    --fx-space-2: 0.5rem;
    --fx-space-3: 0.75rem;
    --fx-space-4: 1rem;
    --fx-space-5: 1.5rem;
    --fx-space-6: 2rem;
    --fx-space-7: 3rem;

    --fx-radius: 0.5rem;
    --fx-radius-sm: 0.3125rem;
    --fx-radius-pill: 999px;

    --fx-shadow: 0 1px 2px rgb(21 26 35 / 6%), 0 2px 8px rgb(21 26 35 / 4%);
    --fx-shadow-lift: 0 2px 4px rgb(21 26 35 / 8%), 0 8px 24px rgb(21 26 35 / 8%);
}

/* ============================================================================
   2. Dark mode

   TOKEN OVERRIDES ONLY, exactly like the directory's. The rule that matters is that the SEMANTIC
   tokens flip and the raw ramp does not: mix them and you get a surface that inverts under
   contents that do not, which is what left the admin sidebar unreadable in dark mode.

   The accent moves LIGHTER rather than staying put, because a colour that carries white text on a
   white page carries nothing on a dark one.
   ========================================================================== */

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --fx-page: #10141b;
        --fx-surface: #171d26;
        --fx-surface-sunken: #1f2733;
        --fx-border: #2b3543;
        --fx-border-strong: #3a4657;
        --fx-text: var(--fx-ink-100);
        --fx-text-muted: var(--fx-ink-400);
        --fx-text-subtle: var(--fx-ink-500);
        --fx-link: var(--fx-accent-300);

        --fx-role-moderator: #b79cf0;
        --fx-role-moderator-bg: #2a2240;
        --fx-role-leader: var(--fx-accent-300);
        --fx-role-leader-bg: #16302e;
        --fx-role-member: var(--fx-ink-300);
        --fx-role-member-bg: #232b38;
        --fx-role-new: #e9a05c;
        --fx-role-new-bg: #33241a;

        --fx-danger: #f2b8b5;
        --fx-danger-bg: #3a1f1d;
        --fx-success: #7fd6a6;
        --fx-success-bg: #14301f;

        --fx-shadow: 0 1px 2px rgb(0 0 0 / 40%), 0 2px 8px rgb(0 0 0 / 24%);
        --fx-shadow-lift: 0 2px 4px rgb(0 0 0 / 48%), 0 8px 24px rgb(0 0 0 / 32%);
    }
}

/* The explicit choice wins in both directions - a reader who picked dark on a light OS gets dark,
   and the media query above cannot undo it. */
:root[data-theme="dark"] {
    --fx-page: #10141b;
    --fx-surface: #171d26;
    --fx-surface-sunken: #1f2733;
    --fx-border: #2b3543;
    --fx-border-strong: #3a4657;
    --fx-text: var(--fx-ink-100);
    --fx-text-muted: var(--fx-ink-400);
    --fx-text-subtle: var(--fx-ink-500);
    --fx-link: var(--fx-accent-300);

    --fx-role-moderator: #b79cf0;
    --fx-role-moderator-bg: #2a2240;
    --fx-role-leader: var(--fx-accent-300);
    --fx-role-leader-bg: #16302e;
    --fx-role-member: var(--fx-ink-300);
    --fx-role-member-bg: #232b38;
    --fx-role-new: #e9a05c;
    --fx-role-new-bg: #33241a;

    --fx-danger: #f2b8b5;
    --fx-danger-bg: #3a1f1d;
    --fx-success: #7fd6a6;
    --fx-success-bg: #14301f;

    --fx-shadow: 0 1px 2px rgb(0 0 0 / 40%), 0 2px 8px rgb(0 0 0 / 24%);
    --fx-shadow-lift: 0 2px 4px rgb(0 0 0 / 48%), 0 8px 24px rgb(0 0 0 / 32%);
}

/* ============================================================================
   3. Reset and base
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

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

body {
    margin: 0;
    font-family: var(--fx-font);
    font-size: var(--fx-text-base);
    line-height: 1.6;
    /* Painted explicitly: a transparent body borrows whatever is behind it, and in dark mode that
       is a white page under dark text. */
    background: var(--fx-page);
    color: var(--fx-text);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0 0 var(--fx-space-3); line-height: 1.25; font-weight: 650; }
p { margin: 0 0 var(--fx-space-4); }
p:last-child { margin-bottom: 0; }

a { color: var(--fx-link); text-decoration: none; }
a:hover { text-decoration: underline; }

img { max-width: 100%; height: auto; }

/* The browser's own [hidden] rule loses to any class that sets display, because a class selector
   outranks an attribute selector in the UA sheet. Without this, every element toggled with
   el.hidden that also carries a display-setting class is permanently visible - which is exactly
   what happened to forty-seven alerts in the directory. */
[hidden] { display: none !important; }

:focus-visible {
    outline: 2px solid var(--fx-accent-600);
    outline-offset: 2px;
    border-radius: var(--fx-radius-sm);
}

/* ============================================================================
   4. Layout
   ========================================================================== */

.fx-skip {
    position: absolute;
    left: -9999px;
    top: 0;
    padding: var(--fx-space-3) var(--fx-space-4);
    background: var(--fx-surface);
    color: var(--fx-text);
    z-index: 100;
}
.fx-skip:focus { left: var(--fx-space-4); top: var(--fx-space-4); }

.fx-container {
    width: 100%;
    max-width: 68rem;
    margin: 0 auto;
    /* One side gutter, set once, so nothing inside has to remember it. */
    padding-left: var(--fx-space-4);
    padding-right: var(--fx-space-4);
}

.fx-container--narrow { max-width: 46rem; }

.fx-main { padding-block: var(--fx-space-6) var(--fx-space-7); }

/* --- header --------------------------------------------------------------- */

.fx-header {
    background: var(--fx-surface);
    border-bottom: 1px solid var(--fx-border);
    position: sticky;
    top: 0;
    z-index: 40;
}

.fx-header__inner {
    display: flex;
    align-items: center;
    gap: var(--fx-space-4);
    min-height: 3.75rem;
    flex-wrap: wrap;
}

.fx-header__brand {
    display: inline-flex;
    align-items: center;
    gap: var(--fx-space-2);
    font-weight: 700;
    font-size: var(--fx-text-md);
    color: var(--fx-text);
    white-space: nowrap;
}
.fx-header__brand:hover { text-decoration: none; }

.fx-header__mark {
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--fx-radius-sm);
    background: var(--fx-accent-600);
    color: var(--fx-ink-0);
    font-size: var(--fx-text-sm);
    font-weight: 700;
}

.fx-header__nav {
    display: flex;
    align-items: center;
    gap: var(--fx-space-4);
    margin-left: auto;
    flex-wrap: wrap;
}

.fx-header__link {
    color: var(--fx-text-muted);
    font-size: var(--fx-text-sm);
    font-weight: 500;
    white-space: nowrap;
}
.fx-header__link:hover { color: var(--fx-text); text-decoration: none; }
.fx-header__link[aria-current="page"] { color: var(--fx-link); }

/* --- footer --------------------------------------------------------------- */

.fx-footer {
    border-top: 1px solid var(--fx-border);
    background: var(--fx-surface);
    padding-block: var(--fx-space-5);
    margin-top: var(--fx-space-7);
    font-size: var(--fx-text-sm);
    color: var(--fx-text-muted);
}

.fx-footer__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fx-space-4);
    align-items: center;
    justify-content: space-between;
}

.fx-footer__links { display: flex; flex-wrap: wrap; gap: var(--fx-space-4); }

/* ============================================================================
   5. Surfaces and grids
   ========================================================================== */

.fx-card {
    background: var(--fx-surface);
    border: 1px solid var(--fx-border);
    border-radius: var(--fx-radius);
    padding: var(--fx-space-5);
}

.fx-panel {
    background: var(--fx-surface);
    border: 1px solid var(--fx-border);
    border-radius: var(--fx-radius);
    overflow: hidden;
}

.fx-panel__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--fx-space-3);
    padding: var(--fx-space-4) var(--fx-space-5);
    border-bottom: 1px solid var(--fx-border);
}

.fx-panel__title { margin: 0; font-size: var(--fx-text-md); }

.fx-panel__more { font-size: var(--fx-text-sm); }

/* auto-fit with a minimum rather than fixed columns, so the count adapts with no breakpoint to
   keep in step. */
.fx-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--fx-space-4);
}

.fx-stack { display: flex; flex-direction: column; gap: var(--fx-space-5); }

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

.fx-section-title {
    font-size: var(--fx-text-lg);
    margin: 0 0 var(--fx-space-4);
}

/* ============================================================================
   6. Buttons, badges, alerts
   ========================================================================== */

.fx-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--fx-space-2);
    padding: 0.5rem 1rem;
    border: 1px solid transparent;
    border-radius: var(--fx-radius-sm);
    font: inherit;
    font-size: var(--fx-text-sm);
    font-weight: 600;
    line-height: 1.4;
    cursor: pointer;
    text-decoration: none;
    background: none;
}
.fx-btn:hover { text-decoration: none; }
.fx-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.fx-btn--primary { background: var(--fx-accent-600); color: var(--fx-ink-0); }
.fx-btn--primary:hover:not(:disabled) { background: var(--fx-accent-700); }

.fx-btn--secondary {
    background: var(--fx-surface);
    border-color: var(--fx-border-strong);
    color: var(--fx-text);
}
.fx-btn--secondary:hover:not(:disabled) { background: var(--fx-surface-sunken); }

.fx-btn--ghost { color: var(--fx-text-muted); }
.fx-btn--ghost:hover:not(:disabled) { color: var(--fx-text); background: var(--fx-surface-sunken); }

.fx-btn--danger { color: var(--fx-danger); }
.fx-btn--danger:hover:not(:disabled) { background: var(--fx-danger-bg); }

.fx-btn--sm { padding: 0.3125rem 0.625rem; font-size: var(--fx-text-xs); }
.fx-btn--block { width: 100%; }

.fx-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--fx-space-1);
    padding: 0.125rem 0.5rem;
    border-radius: var(--fx-radius-pill);
    font-size: var(--fx-text-xs);
    font-weight: 600;
    background: var(--fx-surface-sunken);
    color: var(--fx-text-muted);
    white-space: nowrap;
}

/* The four roles. This is where the theme spends its colour, and it is the reason a second palette
   was worth having: a moderator has to be recognisable in a thread of twenty replies without the
   reader stopping to read the word. */
.fx-badge--moderator { background: var(--fx-role-moderator-bg); color: var(--fx-role-moderator); }
.fx-badge--leader { background: var(--fx-role-leader-bg); color: var(--fx-role-leader); }
.fx-badge--member { background: var(--fx-role-member-bg); color: var(--fx-role-member); }
.fx-badge--new { background: var(--fx-role-new-bg); color: var(--fx-role-new); }

.fx-badge--category {
    background: var(--fx-accent-50);
    color: var(--fx-accent-700);
}

.fx-alert {
    display: flex;
    gap: var(--fx-space-3);
    padding: var(--fx-space-4);
    border-radius: var(--fx-radius);
    border: 1px solid var(--fx-border);
    background: var(--fx-surface-sunken);
    margin-bottom: var(--fx-space-5);
    font-size: var(--fx-text-sm);
}

.fx-alert--error { background: var(--fx-danger-bg); border-color: var(--fx-danger); color: var(--fx-danger); }
.fx-alert--success { background: var(--fx-success-bg); border-color: var(--fx-success); color: var(--fx-success); }

/* ============================================================================
   7. Discussions
   ========================================================================== */

.fx-threads {
    list-style: none;
    margin: 0;
    padding: 0;
}

.fx-thread {
    display: flex;
    gap: var(--fx-space-4);
    align-items: flex-start;
    padding: var(--fx-space-4) var(--fx-space-5);
    border-bottom: 1px solid var(--fx-border);
}
.fx-thread:last-child { border-bottom: 0; }

.fx-thread__body { flex: 1 1 auto; min-width: 0; }

.fx-thread__title {
    margin: 0 0 var(--fx-space-1);
    font-size: var(--fx-text-md);
    font-weight: 600;
    line-height: 1.35;
}
.fx-thread__title a { color: var(--fx-text); }
.fx-thread__title a:hover { color: var(--fx-link); text-decoration: none; }

.fx-thread__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--fx-space-2);
    font-size: var(--fx-text-xs);
    color: var(--fx-text-muted);
    margin: 0;
}

/* The reply count. Fixed width so a column of them lines up, and tabular figures so 9 and 11 do
   not shift the box. */
.fx-thread__replies {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    min-width: 3.25rem;
    padding: var(--fx-space-2);
    border-radius: var(--fx-radius-sm);
    background: var(--fx-surface-sunken);
    color: var(--fx-text-muted);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.fx-thread__replies strong { font-size: var(--fx-text-md); color: var(--fx-text); }
.fx-thread__replies span { font-size: var(--fx-text-xs); }

/* A thread with answers reads differently from one without, at a glance. */
.fx-thread__replies--answered { background: var(--fx-accent-50); }
.fx-thread__replies--answered strong { color: var(--fx-accent-700); }

/* --- one discussion ------------------------------------------------------- */

.fx-post {
    background: var(--fx-surface);
    border: 1px solid var(--fx-border);
    border-radius: var(--fx-radius);
    padding: var(--fx-space-5);
}

.fx-post__head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fx-space-3);
    align-items: center;
    margin-bottom: var(--fx-space-4);
}

.fx-post__author { display: flex; align-items: center; gap: var(--fx-space-2); }

.fx-post__name { font-weight: 600; font-size: var(--fx-text-sm); color: var(--fx-text); }

.fx-post__when { font-size: var(--fx-text-xs); color: var(--fx-text-subtle); }

/* The one place user-written prose is rendered. white-space: pre-wrap is what makes a plain
   textarea work as a post: the paragraphs somebody typed survive without any markup, and because
   nothing is parsed there is no markup to sanitise. Blade escapes the value; this only decides how
   the whitespace inside it is shown. */
.fx-post__body {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-size: var(--fx-text-md);
    line-height: 1.7;
    color: var(--fx-text);
}

.fx-post__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fx-space-2);
    margin-top: var(--fx-space-4);
    padding-top: var(--fx-space-3);
    border-top: 1px solid var(--fx-border);
}

.fx-replies { margin-top: var(--fx-space-6); }

.fx-reply {
    padding: var(--fx-space-4) 0;
    border-bottom: 1px solid var(--fx-border);
}
.fx-reply:last-child { border-bottom: 0; }

/* A hidden post, shown only to its author and to moderators. Striped rather than merely tinted, so
   it cannot be mistaken for an ordinary post in either theme. */
.fx-post--hidden,
.fx-reply--hidden {
    border-left: 3px solid var(--fx-danger);
    padding-left: var(--fx-space-4);
    background: var(--fx-danger-bg);
    border-radius: var(--fx-radius-sm);
}

/* The moderator's own controls, on a post or a reply.

   DELIBERATELY DULL AND DELIBERATELY LAST. It sits below the post's own actions, in muted ink on a
   sunken ground, because a moderator is reading the thread as a reader first - a red panel at the
   top of every post would make the forum look like a moderation queue to the handful of people who
   run it. The Hide button carries the only colour in the block, which is the one control here that
   takes something away from somebody. */
.fx-mod {
    margin-top: var(--fx-space-4);
    padding: var(--fx-space-3);
    background: var(--fx-surface-sunken);
    border: 1px dashed var(--fx-border-strong);
    border-radius: var(--fx-radius-sm);
}

.fx-mod__label {
    margin: 0 0 var(--fx-space-2);
    font-size: var(--fx-text-xs);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--fx-text-muted);
}

.fx-mod__hint {
    display: block;
    margin-bottom: var(--fx-space-2);
    font-size: var(--fx-text-xs);
    color: var(--fx-text-muted);
}

/* The field takes the room and the button keeps its own width.

   The basis is 16rem rather than 12: .fx-row wraps, but at 12rem the input and the button both fit
   on one line inside a 390px phone and the field ends up too narrow to read what has been typed
   into it - "Off topic, promotional, abu:". Wide enough to force the wrap is wide enough to use. */
.fx-mod__input { flex: 1 1 16rem; min-width: 0; }

.fx-mod__note {
    margin: 0 0 var(--fx-space-2);
    font-size: var(--fx-text-sm);
    color: var(--fx-danger);
}

/* ============================================================================
   8. Avatars
   ========================================================================== */

.fx-avatar {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 2rem;
    height: 2rem;
    border-radius: var(--fx-radius-pill);
    background: var(--fx-accent-100);
    color: var(--fx-accent-700);
    font-size: var(--fx-text-xs);
    font-weight: 700;
    text-transform: uppercase;
}

.fx-avatar--lg { width: 3.5rem; height: 3.5rem; font-size: var(--fx-text-lg); }

/* ============================================================================
   9. Forms
   ========================================================================== */

.fx-field { margin-bottom: var(--fx-space-5); }

.fx-label {
    display: block;
    margin-bottom: var(--fx-space-2);
    font-size: var(--fx-text-sm);
    font-weight: 600;
}

.fx-input,
.fx-textarea,
.fx-select {
    width: 100%;
    padding: 0.5rem 0.75rem;
    font: inherit;
    font-size: var(--fx-text-base);
    color: var(--fx-text);
    background: var(--fx-surface);
    border: 1px solid var(--fx-border-strong);
    border-radius: var(--fx-radius-sm);
}

.fx-input:focus,
.fx-textarea:focus,
.fx-select:focus {
    outline: 2px solid var(--fx-accent-600);
    outline-offset: -1px;
    border-color: var(--fx-accent-600);
}

.fx-textarea { min-height: 12rem; resize: vertical; line-height: 1.6; }
.fx-textarea--reply { min-height: 7rem; }

.fx-hint {
    margin: var(--fx-space-2) 0 0;
    font-size: var(--fx-text-xs);
    color: var(--fx-text-muted);
}

.fx-error {
    margin: var(--fx-space-2) 0 0;
    font-size: var(--fx-text-xs);
    color: var(--fx-danger);
    font-weight: 500;
}

.fx-input[aria-invalid="true"],
.fx-textarea[aria-invalid="true"],
.fx-select[aria-invalid="true"] { border-color: var(--fx-danger); }

/* The live word count. Turns amber near the limit and red past it - and the field is still
   submittable either way, because the server is what enforces the limit and a control that
   silently refuses keystrokes is worse than one that warns. */
.fx-count { font-variant-numeric: tabular-nums; }
.fx-count--near { color: var(--fx-warm-700); font-weight: 600; }
.fx-count--over { color: var(--fx-danger); font-weight: 600; }

.fx-search {
    display: flex;
    gap: var(--fx-space-2);
    flex-wrap: wrap;
}
.fx-search .fx-input { flex: 1 1 14rem; min-width: 0; }

/* ============================================================================
   10. Categories and company strip
   ========================================================================== */

.fx-category {
    display: block;
    padding: var(--fx-space-4);
    background: var(--fx-surface);
    border: 1px solid var(--fx-border);
    border-radius: var(--fx-radius);
    color: var(--fx-text);
}
.fx-category:hover {
    text-decoration: none;
    border-color: var(--fx-accent-300);
    box-shadow: var(--fx-shadow-lift);
}

.fx-category__name { font-weight: 650; margin-bottom: var(--fx-space-1); }
.fx-category__desc { font-size: var(--fx-text-sm); color: var(--fx-text-muted); margin: 0 0 var(--fx-space-2); }
.fx-category__count { font-size: var(--fx-text-xs); color: var(--fx-text-subtle); }

.fx-company {
    display: flex;
    gap: var(--fx-space-3);
    align-items: center;
    padding: var(--fx-space-3);
    background: var(--fx-surface);
    border: 1px solid var(--fx-border);
    border-radius: var(--fx-radius);
    color: var(--fx-text);
}
.fx-company:hover { text-decoration: none; border-color: var(--fx-accent-300); }

.fx-company__name { font-weight: 600; font-size: var(--fx-text-sm); }
.fx-company__meta { font-size: var(--fx-text-xs); color: var(--fx-text-muted); }

/* ============================================================================
   11. Profile
   ========================================================================== */

.fx-profile {
    display: flex;
    gap: var(--fx-space-4);
    align-items: flex-start;
    flex-wrap: wrap;
}

.fx-profile__stats {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fx-space-5);
    margin-top: var(--fx-space-4);
}

.fx-stat__figure {
    font-size: var(--fx-text-xl);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.fx-stat__label { font-size: var(--fx-text-xs); color: var(--fx-text-muted); }

/* ============================================================================
   12. Empty states and pagination
   ========================================================================== */

.fx-empty {
    padding: var(--fx-space-7) var(--fx-space-5);
    text-align: center;
    color: var(--fx-text-muted);
}

.fx-empty__title { color: var(--fx-text); margin-bottom: var(--fx-space-2); }

/* Laravel's paginator is pointed at Bootstrap markup application-wide, and the forum loads no
   Bootstrap - so these style Bootstrap's own class names directly. Without this block, every
   paginated forum page renders an unstyled list of numbers. */
.fx-pagination { margin-top: var(--fx-space-5); }

.fx-pagination .pagination {
    display: flex;
    flex-wrap: wrap;
    gap: var(--fx-space-1);
    list-style: none;
    margin: 0;
    padding: 0;
    justify-content: center;
}

.fx-pagination .page-link {
    display: block;
    padding: 0.375rem 0.75rem;
    border: 1px solid var(--fx-border);
    border-radius: var(--fx-radius-sm);
    color: var(--fx-text-muted);
    font-size: var(--fx-text-sm);
    background: var(--fx-surface);
}
.fx-pagination .page-link:hover { text-decoration: none; background: var(--fx-surface-sunken); }

.fx-pagination .active .page-link {
    background: var(--fx-accent-600);
    border-color: var(--fx-accent-600);
    color: var(--fx-ink-0);
}

.fx-pagination .disabled .page-link { opacity: 0.45; pointer-events: none; }

/* Bootstrap's own paginator emits a compact control set for small screens and a full one for
   large, and relies on Bootstrap's display utilities to hide one of them. With no Bootstrap
   loaded both would render at once - two sets of controls, which is what happened across
   twenty-five directory pages when the paginator was still emitting Tailwind. */
.fx-pagination .pagination + .pagination { display: none; }

/* ============================================================================
   13. Utilities. A short list on purpose - anything used three times earns a component.
   ========================================================================== */

.fx-muted { color: var(--fx-text-muted); }
.fx-subtle { color: var(--fx-text-subtle); font-size: var(--fx-text-sm); }
.fx-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Two lines, then an ellipsis. -webkit-line-clamp is inert without display: -webkit-box, which is
   the silent half of this pattern. */
.fx-clamp-2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

@media (max-width: 40rem) {
    .fx-thread { padding: var(--fx-space-4) var(--fx-space-4); gap: var(--fx-space-3); }
    .fx-thread__replies { min-width: 2.75rem; }
    .fx-main { padding-block: var(--fx-space-5) var(--fx-space-6); }
}
