:root {
    --color-teal-dark: #1F6F5F;
    --color-teal: #2FA084;
    --color-mint: #6FCF97;
    --color-cloud: #EEEEEE;

    /* Bootstrap link color is driven by these root variables (Reboot's `a{}`
       rule reads the -rgb variant, .btn-link reads the plain one), so this
       alone re-themes every plain link and link-styled button. */
    --bs-link-color: var(--color-teal);
    --bs-link-color-rgb: 47, 160, 132;
    --bs-link-hover-color: var(--color-teal-dark);
    --bs-link-hover-color-rgb: 31, 111, 95;

    /* Same for the dropdown menu (used by the header's profile menu). */
    --bs-dropdown-link-hover-bg: var(--color-cloud);
    --bs-dropdown-link-active-bg: var(--color-teal);
}

/* Bootstrap's own .card rule redeclares --bs-card-bg on itself (not just at
   :root), and any rule that directly targets an element always overrides an
   inherited value regardless of load order — so setting this at :root alone
   never reached plain .card elements. Overriding it here, on .card itself,
   re-themes every card (including ones with no .game-card/.auth-card/
   .mode-card) from Bootstrap's default white in one place. .card-header/
   .card-footer read the same variable via inheritance from this element, so
   an explicit .bg-white on a header would still override it — those have
   been removed at their call sites instead. */
.card {
    --bs-card-bg: #f8f8f7;
}

/* Same reasoning as .card above — .dropdown-menu redeclares --bs-dropdown-bg
   on itself, so this re-themes every dropdown panel (Game options, Invite
   friends, the header's profile menu, etc.) from Bootstrap's default white. */
.dropdown-menu {
    --bs-dropdown-bg: #f8f8f7;
}

html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    background-color: var(--color-cloud);
    background-image: url('/backgrounds/achtergrond-patroon-v2.png');
    background-position: center;
    background-size: contain;
    background-repeat: repeat;
    background-attachment: fixed;
    /* Safety net — something that doesn't quite fit a very narrow phone
       should clip, not push the whole page into a horizontal scroll (which
       reads as everything being broken/oversized, not just one element). */
    overflow-x: hidden;
}

/* Global (not page-scoped) — used across pages (Profile, Stats via the
   shared PlayerStatsGrid component, CanvasTest) and by reusable components,
   which page-scoped CSS never reaches (see .chat-avatar's history). */
.game-card {
    --bs-card-border-radius: 24px;
    --bs-card-inner-border-radius: 24px;
    --bs-card-spacer-y: 0.75rem;
    --bs-card-spacer-x: 0.85rem;
    --bs-card-cap-padding-y: 0.5rem;
    --bs-card-cap-padding-x: 0.85rem;
    border: none;
    box-shadow: 0 4px 16px rgba(31, 111, 95, 0.12);
    position: relative;
    background-color: #f8f8f7;
    overflow: hidden;
}

/* A faint hint of the page's own decorative pattern, tucked in the
   bottom-right corner behind the content — ties the cards to the rest of
   the page without competing with the content inside. */
.game-card::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background-image: url('/backgrounds/achtergrond-patroon-v2.png');
    background-position: bottom right;
    background-repeat: no-repeat;
    background-size: 320px;
    opacity: 0.35;
    pointer-events: none;
}

h1:focus {
    outline: none;
}

.btn-primary {
    color: #fff;
    background-color: var(--color-teal);
    border-color: var(--color-teal-dark);
}

    .btn-primary:hover, .btn-primary:active {
        background-color: var(--color-teal-dark);
        border-color: var(--color-teal-dark);
    }

.btn-outline-primary {
    --bs-btn-color: var(--color-teal);
    --bs-btn-border-color: var(--color-teal);
    --bs-btn-hover-bg: var(--color-teal);
    --bs-btn-hover-border-color: var(--color-teal);
    --bs-btn-active-bg: var(--color-teal-dark);
    --bs-btn-active-border-color: var(--color-teal-dark);
    --bs-btn-disabled-color: var(--color-teal);
    --bs-btn-disabled-border-color: var(--color-teal);
}

