/* ==========================================================================
   The Abandonware Ring Redesign — shared design system
   --------------------------------------------------------------------------
   ONE source of truth for every redesign page (homepage, news article, ...).
   No per-page stylesheets: components are plain BEM classes used by any page
   that carries the `redesign` body class.

   Page-specific layout tweaks live in the small "page tweaks" section below
   (e.g. .article-page .layout__aside alignment). Add new pages there.

   Naming & tokens (ACSS card-workflow + BEM conventions):
   - Blocks are components (e.g. .side-wrapper, .userdetails, .bordered-section);
     elements are `block__element`, modifiers are `block--modifier`.
   - Keep selectors flat and low-specificity: one class per element, no deep
     nesting. The block name is the component name; elements are literal
     (__title, __content, __avatar, __media, __top, __body, __meta).
   - Convention names the framework relies on: media containers use __media,
     profile photos use __avatar, headings stay semantic (h1-h6).
   - Empty states are the shared .empty-state component (--tall modifier).
   - Override with tokens, not hard values: card fundamentals and the avatar
     scale live in the .redesign token block above; reuse them instead of
     writing px by hand.
   - New cards join the system immediately — no hand-styling "to be cleaned
     up later".
   ========================================================================== */

/* --------------------------------------------------------------------------
   Webfonts (self-hosted, latin subset only; variable-weight woff2 so the
   single file covers the 400-700 range for each family)
   -------------------------------------------------------------------------- */
@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("/font/Inter-latin.woff2") format("woff2");
}

@font-face {
    font-family: "Inter";
    font-style: italic;
    font-weight: 100 900;
    font-display: swap;
    src: url("/font/Inter-latin-italic.woff2") format("woff2");
}

@font-face {
    font-family: "JetBrains Mono";
    font-style: normal;
    font-weight: 100 800;
    font-display: swap;
    src: url("/font/JetBrainsMono-latin.woff2") format("woff2");
}

@font-face {
    font-family: "JetBrains Mono";
    font-style: italic;
    font-weight: 100 800;
    font-display: swap;
    src: url("/font/JetBrainsMono-latin-italic.woff2") format("woff2");
}

/* --------------------------------------------------------------------------
   Base scope
   -------------------------------------------------------------------------- */
.redesign {
    /* Surfaces/lines/text/accent/fonts resolve to the shared foundation
       tokens. Only card-scoped tokens stay here. */
    --font-family: var(--sans);
    --gradient-step-1: #04081f00;
    --gradient-step-2: #04081f1a;
    --gradient-step-3: #04081f3d;
    --action2: #d64764;

    /* User profile palette + typography */
    --active-color: #fff;
    --game-title-size: 16px;
    --game-subtitle-size: 14px;
    --game-title-color: var(--text-primary);
    --game-desc-color: var(--text-content);
    --level-0: #fff;
    --level-1: #bfc4d0;
    --level-2: #59fcff;
    --level-3: #297bff;
    --level-4: #ff8af3;
    --level-5: #f03b75;
    --level-6: #ffbb8a;
    --userdetails-bg: var(--secondarybg-color);
    --userdetails-border: 2px solid #eaeaf100;

    /* Card fundamentals (ACSS card-workflow: shared defaults, override w/ tokens) */
    --card-radius: 0;
    --card-bg: var(--secondarybg-color);
    --card-padding: 17px 13px;
    --avatar-s: 24px;
    --avatar-m: 37px;
    --avatar-l: 44px;
    --avatar-xl: 99px;
    --empty-state-h: 160px;

    background-color: var(--bg-color);
    color: var(--text-primary);
    font-family: var(--font-family);
    letter-spacing: -0.3px;
    min-height: 100vh;
}

.redesign .container {
    max-width: var(--container-width);
    margin: 0 auto;
    /* Bootstrap .container adds 12px gutters each side — use the full width */
    padding-left: 0;
    padding-right: 0;
}

.redesign h1, .redesign h2, .redesign h3,
.redesign h4, .redesign h5, .redesign h6 {
    font-family: var(--font-family) !important;
    letter-spacing: -0.3px;
    color: var(--text-primary) !important;
    font-weight: 700;
}

/* Links inside headings must not fall back to the legacy yellow (base.css
   `h1 a..h6 a { color:#ffc107 !important }`). */
.redesign h1 a, .redesign h2 a, .redesign h3 a,
.redesign h4 a, .redesign h5 a, .redesign h6 a {
    color: var(--text-primary) !important;
    text-decoration: none;
}

/* Bare links: secondary, no underline (components override below) */
.redesign :where(a) {
    color: var(--text-secondary);
    text-decoration: none;
}

.redesign :where(a):hover {
    color: var(--text-primary);
}

/* Links inside article/comment text: white + underline, no hover effect
   (base selector outspecifies the :where(a) resets, so hover produces no
   color or decoration change) */
.article-body a {
    color: var(--text-primary);
    text-decoration: underline;
}

.redesign .text-info {
    color: var(--accent) !important;
    text-decoration: underline;
}

/* ==========================================================================
   Component: tab-filters (sticky frosted-glass filter bar)
   ========================================================================== */
.tab-filters {
    box-sizing: border-box;
    margin-top: 25px;
    margin-bottom: 40px;
    gap: 10px;
    position: -webkit-sticky;
    position: sticky;
    top: 98px;
    /* z-index 11 keeps the sticky filter bar above .userdetails__header
       (z-index 10) so the userdetails card scrolls underneath it instead
       of painting over it. Still well below the topbar at z-index 100. */
    z-index: 11;
    -webkit-backdrop-filter: blur(50px);
    backdrop-filter: blur(50px);
    padding: 10px;
    border-radius: 0;
    font-size: 14px;
    display: flex;
    width: 100%;
    justify-content: space-between;
    border: 1px solid var(--border-color);
    align-items: center;
    min-height: 49px;
}

.tab-filters__left,
.tab-filters__right {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    min-width: 0;
}

/* Compact bar controls (search input, field wrapper, reset link). These are
   deliberately distinct from the stacked .form-field primitives. */
.tab-filters__search {
    width: 220px;
    box-sizing: border-box;
    height: 27px;
    background: var(--bg-color);
    border: 1px solid var(--border-color);
    border-radius: 0;
    color: var(--text-primary);
    font-size: 13px;
    font-family: var(--mono);
    padding: 0 10px;
    outline: none;
}

.tab-filters__search::placeholder {
    color: var(--text-muted);
}

.tab-filters__search:focus {
    border-color: var(--accent);
}

.tab-filters__field {
    display: flex;
    align-items: center;
    gap: 6px;
}

.tab-filters__label {
    font-size: 12px;
    color: var(--text-muted);
}

