/* ============================================================
   SexMap Web — design system
   Semantic colors come from themes.css (generated from the MAUI
   theme dictionaries). This file adds typography, layout, the
   glassmorphism surfaces and the component styles.
   ============================================================ */

/* ---------- Fonts ---------- */
@font-face {
    font-family: 'OpenSans';
    src: url('../assets/fonts/OpenSans-Regular.ttf') format('truetype');
    font-weight: 400;
    font-display: swap;
}

@font-face {
    font-family: 'OpenSans';
    src: url('../assets/fonts/OpenSans-Semibold.ttf') format('truetype');
    font-weight: 600;
    font-display: swap;
}

/* ---------- Reset & base ---------- */
*, *::before, *::after { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    min-height: 100%;
}

html { scrollbar-gutter: stable; }

body {
    font-family: 'OpenSans', 'Segoe UI', system-ui, sans-serif;
    font-size: 16px;
    color: var(--PrimaryTextColor);
    background: var(--PageBackgroundColor);
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

/* Ambient animated wallpaper behind everything */
body::before {
    content: "";
    position: fixed;
    inset: -20%;
    z-index: -2;
    background:
        radial-gradient(circle 42% at 18% 12%, color-mix(in srgb, var(--AccentSoftColor) 60%, transparent), transparent 70%),
        radial-gradient(circle 50% at 85% 30%, color-mix(in srgb, var(--MutedTextColor) 14%, transparent), transparent 72%),
        radial-gradient(circle 46% at 45% 95%, color-mix(in srgb, var(--SectionTitleColor) 12%, transparent), transparent 70%),
        var(--PageBackgroundColor);
    animation: sm-ambient 26s ease-in-out infinite alternate;
}

@keyframes sm-ambient {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    50%  { transform: translate3d(2%, -1.5%, 0) scale(1.04); }
    100% { transform: translate3d(-2%, 1.5%, 0) scale(1.02); }
}

@media (prefers-reduced-motion: reduce) {
    body::before { animation: none; }
    * { animation-duration: 0.001s !important; transition-duration: 0.001s !important; }
}

h1, h2, h3, h4 { font-weight: 600; margin: 0 0 .5rem; }
a { color: var(--SectionTitleColor); text-decoration: none; }
img { max-width: 100%; }
button { font-family: inherit; }

::placeholder { color: var(--InputPlaceholderColor); opacity: 1; }

::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-thumb { background: var(--BorderColor); border-radius: 8px; }
::-webkit-scrollbar-track { background: transparent; }

/* ---------- Boot splash ---------- */
.boot-splash {
    position: fixed;
    inset: 0;
    display: grid;
    place-items: center;
    background: #2f011d;
}

.boot-splash img { width: 110px; z-index: 1; animation: sm-breathe 2.2s ease-in-out infinite; }

.boot-splash-halo {
    position: absolute;
    width: 340px; height: 340px;
    border-radius: 50%;
    background: radial-gradient(circle, #9e3cb266 0%, #8f2da033 45%, transparent 70%);
    animation: sm-breathe 2.2s ease-in-out infinite;
}

.boot-splash-ring {
    position: absolute;
    width: 170px; height: 170px;
    border-radius: 50%;
    border: 2px solid #f5aec733;
    border-top-color: #f5aec7;
    animation: sm-spin 1.1s linear infinite;
}

@keyframes sm-spin { to { transform: rotate(360deg); } }
@keyframes sm-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0; left: 0; right: 0;
    z-index: 3000;
    padding: .8rem 1.2rem;
    background: #aa5a5a;
    color: #fff;
}
#blazor-error-ui .dismiss { float: right; cursor: pointer; }

/* ---------- Glass surfaces ---------- */
.glass-card {
    background: var(--CardBackgroundColor);
    border: 1px solid var(--CardBorderColor);
    border-radius: 22px;
    backdrop-filter: blur(18px) saturate(1.35);
    -webkit-backdrop-filter: blur(18px) saturate(1.35);
    box-shadow: 0 10px 34px -14px var(--FloatingChromeShadowColor),
                inset 0 1px 0 color-mix(in srgb, #ffffff 14%, transparent);
    padding: 1.1rem 1.2rem;
}

.glass-panel {
    background: var(--PageSurfaceColor);
    border: 1px solid var(--FloatingChromeBorderColor);
    border-radius: 26px;
    backdrop-filter: blur(24px) saturate(1.4);
    -webkit-backdrop-filter: blur(24px) saturate(1.4);
    box-shadow: 0 18px 44px -18px var(--FloatingChromeShadowColor);
}

.sheen {
    position: relative;
    overflow: hidden;
}
.sheen::after {
    content: "";
    position: absolute;
    top: 0; left: -80%;
    width: 60%; height: 100%;
    background: linear-gradient(105deg, transparent, color-mix(in srgb, #ffffff 12%, transparent), transparent);
    transform: skewX(-18deg);
    transition: left .8s ease;
    pointer-events: none;
}
.sheen:hover::after { left: 130%; }

/* ---------- Page scaffold ---------- */
.sm-shell {
    max-width: 560px;
    margin: 0 auto;
    padding: max(14px, env(safe-area-inset-top)) 16px calc(120px + env(safe-area-inset-bottom));
    min-height: 100dvh;
    /* "backwards" and NOT "both"/"forwards": a filling animation keeps applying its
       transform, so the computed value stays matrix(1,0,0,1,0,0) instead of none.
       Any transform other than none makes this a stacking context (trapping sheets
       and the image viewer *under* the floating tab bar) and makes it the containing
       block for position:fixed descendants (shrinking the modal backdrop to this
       column instead of the viewport). The last keyframe equals the base style, so
       dropping the forwards fill changes nothing visually. */
    animation: sm-page-in .35s ease backwards;
}

@media (min-width: 900px) {
    .sm-shell { max-width: 720px; }
    .sm-shell.sm-shell-wide { max-width: 1060px; }
}

@keyframes sm-page-in {
    from { opacity: 0; transform: translateY(14px) scale(.995); }
    to   { opacity: 1; transform: none; }
}

.page-title { font-size: 30px; color: var(--PrimaryTextColor); }
.section-title { font-size: 15px; font-weight: 600; color: var(--SectionTitleColor); letter-spacing: .06em; text-transform: uppercase; margin: 1.4rem 0 .6rem; }
.muted { color: var(--MutedTextColor); }
.text-secondary { color: var(--SecondaryTextColor); }

/* Sticky so the title and its action bubbles stay legible over whatever scrolls past
   (and over the full-bleed map); sits above .sm-scrim-top, which fades the content out
   behind it. */
.page-header {
    position: sticky;
    /* Offset by the offline banner when it is up, otherwise the sticky header slides
       under it as soon as the page scrolls. */
    top: var(--sm-banner-h, 0px);
    z-index: 95;
    display: flex;
    align-items: center;
    gap: .8rem;
    padding: .3rem 0 .5rem;
    margin-bottom: .8rem;
}
.page-header .spacer { flex: 1; }

.icon-bubble {
    display: grid;
    place-items: center;
    width: 44px; height: 44px;
    flex-shrink: 0;
    border-radius: 50%;
    border: 1px solid var(--AuthHeaderBubbleStrokeColor);
    background: var(--AuthHeaderBubbleBackgroundColor);
    color: var(--AuthHeaderBubbleTextColor);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    cursor: pointer;
    font-size: 18px;
    transition: transform .18s ease, background .18s ease;
    user-select: none;
}
.icon-bubble:hover { background: var(--AuthHeaderBubblePressedBackgroundColor); transform: translateY(-1px); }
.icon-bubble:active { transform: scale(.94); }

/* ---------- Buttons ---------- */
.btn-primary, .btn-secondary, .btn-danger, .btn-ghost {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    min-height: 46px;
    padding: .55rem 1.35rem;
    border-radius: 24px;
    border: 1px solid transparent;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: transform .16s ease, box-shadow .25s ease, filter .2s ease;
    text-decoration: none;
}

.btn-primary {
    background: var(--AuthPrimaryButtonBrush, var(--ButtonPrimaryBackgroundColor));
    color: var(--AuthPrimaryButtonTextColor, var(--ButtonPrimaryTextColor));
    border-color: var(--AuthPrimaryButtonStrokeColor, transparent);
    box-shadow: 0 10px 26px -10px var(--AuthPrimaryButtonOuterShadowColor, transparent),
                inset 0 1px 0 var(--AuthPrimaryButtonInnerShadowColor, transparent);
}
.btn-primary:hover:not(:disabled) { transform: translateY(-2px); filter: brightness(1.1); }
.btn-primary:active:not(:disabled) { transform: scale(.97); }
.btn-primary:disabled {
    background: var(--AuthPrimaryButtonDisabledBrush, var(--ButtonPrimaryBackgroundColor));
    color: var(--AuthPrimaryButtonDisabledTextColor);
    border-color: var(--AuthPrimaryButtonDisabledStrokeColor);
    cursor: not-allowed;
    box-shadow: none;
}

.btn-secondary {
    background: var(--ButtonSecondaryBackgroundColor);
    color: var(--ButtonSecondaryTextColor);
    border-color: var(--ButtonSecondaryBorderColor);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}
.btn-secondary:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.12); }

.btn-danger { background: var(--ButtonDangerBackgroundColor); color: var(--ButtonDangerTextColor); }
.btn-danger:hover:not(:disabled) { filter: brightness(1.1); }

.btn-ghost { background: transparent; color: var(--ButtonGhostTextColor); min-height: 44px; }
.btn-ghost:hover:not(:disabled) { background: var(--AccentSoftColor); }

button:disabled { opacity: .65; cursor: not-allowed; }

.btn-social {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .7rem;
    width: 100%;
    min-height: 50px;
    border-radius: 26px;
    border: 1px solid var(--AuthSocialButtonStrokeColor);
    background: var(--AuthSocialButtonRestingBackgroundColor);
    color: var(--AuthCardTextColor);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    box-shadow: 0 8px 22px -12px var(--AuthSocialButtonOuterShadowColor),
                inset 0 1px 0 var(--AuthSocialButtonInnerShadowColor);
    transition: transform .16s ease, background .2s ease;
}
.btn-social:hover { transform: translateY(-2px); background: var(--AuthSocialButtonBackgroundColor); }
.btn-social img { width: 22px; height: 22px; }

/* ---------- Inputs ---------- */
.sm-field { margin-bottom: 1rem; }
.sm-field > label {
    display: block;
    margin: 0 .2rem .35rem;
    font-size: 13px;
    font-weight: 600;
    color: var(--SecondaryTextColor);
}

.sm-input-wrap {
    display: flex;
    align-items: center;
    gap: .55rem;
    border-radius: 18px;
    border: 1px solid var(--InputBorderColor);
    background: var(--InputBackgroundColor);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    padding: 0 1rem;
    min-height: 50px;
    transition: border-color .2s ease, box-shadow .2s ease;
}
.sm-input-wrap:focus-within {
    border-color: var(--AuthInputFocusedStrokeColor);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--AuthInputFocusedStrokeColor) 22%, transparent);
}
.sm-input-wrap.invalid { border-color: var(--AuthInputInvalidStrokeColor); background: var(--AuthInputInvalidBackgroundColor); }
.sm-input-wrap.valid { border-color: var(--AuthPseudoAvailableStrokeColor); }