.btn-outline-secondary {
    --bs-btn-color: var(--color-teal-dark);
    --bs-btn-border-color: var(--color-teal-dark);
    --bs-btn-hover-bg: var(--color-teal-dark);
    --bs-btn-hover-border-color: var(--color-teal-dark);
    --bs-btn-active-bg: var(--color-teal-dark);
    --bs-btn-active-border-color: var(--color-teal-dark);
    --bs-btn-disabled-color: var(--color-teal-dark);
    --bs-btn-disabled-border-color: var(--color-teal-dark);
}

/* .btn-outline-danger / .btn-danger are deliberately left on Bootstrap's
   red — the given palette has no warning tone, and destructive actions
   (Kick, Clear) should still read as "different/risky" at a glance. */

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem var(--color-mint);
}

/* General form-field theming — rounder corners and a warmer border than
   Bootstrap's default gray/blue, to match the pill buttons and soft card
   corners used everywhere else. */
.form-control,
.form-select {
    border-radius: 12px;
    border-color: #ddd8cc;
}

    .form-control:focus,
    .form-select:focus {
        border-color: var(--color-teal);
    }

/* Checkboxes/radios/switches default to Bootstrap blue when checked —
   re-themed to the same teal used by buttons and focus rings. */
.form-check-input:checked {
    background-color: var(--color-teal);
    border-color: var(--color-teal);
}