.tab-filters__reset {
    display: inline-flex;
    align-items: center;
    height: 27px;
    padding: 0 10px;
    border-radius: 0;
    border: 1px solid var(--border-color);
    background: transparent;
    color: var(--text-secondary);
    font-size: 12px;
    cursor: pointer;
    text-decoration: none;
    transition: color 0.15s ease, box-shadow 0.15s ease;
    font-family: var(--mono);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.tab-filters__reset:hover {
    box-shadow: 0 0 0 1px var(--text-primary) inset;
    color: var(--text-primary);
}

/* Full-width reset variant (sidebar action, e.g. member_profile Edit Site). */
.tab-filters__reset--block {
    width: 100%;
    justify-content: center;
}

/* Keep the gap between the Visit button and the Edit Site button. */
.side-wrapper__visit + .tab-filters__reset--block {
    margin-top: 10px;
}

/* ==========================================================================
   Component: quick-nav
   ========================================================================== */
.quick-nav {
    display: flex;
    gap: 25px;
    align-self: center;
    align-items: center;
    padding-left: 5px;
    flex-wrap: nowrap;
}

.quick-nav__item {
    color: transparent;
    font-size: 12px;
    box-sizing: border-box;
    text-align: center;
    min-width: -moz-fit-content;
    position: relative;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    cursor: pointer;
    font-family: var(--mono);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.quick-nav__item:hover .quick-nav__label {
    color: var(--text-primary);
}

.quick-nav__label {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: var(--text-secondary);
    font-weight: 400;
    white-space: nowrap;
    pointer-events: none;
}

.quick-nav__item--active .quick-nav__label {
    color: var(--text-primary);
}

.quick-nav__underline {
    position: absolute;
    bottom: -14px;
    left: 0;
    width: 100%;
    height: 3px;
    background-color: var(--text-primary);
    border-radius: 0;
}

/* Labeled quick-nav items render their node text directly instead of the
   overlay-label trick, whose width invariant breaks once labels carry
   counts or carets (the overflowing label then collides with neighbours
   and desyncs from the underline). Works at strip level
   (.quick-nav--labeled) or per item (.quick-nav__item--labeled). */
.quick-nav--labeled .quick-nav__item,
.quick-nav__item--labeled {
    color: var(--text-secondary);
}

.quick-nav--labeled .quick-nav__item:hover,
.quick-nav--labeled .quick-nav__item--active,
.quick-nav__item--labeled:hover,
.quick-nav__item--labeled.quick-nav__item--active {
    color: var(--text-primary);
}

.quick-nav__item--labeled .quick-nav__caret {
    margin-left: 5px;
}

/* Trigger <button> inside a labeled quick-nav item. The item itself is a
   plain positioning/hover context; the button carries focus + ARIA state so
   the dropdown options are no longer nested inside a role="button". Spacing
   to the caret comes from .quick-nav__item--labeled .quick-nav__caret. */
.quick-nav__more-trigger {
    display: flex;
    align-items: center;
    margin: 0;
    padding: 0;
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
}

/* More dropdown (sort by games / apps / docs / tracks) */
.quick-nav__caret {
    flex-shrink: 0;
}

.quick-nav__dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    min-width: 170px;
    background: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    border-radius: 0;
    padding: 7px 5px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
    /* Default z-index; the open state elevates above the sticky header,
       the tab bar, and the page content so the dropdown isn't clipped. */
    z-index: 20;
    display: none;
}

.quick-nav__dropdown::before {
    content: '';
    position: absolute;
    top: -6px;
    left: 0;
    width: 100%;
    height: 6px;
}

.quick-nav__more::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%;
    height: 6px;
    display: none;
}

.quick-nav__more:hover::after,
.quick-nav__more:focus-within::after {
    display: block;
}

.quick-nav__dropdown--open {
    display: block;
}

.quick-nav__dropdown-option {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    color: var(--text-secondary);
    font-size: 12px;
    padding: 5px;
    border-radius: 0;
    cursor: pointer;
    font-family: var(--mono);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.quick-nav__dropdown-option:hover {
    color: var(--text-primary);
    background: var(--secondary-hover);
}

.quick-nav__dropdown-option--active {
    color: var(--text-primary);
    background: var(--tag-active-bg);
}

/* ==========================================================================
   Component: filter-panel (Filter sites button + dropdown)
   ========================================================================== */
.filter-panel {
    position: relative;
}

.filter-panel__trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    font-size: 12px;
    padding: 0 10px;
    height: 27px;
    border-radius: 0;
    cursor: pointer;
    font-family: var(--mono);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.filter-panel__trigger:hover,
.filter-panel__trigger--open {
    color: var(--text-primary);
    background: var(--secondary-hover);
    border-color: var(--text-primary);
}

.filter-panel__trigger svg {
    flex-shrink: 0;
}

.filter-panel__menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 220px;
    background: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    border-radius: 0;
    padding: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
    z-index: 20;
    display: none;
}

.filter-panel__menu--open {
    display: block;
}

.filter-panel__search {
    width: 100%;
    box-sizing: border-box;
    background: var(--bg-color);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    border-radius: 0;
    padding: 7px 10px;
    font-size: 14px;
    margin-bottom: 8px;
}

.filter-panel__search::placeholder {
    color: var(--text-muted);
}

.filter-panel__search:focus {
    outline: none;
    border-color: var(--accent);
}

.filter-panel__options {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 260px;
    overflow-y: auto;
}

.filter-panel__option {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    color: var(--text-secondary);
    font-size: 14px;
    padding: 7px 10px;
    border-radius: 0;
    cursor: pointer;
}

.filter-panel__option:hover {
    color: var(--text-primary);
    background: var(--secondary-hover);
}

.filter-panel__option--active {
    color: var(--text-primary);
    background: var(--tag-active-bg);
}

/* ==========================================================================
   Component: view-select (List / Grid / Grid full width)
   ========================================================================== */
.view-select {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 27px;
    background: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    border-radius: 0;
    padding: 0 10px;
    color: var(--text-primary);
}

/* Chip icon (e.g. the homepage grid glyph), shown before the <select>. */
.view-select__icon {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
}

.view-select__select {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: none;
    color: var(--text-primary);
    font-size: 12px;
    cursor: pointer;
    outline: none;
    font-family: var(--mono);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.view-select__select option {
    background: var(--secondarybg-color);
    color: var(--text-primary);
}

.view-select__suffix {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    color: var(--text-secondary);
}

.view-select:hover {
    border-color: var(--text-primary);
}

/* ==========================================================================
   Component: layout (main + sidebar)
   ========================================================================== */
.layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 1.75rem;
    padding: 0 0 2.5rem;
}

.layout--full {
    grid-template-columns: minmax(0, 1fr);
}

.layout__main {
    min-width: 0;
}

.layout__aside {
    display: flex;
    flex-direction: column;
    gap: 40px;
}

.layout--full .layout__aside {
    display: none;
}

/* ==========================================================================
   Component: site-grid
   ========================================================================== */
.site-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}

.site-grid--list {
    grid-template-columns: 1fr;
}

.site-grid--full {
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
}

/* News grid — mirrors .site-grid so the listings share responsive language.
   Kept as a separate class (rather than reusing .site-grid) because the
   markup contract is different: site-grid wraps site cards, news-grid
   wraps news items. Update both rules together when changing the layout. */
.news-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 10px;
}

/* Grid nested in a bordered-section (member "More like this") */
.bordered-section__content .site-grid {
    width: 100%;
}

/* ==========================================================================
   Component: site-card
   ========================================================================== */
.site-card {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    border: 1px solid var(--border-color);
    box-sizing: border-box;
    overflow: hidden;
    background: transparent;
    color: inherit;
}

.site-card:hover {
    border: 1px solid var(--text-primary);
    border-radius: 0;
}

/* Media (image box) */
.site-card__media {
    position: relative;
    border-radius: 0;
    overflow: hidden;
    background: var(--third-bg-color);
    flex-shrink: 0;
}

.site-card__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
}

.site-card__body {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}

.site-card__title {
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1;
    text-decoration: none;
    font-family: var(--mono);
}

/* Clickable parent: extend the title link over the whole card.
   The card is position:relative, so this pseudo-element (inset:0) covers it.
   z-index 2 puts the stretched link on top of the body; individual
   interactive children (stats, link button) opt back up to z-index 3
   so they can take pointer events and show their own tooltips. */
.site-card__title::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
}