.sm-input-wrap input, .sm-input-wrap select, .sm-input-wrap textarea {
    flex: 1;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    color: var(--InputTextColor);
    /* 16px keeps iOS Safari from auto-zooming the page on focus. */
    font-size: 16px;
    font-family: inherit;
    padding: .7rem 0;
}
.sm-input-wrap textarea { resize: vertical; min-height: 90px; }
.sm-input-wrap select { max-width: 100%; }
.sm-input-wrap select option { background: var(--ShellBackgroundColor); color: var(--PrimaryTextColor); }
.sm-input-wrap .trailing { cursor: pointer; font-size: 18px; user-select: none; color: var(--MutedTextColor); background:none; border:none; padding: .4rem; margin: 0 -.2rem; }

.field-message { margin: .3rem .3rem 0; font-size: 13px; color: var(--AuthValidationErrorTextColor); }
.field-message.ok { color: var(--AuthPasswordRuleValidTextColor); }
.field-message.muted { color: var(--AuthMutedValidationTextColor); }

/* ---------- Chips ---------- */
.chip-row { display: flex; gap: .5rem; overflow-x: auto; padding: .2rem .1rem .5rem; scrollbar-width: none; }
.chip-row::-webkit-scrollbar { display: none; }

.chip {
    flex: 0 0 auto;
    padding: .42rem 1rem;
    border-radius: 20px;
    border: 1px solid transparent;
    background: var(--StateChipUnselectedBackgroundColor);
    color: var(--StateChipUnselectedTextColor);
    font-size: 13.5px;
    font-weight: 600;
    cursor: pointer;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    transition: transform .15s ease, background .2s ease, color .2s ease;
    white-space: nowrap;
}
.chip:hover { transform: translateY(-1px); }
.chip.selected { background: var(--StateChipSelectedBackgroundColor); color: var(--StateChipSelectedTextColor); }

