/* Custom styles for Bitcoin Meme Search Engine
 * ============================================================================
 * DESIGN TOKENS — the only place colour is decided.
 *
 * ▶ To restyle: change the values in the two blocks below. Nothing else in this
 *   file, and nothing in the templates, should contain a raw colour.
 *
 * Until 2026-07-27 the palette was not chosen, it was inherited: 13 pages loaded
 * `cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css`, which set
 * `--bs-body-bg: #1C2333` (the navy) and `--bs-info: #27B9D6` (the odd light
 * blue the app used 67 times as its accent). That file also put dark values at
 * `:root`, inverting Bootstrap 5.3's model — stock 5.3 keeps light at `:root`
 * and overrides for dark — so a light mode there would only half-apply. It is
 * gone; Bootstrap is vendored in static/vendor and light/dark both work.
 *
 * The values here are deliberately plain: near-neutral surfaces so the memes
 * carry the colour, with Bitcoin orange (already the favicon and 49 uses of
 * text-warning) as the single accent. They are a working placeholder for a
 * Claude Design pass, not a finished identity.
 * ============================================================================
 */

:root,
[data-bs-theme="light"] {
    /* ONE HUE: gold. Everything that is not gold is a warm neutral.
     * `info` is folded into gold rather than given its own colour — the app uses
     * it 67 times as a de-facto accent (23 text-info, 13 btn-outline-info, 20
     * bg-info, 11 btn-info), so any separate hue there competes with the brand.
     * Filled vs outline now carries the active/inactive distinction instead. */
    --bs-primary: #b45309;
    --bs-primary-rgb: 180, 83, 9;
    --bs-info: #b45309;
    --bs-info-rgb: 180, 83, 9;
    --bs-warning: #b45309;
    --bs-warning-rgb: 180, 83, 9;
    --bs-link-color: #b45309;
    --bs-link-hover-color: #7c3d07;
    /* Subtle/emphasis triplets, so alerts and tinted cards follow the accent
     * instead of falling back to Bootstrap's cyan. */
    --bs-info-bg-subtle: #fdf2e3;
    --bs-info-border-subtle: #f0cfa0;
    --bs-info-text-emphasis: #7c3d07;
    --bs-warning-bg-subtle: #fdf2e3;
    --bs-warning-border-subtle: #f0cfa0;
    --bs-warning-text-emphasis: #7c3d07;
    /* Neutral, faintly warm so it never reads as cold blue-grey */
    --ms-neutral: #6b6459;

    /* App surfaces: page < card < raised, with one line colour.
     * ⚠ The page MUST differ from the card. Both were #ffffff, i.e. 1.00:1 —
     * literally indistinguishable, which is why light mode was hard to read. */
    --bs-body-bg: #eef0f3;
    --ms-surface: #ffffff;
    --ms-surface-raised: #e4e7ec;
    --ms-line: #cfd4db;
    --ms-image-mat: #dfe3e8;      /* behind a contained meme */
    --ms-accent: #b45309;
    --ms-accent-hover: #92400e;
    --ms-accent-active: #7c3d07;
    /* Ink on filled gold. Measured: white on #b45309 is 5.02:1, near-black only
     * 3.60:1 — so light mode takes white and dark mode takes near-black. One ink
     * for both themes fails one of them. */
    --ms-on-accent: #ffffff;
    --ms-shadow: rgba(15, 23, 42, 0.12);
    --ms-overlay-scrim: rgba(15, 23, 42, 0.86);
    --ms-shimmer: rgba(15, 23, 42, 0.06);
}