.page {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.content {
    padding-top: 1.1rem;
    flex: 1 0 auto;
}

.app-footer {
    margin: 1.5rem 1rem 1rem;
    padding: 1.25rem 1.5rem;
    text-align: center;
}

.app-footer-links {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.4rem 1.5rem;
    margin-bottom: 0.75rem;
}

    .app-footer-links a {
        color: var(--color-teal-dark);
        font-weight: 500;
        font-size: 0.9rem;
        text-decoration: none;
    }

        .app-footer-links a:hover {
            text-decoration: underline;
        }

.app-footer-disclaimer {
    color: #888;
    font-size: 0.78rem;
    max-width: 40rem;
    margin: 0 auto;
}

.app-header {
    height: 3.75rem;
    gap: 1.25rem;
    margin: 1rem;
    padding-left: 1.25rem !important;
    padding-right: 0.85rem !important;
    background-color: #f8f8f7;
    border-radius: 999px;
    box-shadow: 0 4px 16px rgba(31, 111, 95, 0.12);
    position: sticky;
    top: 1rem;
    z-index: 10;
    flex-wrap: nowrap;
}

.app-header-brand {
    font-weight: 700;
    font-size: 1.25rem;
    color: var(--color-teal-dark);
}

.app-header-nav {
    display: flex;
    flex-grow: 1;
    flex-wrap: nowrap;
    gap: 0.25rem;
}

.app-header-nav .nav-link {
    color: #555;
    font-weight: 500;
    border-radius: 999px;
    padding: 0.5rem 1.1rem;
    white-space: nowrap;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.app-header-nav .nav-link:hover {
    background-color: var(--color-cloud);
    color: var(--color-teal-dark);
}

.app-header-nav .nav-link.active {
    background-color: var(--color-teal);
    color: #fff;
}

.app-header-icon-nav {
    gap: 0.4rem;
}

/* Only takes effect below lg — MainLayout applies this class for the whole
   time a player is on CanvasTest.razor (see AppChromeService), since that
   page builds its own compact header replica and .room-view-shell fills the
   full screen itself, edge to edge. Desktop always keeps its header/footer
   regardless of this class. The adjacent .content also loses its normal
   padding-top/left/right in that case — .room-view-shell's height:100dvh is
   measured from the viewport, so any leftover top offset here just pushes
   its bottom edge past the viewport by the same amount, and the page's
   normal .px-4 side padding would otherwise inset the whole mobile card
   from the screen edges instead of the full-bleed look the room view wants. */
@media (max-width: 991.98px) {
    .app-header-wrap-hide-mobile {
        display: none;
    }

    .app-header-wrap-hide-mobile + .content {
        padding: 0 !important;
    }
}

.app-header-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex: 0 0 auto;
    border: none;
    border-radius: 10px;
    background-color: var(--color-cloud);
    color: var(--color-teal-dark);
    font-size: 1rem;
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}

    .app-header-icon-btn:hover {
        background-color: var(--color-mint);
    }

    .app-header-icon-btn.active {
        background-color: var(--color-teal);
        color: #fff;
    }

@media (max-width: 576px) {
    .app-header-icon-btn {
        width: 2rem;
        height: 2rem;
        font-size: 0.9rem;
    }
}

.language-switcher {
    display: inline-flex;
    background-color: var(--color-cloud);
    border-radius: 999px;
    padding: 0.15rem;
    gap: 0.15rem;
}

.language-switcher-btn {
    border: none;
    background: none;
    border-radius: 999px;
    padding: 0.3rem 0.7rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: #888;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

    .language-switcher-btn.language-switcher-btn-active {
        background-color: var(--color-teal);
        color: #fff;
    }

/* Compact page header — a circular back button beside the title on one row,
   instead of a separate "Back to X" link line sitting above a full-size
   <h1>. Used by the Play sub-pages (Classic/Teams/Battle Royale) so the
   actual page content (host/join a game) starts higher up. */
.page-header-compact {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.page-back-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex: 0 0 auto;
    border-radius: 50%;
    /* --color-cloud is the same tone as the page background itself (see
       html, body above) — it would be invisible here, unlike on the header
       bar. #f8f8f7 matches the header/card tone instead, so it reads as a
       floating button the same way the header does against the page. */
    background-color: #f8f8f7;
    color: var(--color-teal-dark);
    font-size: 1rem;
    text-decoration: none;
    transition: background-color 0.15s ease;
    box-shadow: 0 2px 8px rgba(31, 111, 95, 0.1);
}

    .page-back-btn:hover {
        background-color: var(--color-mint);
        color: var(--color-teal-dark);
    }

.page-header-title {
    margin: 0;
    font-size: 1.6rem;
}

.header-auth-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border-radius: 999px;
    padding: 0.5rem 1.1rem;
    font-weight: 500;
    font-size: 0.9rem;
}

@media (max-width: 991.98px) {
    .header-auth-btn {
        padding: 0.4rem 0.65rem;
        font-size: 0.8rem;
        gap: 0.3rem;
    }
}

@media (max-width: 576px) {
    .header-auth-btn {
        padding: 0.35rem 0.5rem;
        font-size: 0.75rem;
    }
}

/* Matches the same range Header.razor hides the Login/Sign up text at
   (d-md-inline) — an icon-only button still needs icon-only (round, no
   text padding) sizing, not the icon+text padding left over from empty
   space where the label used to be. Placed after the rules above so it
   wins the cascade at the narrow end despite equal selector specificity. */
@media (max-width: 767.98px) {
    .header-auth-btn {
        width: 1.85rem;
        height: 1.85rem;
        padding: 0;
        justify-content: center;
        border-radius: 50%;
    }
}

.app-header-profile-btn {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    border: none;
    border-radius: 999px;
    padding: 0.35rem 1rem 0.35rem 0.35rem;
    background-color: var(--color-cloud);
    color: var(--color-teal-dark);
    font-weight: 500;
}

    .app-header-profile-btn:hover {
        background-color: var(--color-mint);
    }

.avatar-circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    background-color: var(--color-teal);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 700;
    object-fit: cover;
}