/* ---------- Badges ---------- */
.badge {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .22rem .7rem;
    border-radius: 14px;
    font-size: 12px;
    font-weight: 600;
    color: #fff;
}
.badge.state-accepted, .badge.state-automaticallyaccepted { background: #2E7D32; }
.badge.state-pending { background: #F59E0B; color: #23180a; }
.badge.state-refused, .badge.state-declined { background: #B91C1C; }
.badge.state-cancelled { background: #6B7280; }

.unread-dot {
    width: 10px; height: 10px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--AuthInputFocusedStrokeColor);
    box-shadow: 0 0 8px var(--AuthInputFocusedStrokeColor);
}

/* ---------- Floating bottom tab bar ---------- */
.bottom-tabbar {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: calc(14px + env(safe-area-inset-bottom));
    z-index: 100;
    width: min(520px, calc(100vw - 28px));
    height: 82px;
    display: grid;
    grid-template-columns: 1fr 1fr 74px 1fr 1fr;
    align-items: center;
    border-radius: 28px;
    background: var(--FloatingChromeBackgroundColor);
    border: 1px solid var(--FloatingChromeBorderColor);
    backdrop-filter: blur(26px) saturate(1.5);
    -webkit-backdrop-filter: blur(26px) saturate(1.5);
    box-shadow: 0 20px 46px -16px var(--FloatingChromeShadowColor);
    animation: sm-tabbar-in .5s cubic-bezier(.2, .9, .3, 1.2) both;
}

@keyframes sm-tabbar-in {
    from { opacity: 0; transform: translateX(-50%) translateY(30px); }
    to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

.bottom-tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    height: 64px;
    margin: 0 3px;
    padding: 0 2px;
    overflow: hidden;
    border-radius: 20px;
    border: none;
    background: transparent;
    color: var(--FloatingChromeInactiveTextColor);
    font-size: 10.5px;
    font-weight: 600;
    cursor: pointer;
    transition: background .22s ease, color .22s ease, transform .16s ease;
}
.bottom-tab > span:not(.tab-icon) { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Same SVG artwork as the Android tab bar (Resources/Images/{light,dark}). The two
   native variants differ only in stroke colour, so a single copy is masked and tinted
   with the theme's chrome icon colour — which covers all four web themes instead of
   just light/dark. Size matches the native Image (21x21). */
.bottom-tab .tab-icon {
    width: 21px; height: 21px;
    background-color: var(--FloatingChromeIconColor);
    -webkit-mask: var(--tab-icon-src) center / contain no-repeat;
    mask: var(--tab-icon-src) center / contain no-repeat;
    transition: transform .2s ease;
}
/* Same Android artwork on the ordinary buttons. One copy per icon is masked and tinted
   with currentColor, so each one picks up its button's text colour and therefore works
   across all four web themes -- the native light/dark pairs differ only in colour, their
   geometry is identical. */
.sm-icon {
    display: inline-block;
    width: 1.15em; height: 1.15em;
    flex-shrink: 0;
    vertical-align: -.16em;
    background-color: currentColor;
    -webkit-mask: var(--sm-icon-src) center / contain no-repeat;
    mask: var(--sm-icon-src) center / contain no-repeat;
}
.sm-icon-back { --sm-icon-src: url("../assets/icons/icon_back.svg"); }
.sm-icon-close { --sm-icon-src: url("../assets/icons/icon_close.svg"); }
.sm-icon-more { --sm-icon-src: url("../assets/icons/icon_more.svg"); }
.sm-icon-edit { --sm-icon-src: url("../assets/icons/icon_edit.svg"); }
.sm-icon-eye { --sm-icon-src: url("../assets/icons/icon_eye.svg"); }
.sm-icon-eye-off { --sm-icon-src: url("../assets/icons/icon_eye_off.svg"); }
.sm-icon-list { --sm-icon-src: url("../assets/icons/icon_list.svg"); }
.sm-icon-map { --sm-icon-src: url("../assets/icons/icon_map.svg"); }
.sm-icon-calendar { --sm-icon-src: url("../assets/icons/icon_calendar.svg"); }
.sm-icon-refresh { --sm-icon-src: url("../assets/icons/icon_refresh.svg"); }
.sm-icon-search { --sm-icon-src: url("../assets/icons/icon_search.svg"); }
.sm-icon-settings { --sm-icon-src: url("../assets/icons/icon_menu_settings.svg"); }
.sm-icon-logout { --sm-icon-src: url("../assets/icons/icon_menu_logout.svg"); }
.sm-icon-notifications { --sm-icon-src: url("../assets/icons/icon_menu_confirmations.svg"); }
.sm-icon-envelope { --sm-icon-src: url("../assets/icons/sexmap_envelope.svg"); }
.sm-icon-partners { --sm-icon-src: url("../assets/icons/icon_menu_partners.svg"); }
.sm-icon-relations { --sm-icon-src: url("../assets/icons/icon_menu_relations.svg"); }
.sm-icon-home { --sm-icon-src: url("../assets/icons/icon_menu_home.svg"); }
.sm-icon-user { --sm-icon-src: url("../assets/icons/sexmap_account_user.svg"); }
.sm-icon-rule-ok { --sm-icon-src: url("../assets/icons/password_rule_valid.svg"); }
.sm-icon-rule-ko { --sm-icon-src: url("../assets/icons/password_rule_invalid.svg"); }
/* Bubble buttons are square with no text beside the glyph: give it a fixed size. */
.icon-bubble .sm-icon { width: 20px; height: 20px; vertical-align: 0; }
/* AuthHeaderBubbleTextColor is authored for the auth screens, where the bubble sits on a
   dark photo backdrop; it is #fdf2ff in every theme. On an ordinary page header in the
   light theme that is white on a #f6edf7 bubble -- invisible. Emoji hid this because they
   carry their own colours; a currentColor-tinted icon does not. */
.sm-shell .icon-bubble { color: var(--PrimaryTextColor); }

.tab-icon-partners { --tab-icon-src: url("../assets/icons/icon_menu_partners.svg"); }
.tab-icon-relations { --tab-icon-src: url("../assets/icons/icon_menu_relations.svg"); }
.tab-icon-home { --tab-icon-src: url("../assets/icons/icon_menu_home.svg"); }
.tab-icon-profile { --tab-icon-src: url("../assets/icons/sexmap_account_user.svg"); }
.bottom-tab.active { background: var(--FloatingChromeActiveBackgroundColor); color: var(--FloatingChromeActiveTextColor); }
.bottom-tab.active .tab-icon { transform: scale(1.12); }
.bottom-tab:active { transform: scale(.93); }

.bottom-tab .tab-avatar {
    width: 26px; height: 26px;
    border-radius: 50%;
    object-fit: cover;
    border: 1.5px solid var(--FloatingChromeIconColor);
}

.center-action {
    justify-self: center;
    width: 62px; height: 62px;
    /* Buttons carry a UA padding of 1px 6px. That left a 46px grid track for a 50px icon,
       and an overflowing grid item is not distributed symmetrically -- the glyph sat 2px
       right of centre. */
    padding: 0;
    /* Vertically centred in the bar, like the native CenterActionHost
       (VerticalOptions="Center"). It used to carry margin-top:-26px, which lifted it
       13px above the bar's centre line and let it poke out over the top edge. */
    border-radius: 50%;
    border: 1px solid var(--AuthPrimaryButtonStrokeColor, var(--FloatingChromeBorderColor));
    background: var(--AuthPrimaryButtonBrush, var(--ButtonPrimaryBackgroundColor));
    color: var(--AuthPrimaryButtonTextColor, #fff);
    font-size: 27px;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
    box-shadow: 0 12px 30px -8px var(--AuthPrimaryButtonOuterShadowColor, var(--FloatingChromeShadowColor)),
                inset 0 1px 0 var(--AuthPrimaryButtonInnerShadowColor, transparent);
    transition: transform .18s cubic-bezier(.2, .9, .3, 1.4), filter .2s ease;
}
.center-action:hover { transform: translateY(-3px) rotate(90deg); filter: brightness(1.15); }
.center-action:active { transform: scale(.9); }
/* icon_add.svg is a filled disc with the plus cut out of it, so at the native 50/60 ratio
   it nearly fills the button and reads as a flat donut. Sizing it down leaves a clear ring
   of the button's own gradient around the glyph, which reads as a floating action button. */
.center-action .center-icon {
    width: 38px; height: 38px;
    background-color: currentColor;
    -webkit-mask: url("../assets/icons/icon_add.svg") center / contain no-repeat;
    mask: url("../assets/icons/icon_add.svg") center / contain no-repeat;
}

/* ---------- Relations top tab bar ---------- */
.top-tabbar {
    position: sticky;
    top: calc(10px + var(--sm-banner-h, 0px));
    /* Above .sm-scrim-top (90), like the page header: the scrim must fade the scrolling
       content out behind this bar, never veil the bar itself. */
    z-index: 95;
    display: grid;
    /* Three equal tabs plus a narrow refresh button; "auto" keeps the refresh from
       stealing a quarter of the bar. */
    grid-template-columns: repeat(3, 1fr) auto;
    gap: 4px;
    padding: 5px;
    border-radius: 24px;
    background: var(--FloatingChromeBackgroundColor);
    border: 1px solid var(--FloatingChromeBorderColor);
    backdrop-filter: blur(24px) saturate(1.5);
    -webkit-backdrop-filter: blur(24px) saturate(1.5);
    box-shadow: 0 14px 34px -16px var(--FloatingChromeShadowColor);
    margin-bottom: 1rem;
}

.top-tabbar { max-width: 560px; margin-left: auto; margin-right: auto; }
.top-tab.refresh { min-width: 44px; padding: 0; }
.top-tab.refresh:disabled { opacity: .5; cursor: default; }
.top-tab {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    min-height: 44px;
    border: none;
    border-radius: 19px;
    background: transparent;
    color: var(--FloatingChromeInactiveTextColor);
    font-size: 14px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background .22s ease, color .22s ease;
}
.top-tab.active { background: var(--FloatingChromeActiveBackgroundColor); color: var(--FloatingChromeActiveTextColor); }

/* ---------- Stat tiles (Home) ---------- */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 14px;
}
@media (min-width: 900px) { .stats-grid { grid-template-columns: repeat(3, 1fr); } }

.stat-tile {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: 1.05rem 1.1rem;
    cursor: pointer;
    transition: transform .2s ease, box-shadow .25s ease;
    animation: sm-page-in .4s ease both;
}
.stat-tile:hover { transform: translateY(-4px); box-shadow: 0 18px 40px -14px var(--FloatingChromeShadowColor); }
.stat-tile .stat-icon { font-size: 24px; }
.stat-tile .stat-value {
    font-size: 34px;
    font-weight: 600;
    background: linear-gradient(120deg, var(--PrimaryTextColor), var(--SectionTitleColor));
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}
.stat-tile .stat-title { font-size: 14px; font-weight: 600; color: var(--SecondaryTextColor); }
.stat-tile .stat-subtitle { font-size: 12.5px; color: var(--MutedTextColor); }
.stat-flags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: .35rem; max-height: 53px; overflow: hidden; }
.stat-flags img { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; border: 1px solid var(--BorderColor); }

/* ---------- Partner tiles ---------- */
.partners-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 14px;
}
@media (min-width: 900px) { .partners-grid { grid-template-columns: repeat(3, 1fr); } }

.partner-tile {
    position: relative;
    aspect-ratio: 3 / 3.6;
    border-radius: 22px;
    overflow: hidden;
    border: 2px solid var(--PartnerTileUnselectedBorderColor);
    cursor: pointer;
    background: var(--CardBackgroundColor);
    transition: transform .2s ease, border-color .2s ease, box-shadow .25s ease;
    animation: sm-page-in .4s ease both;
}
.partner-tile:hover { transform: translateY(-4px) scale(1.015); box-shadow: 0 18px 40px -14px var(--FloatingChromeShadowColor); }
.partner-tile.selected { border-color: var(--PartnerTileSelectedBorderColor); }
.partner-tile.focused { border-color: var(--AuthInputFocusedStrokeColor); box-shadow: 0 0 0 4px color-mix(in srgb, var(--AuthInputFocusedStrokeColor) 30%, transparent); }

.partner-tile img.photo { width: 100%; height: 100%; object-fit: cover; }
.partner-tile .overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, var(--PartnerTileOverlayStartColor), var(--PartnerTileOverlayEndColor));
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: .8rem .9rem;
    color: var(--PartnerTileTextColor);
}
.partner-tile .overlay .name { font-weight: 600; font-size: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.partner-tile .overlay > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.partner-tile .tile-badge { position: absolute; top: 10px; left: 10px; }
.partner-tile .switch-btn {
    position: absolute;
    top: 8px; right: 8px;
    width: 36px; height: 36px;
    border-radius: 50%;
    border: none;
    background: var(--IconBubbleBackgroundColor);
    color: #fff;
    cursor: pointer;
    font-size: 15px;
}
.partner-tile .selection-badge {
    position: absolute;
    top: 10px; right: 10px;
    width: 26px; height: 26px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--PartnerTileSelectionBadgeBackgroundColor);
    color: var(--PartnerTileSelectionBadgeTextColor);
    font-size: 14px;
    font-weight: 700;
}
.partner-tile .actions {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    margin-top: .55rem;
}
.partner-tile .actions button { width: 100%; min-height: 34px; font-size: 13px; padding: .3rem .5rem; border-radius: 14px; white-space: nowrap; }
@media (min-width: 480px) {
    /* Roomy tiles (desktop 3-col) can put Accept/Decline side by side. */
    .partner-tile .actions { flex-direction: row; }
    .partner-tile .actions button { flex: 1; width: auto; }
}