[data-bs-theme="dark"] {
    --bs-body-bg: #14161b;
    --bs-body-bg-rgb: 20, 22, 27;
    --bs-primary: #f7931a;        /* Bitcoin gold, matching the favicon */
    --bs-primary-rgb: 247, 147, 26;
    --bs-info: #f7931a;           /* was a teal blue; gold is the only hue now */
    --bs-info-rgb: 247, 147, 26;
    --bs-warning: #f7931a;
    --bs-warning-rgb: 247, 147, 26;
    --bs-link-color: #f7931a;
    --bs-link-hover-color: #ffab45;
    --bs-info-bg-subtle: #2a1f0c;
    --bs-info-border-subtle: #5c4212;
    --bs-info-text-emphasis: #f9b45c;
    --bs-warning-bg-subtle: #2a1f0c;
    --bs-warning-border-subtle: #5c4212;
    --bs-warning-text-emphasis: #f9b45c;
    --ms-neutral: #9a9186;

    --ms-surface: #1b1e24;
    --ms-surface-raised: #23272f;
    --ms-line: #313640;
    --ms-image-mat: #101216;
    --ms-accent: #f7931a;
    --ms-accent-hover: #ffa733;
    --ms-accent-active: #d97b0d;
    --ms-on-accent: #14161b;      /* 7.88:1 on the gold; white would be 2.30:1 */
    --ms-shadow: rgba(0, 0, 0, 0.45);
    --ms-overlay-scrim: rgba(0, 0, 0, 0.9);
    --ms-shimmer: rgba(255, 255, 255, 0.08);
}

/* ---------------------------------------------------------------------------
 * THEME SHIM.
 *
 * The markup hardcodes dark into ~500 utility classes — bg-dark ×119,
 * border-secondary ×123, text-muted ×149, text-light ×66, navbar-dark ×15 —
 * which is why there was no light mode: those classes mean "dark" literally,
 * whatever `data-bs-theme` says. Remapping them onto the tokens above makes the
 * toggle work everywhere today without touching 20 templates.
 *
 * ⚠ This is a bridge, not the destination. During the restyle port, replace the
 * classes in the markup with Bootstrap 5.3's theme-aware ones (bg-body-tertiary,
 * text-body-secondary, border) and delete this block. `!important` is required
 * only because it is overriding Bootstrap's own utilities.
 * ------------------------------------------------------------------------- */
.bg-dark { background-color: var(--ms-surface) !important; }
.card.bg-dark { background-color: var(--ms-surface) !important; }
.navbar.bg-secondary,
.navbar.bg-dark { background-color: var(--ms-surface-raised) !important; }
.text-light { color: var(--bs-body-color) !important; }
.text-muted { color: var(--bs-secondary-color) !important; }
.border-secondary { border-color: var(--ms-line) !important; }
.table-dark {
    --bs-table-bg: var(--ms-surface);
    --bs-table-color: var(--bs-body-color);
    --bs-table-border-color: var(--ms-line);
}
/* navbar-dark forces light-on-dark text regardless of colour mode */
[data-bs-theme="light"] .navbar-dark {
    --bs-navbar-color: rgba(15, 23, 42, 0.7);
    --bs-navbar-hover-color: rgba(15, 23, 42, 0.9);
    --bs-navbar-active-color: rgba(15, 23, 42, 1);
    --bs-navbar-brand-color: rgba(15, 23, 42, 1);
    --bs-navbar-brand-hover-color: rgba(15, 23, 42, 1);
    --bs-navbar-toggler-border-color: var(--ms-line);
}

.ms-theme-toggle {
    color: var(--bs-navbar-color, inherit);
    line-height: 1;
}
.ms-theme-toggle:hover { color: var(--ms-accent); }

/* Meme image styling */
.meme-image-container {
    position: relative;
    height: 250px;
    overflow: hidden;
    border-radius: 0.375rem 0.375rem 0 0;
}

.meme-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.3s ease;
}

.meme-card:hover .meme-image {
    transform: scale(1.05);
}

/* Card hover effects */
.meme-card {
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.meme-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 25px var(--ms-shadow);
}

/* Badge styling */
.badge {
    font-size: 0.75em;
    font-weight: 500;
}

/* Button hover effects */
.btn {
    transition: all 0.2s ease;
}

.dunk-btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 8px rgba(var(--bs-primary-rgb), 0.3);
}

/* File input styling */
.form-control:focus {
    border-color: var(--bs-info);
    box-shadow: 0 0 0 0.2rem rgba(var(--bs-info-rgb), 0.25);
}

/* Loading animation for upload */
.upload-loading {
    position: relative;
    overflow: hidden;
}