/* Global (not page-scoped) because both this and .avatar-circle above are
   rendered by the shared PlayerAvatar component — Blazor's CSS isolation
   scope attribute does NOT reach a child component's root element the way a
   page's own markup does, so a page-scoped .razor.css rule silently never
   matched here (confirmed: the element rendered with no "b-xxxx" attribute
   at all), leaving the avatar completely unstyled and full native SVG size. */
.chat-avatar {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: 50%;
    color: #fff;
    font-size: 0.65rem;
    font-weight: 700;
    box-shadow: 0 0 0 2px var(--team-accent, transparent);
    object-fit: cover;
}

.chat-avatar-disconnected {
    opacity: 0.45;
}

.chat-avatar-eliminated {
    opacity: 0.45;
    filter: grayscale(1);
}

.avatar-circle-wrap {
    position: relative;
    display: inline-flex;
}

.avatar-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(3.5rem, 1fr));
    gap: 0.75rem;
}

.profile-hero .avatar-circle {
    box-shadow: 0 4px 12px rgba(31, 111, 95, 0.25);
}

.avatar-gallery-item {
    border: 2px solid transparent;
    background: none;
    border-radius: 50%;
    padding: 0.15rem;
    cursor: pointer;
    line-height: 0;
}

    .avatar-gallery-item img {
        width: 100%;
        aspect-ratio: 1;
        border-radius: 50%;
        object-fit: cover;
    }

    .avatar-gallery-item:hover {
        border-color: var(--color-mint);
    }

    .avatar-gallery-item.avatar-gallery-item-selected {
        border-color: var(--color-teal-dark);
    }

.achievement-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: 0.85rem;
}

/* Sits inside Profile.razor's own outer card — same inset-tile relationship
   as .stats-panel, so this uses --color-cloud rather than a second nested
   card's worth of background/shadow. */
.achievement-badge {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.4rem;
    padding: 1.15rem 0.75rem;
    border-radius: 14px;
    background-color: var(--color-cloud);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.achievement-badge-unlocked:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(31, 111, 95, 0.18);
}

/* Unlocked achievements get a colored icon "medal" — same circular-icon
   language as .mode-icon/.app-header-icon-btn elsewhere in the app.
   Locked ones stay a flat neutral gray so the two states read apart at a
   glance without needing the grayscale filter this used to rely on. */
.achievement-badge-icon-wrap {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 50%;
    background-color: var(--color-mint);
}

.achievement-badge-locked .achievement-badge-icon-wrap {
    background-color: var(--color-cloud);
}

.achievement-badge-icon {
    font-size: 1.25rem;
    color: #fff;
}

.achievement-badge-locked .achievement-badge-icon {
    color: #999;
}

.achievement-badge-name {
    font-weight: 600;
    font-size: 0.85rem;
}

.achievement-badge-desc {
    font-size: 0.72rem;
    color: #888;
}

.achievement-badge-locked .achievement-badge-name,
.achievement-badge-locked .achievement-badge-desc {
    color: #999;
}

.achievement-badge-lock {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    font-size: 0.8rem;
    color: #999;
}

.header-pending-badge {
    position: absolute;
    top: -0.25rem;
    right: -0.25rem;
    min-width: 1.1rem;
    height: 1.1rem;
    padding: 0 0.25rem;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #dc3545;
    color: #fff;
    border-radius: 999px;
    font-size: 0.62rem;
    font-weight: 700;
    border: 2px solid #fff;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid red;
}

.validation-message {
    color: red;
}

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto 0 auto;
}

    .loading-progress circle {
        fill: none;
        stroke: #e0e0e0;
        stroke-width: 0.6rem;
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
    }

        .loading-progress circle:last-child {
            stroke: var(--color-teal);
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
            transition: stroke-dasharray 0.05s ease-in-out;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Loading");
    }