/* ---------- Relation tiles ---------- */
.relation-tile {
    position: relative;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .95rem 1.05rem;
    overflow: hidden;
    cursor: pointer;
    transition: transform .2s ease, box-shadow .25s ease;
    animation: sm-page-in .4s ease both;
}
.relation-tile:hover { transform: translateY(-3px); box-shadow: 0 16px 38px -14px var(--FloatingChromeShadowColor); }
.relation-tile .bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    opacity: .28;
    z-index: 0;
}
.relation-tile > * { position: relative; z-index: 1; }
.relation-tile .info { flex: 1; min-width: 0; }
.relation-tile .title { font-size: 16.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.relation-tile .meta { font-size: 13px; color: var(--MutedTextColor); }

/* ---------- Catalog tiles (relation editor) ---------- */
.catalog-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}
@media (min-width: 900px) { .catalog-grid { grid-template-columns: repeat(3, 1fr); } }

.catalog-tile {
    position: relative;
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .7rem .8rem;
    border-radius: 18px;
    background: var(--CardBackgroundColor);
    border: 2px solid var(--CardBorderColor);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    cursor: pointer;
    transition: border-color .2s ease, transform .15s ease;
    user-select: none;
}
.catalog-tile:hover { transform: translateY(-2px); }
.catalog-tile.selected { border-color: var(--PartnerTileSelectedBorderColor); background: color-mix(in srgb, var(--AccentSoftColor) 80%, transparent); }
.catalog-tile img { width: 36px; height: 36px; }
.catalog-tile .label { flex: 1; font-size: 13.5px; font-weight: 600; }
.catalog-tile .mode { font-size: 11.5px; color: var(--SectionTitleColor); }
.catalog-tile .info-btn {
    border: none;
    background: var(--IconBubbleBackgroundColor);
    color: #fff;
    width: 26px; height: 26px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 13px;
}

/* ---------- Rating stars ---------- */
.rating-stars { display: inline-flex; gap: .35rem; font-size: 30px; cursor: pointer; user-select: none; }
.rating-stars .star { color: var(--MutedTextColor); opacity: .45; transition: transform .15s ease, opacity .15s ease; }
.rating-stars .star.filled { color: #f6c26a; opacity: 1; text-shadow: 0 0 14px #f6c26a88; }
.rating-stars .star:hover { transform: scale(1.2); }

/* ---------- Skeletons ---------- */
.skeleton {
    position: relative;
    overflow: hidden;
    background: var(--SkeletonBaseColor);
    border-radius: 14px;
}
.skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, var(--SkeletonHighlightColor), transparent);
    animation: sm-shimmer 1.4s infinite;
}
@keyframes sm-shimmer { to { transform: translateX(100%); } }