.upload-loading::after {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, var(--ms-shimmer), transparent);
    animation: loading 2s infinite;
}

@keyframes loading {
    0% { left: -100%; }
    100% { left: 100%; }
}

/* Search results grid responsiveness */
@media (max-width: 768px) {
    .meme-image-container {
        height: 200px;
    }
    
    .display-4 {
        font-size: 2rem;
    }
    
    .lead {
        font-size: 1rem;
    }
}

/* Smooth scrolling */
html {
    scroll-behavior: smooth;
}

/* Custom scrollbar for dark theme */
::-webkit-scrollbar {
    width: 8px;
}

::-webkit-scrollbar-track {
    background: var(--ms-surface-raised);
}

::-webkit-scrollbar-thumb {
    background: var(--bs-secondary);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--bs-info);
}

/* Alert styling */
.alert {
    border: none;
    border-radius: 0.5rem;
}

/* Modal styling */
.modal-content {
    border-radius: 0.5rem;
}

/* Typography improvements */
.font-monospace {
    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
    font-size: 0.85em;
    line-height: 1.4;
}

/* Icon enhancements */
.bi {
    vertical-align: -0.125em;
}

/* Card improvements */
.card {
    border-radius: 0.5rem;
    overflow: hidden;
}

.card-body {
    padding: 1.25rem;
}

/* Feature cards on home page */
.card.h-100 {
    transition: all 0.2s ease;
}

.card.h-100:hover {
    transform: translateY(-3px);
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}

/* Search input enhancements */
.input-group-lg .form-control {
    font-size: 1.1rem;
}

/* Footer styling */
footer {
    border-top: 1px solid var(--bs-border-color);
    margin-top: 3rem;
}

/* Loading states */
.btn:disabled {
    cursor: not-allowed;
    opacity: 0.6;
}

/* Tag styling. The tag pills are the most-clicked thing on the site, so they get
 * a real surface rather than Bootstrap's flat grey `secondary`, and they read the
 * same way in both modes. */
.badge.bg-secondary {
    background-color: var(--ms-surface-raised) !important;
    color: var(--bs-body-color);
    border: 1px solid var(--ms-line);
}
a.badge.bg-secondary:hover {
    border-color: var(--ms-accent);
    color: var(--ms-accent);
}

/* Image placeholder */
.image-placeholder {
    background: linear-gradient(45deg, var(--ms-surface-raised) 25%, transparent 25%),
                linear-gradient(-45deg, var(--ms-surface-raised) 25%, transparent 25%),
                linear-gradient(45deg, transparent 75%, var(--ms-surface-raised) 75%),
                linear-gradient(-45deg, transparent 75%, var(--ms-surface-raised) 75%);
    background-size: 20px 20px;
    background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}

/* Voting buttons styling */
.vote-btn {
    transition: all 0.2s ease;
    font-size: 0.85rem;
    padding: 0.25rem 0.5rem;
    min-width: 60px;
    position: relative;
}

.vote-btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.vote-btn i {
    font-size: 1rem;
    margin-right: 0.25rem;
}

.vote-btn:disabled {
    cursor: not-allowed;
    opacity: 0.6;
    transform: none;
}

/* Vote count styling */
.upvote-count, .downvote-count {
    font-weight: bold;
    font-size: 0.8rem;
}

.vote-score-display {
    font-weight: bold;
    color: var(--bs-warning);
}

/* Voting section styling */
.voting-section {
    border-top: 1px solid var(--bs-border-color);
    padding-top: 0.75rem;
}

/* Responsive voting buttons */
@media (max-width: 576px) {
    .vote-btn {
        min-width: 50px;
        font-size: 0.75rem;
        padding: 0.2rem 0.4rem;
    }
    
    .vote-btn i {
        font-size: 0.9rem;
        margin-right: 0.2rem;
    }
}

/* Vote button animations */
.vote-btn.btn-success {
    animation: voteSuccess 0.3s ease;
}

.vote-btn.btn-danger {
    animation: voteDanger 0.3s ease;
}

@keyframes voteSuccess {
    0% { transform: scale(1); }
    50% { transform: scale(1.1); }
    100% { transform: scale(1); }
}