.site-card__desc {
    font-size: 15px;
    color: var(--text-content);
    font-weight: 400;
    line-height: 1.4;
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.site-card:hover .site-card__desc {
    color: var(--text-primary);
}

.site-card__bottom {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: auto;
}

.site-card__stat {
    position: relative;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 25px;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    padding: 0 12px;
    line-height: 1;
    white-space: nowrap;
    cursor: default;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
}

.site-card__stat .bi {
    font-size: 14px;
    line-height: 1;
}

.site-card__stat-value {
    font-variant-numeric: tabular-nums;
}

/* Tooltip child — a real element (not a pseudo) so its z-index is
   independent of the parent and so :has() can target the card. */
.site-card__stat-tooltip {
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    padding: 5px 9px;
    background: var(--panel);
    color: var(--text-primary);
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    border: 1px solid var(--border-color);
    border-radius: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
    z-index: 6;
}

.site-card__stat:hover .site-card__stat-tooltip,
.site-card__stat:focus-within .site-card__stat-tooltip {
    opacity: 1;
}

/* When any stat is hovered/focused, promote the whole card so the
   tooltip isn't clipped by the card's overflow:hidden or by sibling cards. */
.site-card:has(.site-card__stat:hover),
.site-card:has(.site-card__stat:focus-within) {
    z-index: 6;
}

.site-card__link {
    position: relative;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 25px;
    border-radius: 0;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    font-weight: 400;
    color: var(--text-secondary);
    background: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    text-decoration: none;
    line-height: 1;
    font-family: var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.site-card__link:hover {
    background: var(--secondary-hover);
    color: var(--text-primary);
    border-color: var(--text-primary);
}

/* -------- ROW variant (list view, mirrors .game) -------- */
.site-card--row {
    flex-direction: row;
}

.site-card--row .site-card__body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 10px;
}

.site-card--row .site-card__media {
    width: 185px;
    height: 87px;
    min-width: 157px;
    margin: 10px 0 10px 10px;
}

.site-card--row .site-card__title {
    margin-bottom: 0;
}

/* -------- TILE variant (grid / full-width, mirrors .game-tile) -------- */
.site-card--tile {
    flex-direction: column;
}

.site-card--tile .site-card__body {
    padding: 10px;
}

.site-card--tile .site-card__media {
    width: 100%;
    height: 106px;
    min-width: 0;
}

.site-card--tile .site-card__title {
    margin-top: 0;
    margin-bottom: 8px;
    font-size: 14px;
    overflow: hidden;
}

/* Grid full width: wider media */
.site-grid--full .site-card--tile .site-card__media {
    height: 120px;
}

/* ==========================================================================
   Component: empty state
   Shared component is .empty-state (+ --tall modifier). Inside card grids
   the message must span all columns.
   ========================================================================== */
.site-grid .empty-state {
    grid-column: 1 / -1;
}

/* ==========================================================================
   Component: form-field (shared single-line field primitives)

   Form PAGE composition — how a whole form is laid out, not just one field.
   Documented here because nothing used to define it, so every page invented
   its own grouping/grid/footer and the pages drifted apart:

     <form>
       <section class="bordered-section">        <- one card per logical group
         <div class="bordered-section__body">
           <div class="bordered-section__top">
             <h2 class="bordered-section__title">Group title</h2>
           </div>
           <div class="bordered-section__content-wrap">
             <div class="form-field">…</div>       <- .form-grid for N-up rows
           </div>
         </div>
       </section>
       <div class="dashboard-panel-actions">       <- the one form footer idiom
         <a class="button button--ghost">Cancel</a>
         <button class="button button--solid">Save</button>
       </div>
     </form>

   .bordered-section is the shared content container the dashboards and
   profile pages already use. .dashboard-panel-actions is the shared action
   row — .detail-actions is NOT for this; it belongs to offcanvas panels
   (see its .detail-footer sibling in dashboard.css).

   Single-line counterpart to .comment-form__textarea, extracted so any
   page can compose full-width fields without borrowing contextual
   classes like .tab-filters__search. Radius 0, no yellow: focus uses
   the primary text color, same as .comment-form__textarea.
   ========================================================================== */
.form-field {
    display: flex;
    flex-direction: column;
    margin-bottom: 1rem;
}

/* Last field in a card: the card's own padding provides the bottom space,
   so the field's margin would read as a stray gap before it. */
.bordered-section__content-wrap .form-field:last-child {
    margin-bottom: 0;
}

/* Last card before the action row: skip the full section rhythm so the
   footer reads as belonging to the form rather than floating below it. */
form > .bordered-section:last-of-type {
    margin-bottom: 24px;
}

.form-field--row {
    flex-direction: row;
    align-items: flex-start;
    gap: 8px;
}

.form-field__label {
    font-size: 12px;
    color: var(--text-muted);
    margin-bottom: 6px;
}

.form-field--row .form-field__label {
    margin-bottom: 0;
}

.form-field__input {
    width: 100%;
    box-sizing: border-box;
    background: var(--comment-input-bg);
    border: 1px solid var(--border-color);
    border-radius: 0;
    color: var(--text-primary);
    font-family: var(--font-family);
    font-size: 14px;
    padding: 10px 12px;
    outline: none;
    transition: border-color 0.15s ease;
}

.form-field__input::placeholder {
    color: var(--text-muted);
}

.form-field__input:focus {
    border-color: var(--text-primary);
}

/* Helper text under a field. Uses the project's muted token rather than
   Bootstrap's .form-text/.text-muted, whose --bs-secondary-color is a dark
   grey (Bootstrap's light default) and would be unreadable on our surfaces.
   `.text-info` must not be used here: `.redesign .text-info` restyles it as
   accent + underline, i.e. as a link. */
.form-field__hint {
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 6px;
}

/* Form field grid — 4-up on desktop, 2-up on small screens. Replaces the
   legacy .detail-counts-grid, which was also being borrowed as a form
   layout helper (sites_editor / member_profile content counts). */
.form-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
}

@media (max-width: 767.98px) {
    .form-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* ==========================================================================
   Component: news-admin (admin listing rows)
   Thumbnail + primary/meta cell for the admin listing tables: Manage Posts,
   Manage Sites (sites_editor.php) and Manage Users (manage_users.php). Was an
   inline <style> block duplicated per page, with two different values for the
   meta colour (#aaa vs var(--text-muted)); this is the single tokenized
   definition.
   ========================================================================== */
.news-admin-info {
    display: flex;
    align-items: center;
    gap: 12px;
}

.news-admin-thumb {
    width: 50px;
    height: 50px;
    object-fit: cover;
    border-radius: 0;
    flex-shrink: 0;
    background: var(--third-bg-color);
}

.news-admin-meta {
    font-size: 0.85em;
    color: var(--text-muted);
}

/* Primary cell links read as the row title (bold, no underline); the global
   bs-yellow :link colour would otherwise tint them. */
.news-admin-info .site-link {
    color: var(--text-primary);
    font-weight: 700;
    text-decoration: none;
}

/* News listing rows navigate on click, so show the pointer like the
   sites/users rows do. */
.news-table .news-row {
    cursor: pointer;
}

/* Comments moderation table: the byline link inherits the muted meta text
   instead of the global table-link yellow. The hover underline needs
   !important because base.css sets ":link:hover { text-decoration: none
   !important }". Scoped two classes deep to beat ".table td a". */
.table .news-admin-meta a {
    color: inherit;
    text-decoration: none;
}

.table .news-admin-meta a:hover {
    color: inherit;
    text-decoration: underline !important;
}

/* Comments moderation table: the "News Post" cell link matches the
   news-admin row-title treatment. Scoped by the table's own class rather than
   a flex info cell, so the first column can stay a normal table cell. */
.comments-table td:nth-child(2) a {
    color: var(--text-primary);
    font-weight: 700;
    text-decoration: none;
}

.comments-table td:nth-child(2) a:hover {
    color: var(--text-primary);
    text-decoration: underline !important;
}

/* ==========================================================================
   Component: feature-vote (Feature Requests rows)
   Vote pill, voted/completed states and the card shell for features.php,
   moved out of the page's inline <style> onto tokens (no gradients, no
   hardcoded colours).
   ========================================================================== */
.vote-button {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 50px;
    height: 50px;
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--tag-bg);
    color: var(--text);
    font-family: var(--mono);
    cursor: pointer;
    text-decoration: none;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.vote-button .bi-caret-up-fill {
    font-size: 1.2rem;
    line-height: 1;
}

.vote-button:hover:not(.voted) {
    border-color: var(--accent);
    color: var(--accent);
}

.vote-button.voted {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-contrast);
}

/* Completed rows swap the caret for a check and read as a success state
   (same --stable treatment as .status-label). The doubled class outranks
   .vote-button:hover:not(.voted) so the state holds on hover. */
.vote-button.vote-button--done,
.vote-button.vote-button--done:hover {
    background: var(--tag-bg);
    border-color: color-mix(in srgb, var(--stable) 72%, var(--line));
    color: var(--stable);
    cursor: default;
}

.vote-button--done .bi-check-lg {
    font-size: 1.8rem;
    line-height: 1;
}

.vote-count {
    font-size: 1.1rem;
    font-weight: 700;
}

.feature-card {
    background: var(--third-bg-color);
    border: 1px solid var(--line);
    border-radius: 0;
    color: var(--text);
}

/* ==========================================================================
   Component: news-item
   Clickable parent: .news-item__link::after covers the whole row; secondary
   links (.news-item__author) sit above it with z-index 2.
   ========================================================================== */

.news-item {
    position: relative;
    width: 100%;
    display: flex;
    border: 1px solid var(--border-color);
    box-sizing: border-box;
}

.news-item:hover {
    border: 1px solid var(--text-primary);
    border-radius: 0;
}

.news-item__media {
    width: 185px;
    min-width: 185px;
    height: 87px;
    flex-shrink: 0;
    margin: 10px 0 10px 10px;
    border-radius: 0;
    overflow: hidden;
    position: relative;
    background: var(--third-bg-color);
}

.news-item__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.news-item__body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 10px;
    padding: 10px;
    min-width: 0;
}