.skeleton-card {
    background: var(--SkeletonCardBackgroundColor);
    border: 1px solid var(--SkeletonBorderColor);
    border-radius: 22px;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: .6rem;
}

/* ---------- Empty state ---------- */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: .8rem;
    padding: 2.4rem 1.4rem;
}
.empty-state img { width: 88px; opacity: .9; }
.empty-state .es-title { font-size: 19px; font-weight: 600; }
.empty-state .es-message { color: var(--MutedTextColor); font-size: 14.5px; max-width: 360px; }

/* ---------- Sheets & modals ---------- */
.sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 900;
    background: #00000088;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    animation: sm-fade .22s ease both;
    display: flex;
    align-items: flex-end;
    justify-content: center;
}
@media (min-width: 700px) { .sheet-backdrop { align-items: center; } }
@keyframes sm-fade { from { opacity: 0; } to { opacity: 1; } }

.bottom-sheet {
    width: min(560px, 100%);
    max-height: 86dvh;
    overflow-y: auto;
    border-radius: 28px 28px 0 0;
    background: var(--BottomSheetBackgroundColor);
    border: 1px solid var(--BottomSheetStrokeColor);
    backdrop-filter: blur(28px) saturate(1.5);
    -webkit-backdrop-filter: blur(28px) saturate(1.5);
    padding: .7rem 1.2rem calc(1.4rem + env(safe-area-inset-bottom));
    color: var(--BottomSheetItemTextColor);
    animation: sm-sheet-in .3s cubic-bezier(.2, .9, .3, 1.1) both;
}
@media (min-width: 700px) { .bottom-sheet { border-radius: 28px; } }
@keyframes sm-sheet-in { from { transform: translateY(60px); opacity: 0; } to { transform: none; opacity: 1; } }

.sheet-handle {
    width: 46px; height: 5px;
    border-radius: 4px;
    background: var(--BottomSheetHandleColor);
    margin: .35rem auto 1rem;
}

.sheet-item {
    display: flex;
    align-items: center;
    gap: .8rem;
    width: 100%;
    padding: .85rem 1rem;
    margin-bottom: .5rem;
    border-radius: 18px;
    border: 1px solid transparent;
    background: var(--BottomSheetItemBackgroundColor);
    color: var(--BottomSheetItemTextColor);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    text-align: left;
    transition: background .18s ease, border-color .18s ease;
}
.sheet-item:hover { border-color: var(--BottomSheetStrokeColor); }
.sheet-item.selected { background: var(--BottomSheetItemSelectedBackgroundColor); border-color: var(--BottomSheetItemSelectedStrokeColor); }
.sheet-item .sub { display: block; font-size: 12.5px; font-weight: 400; color: var(--BottomSheetItemSecondaryTextColor); }
.sheet-item .glyph { font-size: 20px; width: 28px; text-align: center; }
.sheet-item .check { margin-left: auto; color: var(--BottomSheetItemSelectedStrokeColor); }

/* ---------- Toasts ---------- */
.toast-host {
    position: fixed;
    bottom: calc(120px + env(safe-area-inset-bottom));
    left: 50%;
    transform: translateX(-50%);
    z-index: 1200;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    align-items: center;
    pointer-events: none;
    width: min(480px, calc(100vw - 32px));
}

.toast {
    padding: .7rem 1.25rem;
    border-radius: 20px;
    background: var(--PageSurfaceColor);
    border: 1px solid var(--FloatingChromeBorderColor);
    color: var(--PrimaryTextColor);
    font-size: 14px;
    backdrop-filter: blur(20px) saturate(1.4);
    -webkit-backdrop-filter: blur(20px) saturate(1.4);
    box-shadow: 0 14px 34px -12px var(--FloatingChromeShadowColor);
    animation: sm-sheet-in .28s ease both;
    text-align: center;
}
.toast.error { border-color: var(--DangerColor); }
.toast.success { border-color: var(--SuccessColor); }

/* ---------- Auth pages ---------- */
.auth-viewport {
    position: relative;
    min-height: 100dvh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: max(24px, env(safe-area-inset-top)) 18px calc(40px + env(safe-area-inset-bottom));
}

.auth-bg, .auth-bg-radial, .auth-bg-fade {
    position: fixed;
    inset: 0;
    pointer-events: none;
}
.auth-bg { background: var(--AuthBackgroundBrush); }
.auth-bg-radial {
    background: var(--AuthBackgroundRadialBrush);
    opacity: .28;
    animation: sm-aurora 14s ease-in-out infinite alternate;
}
.auth-bg-fade { background: var(--AuthBottomFadeBrush); opacity: .35; }

@keyframes sm-aurora {
    0%   { transform: translate3d(-4%, -2%, 0) scale(1); }
    100% { transform: translate3d(4%, 3%, 0) scale(1.12); }
}

.auth-card {
    width: min(430px, 100%);
    border-radius: 30px;
    background: var(--AuthCardBackgroundColor);
    border: 1px solid var(--AuthCardStrokeColor);
    backdrop-filter: blur(26px) saturate(1.5);
    -webkit-backdrop-filter: blur(26px) saturate(1.5);
    box-shadow: 0 24px 60px -22px #00000088, inset 0 1px 0 color-mix(in srgb, #ffffff 16%, transparent);
    padding: 1.6rem 1.5rem;
    color: var(--AuthCardTextColor);
    position: relative;
    z-index: 1;
    animation: sm-page-in .45s ease both;
}

.auth-logo { display: flex; flex-direction: column; align-items: center; gap: .5rem; margin: 1.6rem 0 1.4rem; z-index: 1; }
/* Full wordmark (sexmap_logo.svg: mark + "SexMap" text), same as the mobile login. */
.auth-logo img { width: min(250px, 62vw); filter: drop-shadow(0 8px 30px #9e3cb288); animation: sm-breathe 4.5s ease-in-out infinite; }
.auth-logo .tagline { color: var(--AuthTaglineTextColor); font-size: 14.5px; text-align: center; }

.auth-header-row {
    width: min(430px, 100%);
    display: flex;
    justify-content: flex-end;
    gap: .6rem;
    z-index: 1;
}

.auth-divider {
    display: flex;
    align-items: center;
    gap: .8rem;
    color: var(--AuthDividerColor);
    font-size: 13px;
    margin: 1.1rem 0;
}
.auth-divider::before, .auth-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--AuthDividerColor);
    opacity: .5;
}

.auth-card .sm-input-wrap {
    background: var(--AuthInputBackgroundColor);
    border-color: var(--AuthInputStrokeColor);
}
.auth-card .sm-input-wrap input { color: var(--AuthInputTextColor); }
.auth-card .sm-input-wrap ::placeholder { color: var(--AuthInputPlaceholderColor); }

.auth-link { color: var(--AuthActionTextColor); font-size: 14px; background: none; border: none; cursor: pointer; text-decoration: underline; display: inline-flex; align-items: center; min-height: 44px; padding: 0 .2rem; }

.password-rules { display: flex; flex-direction: column; gap: .25rem; margin: .4rem .2rem .8rem; }
.password-rule { font-size: 13px; display: flex; gap: .45rem; align-items: center; transition: color .2s ease; }
.password-rule.ok { color: var(--AuthPasswordRuleValidTextColor); }
.password-rule.ko { color: var(--AuthPasswordRuleInvalidTextColor); }