code {
    color: #c02d76;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

.auth-page {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 65vh;
    padding: 2rem 1rem;
}

.auth-card {
    width: 100%;
    max-width: 380px;
}

.auth-icon {
    width: 3.5rem;
    height: 3.5rem;
    margin: 0 auto 1rem;
    border-radius: 50%;
    background-color: var(--color-mint);
    color: var(--color-teal-dark);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
}

.leaderboard-table {
    margin-bottom: 0;
}

    .leaderboard-table th {
        font-size: 0.7rem;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.05rem;
        color: #999;
        border-bottom-width: 1px;
    }

    .leaderboard-table td {
        vertical-align: middle;
    }

.leaderboard-row-me {
    background-color: rgba(111, 207, 151, 0.2);
}

.leaderboard-my-rank {
    border: 1px solid var(--color-teal);
}

.leaderboard-rank {
    font-weight: 700;
    color: var(--color-teal-dark);
    width: 2.5rem;
    flex: 0 0 auto;
}

.leaderboard-rating {
    font-weight: 600;
    color: var(--color-teal-dark);
}

.auth-title {
    color: var(--color-teal-dark);
    font-weight: 700;
    margin-bottom: 0.25rem;
}

.auth-subtitle {
    color: #777;
    font-size: 0.9rem;
    margin-bottom: 1.25rem;
}

.auth-switch {
    font-size: 0.9rem;
    color: #666;
}

/* Placed at the very end of the file, deliberately — every rule here
   overrides a base rule that (for several of these selectors) is defined
   LATER in the file than where this media query used to live, and CSS
   resolves same-specificity conflicts by source order, not by which rule
   is "more specific to a media query". Having this block appear BEFORE
   .app-header-brand/.app-header-icon-btn/.language-switcher's own base
   rules meant every one of these overrides was silently losing the
   cascade — confirmed via getBoundingClientRect() measurements showing the
   unmodified base sizes rendering even inside this @media block's range.
   Real narrow phones (Galaxy S8+ at 360px, iPhone SE at 320px) don't have
   room for brand + icon-nav + language switcher + login/signup at their
   >576px sizing, and .app-header has flex-wrap:nowrap (a wrapped header
   looks worse) — so all of this genuinely has to shrink to fit one line. */
@media (max-width: 576px) {
    .app-header {
        height: 3.25rem;
        gap: 0.35rem;
        margin: 0.5rem;
        padding-left: 0.6rem !important;
        padding-right: 0.4rem !important;
        top: 0.5rem;
    }

    .app-header-brand {
        font-size: 0.85rem;
    }

    .avatar-circle {
        width: 1.85rem;
        height: 1.85rem;
        font-size: 0.7rem;
    }

    .app-header-icon-nav {
        gap: 0.2rem;
    }

    .app-header-icon-btn {
        width: 1.75rem;
        height: 1.75rem;
        font-size: 0.8rem;
    }

    .language-switcher {
        padding: 0.1rem;
        gap: 0.1rem;
    }

    .language-switcher-btn {
        padding: 0.15rem 0.4rem;
        font-size: 0.68rem;
    }
}

/* iPhone SE and similar (320px) still ran ~10-15px over budget even with
   the 576px shrinking above — this trims the last bit specifically for
   that narrowest realistic width, verified empirically (no more manual
   pixel-math guessing) via Playwright's getBoundingClientRect() across
   320/360/375/390/412px in both guest and signed-in states. */
@media (max-width: 340px) {
    .app-header {
        gap: 0.2rem;
        padding-left: 0.4rem !important;
        padding-right: 0.3rem !important;
    }

    .app-header-brand {
        font-size: 0.75rem;
    }

    .app-header-icon-nav {
        gap: 0.15rem;
    }

    .app-header-icon-btn {
        width: 1.6rem;
        height: 1.6rem;
        font-size: 0.72rem;
    }

    .language-switcher-btn {
        padding: 0.12rem 0.3rem;
        font-size: 0.62rem;
    }

    .header-auth-btn,
    .avatar-circle {
        width: 1.6rem;
        height: 1.6rem;
    }
}