.news-item__title {
    margin: 0;
    font-size: 15px;
    font-weight: 700;
    line-height: 1;
    font-family: var(--mono);
    color: var(--text-primary);
}

.news-item__link {
    color: var(--text-primary);
    text-decoration: none;
}

/* Clickable parent: extend the title link over the whole row */
.news-item__link::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
}

.news-item__desc {
    font-size: 15px;
    line-height: 1.3rem;
    color: var(--text-content);
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}

.news-item:hover .news-item__desc {
    color: var(--text-primary);
}

.news-item__footer {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}

.news-item__author {
    display: flex;
    align-items: center;
    gap: 6px;
    position: relative;
    z-index: 2;
    font-size: 14px;
    color: var(--text-secondary);
    text-decoration: none;
}

.news-item__author:hover {
    color: var(--text-primary);
}

.news-item__author span {
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
    white-space: nowrap;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
}

.news-item__avatar {
    width: var(--avatar-s);
    height: var(--avatar-s);
    border-radius: 0;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--third-bg-color);
}

.news-item__stat {
    position: relative;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 25px;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    padding: 0 12px;
    line-height: 1;
    white-space: nowrap;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
}

/* ==========================================================================
   Component: side-wrapper (reusable sidebar card)
   ========================================================================== */
.side-wrapper {
    width: 100%;
    background: var(--secondarybg-color);
    border: 0 solid var(--border-color);
    border-radius: 0;
    padding: 10px;
    box-sizing: border-box;
}

.side-wrapper__top {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 17px;
}

.side-wrapper:hover .side-wrapper__button {
    visibility: visible;
}

.side-wrapper__title {
    display: flex;
    align-items: center;
    color: var(--text-primary);
    font-weight: 700;
}

.side-wrapper__button {
    text-align: center;
    padding: 1px 6px;
    box-sizing: border-box;
    color: var(--text-secondary);
    border: 1px solid var(--border-color);
    font-size: 13px;
    background: var(--secondarybg-color);
    border-radius: 0;
    display: flex;
    align-items: center;
    margin-left: 7px;
    text-decoration: none;
    visibility: hidden;
}

.side-wrapper__button:hover {
    box-shadow: 0 0 0 1px var(--text-primary) inset;
    cursor: pointer;
    color: var(--text-primary);
}

.side-wrapper__content {
    padding: 0;
}

/* Full-width shared .button variant; a link, so it needs its own
   text-decoration handling */
.side-wrapper__visit {
    width: 100%;
    justify-content: center;
    text-decoration: none;
}

.side-wrapper__visit:hover {
    color: var(--accent-contrast);
    text-decoration: none;
}

.side-wrapper__action {
    display: block;
    border-radius: 0;
    overflow: hidden;
    background: var(--secondary-hover);
}

.side-wrapper__action .sponsor__media {
    display: block;
}

.side-wrapper__content.sponsor {
    padding: 0;
}

/* Key-value stat rows (site info, any sidebar stat list) */
.side-wrapper__kv-list {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.side-wrapper__kv {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.side-wrapper__kv dt {
    font-size: 13px;
    color: var(--text-muted);
}

.side-wrapper__kv dd {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

/* Stats card — stacked big-number card (sidebars: site stats, news stats) */
.stats-card {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 0.9rem 0;
}

.stats-card__row {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.stats-card__value {
    font-size: 1.75rem;
    font-weight: 800;
    color: var(--text-primary);
    line-height: 1.1;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
}

.stats-card__label {
    font-size: var(--tag-font-size);
    color: var(--text-muted);
    font-family: var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.035em;
}

/* Grid variant — the same big-number recipe laid out as a row of boxed cells
   instead of a stacked sidebar column (my_stats Activity, admin detail
   panels). Uses the .stats-card__value/__label above, so the numbers stay
   mono + tabular-nums and take their colour from the tokens: the legacy
   .detail-count-* cards this replaces hardcoded a yellow value, a sans
   font and 8px radii. */
.stats-card--grid {
    display: grid;
    padding: 0;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
}

.stats-card--grid .stats-card__row {
    padding: 12px 14px;
    border: 1px solid var(--border-color);
    background: var(--secondarybg-color);
}

/* Slightly tighter than the sidebar stack: four cells share the width. */
.stats-card--grid .stats-card__value {
    font-size: 1.5rem;
}

@media (max-width: 767.98px) {
    .stats-card--grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* ==========================================================================
   Component: list-row (generic media list row: news, etc.)
   ========================================================================== */
.list-row {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 10px 5px;
    border: 1px solid transparent;
    border-top: 1px solid var(--secondary-border);
    position: relative;
}

.list-row:hover {
    border: 1px solid var(--text-primary);
    background: var(--third-bg-color);
    border-radius: 0;
}

/* Clickable parent: the title link covers the whole row (news detail) */
.list-row__title::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
}

.list-row__thumb {
    width: 56px;
    height: 40px;
    border-radius: 0;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--third-bg-color);
}

.list-row__content {
    min-width: 0;
    flex-grow: 1;
}

.list-row__title {
    font-size: var(--subtitle-size);
    color: var(--text-primary);
    font-weight: 500;
    line-height: 1.35;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.list-row__title:hover {
    color: var(--text-secondary);
}

.list-row__meta {
    font-size: var(--tag-font-size);
    color: var(--text-muted);
    margin-top: 0.15rem;
}

/* ==========================================================================
   Component: activity (community feed rows)
   ========================================================================== */
.activity {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 10px 5px;
    border: 1px solid transparent;
    border-top: 1px solid var(--secondary-border);
    position: relative;
}

.activity:hover {
    border: 1px solid var(--text-primary);
    background: var(--third-bg-color);
    border-radius: 0;
}

/* Clickable parent: the comment title link covers the whole row (permalink) */
.activity__title::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
}

/* Secondary links stay clickable above the stretched title link */
.activity__avatar-link {
    position: relative;
    z-index: 2;
    display: block;
}

.activity__user {
    position: relative;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
    white-space: nowrap;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
}

.activity__user-row {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    color: var(--text-secondary);
}

.activity__user-info {
    display: flex;
    flex-direction: column;
    min-width: 0;
    gap: 0.15rem;
}

.activity__avatar {
    width: var(--avatar-s);
    height: var(--avatar-s);
    border-radius: 0;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--third-bg-color);
}

.activity__title {
    font-size: var(--subtitle-size);
    color: var(--text-primary);
    font-weight: 500;
    line-height: 1.35;
    margin-bottom: 9px;
    white-space: nowrap;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 28px), transparent 100%);
}