.notfound-panel { display: flex; flex-direction: column; align-items: center; gap: 1rem; margin: auto; z-index: 1; }

/* ---------- Profile visibility badges ---------- */
.vis-badge {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .28rem .7rem;
    border-radius: 16px;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    border: 1.5px solid;
    user-select: none;
    flex-shrink: 0;
    max-width: 100%;
    white-space: nowrap;
    transition: transform .15s ease;
}
.vis-badge:active { transform: scale(.93); }
.vis-badge.private { background: var(--ProfileVisibilityPrivateBackgroundColor); border-color: var(--ProfileVisibilityPrivateStrokeColor); color: var(--ProfileVisibilityPrivateTextColor); }
.vis-badge.partners { background: var(--ProfileVisibilityPartnersBackgroundColor); border-color: var(--ProfileVisibilityPartnersStrokeColor); color: var(--ProfileVisibilityPartnersTextColor); }
.vis-badge.discoverable { background: var(--ProfileVisibilityDiscoverableBackgroundColor); border-color: var(--ProfileVisibilityDiscoverableStrokeColor); color: var(--ProfileVisibilityDiscoverableTextColor); }

/* Explainer cards in the visibility sheet, reusing the same per-level palette as the
   badges so a card and its badge read as the same thing. */
.vis-info {
    display: flex; gap: .8rem; align-items: flex-start;
    padding: .85rem;
    border-radius: 18px;
    border: 1.5px solid;
    margin-top: .7rem;
    font-size: 13.5px;
    line-height: 1.35;
}
.vis-info .glyph { font-size: 20px; line-height: 1.2; }
.vis-info strong { display: block; margin-bottom: .15rem; }
.vis-info.private { background: var(--ProfileVisibilityPrivateBackgroundColor); border-color: var(--ProfileVisibilityPrivateStrokeColor); color: var(--ProfileVisibilityPrivateTextColor); }
.vis-info.partners { background: var(--ProfileVisibilityPartnersBackgroundColor); border-color: var(--ProfileVisibilityPartnersStrokeColor); color: var(--ProfileVisibilityPartnersTextColor); }
.vis-info.discoverable { background: var(--ProfileVisibilityDiscoverableBackgroundColor); border-color: var(--ProfileVisibilityDiscoverableStrokeColor); color: var(--ProfileVisibilityDiscoverableTextColor); }

/* ---------- Maps ---------- */
.map-host {
    width: 100%;
    height: calc(100dvh - 320px);
    min-height: 340px;
    border-radius: 24px;
    overflow: hidden;
    border: 1px solid var(--CardBorderColor);
    box-shadow: 0 16px 40px -16px var(--FloatingChromeShadowColor);
    z-index: 0;
}

/* ---------- Map frame and centre crosshair ---------- */
.map-frame { position: relative; }
/* Only the framed (non-wallpaper) map needs its own box: the full-bleed one is fixed. */
.map-frame:not(:has(.map-wallpaper)) > .map-host { height: 100%; }
.map-center-pin {
    position: absolute;
    left: 50%; top: 50%;
    /* The tip of the tail marks the point, so the pin sits entirely above the centre. */
    transform: translate(-50%, -100%);
    pointer-events: none;
    z-index: 5;
    filter: drop-shadow(0 5px 8px rgba(0, 0, 0, .45));
}
.map-center-pin__head {
    width: 26px; height: 26px;
    border-radius: 50%;
    border: 3px solid #ffffff;
    background: var(--ButtonPrimaryBackgroundColor);
}
.map-center-pin__tail {
    width: 0; height: 0;
    margin: -3px auto 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 9px solid #ffffff;
}
/* Ground dot at the exact centre, so the picked point stays readable while panning. */
.map-center-pin__dot {
    position: absolute;
    left: 50%; bottom: -3px;
    transform: translateX(-50%);
    width: 6px; height: 6px;
    border-radius: 50%;
    background: #ffffff;
    opacity: .9;
}

/* ---------- Custom map markers ---------- */
/* HTML markers (not MapLibre's default image pins): a monogram badge on a teardrop,
   themed per marker kind so pins belong to the app's design language. */