@keyframes voteDanger {
    0% { transform: scale(1); }
    50% { transform: scale(1.1); }
    100% { transform: scale(1); }
}

/* Vote score highlighting */
.vote-score-display.positive {
    color: var(--bs-success);
}

.vote-score-display.negative {
    color: var(--bs-danger);
}

.vote-score-display.neutral {
    color: var(--bs-secondary);
}

/* Tab styling for dark theme */
.nav-tabs .nav-link {
    border-bottom: 1px solid transparent;
    background-color: var(--ms-surface);
    color: var(--bs-secondary-color);
    border-top-left-radius: 0.375rem;
    border-top-right-radius: 0.375rem;
}

.nav-tabs .nav-link:hover {
    border-color: var(--ms-line);
    background-color: var(--ms-surface-raised);
    color: var(--bs-body-color);
}

.nav-tabs .nav-link.active {
    background-color: var(--ms-surface);
    border-color: var(--ms-line) var(--ms-line) var(--ms-surface);
    color: var(--bs-body-color);
}

.tab-content > .tab-pane {
    border: 1px solid var(--ms-line);
    border-top: none;
    border-bottom-left-radius: 0.375rem;
    border-bottom-right-radius: 0.375rem;
}

/* Browse Page - Improved Meme Cards
 *
 * ⚠ NEVER crop a meme in a card. This was `aspect-ratio: 1/1` with
 * `object-fit: cover`, i.e. a square crop forced onto a corpus whose images are
 * four-panel comics, tall two-panel stacks and wide screenshots. On a meme
 * search site the TEXT IS THE CONTENT, so cropping doesn't just look untidy, it
 * destroys the thing the visitor came to read: measured on the live grid, the
 * top line of "Knots Mempool vs Core Mempool", the left column of the
 * store-100kb comic and the punchline of "Adam Back's Intern is Speaking" were
 * all outside the frame. A hover `scale(1.08)` cropped further still.
 *
 * So: `contain`, always — nothing is ever cut off, whatever shape it is. The
 * letterbox that `contain` leaves is filled by the image itself, blurred, as a
 * decorative backdrop, so cards stay full-bleed instead of sitting on dead
 * bars. Same URL as the real image, so it costs no extra request.
 */
.meme-image-wrapper {
    position: relative;
    aspect-ratio: 4 / 5;
    overflow: hidden;
    background: var(--ms-image-mat);
}

.meme-backdrop {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* scale past the edges so the blur doesn't fade to the card background */
    transform: scale(1.2);
    filter: blur(28px) brightness(0.5) saturate(1.15);
    z-index: 0;
    pointer-events: none;
}

.meme-image-wrapper .meme-image {
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
    object-fit: contain;
    transition: filter 0.3s ease;
}

/* Dim only slightly on hover — the overlay gradient already darkens, and the
 * old brightness(0.7) made the meme's own text hard to read. */
.meme-card:hover .meme-image-wrapper .meme-image {
    filter: brightness(0.88);
}

/* Hover Overlay */
.meme-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(to top, var(--ms-overlay-scrim) 0%, rgba(0,0,0,0.25) 45%, transparent 75%);
    display: flex;
    flex-direction: column;
    /* Pinned to the bottom edge, not centred. Centred controls sat squarely on
     * top of the meme's punchline — visible on "Adam Back's Intern is Speaking",
     * where the eye/copy buttons covered the caption. */
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 1rem;
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
    z-index: 2;
}

.meme-overlay * {
    pointer-events: auto;
}

.meme-card:hover .meme-overlay {
    opacity: 1;
}

.overlay-actions {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
}