.activity__title:hover {
    color: var(--text-secondary);
}

/* ==========================================================================
   Component: contributor (Top contributors row in the news sidebar)
   Single-line horizontal layout: avatar > username pill > news stat > comments stat.
   The stat pills reuse .site-card__stat so they share the same recipe
   (mono, uppercase, 0.035em, var(--tag-bg)) and tooltip behavior as the
   site-card stats on the homepage. The :has() z-index promotion on
   .site-card does NOT fire here because the parent is .contributor.
   .contributor-list owns the vertical gap between rows; per-item margin
   is not used so the first row sits flush against the widget content.
   ========================================================================== */
.contributor-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.contributor {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.contributor__avatar {
    width: 25px;
    height: 25px;
    border-radius: 0;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--third-bg-color);
}

.contributor__name {
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-decoration: none;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
    min-width: 0;
    flex-shrink: 1;
}

.contributor__name:hover {
    color: var(--text-primary);
    border-color: var(--text-primary);
}

/* ==========================================================================
   Component: compare (field-by-field Current vs Submitted panels)
   Admin review pages (pending_sites / pending_updates) diff a submission
   against the live listing. Rows whose value differs carry --changed.
   ========================================================================== */
.compare {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    width: 100%;
}

.compare--single {
    grid-template-columns: 1fr;
}

.compare__col {
    min-width: 0;
    background: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    border-radius: 0;
    padding: 13px;
}

.compare__heading {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    margin-bottom: 6px;
}

.compare__fields {
    margin: 0;
}

.compare__field {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 9px 6px;
    border-radius: 0;
}

.compare__field + .compare__field {
    border-top: 1px solid var(--secondary-border);
}

.compare__dt {
    font-size: 12px;
    color: var(--text-muted);
}

.compare__dd {
    margin: 0;
    font-size: 14px;
    line-height: 1.45;
    color: var(--text-content);
    word-break: break-word;
}

/* Diff highlight: the submitted value differs from the live listing */
.compare__field--changed {
    background: var(--tag-active-bg);
}

.compare__field--changed .compare__dd {
    color: var(--text-primary);
    font-weight: 600;
}

.compare__img {
    display: block;
    max-width: 200px;
    height: auto;
    border: 1px solid var(--border-color);
    border-radius: 0;
    background: var(--third-bg-color);
}

/* ==========================================================================
   Component: sponsor
   ========================================================================== */
.sponsor {
    text-align: center;
    padding: 0.75rem 0.5rem;
}

.sponsor__media {
    max-width: 100%;
    height: auto;
    border-radius: 0;
}

.sponsor__caption {
    font-size: var(--tag-font-size);
    color: var(--text-muted);
    margin-top: 0.5rem;
}

/* ==========================================================================
   Component: article (news article page)
   ========================================================================== */
.article {
    box-sizing: border-box;
    margin-top: 40px;
}

.article__header {
    margin-bottom: 40px;
}

.article__header-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

/* It's a link, not a button element, so it keeps its own text-decoration
   handling; the look comes from the shared .button block above */
.article__edit-btn {
    gap: 6px;
    text-decoration: none;
}

.article__edit-btn:hover {
    color: var(--accent-contrast);
    text-decoration: none;
}

.article__title {
    font-size: 26px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--text-primary);
    margin: 0;
}

.article-divider {
    width: 100%;
    height: 1px;
    background-color: var(--border-color);
    opacity: 0.3;
    border: 0;
}

.article-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 10px 0;
}

.article-info__author {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.article-info__avatar {
    width: var(--avatar-l);
    height: var(--avatar-l);
    border-radius: 0;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--third-bg-color);
}

.article-info__name {
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    font-weight: 400;
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-decoration: none;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
    min-width: 0;
}

.article-info__name:hover {
    color: var(--text-primary);
    border-color: var(--text-primary);
}