.sm-pin {
    display: flex;
    flex-direction: column;
    align-items: center;
    cursor: pointer;
    filter: drop-shadow(0 6px 10px rgba(0, 0, 0, .45));
    transition: transform .16s cubic-bezier(.2, .9, .3, 1.4);
}
.sm-pin:hover { transform: translateY(-3px) scale(1.06); }
.sm-pin__head {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    border-radius: 50%;
    border: 2px solid var(--PinRingColor, #ffffff);
    background: var(--PinFillColor, var(--ButtonPrimaryBackgroundColor));
    color: var(--PinTextColor, #ffffff);
    font-size: 15px;
    font-weight: 700;
    line-height: 1;
    user-select: none;
}
/* Untitled pins have no monogram to show, so they get a centred dot. (The map-pins SVGs
   are whole pin illustrations, not inner glyphs -- masking the head with one just yields a
   teardrop-shaped blob.) */
.sm-pin__head--empty::after {
    content: "";
    width: 10px; height: 10px;
    border-radius: 50%;
    background: currentColor;
}
.sm-pin__tail {
    width: 0; height: 0;
    margin-top: -3px;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-top: 9px solid var(--PinRingColor, #ffffff);
}
/* Marker kinds mirror the native MapMarkerIconKey values. */
.sm-pin--favorite { --PinFillColor: #f0a52a; --PinTextColor: #2f011d; }
.sm-pin--heart { --PinFillColor: #e0416f; }
.sm-pin--memory { --PinFillColor: #7b5cd6; }

/* ---------- Full-bleed map wallpaper ---------- */
/* Mirrors the native BaseView.WallpaperContent: the map fills the window behind the
   page, and the controls float over it. z-index 0 keeps it above the ambient body
   backdrop (-2) while every in-flow control paints on top. */
.map-host.map-wallpaper {
    position: fixed;
    inset: 0;
    width: auto;
    height: auto;
    min-height: 0;
    border: none;
    border-radius: 0;
    box-shadow: none;
    z-index: 0;
}
/* The shell is a full-viewport transparent box, so it would swallow every pan/zoom
   aimed at the map. Nothing in it is clickable in map mode except the actual controls. */
body.sm-map-mode .sm-shell { pointer-events: none; background: transparent; }
/* pointer-events is inherited, so the map -- which lives inside the shell -- has to opt
   back in explicitly or it takes no pan/zoom at all. */
body.sm-map-mode .sm-shell .map-host,
body.sm-map-mode .sm-shell .top-tabbar,
body.sm-map-mode .sm-shell .map-filter-panel,
body.sm-map-mode .sm-shell .map-controls,
body.sm-map-mode .sm-shell .map-card-carousel,
body.sm-map-mode .sm-shell .empty-state { pointer-events: auto; }
/* Hide the decorative ambient wash so the map really is the wallpaper. */
body.sm-map-mode::before { opacity: 0; }

/* With the map out of flow, the selected-relation carousel would bunch up under the
   filters; dock it just above the tab bar the way the native map does. */
body.sm-map-mode .map-card-carousel {
    position: fixed;
    left: 0; right: 0;
    bottom: calc(112px + env(safe-area-inset-bottom));
    z-index: 92;
    padding-left: max(16px, calc((100vw - 560px) / 2));
    padding-right: max(16px, calc((100vw - 560px) / 2));
}

/* Floating glass control cluster over the map. */
.map-controls {
    position: relative;
    z-index: 95;
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    padding: .35rem .8rem .35rem .35rem;
    border-radius: 999px;
    background: var(--FloatingChromeBackgroundColor);
    border: 1px solid var(--FloatingChromeBorderColor);
    backdrop-filter: blur(22px) saturate(1.4);
    -webkit-backdrop-filter: blur(22px) saturate(1.4);
    box-shadow: 0 14px 34px -14px var(--FloatingChromeShadowColor);
}

/* Over map tiles, the standard translucent card washes out. The theme ships tokens
   sized for exactly this (the native map filter panel uses them), so reuse those
   instead of inventing per-theme values. */
body.sm-map-mode .map-card,
body.sm-map-mode .map-controls {
    background: var(--MapFilterPanelBackgroundColor);
    border: 1px solid var(--MapFilterPanelBorderColor);
    color: var(--MapFilterLabelTextColor);
}
body.sm-map-mode .map-card .muted,
body.sm-map-mode .map-controls .muted {
    color: color-mix(in srgb, var(--MapFilterLabelTextColor) 78%, transparent);
}

/* ---------- Top/bottom scrims ---------- */
/* Fade the page background back in at both edges (transparent through the middle) so
   the header and the floating tab bar stay readable over any content or map beneath.
   Purely decorative: never intercepts input. */
.sm-scrim {
    position: fixed;
    left: 0; right: 0;
    z-index: 90;
    pointer-events: none;
}
/* Stays near-opaque for the height of the sticky header, then fades out, so the title
   never has card edges showing through it. */
.sm-scrim-top {
    top: 0;
    height: calc(140px + env(safe-area-inset-top));
    background: linear-gradient(to bottom,
        var(--PageBackgroundColor) 0%,
        var(--PageBackgroundColor) 30%,
        color-mix(in srgb, var(--PageBackgroundColor) 88%, transparent) 52%,
        color-mix(in srgb, var(--PageBackgroundColor) 45%, transparent) 76%,
        transparent 100%);
}
.sm-scrim-bottom {
    bottom: 0;
    height: calc(168px + env(safe-area-inset-bottom));
    background: linear-gradient(to top,
        var(--PageBackgroundColor) 0%,
        color-mix(in srgb, var(--PageBackgroundColor) 86%, transparent) 34%,
        color-mix(in srgb, var(--PageBackgroundColor) 46%, transparent) 64%,
        transparent 100%);
}
.leaflet-container { background: var(--PageBackgroundColor); font-family: inherit; }
.sm-map-pin { filter: drop-shadow(0 6px 8px #00000066); }

.map-card-carousel {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding: 12px 4px;
    scrollbar-width: none;
}
.map-card-carousel::-webkit-scrollbar { display: none; }
.map-card {
    flex: 0 0 86%;
    scroll-snap-align: center;
    max-width: 420px;
}
.map-card .row { flex-wrap: wrap; }
.map-card .row > .btn-primary,
.map-card .row > .btn-secondary { flex: 1 1 auto; min-width: 120px; }

.map-filter-panel {
    /* Positioned so it can sit above .sm-scrim-top (90); a static box would be painted
       under the fixed scrim whatever its z-index. */
    position: relative;
    z-index: 95;
    background: var(--MapFilterPanelBackgroundColor);
    border: 1px solid var(--MapFilterPanelBorderColor);
    border-radius: 20px;
    backdrop-filter: blur(20px) saturate(1.4);
    -webkit-backdrop-filter: blur(20px) saturate(1.4);
    padding: .7rem .9rem;
    color: var(--MapFilterLabelTextColor);
}
.map-filter-panel .chip { background: var(--MapStateChipUnselectedBackgroundColor); color: var(--MapStateChipUnselectedTextColor); }
.map-filter-panel .chip.selected { background: var(--MapStateChipSelectedBackgroundColor); color: var(--MapStateChipSelectedTextColor); }

/* ---------- Calendar ---------- */
.calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 5px;
    max-width: 520px;
    margin-left: auto;
    margin-right: auto;
}
.calendar-head { text-align: center; font-size: 12px; font-weight: 700; color: var(--MutedTextColor); padding: .3rem 0; }
.calendar-day {
    aspect-ratio: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    border-radius: 14px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--PrimaryTextColor);
    font-size: 14px;
    cursor: pointer;
    transition: background .18s ease, border-color .18s ease, transform .13s ease;
}
.calendar-day:hover { background: var(--AccentSoftColor); }
.calendar-day.outside { opacity: .32; }
.calendar-day.today { border-color: var(--SectionTitleColor); }
.calendar-day.selected { background: var(--StateChipSelectedBackgroundColor); color: var(--StateChipSelectedTextColor); font-weight: 700; }
.calendar-day .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--AuthInputFocusedStrokeColor); box-shadow: 0 0 8px var(--AuthInputFocusedStrokeColor); }
.calendar-day.selected .dot { background: currentColor; box-shadow: none; }

/* ---------- Image galleries / viewer ---------- */
.gallery-row { display: flex; gap: 10px; overflow-x: auto; padding: .3rem 0 .6rem; }
.gallery-row .gallery-thumb {
    position: relative;
    flex: 0 0 auto;
    width: 108px; height: 108px;
    border-radius: 18px;
    overflow: hidden;
    border: 1px solid var(--CardBorderColor);
    cursor: pointer;
}
.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }
.gallery-thumb .remove {
    position: absolute; top: 5px; right: 5px;
    width: 26px; height: 26px;
    border-radius: 50%; border: none;
    background: var(--IconBubbleBackgroundColor); color: #fff;
    cursor: pointer; font-size: 12px;
}
.gallery-add {
    flex: 0 0 auto;
    width: 108px; height: 108px;
    border-radius: 18px;
    border: 1.5px dashed var(--BorderColor);
    background: transparent;
    color: var(--MutedTextColor);
    font-size: 26px;
    cursor: pointer;
}

html.sm-viewer-open, html.sm-viewer-open body { overflow: hidden; }

.viewer-backdrop {
    position: fixed; inset: 0; z-index: 1500;
    /* Opaque black like the native ImageVisualiserPage: a translucent backdrop let the
       page bleed through and made dark photos hard to read. */
    background: #000;
    display: flex; align-items: center; justify-content: center;
    animation: sm-fade .2s ease both;
    overscroll-behavior: contain;
}
/* The stage is what smViewer transforms; touch-action must be off or the browser
   claims the pinch gesture for page zoom before the handlers ever see it. */
.viewer-stage {
    width: 100%; height: 100%;
    display: flex; align-items: center; justify-content: center;
    touch-action: none;
    will-change: transform;
    cursor: zoom-in;
}
.viewer-stage.zoomed { cursor: grab; }
/* Percentages, not 100vw: vw includes the scrollbar, which shifted the photo a few
   pixels off-centre. */
.viewer-backdrop img {
    max-width: 100%; max-height: 100%;
    object-fit: contain;
    -webkit-user-select: none; user-select: none; -webkit-user-drag: none;
}
.viewer-controls {
    position: fixed;
    top: calc(18px + env(safe-area-inset-top)); right: 18px;
    display: flex; gap: .6rem;
}
.viewer-controls .icon-bubble { background: #ffffff26; color: #fff; border-color: #ffffff33; }
.viewer-title {
    position: fixed;
    top: calc(24px + env(safe-area-inset-top)); left: 18px; right: 76px;
    color: #fff; font-weight: 600; font-size: 15px;
    text-shadow: 0 1px 4px #000a;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.viewer-nav {
    position: fixed; top: 50%; transform: translateY(-50%);
    width: 46px; height: 46px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: #ffffff26; border: 1px solid #ffffff33; color: #fff;
    font-size: 30px; line-height: 1; padding-bottom: 4px;
    cursor: pointer; backdrop-filter: blur(6px);
}
.viewer-nav.prev { left: 14px; }
.viewer-nav.next { right: 14px; }
.viewer-counter {
    position: fixed; left: 50%; transform: translateX(-50%);
    bottom: calc(22px + env(safe-area-inset-bottom));
    padding: .3rem .8rem; border-radius: 999px;
    background: #ffffff26; border: 1px solid #ffffff33; color: #fff;
    font-size: 13px; backdrop-filter: blur(6px);
}

/* ---------- Steps (relation editor) ---------- */
.steps-bar { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-bottom: 1.1rem; }
.step-btn {
    padding: .55rem .4rem;
    border-radius: 18px;
    border: 1px solid var(--CardBorderColor);
    background: var(--CardBackgroundColor);
    color: var(--SecondaryTextColor);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    transition: background .2s ease, color .2s ease;
}
.step-btn.active { background: var(--StateChipSelectedBackgroundColor); color: var(--StateChipSelectedTextColor); }

.editor-actionbar {
    position: sticky;
    bottom: calc(108px + env(safe-area-inset-bottom));
    z-index: 50;
    display: flex;
    gap: .6rem;
    padding: .7rem;
    margin-top: 1.2rem;
}
.editor-actionbar > * { flex: 1; min-width: 0; }
/* Keep 3 buttons (Previous / Next / Save) on one line on narrow phones; the
   Save CTA carries the longest label ("Créer maintenant") so give it more room.
   min-width:0 + ellipsis guarantees no horizontal overflow whatever the locale. */
.editor-actionbar .btn-primary,
.editor-actionbar .btn-secondary { padding-left: .5rem; padding-right: .5rem; font-size: 14px; white-space: nowrap; overflow: hidden; }
.editor-actionbar .btn-primary { flex: 1.7; }
@media (max-width: 430px) {
    /* Very narrow: keep Previous/Next on the top row and drop the (longest) Save
       CTA onto its own full-width row so it's always fully legible. */
    .editor-actionbar { flex-wrap: wrap; gap: .5rem; }
    .editor-actionbar .btn-primary { flex: 1 1 100%; }
}

/* ---------- Settings rows ---------- */
.settings-row {
    display: flex;
    align-items: center;
    gap: .9rem;
    width: 100%;
    padding: 1rem 1.1rem;
    border: none;
    text-align: left;
    font: inherit;
    color: var(--PrimaryTextColor);
    background: transparent;
    cursor: pointer;
    border-radius: 18px;
    transition: background .18s ease;
}
.settings-row:hover { background: var(--AccentSoftColor); }
.settings-row .glyph { font-size: 21px; width: 30px; text-align: center; }
.settings-row .grow { flex: 1; min-width: 0; }
.settings-row .value { color: var(--MutedTextColor); font-size: 13.5px; }
.settings-row .chevron { color: var(--MutedTextColor); }

.count-badge {
    min-width: 24px; height: 24px;
    padding: 0 7px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 12px;
    background: var(--AuthInputFocusedStrokeColor);
    color: #2f011d;
    font-size: 12.5px;
    font-weight: 700;
}

/* ---------- Avatar ---------- */
.avatar { border-radius: 50%; object-fit: cover; border: 2px solid var(--BorderColor); background: var(--CardBackgroundColor); }
.avatar.lg { width: 128px; height: 128px; border-width: 4px; }
.avatar.md { width: 64px; height: 64px; }
.avatar.sm { width: 40px; height: 40px; }

/* Profile page hero avatar, matching the native ProfilePage: a 156px circle with a
   hairline stroke (StrokeThickness="1", CardBorderColor) and a *centred* glow —
   the native Shadow uses Offset="0,0" with Radius="15", i.e. no directional offset. */
.avatar.hero {
    width: 156px; height: 156px;
    border-width: 1px;
    border-color: var(--CardBorderColor);
    box-shadow: 0 0 15px var(--AvatarHeroGlowColor);
}
/* Native: white at 50% on dark themes, black on light (ProfilePage.xaml.cs). */
:root, :root[data-theme="signature"], :root[data-theme="dark"] { --AvatarHeroGlowColor: #80ffffff; }
:root[data-theme="light"] { --AvatarHeroGlowColor: #000000; }

/* ---------- Utility ---------- */
.row { display: flex; align-items: center; gap: .7rem; }
.grow { flex: 1; min-width: 0; }
.stack { display: flex; flex-direction: column; gap: .8rem; }
.center { text-align: center; }
.mt-1 { margin-top: .5rem; } .mt-2 { margin-top: 1rem; } .mt-3 { margin-top: 1.6rem; }
.mb-1 { margin-bottom: .5rem; } .mb-2 { margin-bottom: 1rem; }
.hidden { display: none; }

/* ---------- Responsive hardening ---------- */
/* Checkboxes and glyphs in flex rows must never deform under long neighbours. */
.row > input[type="checkbox"] { flex-shrink: 0; width: 18px; height: 18px; }
.settings-row .glyph, .sheet-item .glyph, .stat-tile .stat-icon { flex-shrink: 0; }
/* A flex item's default min-width:auto is its min-content width, and a <select> is
   sized by its widest <option> -- country lists ("République démocratique du Congo")
   then blow past the declared flex-basis and push row siblings off screen. The inner
   control already has min-width:0; the wrapper needs it too, since it is the flex item. */
.row > .sm-input-wrap, .row > .sm-field { min-width: 0; }
/* Keep the sticky editor action bar aligned with the capped column on desktop. */
@media (min-width: 900px) {
    .editor-actionbar { max-width: 640px; margin-left: auto; margin-right: auto; }
    .steps-bar { max-width: 640px; margin-left: auto; margin-right: auto; }
}

/* ---------- Infinite scroll ---------- */
/* The sentinel must occupy real height, otherwise a zero-height element never
   intersects and the next page is never requested. */
.infinite-sentinel { min-height: 56px; margin-top: .8rem; }

/* ---------- Offline banner ---------- */
.offline-banner {
    position: fixed; top: 0; left: 0; right: 0; z-index: 200;
    display: flex; align-items: center; justify-content: center; gap: .5rem;
    padding: calc(.4rem + env(safe-area-inset-top)) .8rem .4rem;
    /* Amber in every theme, so a fixed near-black foreground keeps the contrast well
       above 4.5:1 without a per-theme override. */
    background: var(--WarningColor, #f6c26a);
    color: #20140a; font-size: 13px; font-weight: 600;
}
/* Push the page down so the banner never hides the header's back button, and raise the
   sticky offsets of everything inside by the same amount. */
.sm-shell.with-offline-banner { --sm-banner-h: 34px; padding-top: 34px; }

/* The carousel card matching the marker the user tapped. */
.map-card.selected { border-color: var(--PartnerTileSelectedBorderColor); }