.overlay-actions .btn {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.overlay-actions .btn:hover {
    transform: scale(1.15);
    box-shadow: 0 4px 15px var(--ms-shadow);
}

.overlay-stats {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

/* Vote Score Badge */
/* The score pill lives in the card BODY, not on the artwork.
 *
 * ⚠ It used to be `position: absolute; top: .75rem; right: .75rem`, i.e. floating
 * over the top-right of the image — which on a meme site means sitting on top of
 * the punchline. Seen in the wild: it covered the end of "FOLKS DONT WANT TO HOST
 * 100KB CSAM" and the "AT" of "SOME DAY, AND THAT DAY MAY NEVER COME". That is the
 * same mistake as cropping: destroying content to display chrome. The hover
 * overlay still shows the up/down counts, so nothing is lost by moving it out. */
.vote-badge {
    display: inline-flex;
    align-items: center;
    background: var(--ms-surface-raised);
    border: 1px solid var(--ms-line);
    color: var(--ms-accent);
    padding: 0.1rem 0.45rem;
    border-radius: 1rem;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}

.vote-badge.positive {
    background: rgba(25, 135, 84, 0.85);
    color: #fff;
}

.vote-badge.negative {
    background: rgba(220, 53, 69, 0.85);
    color: #fff;
}

/* Tags Container */
.tags-container {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.tag-badge {
    font-size: 0.7rem;
    padding: 0.25rem 0.5rem;
    transition: background-color 0.2s ease, transform 0.2s ease;
}

.tag-badge:hover {
    background-color: var(--bs-info) !important;
    transform: translateY(-1px);
}

/* Card Title Link */
.card-title a:hover {
    color: var(--bs-warning) !important;
}

/* Copy Button Success State */
.copy-btn.copied {
    background-color: #198754 !important;
    border-color: #198754 !important;
}

/* Responsive adjustments for browse grid. Cards get wider as columns drop, so
 * a squarer box keeps them from becoming very tall. Nothing crops either way —
 * the aspect ratio only decides how much blurred backdrop shows. */
@media (max-width: 1200px) {
    .meme-image-wrapper {
        aspect-ratio: 1 / 1;
    }
}

@media (max-width: 768px) {
    .overlay-actions .btn {
        width: 36px;
        height: 36px;
    }
    
    .vote-badge {
        font-size: 0.7rem;
        padding: 0.25rem 0.5rem;
    }
}

/* Lightbox Modal Styles */
#imagePreviewModal .modal-dialog {
    max-width: 90vw;
}

#imagePreviewModal .modal-body {
    min-height: 50vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ms-image-mat);
}

#imagePreviewModal #previewImage {
    max-height: 75vh;
    box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
}

#imagePreviewModal .btn-outline-light {
    border-width: 2px;
    font-size: 1.5rem;
    width: 50px;
    height: 50px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: all 0.2s ease;
}

#imagePreviewModal .btn-outline-light:hover {
    background: var(--ms-shimmer);
    transform: scale(1.1);
}

#imagePreviewModal .btn-outline-light:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

#imagePreviewModal .modal-footer {
    background: var(--ms-surface);
}

#imageCounter {
    font-weight: 600;
    color: var(--bs-info);
}

/* Clickable image cursor */
.clickable-image {
    cursor: zoom-in;
    transition: opacity 0.2s ease;
}

.clickable-image:hover {
    opacity: 0.9;
}

/* ---------------------------------------------------------------------------
 * Gold needs dark ink. White on #f7931a is about 2:1, well under any threshold,
 * and Bootstrap's .badge defaults to white text. Buttons already default to
 * black ink for info/warning, so only the filled badges and the scrollbar need
 * saying explicitly.
 * ------------------------------------------------------------------------- */
.badge.bg-info,
.badge.bg-warning,
.badge.bg-primary {
    color: #14161b;
}

/* Data, not an action — counts and metadata read as neutral, never as accent. */
.usage-count {
    background: color-mix(in srgb, var(--ms-neutral) 18%, transparent) !important;
    color: var(--ms-neutral) !important;
}

::-webkit-scrollbar-thumb:hover { background: var(--ms-accent); }

/* ---------------------------------------------------------------------------
 * BUTTON VARIANTS.
 *
 * ⚠⚠ Bootstrap 5.3 compiles button variants with LITERAL hex, not variables:
 *   .btn-info{--bs-btn-bg:#0dcaf0}   .btn-primary{--bs-btn-bg:#0d6efd}
 * so overriding --bs-info / --bs-primary changes .text-info and .bg-info (which
 * do read the -rgb vars) while leaving every BUTTON its stock blue. That is why
 * the first attempt at removing the blue appeared to do nothing: the Recent /
 * Popular toggle and Suggest Edit are btn-info and btn-outline-info, and
 * btn-primary is used 24 times. Each variant has to be restated by hand.
 * ------------------------------------------------------------------------- */