.article-info__stats {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.article-info__stat {
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    line-height: 1;
    white-space: nowrap;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
}

.article-hero {
    width: 100%;
    height: 300px;
    margin: 50px 0;
    border-radius: 0;
    overflow: hidden;
    background: var(--secondarybg-color);
}

.article-hero__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.article-body {
    font-size: 16px;
    line-height: 1.6;
    color: var(--text-secondary);
    word-wrap: break-word;
    overflow-wrap: break-word;
    max-width: 65ch;
}

.article-body p:last-child {
    margin-bottom: 0;
}

.article-body img {
    max-width: 100%;
    height: auto;
    border-radius: 0;
}

.article .news-metadata-container {
    margin-top: 2rem;
}

/* ==========================================================================
   Component: article-tags (category/tags chips)
   ========================================================================== */
.article-tags {
    font-family: var(--mono);
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 2rem;
}

/* Breathing room between the article body/tags and the comments section.
   Keeps the tags row from visually touching the comments card. */
.comments-section {
    margin-top: 2.5rem;
}

.article-tags__group {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
}

.article-tags__label {
    font-size: 12px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.article-tags__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.article-tags__chip {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border: 1px solid var(--border-color);
    border-radius: 0;
    color: var(--text-secondary);
    font-size: 13px;
    text-decoration: none;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.article-tags__chip:hover {
    color: var(--text-primary);
    border-color: var(--text-primary);
}

/* Real buttons used with the quick-nav tab language (features / comments sort) */
button.quick-nav__item {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
}

.comment-form {
    position: relative;
    margin-bottom: 20px;
}

.comment-form__form {
    display: flex;
    flex-direction: column;
}

.comment-form__textarea {
    width: 100%;
    box-sizing: border-box;
    background: var(--comment-input-bg);
    border: 1px solid var(--border-color);
    border-radius: 0;
    color: var(--text-primary);
    font-family: var(--font-family);
    font-size: 14px;
    resize: vertical;
    min-height: 120px;
    padding: 16px;
    outline: none;
    transition: border-color 0.15s ease;
}

.comment-form__textarea::placeholder {
    color: var(--text-muted);
}

.comment-form__textarea:focus {
    border-color: var(--text-primary);
}

/* Actions row sits BELOW the textarea: a char counter on the left, the
   submit button on the right. This keeps the button visually attached to
   the field it submits and makes the form read top-to-bottom in the
   natural order (write -> submit). */
.comment-form__actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
}

/* The submit button's look comes from the shared .button block above */

.comment-form__count {
    font-size: 12px;
    color: var(--text-muted);
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
}

.comment-form__count--near {
    color: var(--updated);
}

/* -------- Profile-page card (shared by Comments and Followers / Following) ----
   Two card variants live here:
     .profile-comment — Comments tab (body + meta row)
     .profile-follow  — Followers / Following tabs (meta row only, optional
                        right-side action via .profile-follow__spacer)
   Both share the same surface, border, padding, and meta-row layout. The
   combined selectors below are intentional: keeping the visuals in one
   place means the two tabs can never drift apart. */
.profile-comment,
.profile-follow {
    display: block;
    padding: 14px 12px;
    background-color: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    border-radius: 0;
    margin-bottom: 14px;
}

.profile-comment:last-child,
.profile-follow:last-child {
    margin-bottom: 0;
}

.profile-comment__body {
    font-size: 15px;
    line-height: 1.5;
    color: var(--text-content);
    white-space: pre-wrap;
    word-wrap: break-word;
    margin-bottom: 10px;
}

.profile-comment__body a {
    color: var(--text-primary);
    text-decoration: underline;
}

/* Staged quote reply ("@user wrote:" + "> excerpt") rendered by
   render_comment_body(). The accent rail and tag-bg wash set the quote apart
   from the reply text while staying on the meta-row tag recipe. */
.comment-quote {
    padding: 8px 12px;
    border-left: 2px solid var(--accent);
    background: var(--tag-bg);
    color: var(--text-muted);
    border-radius: 0;
}

.comment-quote-attrib {
    margin: 0 0 4px;
    font-family: var(--mono);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.035em;
    color: var(--text-muted);
}

.profile-comment__meta,
.profile-follow__meta {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    font-size: 13px;
}

.profile-comment__avatar-link,
.profile-follow__avatar-link {
    flex-shrink: 0;
    line-height: 0;
}

.profile-comment__avatar,
.profile-follow__avatar {
    width: 24px;
    height: 24px;
    object-fit: cover;
    border-radius: 0;
    background: var(--third-bg-color);
    display: block;
}

.profile-comment__author,
.profile-follow__author {
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    text-decoration: none;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
    flex-shrink: 1;
    min-width: 0;
    max-width: 220px;
    font-weight: 400;
}

.profile-comment__author:hover,
.profile-follow__author:hover {
    color: var(--text-primary);
    border-color: var(--text-primary);
    text-decoration: none;
}

.profile-comment__time,
.profile-follow__time {
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
    white-space: nowrap;
    font-family: var(--mono);
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.035em;
    flex-shrink: 0;
}

.profile-comment__sep,
.profile-follow__sep {
    color: var(--text-muted);
    user-select: none;
}

.profile-comment__source,
.profile-follow__source {
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 12px;
}

.profile-comment__source:hover,
.profile-follow__source:hover {
    color: var(--text-primary);
    text-decoration: underline;
}

/* Spacer inside the meta row that pushes a trailing action (e.g. the
   Follow back / Following button in the Followers tab) to the right edge.
   Uses flex auto-grow so the username pill truncates with ellipsis before
   the action is pushed off-screen. */
.profile-comment__spacer,
.profile-follow__spacer {
    flex: 1 1 auto;
    min-width: 8px;
}

/* "Author" pill shown next to a comment from the news author. Same recipe
   as the meta-row author/time pills so the OP marker sits on the same
   baseline as the rest of the meta row. */
.profile-comment__badge {
    display: inline-flex;
    align-items: center;
    height: 25px;
    padding: 0 12px;
    font-size: var(--tag-font-size);
    font-family: var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.035em;
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
    flex-shrink: 0;
}

/* Upvote pill that lives at the right of the meta row on a comment card.
   Mirrors the height/recipe of the other meta pills so the whole row
   stays a single 25px-tall line. The spacer in the meta row pushes it
   to the right edge; flex-shrink:0 keeps the count readable on narrow
   viewports. */
.profile-comment__vote {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 25px;
    padding: 0 10px;
    font-size: var(--tag-font-size);
    font-family: var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.035em;
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    text-decoration: none;
    cursor: pointer;
    flex-shrink: 0;
    line-height: 1;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.profile-comment__vote:hover {
    color: var(--text-primary);
    border-color: var(--text-primary);
}

.profile-comment__vote.active {
    color: var(--accent);
    border-color: var(--accent);
}

/* "Replying to <user>" pill shown above the comment form while a quote
   reply is staged. Reuses the meta-row pill recipe (25px mono uppercase
   tag pill) so it reads as the same family as the upvote/author pills. */
.reply-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    height: 25px;
    padding: 0 10px;
    margin-bottom: 10px;
    font-size: var(--tag-font-size);
    font-family: var(--mono);
    text-transform: uppercase;
    letter-spacing: 0.035em;
    color: var(--text-secondary);
    background: var(--tag-bg);
    border: 1px solid var(--secondary-border);
    border-radius: 0;
    line-height: 1;
}

.reply-pill__label {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.reply-pill__cancel {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    padding: 0;
    font-family: var(--mono);
    font-size: 14px;
    line-height: 1;
    color: var(--text-secondary);
    background: transparent;
    border: none;
    cursor: pointer;
    transition: color 0.15s ease;
}

.reply-pill__cancel:hover {
    color: var(--text-primary);
}

/* Empty state (shared by comments, profile sections) */
.empty-state {
    width: 100%;
    height: 100px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    font-size: 14px;
}

.empty-state--tall {
    height: var(--empty-state-h);
}

/* ==========================================================================
   Component: notice (status messages on redesign pages)
   ========================================================================== */
.notice {
    box-sizing: border-box;
    padding: 12px 16px;
    margin-bottom: 20px;
    border: 1px solid var(--border-color);
    border-radius: 0;
    color: var(--text-secondary);
    font-size: 14px;
}

/* Status colours mirror the .status-label / .button mapping:
   success = --stable (green), warning = --updated (amber),
   danger = --action2 (red). --accent is the interactive/brand hue and
   must not carry status meaning here. */
.notice--success {
    border-color: var(--stable);
    color: var(--text-primary);
}

.notice--warning {
    border-color: var(--updated);
    color: var(--text-primary);
}

.notice--danger {
    border-color: var(--action2);
    color: var(--text-primary);
}

/* Links inside a notice: primary text + underline (the notice surface,
   not the legacy yellow, carries the emphasis). */
.notice a {
    color: var(--text-primary);
    text-decoration: underline;
    text-decoration-thickness: from-font;
    text-underline-position: from-font;
    text-underline-offset: 2px;
    text-decoration-skip-ink: auto;
}

/* ==========================================================================
   Component: button (shared button primitives)
   One base for every action button: a 27px bordered mono pill, plus solid /
   ghost / danger / success fills and a block modifier. Contextual CTAs
   compose the base with one modifier, e.g.
   class="button button--solid".
   ========================================================================== */
.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 27px;
    padding: 0 14px;
    border-radius: 0;
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    cursor: pointer;
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}

/* Every variant carries a border so their box heights match: the borderless
   solid used to render 2px shorter than the bordered ghost, and an action
   row's flex stretch then hid it whenever a ghost sat beside it — so the
   same primary button measured 30px alone and 32px next to a Cancel. */
.button--solid {
    border: 1px solid transparent;
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: 700;
}

.button--solid:hover {
    background: var(--accent-hover);
    color: var(--accent-contrast);
}

.button--success {
    border: 1px solid transparent;
    background: var(--stable);
    color: var(--accent-contrast);
    font-weight: 700;
}

.button--success:hover {
    filter: brightness(1.08);
    color: var(--accent-contrast);
}

.button--ghost {
    border: 1px solid var(--border-color);
    background: transparent;
    color: var(--text-secondary);
}

.button--ghost:hover {
    box-shadow: 0 0 0 1px var(--text-primary) inset;
    color: var(--text-primary);
}

.button--danger {
    border: 1px solid var(--action2);
    background: transparent;
    color: var(--action2);
}

.button--danger:hover {
    background: var(--action2);
    border-color: var(--action2);
    color: var(--text-primary);
    box-shadow: none;
}

.button--block {
    width: 100%;
}

.button--block + .button--block {
    margin-top: 10px;
}

/* ==========================================================================
   Component: user details (banner + header + stacked sections)
   ========================================================================== */

/* -------- Content card -------- */
.userdetails {
    width: 100%;
    background-color: var(--card-bg);
    border-radius: 0;
    position: relative;
    border: var(--userdetails-border);
    overflow: hidden;
}

/* -------- Banner -------- */
.userdetails__banner {
    height: 205px;
    border-radius: 0;
    overflow: hidden;
    position: relative;
    background-color: var(--third-bg-color);
    margin-bottom: -67px;
}

.userdetails__banner img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.userdetails__gradient {
    background: linear-gradient(180deg,
        var(--gradient-step-1) 0%,
        var(--gradient-step-2) 15%,
        var(--gradient-step-3) 30%,
        var(--secondarybg-color) 100%);
    position: absolute;
    bottom: 0;
    left: 0;
    height: 69%;
    width: 100%;
    /* Below the sticky topbar (z-index: 100) so the header stays on top */
    z-index: 5;
}

/* -------- Header block -------- */
.userdetails__header {
    position: relative;
    /* Below the sticky topbar (z-index: 100) so the header stays on top */
    z-index: 10;
    padding: 40px 20px 0;
    margin-bottom: 32px;
}

.userdetails__top {
    display: flex;
    justify-content: space-between;
    width: 100%;
    align-items: flex-end;
}

.userdetails__avatar {
    width: var(--avatar-xl);
    min-width: var(--avatar-xl);
    min-height: var(--avatar-xl);
    height: var(--avatar-xl);
    object-fit: cover;
    border: 3px solid var(--secondarybg-color);
    background-color: var(--third-bg-color);
    margin-right: 11px;
    position: relative;
    border-radius: 0;
    overflow: hidden;
    flex-shrink: 0;
}

.userdetails__head {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    min-width: 0;
    flex: 1;
    padding-bottom: 10px;
}

/* Specificity: pair with .redesign so this beats the global heading reset
   (.redesign h1…h6 { font-family: var(--font-family) !important }). The
   !important on font-family is necessary because the global heading
   reset uses !important too; the color/weight remain subject to the
   reset's !important overrides for consistency with the rest of the
   redesign's H1 styling. */
.redesign .userdetails__name {
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: var(--mono) !important;
    font-size: 20px;
    font-weight: 700;
    color: var(--game-title-color) !important;
    line-height: 1.2;
    word-break: break-word;
}

.userdetails__badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background-color: var(--third-hover);
    border: 1px solid var(--border-color);
    width: fit-content;
    padding: 2px 10px;
    box-sizing: border-box;
    border-radius: 0;
    font-family: var(--font-family);
    font-size: 12px;
    color: var(--active-color);
}

.userdetails__desc {
    margin: 26px 20px 39px;
    font-size: 16px;
    line-height: 1.5;
    color: var(--game-desc-color);
    word-wrap: break-word;
}

/* Links inside the profile description follow the same recipe as
   .article-body links: white + underline, no hover effect
   (base selector outspecifies the :where(a) resets). */
.userdetails__desc a {
    color: var(--text-primary);
    text-decoration: underline;
}

/* Byline under the name (webmaster link on member cards) */
.userdetails__byline {
    font-size: 13px;
    color: var(--text-secondary);
    text-decoration: none;
}

.userdetails__byline:hover {
    color: var(--text-primary);
}

/* Byline under the site name: contributor-style avatar + username chip */
.userdetails__byline.contributor {
    margin-top: 4px;
}

.userdetails__byline .contributor__avatar {
    width: 25px;
    height: 25px;
}

/* -------- Follow / unfollow action (user profile header) -------- */
.userdetails__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    padding-bottom: 10px;
}

.follow-btn,
.follow-btn--login {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    min-height: 32px;
    background-color: transparent;
    color: var(--text-primary);
    border: 1px solid var(--border-color);
    border-radius: 0;
    font-family: 'JetBrains Mono', monospace;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.follow-btn:hover,
.follow-btn--login:hover {
    background-color: var(--third-hover);
    border-color: var(--accent);
    color: var(--accent);
}

.follow-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.follow-btn--following,
.follow-btn--following:hover {
    background-color: var(--accent);
    border-color: var(--accent);
    color: var(--accent-contrast);
}

.follow-btn[disabled] {
    opacity: 0.7;
    cursor: progress;
}

.follow-btn__icon {
    font-size: 14px;
    line-height: 1;
}

/* -------- Small follow-back button (Followers / Following lists) -------- */
.follow-btn--sm {
    min-height: 25px;
    height: 25px;
    padding: 0 10px;
    font-size: 11px;
    flex-shrink: 0;
    /* The list-row title's ::after stretched link sits at z-index: 1
       over the whole row. The button must sit above it to be clickable. */
    position: relative;
    z-index: 3;
}

.follow-btn--sm .follow-btn__icon {
    font-size: 12px;
}

/* Chips inside the profile card (member category/tags) */
.userdetails__tags {
    margin: 0 20px 30px;
}

/* -------- Stacked sections -------- */
.userdetails__sections {
    padding: 0 20px;
}

.bordered-section {
    display: flex;
    padding: var(--card-padding);
    border-radius: 0;
    margin-bottom: 50px;
    background-color: var(--third-bg-color);
}

.bordered-section__body {
    width: 100%;
    display: flex;
    flex-direction: column;
}

.bordered-section__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}

.bordered-section__title {
    display: flex;
    align-items: center;
    font-size: var(--game-title-size);
    color: var(--game-title-color);
    font-weight: 700;
}

.bordered-section__count {
    font-size: var(--game-subtitle-size);
    color: var(--text-muted);
    font-weight: 400;
}

/* Counts on the Feature Requests page and the news comments section read as
   accent + mono; other pages keep the muted base treatment. */
.features-page .bordered-section__count,
.comments-section .bordered-section__count {
    color: var(--accent);
    font-family: var(--mono);
}

.bordered-section__content-wrap {
    width: 100%;
}

.bordered-section__content {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    width: 100%;
}

/* List rows stack full-width with divider borders (news / comments / people) */
.bordered-section__content--list {
    flex-direction: column;
    gap: 0;
}

/* ==========================================================================
   Component: pagination (3-column grid: prev | page | next)

   A borderless
   3-column grid where the previous and next buttons flank a centered
   "PAGE N / TOTAL" indicator. Each button carries a small uppercase
   direction label on top and the target page number below. Buttons
   stretch to fill their half of the bar; the center column stays
   fixed-width.
   ========================================================================== */
.pagination {
    display: grid;
    margin-top: 36px;
    border: 1px solid var(--border-color);
    grid-template-columns: minmax(0, 1fr) 120px minmax(0, 1fr);
}

.pagination[hidden] {
    display: none;
}

.pagination__button {
    display: flex;
    min-height: 64px;
    padding: 12px 16px;
    border: 0;
    background: transparent;
    align-items: flex-start;
    justify-content: center;
    flex-direction: column;
    gap: 4px;
    color: var(--text-secondary);
    text-align: left;
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
    cursor: pointer;
}

.pagination__button--next {
    align-items: flex-end;
    text-align: right;
}

.pagination__button:not(:disabled):not([aria-disabled="true"]):hover {
    background: var(--third-bg-color);
}

.pagination__button:not(:disabled):not([aria-disabled="true"]):hover .pagination__direction {
    color: var(--accent);
}

.pagination__button:focus-visible {
    position: relative;
    z-index: 1;
    outline: 1px solid var(--accent);
    outline-offset: -1px;
}

.pagination__button:disabled,
.pagination__button[aria-disabled="true"] {
    cursor: not-allowed;
    opacity: 0.42;
}