.btn-primary,
.btn-info,
.btn-warning {
    --bs-btn-color: var(--ms-on-accent);
    --bs-btn-bg: var(--ms-accent);
    --bs-btn-border-color: var(--ms-accent);
    --bs-btn-hover-color: var(--ms-on-accent);
    --bs-btn-hover-bg: var(--ms-accent-hover);
    --bs-btn-hover-border-color: var(--ms-accent-hover);
    --bs-btn-active-color: var(--ms-on-accent);
    --bs-btn-active-bg: var(--ms-accent-active);
    --bs-btn-active-border-color: var(--ms-accent-active);
    --bs-btn-disabled-color: var(--ms-on-accent);
    --bs-btn-disabled-bg: var(--ms-accent);
    --bs-btn-disabled-border-color: var(--ms-accent);
    --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

.btn-outline-primary,
.btn-outline-info,
.btn-outline-warning {
    --bs-btn-color: var(--ms-accent);
    --bs-btn-border-color: var(--ms-accent);
    --bs-btn-hover-color: var(--ms-on-accent);
    --bs-btn-hover-bg: var(--ms-accent);
    --bs-btn-hover-border-color: var(--ms-accent);
    --bs-btn-active-color: var(--ms-on-accent);
    --bs-btn-active-bg: var(--ms-accent);
    --bs-btn-active-border-color: var(--ms-accent);
    --bs-btn-disabled-color: var(--ms-accent);
    --bs-btn-disabled-border-color: var(--ms-accent);
    --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

/* btn-outline-light and btn-outline-secondary are near-invisible on a light
 * page. Both become a neutral outline that works in either mode. */
.btn-outline-light,
.btn-outline-secondary {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-border-color: var(--ms-line);
    --bs-btn-hover-color: var(--bs-body-color);
    --bs-btn-hover-bg: var(--ms-surface-raised);
    --bs-btn-hover-border-color: var(--ms-neutral);
    --bs-btn-active-color: var(--bs-body-color);
    --bs-btn-active-bg: var(--ms-surface-raised);
    --bs-btn-active-border-color: var(--ms-neutral);
}

/* The navbar sits on a page only ~1.1:1 away from it in light mode, so it needs
 * an edge to read as a bar rather than as part of the page. */
.navbar {
    border-bottom: 1px solid var(--ms-line);
}

/* A shorter box for the related-memes strip, which sits three-up inside a card.
 * Same no-crop rule as everywhere else — only the ratio differs. */
.meme-image-wrapper.is-compact { aspect-ratio: 1 / 1; }

/* Tag overflow. The "+5" badge used to be inert text, so the other five tags on a
 * card were simply unreachable — a tag is the primary navigation on this site, so
 * hiding five of eight behind a dead label loses most of the routes into the
 * corpus. It is now a button that reveals them in place. */
.tag-extra[hidden] { display: none; }
button.tag-more {
    background: var(--ms-surface-raised);
    border: 1px solid var(--ms-line);
    color: var(--bs-secondary-color);
    font-size: 0.75em;
    font-weight: 500;
    padding: 0.25em 0.55em;
    border-radius: 0.375rem;
    cursor: pointer;
    line-height: inherit;
}
button.tag-more:hover {
    color: var(--ms-accent);
    border-color: var(--ms-accent);
}

/* Reactions. Weighted, and nothing subtracts — see models.REACTIONS. */
.reaction-btn { line-height: 1.2; padding: 0.3rem 0.6rem; }
.reaction-btn .reaction-emoji { font-size: 1.1rem; vertical-align: -0.1em; }
.reaction-btn .reaction-count { font-variant-numeric: tabular-nums; margin-left: 0.25rem; }
/* The zero-weight ones sit back a little: available, not advertised. */
.reaction-btn[title*="does not affect"] { opacity: 0.72; }
.reaction-btn[title*="does not affect"]:hover { opacity: 1; }