.pagination__summary {
    display: flex;
    border-right: 1px solid var(--border-color);
    border-left: 1px solid var(--border-color);
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 4px;
    color: var(--text-muted);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.pagination__summary-value {
    color: var(--text-primary);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.pagination__direction {
    color: var(--text-secondary);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.pagination__target {
    color: var(--text-primary);
    font-size: 13px;
    font-weight: 600;
}

/* Positioning context for Bootstrap-style .dropdown elements (e.g. the nav
   message panel thread-actions menu). */
.dropdown {
    position: relative;
    display: inline-block;
    min-width: 0;
}

/* ==========================================================================
   Component: site-picker (searchable member-site picker)
   Replaces the old scroll-only <select> for "use a site's screenshot" on
   the news admin form. A search field filters the embedded site list and
   each row shows the site's screenshot thumbnail so the choice is visual;
   the chosen member_id goes into a hidden input. Uses the same dark panel
   and token borders as the other dropdowns.
   Wired by App.initSitePicker in main.js.
   ========================================================================== */
.site-picker {
    position: relative;
}

.site-picker__list {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    max-height: 280px;
    overflow-y: auto;
    margin: 0;
    padding: 4px;
    list-style: none;
    background: var(--secondarybg-color);
    border: 1px solid var(--border-color);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
    z-index: 50;
}

.site-picker__list[hidden] {
    display: none;
}

.site-picker__option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 8px;
    color: var(--text-secondary);
    cursor: pointer;
}

.site-picker__option:hover,
.site-picker__option--focused {
    background: var(--secondary-hover);
    color: var(--text-primary);
}

.site-picker__option--active {
    background: var(--tag-active-bg);
    color: var(--text-primary);
}

.site-picker__thumb {
    width: 64px;
    height: 36px;
    flex-shrink: 0;
    object-fit: cover;
    border: 1px solid var(--border-color);
    background: var(--third-bg-color);
}

.site-picker__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 14px;
}

.site-picker__empty {
    padding: 7px 10px;
    color: var(--text-muted);
    font-size: 14px;
}

/* ==========================================================================
   Component: dash-links (user dashboard action lists)
   Stacked full-width rows inside a .bordered-section. Each row is a real
   link carrying a title + one-line description; the divider language
   mirrors .list-row without a thumbnail.
   ========================================================================== */
.dash-links {
    display: flex;
    flex-direction: column;
    width: 100%;
}

.dash-link {
    display: block;
    padding: 12px 10px;
    border-top: 1px solid var(--secondary-border);
    text-decoration: none;
}

.dash-link:first-child {
    border-top: 0;
}

.dash-link:hover {
    background: var(--third-bg-color);
}

.dash-link__title {
    display: block;
    color: var(--text-primary);
    font-family: var(--mono);
    font-size: 13px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.dash-link:hover .dash-link__title {
    color: var(--accent);
}

.dash-link__count {
    color: var(--accent);
    font-weight: 700;
}

.dash-link__desc {
    display: block;
    margin-top: 3px;
    color: var(--text-muted);
    font-size: 13px;
    line-height: 1.5;
}

/* ==========================================================================
   Component: media-upload (click-to-replace image field)
   A label-wrapped preview that opens the file picker natively; the live
   thumbnail comes from App.initMediaUpload. Modifiers set the preview's
   shape; both keep the 1px token border and squared corners used by every
   other surface.
   ========================================================================== */
.media-upload {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}

.media-upload__preview {
    position: relative;
    display: block;
    overflow: hidden;
    cursor: pointer;
    border: 1px solid var(--border-color);
    background: var(--third-bg-color);
}

.media-upload__preview:hover {
    border-color: var(--accent);
}

/* The real control is the visually hidden file input (.visually-hidden in the
   markup), so mirror its focus ring onto the preview the user can see. */
.media-upload__input:focus-visible + .media-upload__preview {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.media-upload__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.media-upload__overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    background: rgba(0, 0, 0, 0.55);
    color: var(--text-primary);
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.media-upload__preview:hover .media-upload__overlay,
.media-upload__input:focus-visible + .media-upload__preview .media-upload__overlay {
    opacity: 1;
}

.media-upload--avatar .media-upload__preview {
    width: var(--avatar-xl);
    height: var(--avatar-xl);
}

.media-upload--header .media-upload__preview {
    /* width:100% keeps the preview fluid on narrow viewports; the max-width
       stops it stretching across the whole form, where the panoramic shape
       made it read as page content instead of a field preview. */
    width: 100%;
    max-width: 360px;
    aspect-ratio: 24 / 5; /* matches the documented 1920x400 recommendation */
}

/* ==========================================================================
   Page tweaks (page-specific alignment/overrides only)
   ========================================================================== */

/* Align the article page sidebar with the article's top edge below the header */
.article-page .layout__aside {
    margin-top: 40px;
}

/* Profile page inside the app-shell: the tab strip sticks directly beneath
   the shell doc-topbar (--topbar) instead of the global topbar it offsets
   from on non-shell pages. z-index 11 keeps it under .doc-topbar (20). */
body.admin-shell .tab-filters {
    top: var(--topbar);
}

/* Community (auth) page: short centered card, so pin the footer to the
   viewport bottom via flex column and vertically center the card in the
   remaining space. Scoped to body.community-page (direct-child container)
   so other pages' structures are untouched. */
.community-page {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

.community-page > .container {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 100%;
    padding-top: 24px;
    padding-bottom: 24px;
}

/* Full-width flash notice above the centered card. */
.community-page > .container > .notice {
    align-self: stretch;
}

/* Auth card: capped width, centered by the flex parent. */
.community-page .auth-card {
    width: 100%;
    max-width: 480px;
}

/* ==========================================================================
   Page header (typographic header for tool/admin pages)
   ========================================================================== */
.page-head {
    margin: 25px 0 40px;
}

.page-head h1 {
    margin: 0 0 4px;
}

.page-head__sub {
    margin: 0;
    font-size: 14px;
    color: var(--text-secondary);
}

/* The Feature Requests hero is the site's one marketing hero: padding carries
   the space, so margins go to zero. Admin page-heads keep the standard. */
.features-page .page-head {
    margin: 0 auto;
    padding: 82px 0 58px;
}

/* Centered panel footer action */
.dashboard-panel-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin-top: 15px;
}

/* ==========================================================================
   Legacy Bootstrap tables — token skin over Bootstrap tables
   (unmigrated admin pages still render Bootstrap tables under .redesign;
    migrated review pages use the native .compare component instead)
   ========================================================================== */
.redesign .table {
    --bs-table-bg: transparent;
    --bs-table-border-color: var(--border-color);
    color: var(--text-secondary);
}

.redesign .table > :not(caption) > * > * {
    background-color: transparent;
    border-color: var(--border-color);
}

.redesign .table thead th {
    color: var(--text-muted);
    font-weight: 600;
}

.redesign .img-thumbnail {
    background: var(--secondarybg-color);
    border-color: var(--border-color);
}

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 992px) {
    .layout {
        grid-template-columns: 1fr;
    }
    .site-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .site-grid--full {
        grid-template-columns: repeat(3, 1fr);
    }
    .news-grid {
        grid-template-columns: 1fr;
    }
    .mobile-hidden {
        display: none;
    }
    .article {
        margin-top: 20px;
    }
}

@media (max-width: 630px) {
    .compare,
    .compare--single {
        grid-template-columns: 1fr;
    }
    .article-hero {
        height: 194px;
        margin: 30px 0;
    }
    .article__title {
        font-size: 22px;
    }
    .userdetails__banner {
        height: 177px;
    }
}

@media (max-width: 576px) {
    .tab-filters {
        flex-direction: column;
        align-items: stretch;
        top: 76px;
    }
    .tab-filters__left,
    .tab-filters__right {
        justify-content: space-between;
    }
    .site-grid,
    .site-grid--full,
    .news-grid {
        grid-template-columns: 1fr;
    }
    .site-card--row {
        flex-direction: column;
    }
    .site-card--row .site-card__media {
        width: calc(100% - 20px);
        height: 106px;
        min-width: 0;
        margin: 10px;
    }
    .tab-filters__search {
        width: 100%;
    }
    .news-item__media {
        width: 110px;
        min-width: 110px;
        height: 70px;
    }
}
