/* ============================================================================
   Theme tokens

   The active layout (data-layout) and colour scheme (data-theme) are set on
   <html> in App.razor: the layout is the administrator's site-wide choice, the
   theme is the visitor's light/dark preference (defaulting to the admin
   default / the OS preference). Each of the six combinations is fully spelled
   out so exactly one block ever matches — no cascade fighting.
   ============================================================================ */

:root {
    --bg: #eef3f7;
    --surface: #ffffff;
    --surface-muted: #f2f6f9;
    --border: #dce4ec;
    --text: #1f2d3a;
    --text-muted: #5d6b7a;
    --heading: #0e3a5d;
    --brand: #11507c;
    --brand-dark: #0e3a5d;
    --brand-soft: #e8f1f8;
    --accent: #e2674a;
    --radius: 0.75rem;
    --shadow: rgba(14, 58, 93, 0.08);
    --head-font: 'Segoe UI', system-ui, -apple-system, Helvetica, Arial, sans-serif;
    --rail-bg: linear-gradient(170deg, #0e3a5d 0%, #11507c 55%, #0d6e8f 100%);
    --rail-fg: rgba(255, 255, 255, 0.82);
    --rail-fg-muted: rgba(255, 255, 255, 0.6);
    --rail-strong: #ffffff;
    --rail-icon: #ffffff;
    --rail-active-bg: rgba(255, 255, 255, 0.22);
    --rail-active-fg: #ffffff;
    --topbar-bg: rgba(255, 255, 255, 0.85);
    color-scheme: light;
}

html[data-layout="marina"][data-theme="dark"] {
    --bg: #0b1620; --surface: #13212e; --surface-muted: #0f1f2b; --border: #243443;
    --text: #e6eef5; --text-muted: #90a4b5; --heading: #cfe3f2;
    --brand: #4aa3d6; --brand-dark: #7bc0e6; --brand-soft: rgba(74, 163, 214, 0.14); --accent: #f0916f;
    --shadow: rgba(0, 0, 0, 0.4);
    --rail-bg: linear-gradient(170deg, #0a2233 0%, #0c3046 100%);
    --rail-fg: rgba(255, 255, 255, 0.8); --rail-fg-muted: rgba(255, 255, 255, 0.55);
    --rail-strong: #ffffff; --rail-icon: #ffffff;
    --rail-active-bg: rgba(255, 255, 255, 0.16); --rail-active-fg: #ffffff;
    --topbar-bg: rgba(15, 28, 40, 0.85);
    color-scheme: dark;
}

html[data-layout="paseo"][data-theme="light"] {
    --bg: #f6f2ea; --surface: #fffdf8; --surface-muted: #efe9dc; --border: #e6ded0;
    --text: #2a2520; --text-muted: #7a7165; --heading: #3a3228;
    --brand: #5b6b3a; --brand-dark: #45522c; --brand-soft: #efe6d4; --accent: #b4552e;
    --radius: 0.4rem; --shadow: rgba(80, 60, 30, 0.08);
    --head-font: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
    --rail-bg: #fffdf8; --rail-fg: #2a2520; --rail-fg-muted: #7a7165;
    --rail-strong: #3a3228; --rail-icon: #b4552e;
    --rail-active-bg: transparent; --rail-active-fg: #b4552e;
    --topbar-bg: #fffdf8;
    color-scheme: light;
}

html[data-layout="paseo"][data-theme="dark"] {
    --bg: #1c1813; --surface: #26211a; --surface-muted: #322b21; --border: #3a3328;
    --text: #efe8da; --text-muted: #a99e8c; --heading: #efe2c9;
    --brand: #9bab6e; --brand-dark: #b6c489; --brand-soft: rgba(155, 171, 110, 0.16); --accent: #d9784e;
    --radius: 0.4rem; --shadow: rgba(0, 0, 0, 0.45);
    --head-font: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
    --rail-bg: #26211a; --rail-fg: #efe8da; --rail-fg-muted: #a99e8c;
    --rail-strong: #efe2c9; --rail-icon: #d9784e;
    --rail-active-bg: transparent; --rail-active-fg: #d9784e;
    --topbar-bg: #26211a;
    color-scheme: dark;
}

html[data-layout="mirador"][data-theme="light"] {
    --bg: #eef1f5; --surface: #ffffff; --surface-muted: #f1f5f9; --border: #e2e8f0;
    --text: #1b2430; --text-muted: #64748b; --heading: #15202b;
    --brand: #0d9488; --brand-dark: #0f766e; --brand-soft: #e3f5f2; --accent: #0d9488;
    --radius: 1rem; --shadow: rgba(15, 30, 50, 0.07);
    --rail-bg: #ffffff; --rail-fg: #64748b; --rail-fg-muted: #94a3b8;
    --rail-strong: #15202b; --rail-icon: #0d9488;
    --rail-active-bg: #e3f5f2; --rail-active-fg: #0d6e63;
    --topbar-bg: rgba(255, 255, 255, 0.85);
    color-scheme: light;
}

html[data-layout="mirador"][data-theme="dark"] {
    --bg: #0c1118; --surface: #161e29; --surface-muted: #1c2733; --border: #243140;
    --text: #e7edf4; --text-muted: #8a99ab; --heading: #d3e3f0;
    --brand: #2dd4bf; --brand-dark: #5eead4; --brand-soft: rgba(45, 212, 191, 0.13); --accent: #2dd4bf;
    --radius: 1rem; --shadow: rgba(0, 0, 0, 0.45);
    --rail-bg: #161e29; --rail-fg: #8a99ab; --rail-fg-muted: #64748b;
    --rail-strong: #d3e3f0; --rail-icon: #2dd4bf;
    --rail-active-bg: #10302c; --rail-active-fg: #2dd4bf;
    --topbar-bg: rgba(15, 22, 32, 0.85);
    color-scheme: dark;
}

/* ============================================================================
   Colour schemes (data-scheme) — palettes layered on top of a layout.
   Each layout's "default" scheme is its built-in palette above; the rules below
   add the alternates an administrator can pick. They override only the
   colour-bearing variables, inheriting structure (radius, fonts, rail style)
   from the layout rules, so they apply alongside them via higher specificity.
   ============================================================================ */

/* ---- Marina alternates (gradient rail, white rail text inherited) ---------- */
html[data-layout="marina"][data-scheme="lagoon"][data-theme="light"] {
    --bg: #eaf4f4; --surface: #ffffff; --surface-muted: #eef6f6; --border: #d3e6e6;
    --text: #16302e; --text-muted: #5a7572; --heading: #0c3b38;
    --brand: #0f8a8a; --brand-dark: #0a6e6e; --brand-soft: #e0f2f1; --accent: #e6a34a;
    --rail-bg: linear-gradient(170deg, #0b4a48 0%, #0f7a78 55%, #129a8f 100%);
    --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="marina"][data-scheme="lagoon"][data-theme="dark"] {
    --bg: #08201f; --surface: #0f2b29; --surface-muted: #0c2523; --border: #1f3a38;
    --text: #e2f1ef; --text-muted: #8aa9a6; --heading: #c3e7e3;
    --brand: #34b3ad; --brand-dark: #5fd0c9; --brand-soft: rgba(52, 179, 173, 0.14); --accent: #f0b86a;
    --rail-bg: linear-gradient(170deg, #072725 0%, #0a3a37 100%);
    --topbar-bg: rgba(10, 30, 28, 0.85);
}

html[data-layout="marina"][data-scheme="sunset"][data-theme="light"] {
    --bg: #f3eef4; --surface: #fffafc; --surface-muted: #f3ebf1; --border: #e6d8e6;
    --text: #2c2433; --text-muted: #756a7d; --heading: #3a2a4a;
    --brand: #7a4a9c; --brand-dark: #5e3a7c; --brand-soft: #efe3f5; --accent: #ee6c4d;
    --rail-bg: linear-gradient(170deg, #3a2a5d 0%, #6e3f86 55%, #a85a7a 100%);
    --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="marina"][data-scheme="sunset"][data-theme="dark"] {
    --bg: #18121f; --surface: #221830; --surface-muted: #1d1428; --border: #342540;
    --text: #ece2f2; --text-muted: #a394b0; --heading: #e0cdee;
    --brand: #b985d6; --brand-dark: #cda3e3; --brand-soft: rgba(185, 133, 214, 0.15); --accent: #f2856a;
    --rail-bg: linear-gradient(170deg, #1f1430 0%, #3a2150 100%);
    --topbar-bg: rgba(20, 14, 28, 0.85);
}

html[data-layout="marina"][data-scheme="regatta"][data-theme="light"] {
    --bg: #eef1f7; --surface: #ffffff; --surface-muted: #eff3fa; --border: #d9e1ee;
    --text: #1b2436; --text-muted: #5c6a82; --heading: #16284e;
    --brand: #1f4eb8; --brand-dark: #173c8f; --brand-soft: #e4ecfb; --accent: #d6342f;
    --rail-bg: linear-gradient(170deg, #16284e 0%, #1f4eb8 60%, #2563c9 100%);
    --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="marina"][data-scheme="regatta"][data-theme="dark"] {
    --bg: #0a0f1c; --surface: #131b2e; --surface-muted: #0f1727; --border: #233152;
    --text: #e4eaf6; --text-muted: #8d9bb6; --heading: #c5d6f2;
    --brand: #5b86e8; --brand-dark: #82a4ef; --brand-soft: rgba(91, 134, 232, 0.14); --accent: #f0625d;
    --rail-bg: linear-gradient(170deg, #0c1530 0%, #16265a 100%);
    --topbar-bg: rgba(12, 18, 32, 0.85);
}

html[data-layout="marina"][data-scheme="slate"][data-theme="light"] {
    --bg: #eef0f2; --surface: #ffffff; --surface-muted: #eef1f4; --border: #dbe0e6;
    --text: #232a31; --text-muted: #65707b; --heading: #2c3742;
    --brand: #4d6577; --brand-dark: #3a4e5d; --brand-soft: #e7edf1; --accent: #c97b54;
    --rail-bg: linear-gradient(170deg, #2f3d49 0%, #46596a 100%);
    --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="marina"][data-scheme="slate"][data-theme="dark"] {
    --bg: #0e1316; --surface: #181f24; --surface-muted: #141a1f; --border: #283139;
    --text: #e3e8ec; --text-muted: #93a0ab; --heading: #cdd8e0;
    --brand: #7f9bb0; --brand-dark: #9db4c6; --brand-soft: rgba(127, 155, 176, 0.14); --accent: #d99a73;
    --rail-bg: linear-gradient(170deg, #141b21 0%, #222e38 100%);
    --topbar-bg: rgba(14, 19, 22, 0.85);
}

/* ---- Paseo alternates (surface rail, dark rail text) ----------------------- */
html[data-layout="paseo"][data-scheme="terracotta"][data-theme="light"] {
    --bg: #f7f0e8; --surface: #fffaf3; --surface-muted: #f0e5d8; --border: #e7d8c6;
    --text: #2e2620; --text-muted: #80715f; --heading: #3d2a20;
    --brand: #b0542e; --brand-dark: #8c3f20; --brand-soft: #f4e2d4; --accent: #c77f3a;
    --rail-bg: #fffaf3; --rail-fg: #2e2620; --rail-fg-muted: #80715f; --rail-strong: #3d2a20;
    --rail-icon: #b0542e; --rail-active-fg: #b0542e; --topbar-bg: #fffaf3;
}
html[data-layout="paseo"][data-scheme="terracotta"][data-theme="dark"] {
    --bg: #1d1611; --surface: #281e17; --surface-muted: #342820; --border: #3d2f24;
    --text: #efe4d7; --text-muted: #ab9a86; --heading: #f0d9c4;
    --brand: #d98a5e; --brand-dark: #e6a37d; --brand-soft: rgba(217, 138, 94, 0.16); --accent: #e0a45c;
    --rail-bg: #281e17; --rail-fg: #efe4d7; --rail-fg-muted: #ab9a86; --rail-strong: #f0d9c4;
    --rail-icon: #d98a5e; --rail-active-fg: #d98a5e; --topbar-bg: #281e17;
}

html[data-layout="paseo"][data-scheme="vino"][data-theme="light"] {
    --bg: #f6efea; --surface: #fffaf6; --surface-muted: #f0e4dd; --border: #e6d5cc;
    --text: #2c211f; --text-muted: #7d6a64; --heading: #4a1f28;
    --brand: #8a2e44; --brand-dark: #6c2334; --brand-soft: #f1dfe2; --accent: #b78a3c;
    --rail-bg: #fffaf6; --rail-fg: #2c211f; --rail-fg-muted: #7d6a64; --rail-strong: #4a1f28;
    --rail-icon: #8a2e44; --rail-active-fg: #8a2e44; --topbar-bg: #fffaf6;
}
html[data-layout="paseo"][data-scheme="vino"][data-theme="dark"] {
    --bg: #1e1416; --surface: #281a1d; --surface-muted: #321f23; --border: #3d262b;
    --text: #efdfe0; --text-muted: #ad9498; --heading: #e9c4cd;
    --brand: #cf6a82; --brand-dark: #df8c9f; --brand-soft: rgba(207, 106, 130, 0.15); --accent: #d4ad5e;
    --rail-bg: #281a1d; --rail-fg: #efdfe0; --rail-fg-muted: #ad9498; --rail-strong: #e9c4cd;
    --rail-icon: #cf6a82; --rail-active-fg: #cf6a82; --topbar-bg: #281a1d;
}

html[data-layout="paseo"][data-scheme="almendra"][data-theme="light"] {
    --bg: #f4f1ea; --surface: #fdfbf5; --surface-muted: #ece7db; --border: #ddd6c7;
    --text: #2d2a23; --text-muted: #7a7264; --heading: #3a362c;
    --brand: #6f7d5a; --brand-dark: #566244; --brand-soft: #e7e8d8; --accent: #a98c63;
    --rail-bg: #fdfbf5; --rail-fg: #2d2a23; --rail-fg-muted: #7a7264; --rail-strong: #3a362c;
    --rail-icon: #6f7d5a; --rail-active-fg: #6f7d5a; --topbar-bg: #fdfbf5;
}
html[data-layout="paseo"][data-scheme="almendra"][data-theme="dark"] {
    --bg: #1a1814; --surface: #24221b; --surface-muted: #2e2b22; --border: #38342a;
    --text: #ece8db; --text-muted: #a39c8a; --heading: #dcd9c4;
    --brand: #a3b07e; --brand-dark: #bcc79a; --brand-soft: rgba(163, 176, 126, 0.15); --accent: #c4a880;
    --rail-bg: #24221b; --rail-fg: #ece8db; --rail-fg-muted: #a39c8a; --rail-strong: #dcd9c4;
    --rail-icon: #a3b07e; --rail-active-fg: #a3b07e; --topbar-bg: #24221b;
}

html[data-layout="paseo"][data-scheme="azulejo"][data-theme="light"] {
    --bg: #f2f0ea; --surface: #fdfcf7; --surface-muted: #e7ebec; --border: #d8dfe0;
    --text: #232a2e; --text-muted: #687680; --heading: #1b3a4a;
    --brand: #246b86; --brand-dark: #1a5168; --brand-soft: #dbe9ef; --accent: #cf8a3a;
    --rail-bg: #fdfcf7; --rail-fg: #232a2e; --rail-fg-muted: #687680; --rail-strong: #1b3a4a;
    --rail-icon: #246b86; --rail-active-fg: #246b86; --topbar-bg: #fdfcf7;
}
html[data-layout="paseo"][data-scheme="azulejo"][data-theme="dark"] {
    --bg: #0f1719; --surface: #172226; --surface-muted: #1d2a2f; --border: #28383d;
    --text: #e3edf0; --text-muted: #90a3aa; --heading: #bfe0ea;
    --brand: #4ba3c2; --brand-dark: #74c0da; --brand-soft: rgba(75, 163, 194, 0.14); --accent: #dba35a;
    --rail-bg: #172226; --rail-fg: #e3edf0; --rail-fg-muted: #90a3aa; --rail-strong: #bfe0ea;
    --rail-icon: #4ba3c2; --rail-active-fg: #4ba3c2; --topbar-bg: #172226;
}

/* ---- Mirador alternates (surface rail, brand-tinted active state) ---------- */
html[data-layout="mirador"][data-scheme="indigo"][data-theme="light"] {
    --bg: #eef0f6; --surface: #ffffff; --surface-muted: #f1f3f9; --border: #e1e5f0;
    --text: #1d2230; --text-muted: #646c82; --heading: #1a1f3a;
    --brand: #4f46e5; --brand-dark: #4338ca; --brand-soft: #e8e7fc; --accent: #4f46e5;
    --rail-bg: #ffffff; --rail-fg: #646c82; --rail-fg-muted: #9aa1b5; --rail-strong: #1a1f3a;
    --rail-icon: #4f46e5; --rail-active-bg: #e8e7fc; --rail-active-fg: #4338ca; --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="mirador"][data-scheme="indigo"][data-theme="dark"] {
    --bg: #0c0e1a; --surface: #161a2b; --surface-muted: #1c2236; --border: #272e47;
    --text: #e6e9f4; --text-muted: #8b94ad; --heading: #cdd2ee;
    --brand: #818cf8; --brand-dark: #a5acfb; --brand-soft: rgba(129, 140, 248, 0.14); --accent: #818cf8;
    --rail-bg: #161a2b; --rail-fg: #8b94ad; --rail-fg-muted: #646c82; --rail-strong: #cdd2ee;
    --rail-icon: #818cf8; --rail-active-bg: #232a4a; --rail-active-fg: #a5acfb; --topbar-bg: rgba(14, 16, 28, 0.85);
}

html[data-layout="mirador"][data-scheme="coral"][data-theme="light"] {
    --bg: #f6eff0; --surface: #ffffff; --surface-muted: #faf0f1; --border: #f0dee0;
    --text: #2c2225; --text-muted: #7c6a6e; --heading: #3a2026;
    --brand: #e1556b; --brand-dark: #c43d54; --brand-soft: #fce2e6; --accent: #e1556b;
    --rail-bg: #ffffff; --rail-fg: #7c6a6e; --rail-fg-muted: #b09a9e; --rail-strong: #3a2026;
    --rail-icon: #e1556b; --rail-active-bg: #fce2e6; --rail-active-fg: #c43d54; --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="mirador"][data-scheme="coral"][data-theme="dark"] {
    --bg: #160f11; --surface: #221619; --surface-muted: #2a1c20; --border: #38262b;
    --text: #f2e2e4; --text-muted: #b0969b; --heading: #f0c8cf;
    --brand: #f47e90; --brand-dark: #f7a0ad; --brand-soft: rgba(244, 126, 144, 0.14); --accent: #f47e90;
    --rail-bg: #221619; --rail-fg: #b0969b; --rail-fg-muted: #806468; --rail-strong: #f0c8cf;
    --rail-icon: #f47e90; --rail-active-bg: #3a242a; --rail-active-fg: #f7a0ad; --topbar-bg: rgba(20, 14, 16, 0.85);
}

html[data-layout="mirador"][data-scheme="amber"][data-theme="light"] {
    --bg: #f6f2e9; --surface: #ffffff; --surface-muted: #faf4e6; --border: #ece0c8;
    --text: #2c2820; --text-muted: #7a7158; --heading: #3a3018;
    --brand: #b9790a; --brand-dark: #9a6408; --brand-soft: #f7ecd0; --accent: #c9890f;
    --rail-bg: #ffffff; --rail-fg: #7a7158; --rail-fg-muted: #aaa180; --rail-strong: #3a3018;
    --rail-icon: #c9890f; --rail-active-bg: #f7ecd0; --rail-active-fg: #9a6408; --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="mirador"][data-scheme="amber"][data-theme="dark"] {
    --bg: #16130b; --surface: #221c11; --surface-muted: #2a2316; --border: #38301c;
    --text: #f0ebdd; --text-muted: #ada185; --heading: #efdcb0;
    --brand: #e3b24f; --brand-dark: #efc873; --brand-soft: rgba(227, 178, 79, 0.15); --accent: #e3b24f;
    --rail-bg: #221c11; --rail-fg: #ada185; --rail-fg-muted: #7a7158; --rail-strong: #efdcb0;
    --rail-icon: #e3b24f; --rail-active-bg: #38301c; --rail-active-fg: #efc873; --topbar-bg: rgba(20, 16, 8, 0.85);
}

html[data-layout="mirador"][data-scheme="forest"][data-theme="light"] {
    --bg: #ecf2ee; --surface: #ffffff; --surface-muted: #eef5f0; --border: #dce8e0;
    --text: #1c2722; --text-muted: #5f7268; --heading: #12352a;
    --brand: #15803d; --brand-dark: #126b33; --brand-soft: #dcf0e3; --accent: #15803d;
    --rail-bg: #ffffff; --rail-fg: #5f7268; --rail-fg-muted: #93a89c; --rail-strong: #12352a;
    --rail-icon: #15803d; --rail-active-bg: #dcf0e3; --rail-active-fg: #126b33; --topbar-bg: rgba(255, 255, 255, 0.85);
}
html[data-layout="mirador"][data-scheme="forest"][data-theme="dark"] {
    --bg: #0a1410; --surface: #131f18; --surface-muted: #18271e; --border: #233429;
    --text: #e2efe7; --text-muted: #87a092; --heading: #bfe6cd;
    --brand: #34d27e; --brand-dark: #5cdf9a; --brand-soft: rgba(52, 210, 126, 0.13); --accent: #34d27e;
    --rail-bg: #131f18; --rail-fg: #87a092; --rail-fg-muted: #5f7268; --rail-strong: #bfe6cd;
    --rail-icon: #34d27e; --rail-active-bg: #163026; --rail-active-fg: #5cdf9a; --topbar-bg: rgba(10, 20, 16, 0.85);
}

html, body {
    font-family: 'Segoe UI', system-ui, -apple-system, Helvetica, Arial, sans-serif;
    background-color: var(--bg);
    color: var(--text);
}

h1, h2, h3 {
    color: var(--heading);
    font-family: var(--head-font);
}

h1 { font-size: 1.6rem; font-weight: 700; }

a, .btn-link { color: var(--brand); }

.btn-primary {
    background-color: var(--brand);
    border-color: var(--brand);
}

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

.content h1:focus { outline: none; }

.valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; }
.invalid { outline: 1px solid #e50000; }
.validation-message { color: #e50000; }

/* ============================================================================
   Application shell — one DOM, reshaped per layout (mobile-first)
   ============================================================================ */

.page {
    display: grid;
    /* Pin the shell to the viewport so the sidebar/top bar stay fixed and only the
       main area scrolls (dvh accounts for mobile browser chrome). */
    height: 100vh;
    height: 100dvh;
    grid-template-columns: 1fr;
    grid-template-rows: 3.5rem 1fr;
    grid-template-areas: "topbar" "main";
    background: var(--bg);
}

.sidebar { grid-area: sidebar; display: none; }
.topbar { grid-area: topbar; }
main {
    grid-area: main;
    display: flex;
    flex-direction: column;
    min-width: 0;
    /* The scroll container: content scrolls here while the shell stays put.
       min-height:0 lets this grid item shrink instead of blowing out the row. */
    overflow-y: auto;
    min-height: 0;
}

.nav-drawer-toggle { display: none; }

/* Brand */
.brand {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    text-decoration: none;
    padding: 0.25rem 0.4rem;
}

.brand-icon { font-size: 1.7rem; color: var(--rail-icon); }
.brand-text { display: flex; flex-direction: column; line-height: 1.2; }
.brand-name { color: var(--rail-strong); font-weight: 700; font-size: 1.05rem; }
.brand-tagline { color: var(--rail-fg-muted); font-size: 0.75rem; }

/* The compact brand lives in the top bar (mobile + Paseo); it sits on the
   light surface, so it uses surface-appropriate colours, not the rail's. */
.brand-compact { display: none; }
.brand-compact .brand-icon { color: var(--brand); font-size: 1.4rem; }
.brand-compact .brand-name { color: var(--heading); font-size: 1rem; }

/* Top bar */
.topbar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    height: 3.5rem;
    padding: 0 1rem;
    background: var(--topbar-bg);
    backdrop-filter: blur(8px);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 10;
}

.nav-toggle {
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    color: var(--heading);
    font-size: 1.5rem;
    padding: 0.1rem 0.35rem;
    border-radius: 0.4rem;
}

.nav-toggle:hover { background: var(--brand-soft); }

.page-title {
    display: none;
    font-weight: 600;
    color: var(--heading);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.top-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-left: auto;
}

.top-link, .theme-toggle {
    color: var(--heading);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    font-size: 1.15rem;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0.2rem;
    line-height: 1;
}

.top-link:hover, .theme-toggle:hover { color: var(--brand); }
.logout-form { display: inline; margin: 0; }

/* Show the single relevant theme toggle for the active scheme. */
html[data-theme="dark"] .theme-to-dark { display: none; }
html[data-theme="light"] .theme-to-light { display: none; }

/* Navigation links (rail / top bar / bottom bar) */
.nav-links { display: flex; flex-direction: column; gap: 0.25rem; }

.nav-link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    height: 2.8rem;
    padding: 0 0.85rem;
    border-radius: 0.5rem;
    color: var(--rail-fg);
    text-decoration: none;
    font-size: 0.95rem;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.nav-link:hover { background: var(--rail-active-bg); color: var(--rail-active-fg); }
.nav-link.active { background: var(--rail-active-bg); color: var(--rail-active-fg); font-weight: 600; }
.nav-icon { font-size: 1.1rem; width: 1.5rem; text-align: center; }

/* The unread counter on the Forum / Messages entries. In the rail and the drawer
   it sits at the far end of the row; the horizontal bar has no far end, so there
   it simply follows the label. */
.nav-badge {
    margin-left: auto;
    min-width: 1.35rem;
    padding: 0.05rem 0.4rem;
    border-radius: 1rem;
    background: var(--accent);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.35;
    text-align: center;
}

/* Content + footer */
.content { padding: 1.25rem 1rem 2rem; max-width: 70rem; flex: 1; }

/* --- Mobile drawer (all layouts): the hamburger opens the sidebar as a drawer
       holding every menu item. ------------------------------------------------ */
.nav-drawer-toggle:checked ~ .sidebar {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0 auto 0 0;
    width: 15rem;
    padding: 1rem 0.75rem;
    background: var(--rail-bg);
    overflow-y: auto;
    z-index: 1200;
}

/* The dimming layer behind the open drawer. A label for the drawer checkbox,
   so tapping anywhere outside the menu closes it. */
.nav-drawer-backdrop { display: none; }
.nav-drawer-toggle:checked ~ .nav-drawer-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 1150;
}

/* --- Mobile icon bar (any layout, optional): a compact bottom bar with a chosen
       subset of nav items. Hidden on desktop; the full menu stays in the
       hamburger drawer. --------------------------------------------------------- */
.icon-bar { display: none; }

@media (max-width: 800.98px) {
    .icon-bar {
        display: flex;
        position: fixed;
        inset: auto 0 0 0;
        gap: 0.15rem;
        padding: 0.25rem;
        background: var(--surface);
        border-top: 1px solid var(--border);
        z-index: 1100;
    }

    .icon-bar .nav-link {
        position: relative;
        flex-direction: column;
        gap: 0.1rem;
        flex: 1;
        min-width: 0;
        height: auto;
        padding: 0.4rem 0.2rem;
        font-size: 0.68rem;
        text-align: center;
        color: var(--text-muted);
    }

    /* Icon over label leaves no room at the end of a row, so the counter rides on
       the icon's shoulder — where an app icon carries one. */
    .icon-bar .nav-badge {
        position: absolute;
        top: 0.1rem;
        left: 50%;
        margin: 0 0 0 0.3rem;
        min-width: 1.05rem;
        padding: 0 0.28rem;
        font-size: 0.62rem;
        line-height: 1.25;
    }

    .icon-bar .nav-link .nav-icon { width: auto; font-size: 1.25rem; }
    .icon-bar .nav-link .nav-text { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .icon-bar .nav-link:hover { background: transparent; color: var(--brand); }
    .icon-bar .nav-link.active { background: transparent; color: var(--accent); }

    /* Keep content clear of the fixed bar. */
    .page.has-iconbar main { padding-bottom: 4rem; }
}

/* ============================================================================
   Desktop (≥ 801px)
   ============================================================================ */
@media (min-width: 801px) {
    .nav-toggle, .brand-compact { display: none !important; }
    .content { padding: 1.75rem 2rem 3rem; }

    /* --- Marina & Mirador: full-height left rail --- */
    html[data-layout="marina"] .page,
    html[data-layout="mirador"] .page {
        grid-template-columns: 16rem 1fr;
        grid-template-rows: 3.5rem 1fr;
        grid-template-areas: "sidebar topbar" "sidebar main";
    }

    html[data-layout="marina"] .sidebar,
    html[data-layout="mirador"] .sidebar {
        display: flex;
        flex-direction: column;
        position: sticky;
        top: 0;
        inset: auto;
        height: 100vh;
        padding: 1rem 0.75rem;
        background: var(--rail-bg);
        overflow-y: auto;
        box-shadow: none;
        z-index: 1;
    }

    html[data-layout="marina"] .sidebar .brand,
    html[data-layout="mirador"] .sidebar .brand { margin-bottom: 1rem; }

    html[data-layout="marina"] .page-title,
    html[data-layout="mirador"] .page-title { display: block; }

    /* Mirador's rail floats as a rounded card. */
    html[data-layout="mirador"] .sidebar {
        margin: 0.75rem 0 0.75rem 0.75rem;
        height: calc(100vh - 1.5rem);
        border: 1px solid var(--border);
        border-radius: 1rem;
    }
    html[data-layout="mirador"] main { padding-bottom: 0; }

    /* --- Paseo: one horizontal top bar (brand + nav | actions) --- */
    html[data-layout="paseo"] .page {
        grid-template-columns: 1fr auto;
        grid-template-rows: 3.75rem 1fr;
        grid-template-areas: "sidebar topbar" "main main";
    }

    html[data-layout="paseo"] .sidebar {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 1.75rem;
        height: 3.75rem;
        padding: 0 0.5rem 0 1.5rem;
        background: var(--topbar-bg);
        border-bottom: 1px solid var(--border);
        position: sticky;
        top: 0;
        overflow: visible;
        z-index: 10;
    }

    html[data-layout="paseo"] .sidebar .brand { margin: 0; padding: 0; }
    html[data-layout="paseo"] .topbar { height: 3.75rem; padding-right: 1.5rem; }
    html[data-layout="paseo"] .nav-links { flex-direction: row; gap: 0.25rem; }

    html[data-layout="paseo"] .nav-link {
        height: auto;
        padding: 0.4rem 0.7rem;
        border-radius: 0.4rem;
        opacity: 0.85;
    }

    html[data-layout="paseo"] .nav-link .nav-icon { display: none; }
    /* A row of entries side by side has no far end to push the counter to. */
    html[data-layout="paseo"] .nav-badge { margin-left: 0.35rem; }
    html[data-layout="paseo"] .nav-link:hover { background: var(--surface-muted); color: var(--text); opacity: 1; }
    html[data-layout="paseo"] .nav-link.active { background: transparent; color: var(--accent); opacity: 1; }

    html[data-layout="paseo"] .content { max-width: 48rem; margin: 0 auto; width: 100%; }
}

/* ============================================================================
   Shared building blocks
   ============================================================================ */

.card-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.25rem;
    margin-bottom: 1rem;
    box-shadow: 0 1px 2px var(--shadow);
}

/* Heading over a run of channel cards. Quiet on purpose: it labels the seam
   between groups, and the channel names under it are what is being read. The
   first one loses its top margin so the list still starts where it always did. */
.channel-group {
    margin: 1.5rem 0 0.5rem;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.channel-group:first-of-type { margin-top: 0; }

/* Standard back link (BackLink.razor): quiet, top-left, above the page title. */
.back-link { margin-bottom: 0.75rem; }
.back-link a {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.875rem;
    color: var(--text-muted);
    text-decoration: none;
}
.back-link a:hover,
.back-link a:focus-visible { color: var(--brand); text-decoration: underline; }

.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.muted { color: var(--text-muted); font-size: 0.875rem; }

/* ---- collapsible find control (SearchBox) ---- */
/* Sits inside .page-header: collapsed it is a quiet icon button at title
   level; expanded it stretches and keeps its controls right-aligned. */
.search-box { display: flex; align-items: center; gap: 0.5rem; }
.search-box-expanded { flex: 1 1 auto; justify-content: flex-end; }
.search-box-toggle {
    border: 0;
    background: transparent;
    color: var(--heading);
    font-size: 1.25rem;
    line-height: 1;
    padding: 0.35rem 0.5rem;
    border-radius: 0.375rem;
    cursor: pointer;
}
.search-box-toggle:hover,
.search-box-toggle:focus-visible { color: var(--brand); background: var(--brand-soft); }

/* Borderless icon toggle at page-title level (e.g. the starred-channels filter);
   lit in the accent colour while the filter is on, matching the channel stars. */
.filter-toggle {
    border: 0;
    background: transparent;
    color: var(--heading);
    font-size: 1.25rem;
    line-height: 1;
    padding: 0.35rem 0.5rem;
    border-radius: 0.375rem;
    cursor: pointer;
}
.filter-toggle:hover,
.filter-toggle:focus-visible { color: var(--brand); background: var(--brand-soft); }
.filter-toggle.is-active { color: var(--accent); }
.search-box-input { flex: 0 1 22rem; min-width: 8rem; }

/* ---- in-channel message search: highlighted hits + hit navigation ---- */
/* One floating cluster holding the search box, hit counter and chevrons.
   Sticky top-right: a chat page rests at the bottom of its scroll container,
   so every search control must stay reachable at any scroll depth. */
.channel-search {
    position: sticky;
    top: 0.5rem;
    z-index: 10;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    width: max-content;
    max-width: 100%;
    margin-left: auto;
    margin-bottom: 0.75rem;
    padding: 0.3rem 0.6rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
/* Idle (just the magnifier): shrink to a quiet compact pill. */
.channel-search:has(.search-box-toggle) { padding: 0.1rem; }
.channel-search-count { font-variant-numeric: tabular-nums; white-space: nowrap; }
mark.search-hit { background: #f5d76e; color: #1b2430; padding: 0 0.06em; border-radius: 2px; }
html[data-theme="dark"] mark.search-hit { background: #8a6d1d; color: #fff; }
/* The hit currently navigated to: accent its bubble and its occurrences. */
.message.search-current { outline: 2px solid var(--accent); outline-offset: 2px; }
.message.search-current mark.search-hit { background: var(--accent); color: #fff; }

/* ---- appearance picker (admin) ---- */
.choice-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.75rem;
}

.choice {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-muted);
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.choice:hover { border-color: var(--brand); }
.choice.selected { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand); }
.choice input { position: absolute; opacity: 0; pointer-events: none; }
.choice-head { font-weight: 600; color: var(--heading); display: flex; align-items: center; gap: 0.5rem; }
.choice-hint { font-size: 0.82rem; color: var(--text-muted); }

/* Home-page widget picker: an orderable list of enable/disable rows. */
.widget-order-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.widget-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-muted);
}
.widget-row.disabled { opacity: 0.6; }
.widget-toggle { display: flex; align-items: flex-start; gap: 0.6rem; margin: 0; flex: 1 1 auto; cursor: pointer; }
.widget-toggle input { margin-top: 0.2rem; flex: 0 0 auto; }
.widget-text { display: flex; flex-direction: column; gap: 0.2rem; }
.widget-move { display: flex; gap: 0.3rem; flex: 0 0 auto; }

/* Colour-scheme preview chips in the appearance picker. */
.swatches { display: inline-flex; border-radius: 999px; overflow: hidden; box-shadow: 0 0 0 1px var(--border); }
.swatch { width: 1.1rem; height: 1.1rem; }

.badge-status {
    display: inline-block;
    padding: 0.2rem 0.65rem;
    border-radius: 1rem;
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
}

/* ---- incident status filter chips ---- */
/* Same shape as the status badges: a chip that is off is outlined and muted;
   switched on it wears the matching badge colours (added as badge-status +
   status-* classes on the button). The border stays on both states so the
   chip doesn't change size when toggled. */
.status-filters { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; }
.status-filter {
    display: inline-block;
    padding: 0.2rem 0.65rem;
    border: 1px solid var(--border);
    border-radius: 1rem;
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.status-filter.badge-status { border-color: transparent; }

/* ---- status pill as a picker (incident list) ---- */
/* The pill keeps its shape and colour; the caret is the whole of the invitation,
   and it only appears for somebody who may actually set the status. */
.status-picker { position: relative; display: inline-block; }
.status-picker-toggle {
    border: 0;
    cursor: pointer;
    font: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
.status-picker-toggle .bi { font-size: 0.7em; opacity: 0.7; }
.status-picker-toggle:hover { filter: brightness(0.95); }

/* Sits over the page so the click that closes the menu is spent on closing it,
   rather than opening the incident the pill sits on. */
.menu-backdrop { position: fixed; inset: 0; z-index: 40; }

.status-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 0.3rem);
    z-index: 50;
    padding: 0.3rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.6rem;
    box-shadow: 0 8px 24px var(--shadow);
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 10rem;
}
.status-menu-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.3rem 0.4rem;
    border: 0;
    border-radius: 0.4rem;
    background: none;
    cursor: pointer;
    color: var(--text);
    text-align: start;
}
.status-menu-item:hover:not(:disabled) { background: var(--brand-soft); }
/* The one it already is: shown with a tick, and not offered as a change. */
.status-menu-item:disabled { cursor: default; opacity: 1; }
/* On phones the chips leave the title row and form their own row below it;
   on wider screens they take the stretch between the title and the search
   controls and center themselves in it. */
@media (max-width: 575.98px) {
    .page-header .status-filters { order: 3; flex-basis: 100%; }
}
@media (min-width: 576px) {
    .page-header .status-filters { flex: 1 1 auto; justify-content: center; }
}
/* The audience chips reuse the filter chips' shape, but they belong beside the
   thing they describe: at the end of a page header (where the status badge sits),
   and inline everywhere else — never stretched across the row like a filter. */
.page-header .status-filters.scope-toggles { flex: 0 0 auto; margin-left: auto; }
.scope-toggles { gap: 0.3rem; }

.badge-status.status-new { background: #fdecea; color: #b3261e; }
.badge-status.status-reported { background: #e2ecfb; color: #2456a8; }
.badge-status.status-inprogress { background: #fff4e0; color: #9a6a00; }
.badge-status.status-resolved { background: #e6f6ed; color: #1c7d4d; }
.badge-status.status-closed { background: #edf0f3; color: #5d6b7a; }
.badge-status.badge-private { background: #ece9fb; color: #5b4bb7; }
.badge-status.badge-public { background: #e8f1f8; color: #11507c; }
.badge-status.badge-announcement { background: #fff4e0; color: #9a6a00; }
/* Deliberately grey: an inactive channel is neither a type nor a warning, just parked. */
.badge-status.badge-inactive { background: #ececec; color: #666; }
/* An audience an incident (or one comment) is open to. Teal: it is not a status
   and not a warning, it is who is in the room. */
.badge-status.badge-scope { background: #e2f2f0; color: #14635c; }

/* An incident the owners at large cannot read. Outlined where every status pill
   beside it is filled, so the two cannot be confused at a glance, and red
   because it marks something withheld. The crossed-out eye carries the same
   meaning without the colour. */
.badge-status.badge-restricted {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: transparent;
    border: 1px solid currentColor;
    color: #b3123a;
}
/* The same signal in the grid, where a chip on every row would be noise. */
.incident-hidden-icon { color: #b3123a; }

html[data-theme="dark"] .badge-status.status-new { background: #3a1b18; color: #f5a99f; }
html[data-theme="dark"] .badge-status.status-reported { background: #16283f; color: #8ab4ea; }
html[data-theme="dark"] .badge-status.status-inprogress { background: #3a2c10; color: #f0c070; }
html[data-theme="dark"] .badge-status.status-resolved { background: #123524; color: #7ddca7; }
html[data-theme="dark"] .badge-status.status-closed { background: #20262d; color: #9aa7b5; }
html[data-theme="dark"] .badge-status.badge-private { background: #211c3a; color: #b3a9ee; }
html[data-theme="dark"] .badge-status.badge-public { background: #102536; color: #7fbce6; }
html[data-theme="dark"] .badge-status.badge-announcement { background: #3a2c10; color: #f0c070; }
html[data-theme="dark"] .badge-status.badge-inactive { background: #2a2f36; color: #9aa4af; }
html[data-theme="dark"] .badge-status.badge-scope { background: #10302d; color: #7fcfc6; }
/* Kept outlined in the dark theme too; only the red is lifted to stay legible. */
html[data-theme="dark"] .badge-status.badge-restricted { background: transparent; color: #f08fa4; }
html[data-theme="dark"] .incident-hidden-icon { color: #f08fa4; }

/* ---- incident card/grid view switch ---- */
.view-toggle { border: 1px solid var(--border); border-radius: 0.5rem; overflow: hidden; }
.view-toggle-btn {
    border: none;
    background: transparent;
    color: var(--text-muted);
    padding: 0.35rem 0.6rem;
    line-height: 1;
    cursor: pointer;
}
.view-toggle-btn.active { background: var(--brand-soft); color: var(--brand-dark); }
.view-toggle-btn + .view-toggle-btn { border-left: 1px solid var(--border); }

/* The day an incident is expected to be settled. Inherits the surrounding muted
   text until the day has passed with the incident still open, which is the only
   case worth colouring — so an overview of incidents that are all on track stays
   quiet. The red matches the "new" status badge rather than inventing a shade. */
.expected-end { white-space: nowrap; }
.expected-end.is-overdue { color: #b3261e; font-weight: 600; }
html[data-theme="dark"] .expected-end.is-overdue { color: #f5a99f; }

/* Incident-detail callout for the same date: the "contacts widget" tinted-card
   treatment (soft brand background, accent left bar, icon badge) so a promised
   date reads as something worth noticing on its own, not a clause in a meta line.
   Switches to the same red as .expected-end.is-overdue once the day has passed. */
.expected-end-card {
    display: flex;
    align-items: center;
    gap: 1rem;
    background: var(--brand-soft);
    border-color: transparent;
    border-left: 4px solid var(--accent);
}
.expected-end-card-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 1.4rem;
    box-shadow: 0 2px 6px var(--shadow);
}
.expected-end-card-body { display: flex; flex-direction: column; gap: 0.1rem; }
.expected-end-card-title { font-weight: 700; color: var(--heading); }
.expected-end-card-date { font-size: 1.05rem; }

.expected-end-card.is-overdue { border-left-color: #b3261e; background: rgba(179, 38, 30, 0.08); }
.expected-end-card.is-overdue .expected-end-card-icon { background: #b3261e; }
.expected-end-card.is-overdue .expected-end-card-title { color: #b3261e; }
html[data-theme="dark"] .expected-end-card.is-overdue { background: rgba(245, 169, 159, 0.12); }
html[data-theme="dark"] .expected-end-card.is-overdue .expected-end-card-icon { background: #f5a99f; color: #3a0e0a; }
html[data-theme="dark"] .expected-end-card.is-overdue .expected-end-card-title { color: #f5a99f; }

/* ---- incident grid view (sortable, paged table) ---- */
/* The card panel supplies the frame; the table scrolls sideways inside it if
   a narrow window makes the columns too tight. */
/* The table scrolls sideways only where it has to. A scroll container clips both
   axes, and the status menu on the last row would open into the clip — so on the
   wide screens this view is meant for (it is hidden on phones) nothing clips. */
.incident-grid { overflow-x: auto; padding: 0.5rem 0; }
@media (min-width: 992px) {
    .incident-grid { overflow: visible; }
}
.incident-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.incident-table th, .incident-table td {
    padding: 0.55rem 1rem;
    text-align: left;
    vertical-align: middle;
    border-bottom: 1px solid var(--border);
}
.incident-table tbody tr:last-child td { border-bottom: none; }
.incident-table tbody tr { cursor: pointer; }
.incident-table tbody tr:hover { background: var(--surface-muted); }
/* Header cells are whole-cell click targets for sorting. */
.incident-table th { padding: 0; white-space: nowrap; }
.incident-sort {
    display: block;
    width: 100%;
    padding: 0.55rem 1rem;
    border: none;
    background: transparent;
    text-align: left;
    font-weight: 600;
    color: var(--heading);
    cursor: pointer;
}
.incident-sort .bi { font-size: 0.7rem; color: var(--brand); }
/* Borderless header button for sortable columns in plain tables (the incident
   grid has its own whole-cell variant above). */
.th-sort { border: none; background: transparent; padding: 0; font-weight: 600; color: var(--heading); cursor: pointer; }
.th-sort .bi { font-size: 0.7rem; color: var(--brand); }
.incident-grid-link { color: var(--text); text-decoration: none; font-weight: 600; }
.incident-grid-link:hover { text-decoration: underline; }
/* The reference number people quote when talking about an incident. */
.incident-id { color: var(--text-muted); font-weight: 600; }

/* ---- admin diagnostics page ---- */
/* Rows of small stat tiles, same look as the pool history stats. */
.diag-stats { display: flex; flex-wrap: wrap; gap: 0.75rem 2.25rem; margin: 0.5rem 0 0; }
.diag-stat dt { font-size: 0.78rem; font-weight: 400; color: var(--text-muted); margin: 0; }
.diag-stat dd { font-size: 1.25rem; font-weight: 700; margin: 0; }
.diag-sub { display: block; font-size: 0.78rem; font-weight: 400; color: var(--text-muted); }
/* Translation credit consumption as a thin fill bar under the number. */
.diag-bar { width: 220px; max-width: 100%; height: 6px; border-radius: 3px; background: var(--surface-muted); margin-top: 0.35rem; overflow: hidden; }
.diag-bar-fill { height: 100%; border-radius: 3px; background: var(--brand); }
.diag-bar-fill.diag-bar-high { background: var(--accent); }

/* ---- language selector ---- */
.lang-menu { position: relative; }

.lang-menu > summary {
    list-style: none;
    cursor: pointer;
    color: var(--heading);
    white-space: nowrap;
    padding: 0.3rem 0.5rem;
    border-radius: 0.5rem;
}

.lang-menu > summary::-webkit-details-marker { display: none; }
.lang-menu > summary:hover { background: var(--brand-soft); }

.lang-menu[open] > ul {
    position: absolute;
    right: 0;
    top: 2.2rem;
    z-index: 50;
    margin: 0;
    padding: 0.4rem;
    list-style: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.6rem;
    box-shadow: 0 8px 24px var(--shadow);
    min-width: 11rem;
    max-height: 70vh;
    overflow-y: auto;
}

.lang-menu li a {
    display: block;
    padding: 0.45rem 0.8rem;
    border-radius: 0.4rem;
    text-decoration: none;
    color: var(--text);
}

.lang-menu li a:hover { background: var(--brand-soft); }
.lang-menu li a.active { font-weight: 700; color: var(--brand); }

/* ---- weather widget ---- */
.weather-strip {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(5.5rem, 1fr);
    gap: 0.5rem;
    overflow-x: auto;
    padding-bottom: 0.25rem;
}

.weather-day {
    text-align: center;
    background: var(--surface-muted);
    border-radius: 0.6rem;
    padding: 0.6rem 0.3rem;
}

.weather-day .day-name { font-size: 0.78rem; font-weight: 600; color: var(--text-muted); text-transform: capitalize; }
.weather-day .day-icon { font-size: 1.6rem; line-height: 2.2rem; color: var(--accent); }
.weather-day .day-temp { font-size: 0.85rem; font-weight: 600; }
.weather-day .day-detail { font-size: 0.72rem; color: var(--text-muted); }

/* The forecast links out to a detailed external forecast. */
.weather-link { display: block; text-decoration: none; color: inherit; border-radius: 0.6rem; }
.weather-link:hover .weather-day { background: var(--surface); outline: 1px solid var(--border); }
.weather-link:hover .weather-more { color: var(--brand); text-decoration: underline; }
.weather-more {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.5rem;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-muted);
}

/* ---- half-width home widgets ----
   Adjacent half-width widgets share an invisible flex row so they sit side by
   side AND stretch to the same height (align-items: stretch). The row has no
   box of its own, so the left card aligns flush-left and the right card
   flush-right with the full-width widget cards. Each column's card fills the
   stretched height. Collapses to a single column on narrow screens. */
.home-widget-row {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 1rem;
    margin-bottom: 1rem;
}
.home-widget-col {
    display: flex;
    flex-direction: column;
    flex: 1 1 calc(50% - 0.5rem);
    min-width: 0;
}
/* The widget's card fills its (equal-height) column. */
.home-widget-col > .card-panel { flex: 1 1 auto; margin-bottom: 0; }
/* Half-width widgets stay side by side on mobile; to fit the narrow cards we
   hide the secondary readings there (see the widget rules further down). */

/* ---- today-only weather widget ---- */
.weather-today { display: flex; align-items: center; gap: 1rem; }
.weather-today-icon { font-size: 3rem; line-height: 1; color: var(--accent); }
.weather-today-date { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); text-transform: capitalize; }
/* Mobile-only compact date (calendar icon + short date); hidden on wide screens. */
.weather-today-date-short { display: none; }
.weather-today-kind { font-size: 0.95rem; font-weight: 600; }
.weather-today-temp { font-size: 1.4rem; font-weight: 700; }
.weather-today-feels { font-size: 0.78rem; color: var(--text-muted); margin-top: 0.05rem; }
.weather-today-meta { display: flex; flex-wrap: wrap; gap: 0.15rem 0.85rem; font-size: 0.74rem; color: var(--text-muted); margin-top: 0.15rem; }
.weather-link:hover .weather-today-icon { color: var(--brand); }

/* ---- pool widget ---- */
.pool-widget { position: relative; }
/* Whole card taps through to the measurement-history graphs. */
.pool-link { display: block; text-decoration: none; color: inherit; }
.pool-link:hover .pool-icon { color: var(--brand); }
.pool-link:hover .pool-temp { color: var(--brand); }
/* Manager edit affordance, layered above the card link in the top-right corner. */
.pool-edit-btn { position: absolute; top: 1.25rem; right: 1.25rem; z-index: 1; }
.pool-widget-head { display: flex; align-items: center; gap: 0.5rem; padding-right: 2rem; }
.pool-widget-head h2 { margin: 0; }
.pool-body { display: flex; align-items: center; gap: 1rem; margin-top: 0.5rem; }
.pool-icon { font-size: 3.2rem; line-height: 1; color: var(--accent); flex: 0 0 auto; }
.pool-readings { flex: 1 1 auto; min-width: 0; }
/* Water temperature matches the weather widget's temperature size. */
.pool-temp { font-size: 1.4rem; font-weight: 700; line-height: 1.2; }
.pool-temp .pool-unit { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); margin-left: 0.15rem; }
.pool-temp-empty { color: var(--text-muted); font-weight: 400; }
.pool-stats { display: flex; flex-wrap: wrap; gap: 0.1rem 1rem; margin: 0.35rem 0 0; }
.pool-stat { display: flex; align-items: baseline; gap: 0.3rem; }
.pool-stat dt { font-size: 0.75rem; color: var(--text-muted); margin: 0; }
.pool-stat dd { font-size: 0.85rem; font-weight: 600; margin: 0; }
/* Shown above the temperature, styled like the weather widget's date line. */
.pool-date { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); margin-bottom: 0.1rem; }
.pool-date-value { text-transform: capitalize; }
/* Mobile-only compact bits (calendar-icon date + one-line stats); hidden wide. */
.pool-date-short { display: none; }
.pool-stats-compact { display: none; }

/* Full title on wide screens; a one-word title on the narrow mobile cards. */
.widget-title-short { display: none; }

/* The half-width cards stay side by side on mobile, so they are narrow: show
   only the essentials (date + temperature) and tighten the icon/spacing. */
@media (max-width: 640.98px) {
    .weather-today-kind,
    .weather-today-feels,
    .weather-today-meta,
    .pool-stats,
    .widget-title-full { display: none; }
    .widget-title-short { display: inline; }
    .weather-today-icon, .pool-icon { font-size: 2.4rem; }
    /* Both cards: keep the icon on the left with date + value at its right;
       the date shrinks to a calendar icon + short date. The weather min
       temperature drops to a smaller regular weight next to the bold max. */
    .weather-today, .pool-body { gap: 0.6rem; }
    .weather-today-date-full, .pool-date-full { display: none; }
    .weather-today-date-short, .pool-date-short { display: inline; }
    .weather-today-temp-min { font-size: 0.95rem; font-weight: 400; }
    /* Pool card: the readings return as one small line under the icon row. */
    .pool-stats-compact {
        display: block;
        font-size: 0.74rem;
        color: var(--text-muted);
        margin-top: 0.4rem;
    }
}

/* History page: the last measured values as a row of small stat tiles. */
.pool-latest { display: flex; flex-wrap: wrap; gap: 0.75rem 2.25rem; margin: 0.5rem 0 0; }
.pool-latest-stat dt { font-size: 0.78rem; font-weight: 400; color: var(--text-muted); margin: 0; }
.pool-latest-stat dd { font-size: 1.25rem; font-weight: 700; margin: 0; }
.pool-latest-unit { font-size: 0.8rem; font-weight: 400; color: var(--text-muted); }

/* Manage page: keep the reading columns compact. */
.pool-table td, .pool-table th { white-space: nowrap; }

/* ---- pool measurement charts (inline SVG line graphs) ---- */
.pool-chart { margin-top: 1.25rem; }
.pool-chart:first-of-type { margin-top: 0.5rem; }
.pool-chart-title { font-weight: 600; margin-bottom: 0.25rem; }
.pool-chart-unit { color: var(--text-muted); font-weight: 400; font-size: 0.85em; }
.pool-chart-svg { width: 100%; height: auto; display: block; overflow: visible; }
.pool-chart-svg .pc-grid { stroke: var(--border); stroke-width: 2; }
.pool-chart-svg .pc-grid-minor { stroke: var(--border); stroke-width: 1; stroke-opacity: 0.7; }
.pool-chart-svg .pc-line { stroke: var(--accent); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.pool-chart-svg .pc-dot { fill: var(--accent); }
.pool-chart-svg .pc-ylabel,
.pool-chart-svg .pc-xlabel { fill: var(--text-muted); font-size: 12px; }

/* ---- weather detail: one row per day (date · icon · temps · stats · wind) ---- */
.weather-daylist { display: flex; flex-direction: column; margin-top: 0.5rem; }
.wdl-row {
    display: grid;
    /* date | icon | temps | sun/precip stats (flexible) | wind */
    grid-template-columns: 3.6rem 2.6rem 3.6rem 1fr auto;
    align-items: center;
    gap: 0.9rem;
    padding: 0.55rem 0.35rem;
    border-top: 1px solid var(--border);
}
.weather-daylist .wdl-row:first-child { border-top: 0; }
.wdl-row.is-today { background: var(--surface); border-radius: 0.5rem; }

.wdl-date { display: flex; flex-direction: column; line-height: 1.25; }
.wdl-dow { font-weight: 700; color: var(--brand); text-transform: capitalize; }
.wdl-dom { font-size: 0.8rem; color: var(--text-muted); }

.wdl-icon { font-size: 1.7rem; line-height: 1; color: var(--accent); text-align: center; }

.wdl-temps { display: flex; flex-direction: column; line-height: 1.25; text-align: right; }
.wdl-tmin { font-size: 0.85rem; color: var(--text-muted); }
.wdl-tmax { font-size: 1.05rem; font-weight: 700; }

/* Wide screens: the three stats sit side by side; a min-width per stat keeps
   them vertically aligned from row to row. They stack on narrow screens below. */
.wdl-stats { display: flex; align-items: center; gap: 0.1rem 1.1rem; font-size: 0.78rem; color: var(--text-muted); line-height: 1.3; }
.wdl-stats span { white-space: nowrap; min-width: 4.5rem; }
.wdl-stats i { width: 1rem; display: inline-block; text-align: center; }

.wdl-wind { display: inline-flex; align-items: center; gap: 0.4rem; justify-self: end; color: var(--text-muted); }
.wdl-wind-value { font-weight: 600; color: var(--text); }
.wdl-unit { font-weight: 400; font-size: 0.78rem; color: var(--text-muted); }

@media (max-width: 930.98px) {
    .wdl-stats { flex-direction: column; align-items: flex-start; font-size: 0.74rem; }
    .wdl-stats span { min-width: 0; }
}
@media (max-width: 480.98px) {
    .wdl-row { grid-template-columns: 3.1rem 2.2rem 3.2rem 1fr auto; gap: 0.6rem; }
    .wdl-icon { font-size: 1.5rem; }
}

/* ---- weather detail page ---- */
/* Current conditions */
.weather-current { display: flex; flex-wrap: wrap; align-items: center; gap: 1.5rem 2.5rem; }
.wc-main { display: flex; align-items: center; gap: 1rem; }
.wc-icon { font-size: 3.4rem; color: var(--accent); line-height: 1; }
.wc-temp { font-size: 2.6rem; font-weight: 700; line-height: 1; }
.wc-desc { color: var(--text-muted); }
.wc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: 0.75rem 1.5rem; margin: 0; flex: 1; min-width: 14rem; }
.wc-stats > div { display: flex; flex-direction: column; }
.wc-stats dt { font-size: 0.75rem; color: var(--text-muted); }
.wc-stats dd { margin: 0; font-weight: 600; }

/* ---- weather charts page (inline SVG line/bar graphs over time) ---- */
.weather-charts-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.weather-charts-inner { min-width: 100%; }
.weather-chart-block { margin-top: 1.5rem; }
.weather-chart-block:first-of-type { margin-top: 0.75rem; }
.weather-chart-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 1rem; margin-bottom: 0.35rem; }
.weather-chart-title { font-weight: 600; }
.weather-chart-unit { color: var(--text-muted); font-weight: 400; font-size: 0.85em; }
.weather-chart-legend { display: flex; flex-wrap: wrap; gap: 0.35rem 0.9rem; font-size: 0.85rem; color: var(--text-muted); }
.weather-chart-legend-item { display: inline-flex; align-items: center; gap: 0.35rem; }
.weather-chart-swatch { width: 0.85rem; height: 0.2rem; border-radius: 1px; }
.weather-chart-swatch.wc-s-temp { background: #e2674a; }
.weather-chart-swatch.wc-s-feels { background: #4a90d9; }
.weather-chart-swatch.wc-s-wind { background: #2fa39a; }
.weather-chart-swatch.wc-s-precip { background: #5b9bd5; }
.weather-chart-svg { width: 100%; height: auto; display: block; overflow: visible; }
.weather-chart-svg .wc-grid { stroke: var(--border); stroke-width: 1; }
.weather-chart-svg .wc-day { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 3; }
.weather-chart-svg .wc-ylabel,
.weather-chart-svg .wc-xlabel { fill: var(--text-muted); font-size: 12px; }
.weather-chart-svg .wc-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.weather-chart-svg .wc-line.wc-s-temp { stroke: #e2674a; }
.weather-chart-svg .wc-line.wc-s-feels { stroke: #4a90d9; }
.weather-chart-svg .wc-line.wc-s-wind { stroke: #2fa39a; }
.weather-chart-svg .wc-bar.wc-s-precip { fill: #5b9bd5; }
@media (max-width: 640.98px) {
    /* Charts are ~2.5x the screen width and pan horizontally (about 3 days
       visible at a time); the shared container keeps the three time axes in
       sync while swiping. Titles/legends stick to the left edge. */
    .weather-charts-inner { min-width: 250%; }
    /* Must fit inside the visible scrollport, or the sticky head runs out of
       room in its containing block at full scroll and gets clipped. */
    .weather-chart-head { position: sticky; left: 0; width: max-content; max-width: 75vw; }
}

/* ---- contacts widget (compact, links to the contacts page) ---- */
/* A prominent "callout" card so important numbers stand out from news cards:
   tinted background, an accent left bar, and a large accent icon badge. */
.contacts-widget {
    background: var(--brand-soft);
    border-color: transparent;
    border-left: 4px solid var(--accent);
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.contacts-widget:hover { transform: translateY(-2px); box-shadow: 0 6px 18px var(--shadow); }
.contacts-link { display: flex; align-items: center; gap: 1rem; text-decoration: none; color: inherit; }
.contacts-widget-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 1.6rem;
    box-shadow: 0 2px 6px var(--shadow);
}
.contacts-widget-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0.15rem; }
.contacts-widget-title { font-weight: 700; font-size: 1.05rem; color: var(--heading); }
.contacts-widget-sub { font-size: 0.85rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.contacts-widget-arrow { flex: 0 0 auto; font-size: 1.3rem; color: var(--accent); transition: transform 0.15s ease; }
.contacts-link:hover .contacts-widget-arrow { transform: translateX(3px); }

/* ---- contacts page ---- */
.contacts-list { display: flex; flex-direction: column; gap: 0.6rem; }
.contact-item { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem; }
.contact-label { font-weight: 600; color: var(--heading); }
/* Methods stack vertically so each can carry its own note on the same line. */
.contact-methods { display: flex; flex-direction: column; gap: 0.35rem; margin-top: 0.3rem; }
.contact-method-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; }
.contact-method { display: inline-flex; align-items: center; gap: 0.4rem; text-decoration: none; color: var(--brand); }
.contact-method:hover { text-decoration: underline; }
.contact-method-note { color: var(--text-muted); font-size: 0.85rem; }
.contact-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }

/* ---- forum ---- */
/* Clickable channel card (navigates on click; inline translate/edit buttons
   stop the click so they don't navigate). */
.channel-card { cursor: pointer; }
.channel-card h2 { color: var(--brand); }
.channel-card:hover { border-color: var(--brand); }

/* The incident card is clickable the same way, and for the same reason: the
   status pill on it is a button. Its title stays an anchor, so an incident can
   still be opened in a tab of its own. */
.incident-card { cursor: pointer; }
.incident-card:hover { border-color: var(--brand); }
.incident-card-link { color: var(--brand); text-decoration: none; }
.incident-card-link:hover { text-decoration: underline; }

/* Last thing said, under a name in the direct-messaging list: one line, cut off
   where it runs out of room — the conversation itself is one tap away. */
.dm-snippet {
    display: flex;
    gap: 0.25rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dm-snippet > * { overflow: hidden; text-overflow: ellipsis; }

/* Member autocomplete dropdown for private channels. */
.member-suggestions { z-index: 1050; max-height: 16rem; overflow-y: auto; }

/* Favourite star toggle on a channel card. */
.favorite-btn {
    border: 0;
    background: none;
    padding: 0 0.15rem;
    line-height: 1;
    font-size: 1.05rem;
    color: var(--text-muted);
    cursor: pointer;
}
.favorite-btn:hover { color: var(--accent); }
.favorite-btn.is-favorite { color: var(--accent); }
/* The bell (subscribe) uses the brand colour when active, to read apart from the gold star. */
.subscribe-btn:hover { color: var(--brand); }
.subscribe-btn.is-subscribed { color: var(--brand); }

.message-list { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1rem; }
/* Top-of-list marker whose visibility triggers loading the previous page. */
.forum-load-older { min-height: 1px; align-self: center; padding: 0.25rem 0; font-size: 0.85rem; }

/* Chat bubbles: other owners on the left, my own messages on the right. */
.message {
    position: relative;   /* anchors the side add-reaction control */
    max-width: min(78%, 42rem);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    border-bottom-left-radius: 0.2rem;   /* little "tail" corner toward the left edge */
    padding: 0.55rem 0.85rem;
    align-self: flex-start;
}

.message.own {
    align-self: flex-end;
    background: var(--brand-soft);
    border-color: color-mix(in srgb, var(--brand) 30%, transparent);
    border-bottom-left-radius: var(--radius);
    border-bottom-right-radius: 0.2rem;  /* tail flips to the right edge */
}

/* ---- incident comments ---- */
/* The same cards, but not a chat. An incident is one thread everybody is writing
   into rather than a conversation between two sides, and each comment carries an
   audience the board can change — so they run the full width of the page, like
   the composer under them, and nothing is aligned by who wrote it. The forum
   keeps its bubbles. */
.incident-comments .message {
    max-width: none;
    align-self: stretch;
    border-bottom-left-radius: var(--radius);
}

/* Wraps: on a narrow screen the audience chips would otherwise push the author's
   name out of its own bubble. */
.message-meta { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: baseline; margin-bottom: 0.2rem; }
.message.own .message-meta { justify-content: flex-end; }
.message-author { font-weight: 700; color: var(--heading); font-size: 0.9rem; }

/* Edit/delete controls on the author's own message, pushed to the right. */
.message-actions { margin-left: auto; display: inline-flex; gap: 0.25rem; }
.msg-action-btn {
    border: 0;
    background: none;
    padding: 0 0.15rem;
    line-height: 1;
    font-size: 0.85rem;
    color: var(--text-muted);
    cursor: pointer;
}
.msg-action-btn:hover { color: var(--brand); }
.message-actions.delete-confirm { align-items: center; }
.message-actions .confirm-text { font-size: 0.85rem; color: var(--text-muted); }

/* Web addresses inside user text (forum messages), rendered as clickable
   links. break-all keeps long URLs from overflowing the chat bubble. */
.text-link { color: var(--accent); text-decoration: underline; word-break: break-all; }

/* @mentions of members inside a message; your own mention pops out more. */
.mention { color: var(--accent); font-weight: 600; }
.mention-self {
    background: color-mix(in srgb, var(--accent) 18%, transparent);
    border-radius: 0.25rem;
    padding: 0 0.15rem;
}

/* **bold** an author typed — in a message, a notice, a poll, an enquiry, wherever
   the markup renderer put it — is shown in the theme accent colour, matching how
   bold renders in news posts (.blog-content strong); *italic* and __underline__
   keep the default treatment. The class is what separates it from the <strong>
   the pages use for their own labels, and it is only ever on rendered text, never
   in the composer. */
.text-bold { color: var(--accent); }

/* The bubble whose text is currently loaded in the composer for editing. */
.message.editing { border: 1px dashed var(--accent); }

/* "Editing message" banner above the composer input, with its cancel button. */
.composer-editing {
    display: flex;
    gap: 0.4rem;
    align-items: center;
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-bottom: 0.35rem;
}

/* The edited message's existing photos, as chips with a remove/restore toggle. */
.edit-photos { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.35rem; }
.edit-photo-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.15rem 0.5rem;
    font-size: 0.82rem;
    border: 1px solid var(--border);
    border-radius: 1rem;
    max-width: 100%;
}
.edit-photo-chip .edit-photo-name {
    max-width: 11rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.edit-photo-chip.removed { opacity: 0.55; }
.edit-photo-chip.removed .edit-photo-name { text-decoration: line-through; }

/* Member typeahead (admin settings): type to filter, suggestions open below. */
.member-typeahead { position: relative; max-width: 24rem; }
.member-typeahead-list {
    position: absolute;
    top: calc(100% + 0.25rem);
    left: 0;
    right: 0;
    max-height: 14rem;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
    z-index: 60;
}
.member-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.35rem; }
.member-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.15rem 0.5rem;
    font-size: 0.9rem;
    border: 1px solid var(--border);
    border-radius: 1rem;
}

/* The member picker shown while typing "@name" in the composer. */
.composer { position: relative; }
.mention-picker {
    position: absolute;
    bottom: calc(100% + 0.25rem);
    left: 0.75rem;
    min-width: 14rem;
    max-width: calc(100% - 1.5rem);
    max-height: 14rem;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
    z-index: 60;
}
.mention-choice {
    border: 0;
    background: none;
    text-align: left;
    padding: 0.45rem 0.75rem;
    cursor: pointer;
    color: inherit;
}
.mention-choice:hover, .mention-choice:focus-visible { background: var(--brand-soft); }

/* A message flagged as inappropriate: visible only to its author (with the
   notice below) and to moderators. */
.message.flagged { border: 1px dashed #b3261e; }
.flag-notice {
    display: flex;
    gap: 0.4rem;
    align-items: baseline;
    color: #b3261e;
    font-size: 0.82rem;
    margin-bottom: 0.3rem;
}
.flag-notice .flag-reason { font-style: italic; }
.flag-form { margin-bottom: 0.4rem; }

/* "X is typing…" indicator above the composer. Kept in the DOM (aria-live) and
   hidden when nobody is typing, so the height doesn't jump. */
.typing-indicator {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 1.2rem;
    margin: 0.1rem 0.2rem 0.4rem;
    font-size: 0.85rem;
}
.typing-indicator.is-hidden { visibility: hidden; }
.typing-dots { display: inline-flex; gap: 0.18rem; }
.typing-dots span {
    width: 0.32rem;
    height: 0.32rem;
    border-radius: 50%;
    background: var(--text-muted);
    animation: typing-bounce 1.2s infinite ease-in-out both;
}
.typing-dots span:nth-child(2) { animation-delay: 0.15s; }
.typing-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes typing-bounce {
    0%, 80%, 100% { opacity: 0.3; transform: translateY(0); }
    40% { opacity: 1; transform: translateY(-0.15rem); }
}

/* Attachments shown with a message/incident: compact thumbnails above the text. */
.message-photos { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.4rem; }
.message.own .message-photos { justify-content: flex-end; }

/* Emoji reactions at the bottom of a message */
.message-reactions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; margin-top: 0.4rem; }
.message.own .message-reactions { justify-content: flex-end; }
.reaction-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.05rem 0.45rem;
    border: 1px solid var(--border);
    border-radius: 1rem;
    background: var(--surface-muted);
    font-size: 0.85rem;
    line-height: 1.5;
    cursor: pointer;
    /* Long-press reveals who reacted; suppress the native selection/callout. */
    -webkit-touch-callout: none;
    user-select: none;
}
.reaction-pill:hover { border-color: var(--brand); }
.reaction-pill.mine { background: var(--brand-soft); border-color: var(--brand); }
.reaction-count { font-size: 0.75rem; font-weight: 600; color: var(--text-muted); }
.reaction-pill.mine .reaction-count { color: var(--brand); }

/* Popover listing who reacted, shown on long-press (touch). Fixed + JS-clamped to the
   viewport, so it never overflows. */
.reaction-names-popover {
    position: fixed;
    z-index: 1100;
    max-width: min(16rem, calc(100vw - 1rem));
    padding: 0.4rem 0.6rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
    font-size: 0.8rem;
    line-height: 1.45;
}
.reaction-names-popover > div { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Floats just outside the bubble — to the right of others' messages, to the left of
   my own — so it doesn't add a row under the message. */
.reaction-add { position: absolute; top: 50%; transform: translateY(-50%); right: -1.75rem; display: inline-flex; z-index: 5; }
.message.own .reaction-add { right: auto; left: -1.75rem; }
.reaction-add-btn {
    border: 1px solid transparent;
    background: none;
    color: var(--text-muted);
    padding: 0.05rem 0.3rem;
    border-radius: 1rem;
    font-size: 0.9rem;
    line-height: 1.4;
    cursor: pointer;
    opacity: 0.5;
    transition: opacity 0.12s ease;
}
/* Fully visible on hover/focus or when hovering the message (still tappable on touch). */
.message:hover .reaction-add-btn,
.reaction-add-btn:hover,
.reaction-add-btn:focus-visible { opacity: 1; }
.reaction-add-btn:hover { color: var(--brand); border-color: var(--border); background: var(--surface-muted); }
.reaction-picker {
    position: absolute;
    top: calc(100% + 0.25rem);
    left: 0;               /* others: open into the right-hand gutter */
    right: auto;
    z-index: 20;
    display: flex;
    flex-wrap: wrap;       /* wrap into rows on narrow screens instead of overflowing */
    justify-content: center;
    max-width: min(14rem, 60vw);
    gap: 0.15rem;
    padding: 0.3rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.6rem;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
}
.message.own .reaction-picker { left: auto; right: 0; }  /* mine: open into the left-hand gutter */
.reaction-choice { border: 0; background: none; font-size: 1.2rem; line-height: 1; padding: 0.2rem; border-radius: 0.4rem; cursor: pointer; }
.reaction-choice:hover { background: var(--surface-muted); }

/* Image attachment thumbnail (ImageAttachmentPreviewer); opens the viewer on click. */
.attachment-thumb {
    border: 0;
    padding: 0;
    width: 104px;
    height: 104px;
    border-radius: 0.5rem;
    overflow: hidden;
    cursor: pointer;
    background: var(--surface-muted);
}
.attachment-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Fallback preview for attachments without a dedicated previewer: a download chip. */
.attachment-file {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    max-width: 16rem;
    padding: 0.4rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--surface-muted);
    color: var(--text);
    text-decoration: none;
}
.attachment-file:hover { border-color: var(--brand); }
.attachment-file-icon { font-size: 1.15rem; color: var(--text-muted); }
.attachment-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Inline video player for a video attachment. */
.attachment-video { max-width: 100%; }
.attachment-video video {
    display: block;
    max-width: min(20rem, 100%);
    max-height: 22rem;
    border-radius: 0.5rem;
    background: #000;
}

/* Message composer: icon-only add-photo button + selected-file chips. */
/* Forum composer: compact single line while idle; expands on focus and reveals
   the attach + send controls on one row. Uses :focus-within so focus moving from
   the textarea to a control (attach/send) keeps it open. */
.composer .composer-input {
    resize: none;
    height: 2.6rem;               /* one line */
    min-height: 2.6rem;
    overflow-y: hidden;
    transition: height 0.15s ease;
}
.composer:focus-within .composer-input {
    height: 7rem;
    overflow-y: auto;
}
.composer-actions { display: none; }
.composer:focus-within .composer-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
}
.composer-actions .photo-attach { margin: 0; }
.composer-actions .composer-send { margin-left: auto; }

/* The composer (MentionComposer, used by the forum composer and the incident
   forms) is a plain textarea plus an @mention dropdown; mention-host is the
   positioning context for that .mention-picker. Links and @mentions are not
   coloured while typing — they colour in the posted message via TranslatedText. */
.mention-host { position: relative; }
.mention-host textarea::placeholder { color: var(--text-muted); opacity: 1; }

/* A bubble whose text is being edited in place (incident comments): bubbles
   are shrink-to-fit, so without this the editor would be sized by the
   textarea's tiny intrinsic width instead of the bubble's. */
.message.editing-inline { width: 100%; }

/* The letter opened for tidying before it becomes a comment. Full width whatever
   the bubble was doing, because it is a form now and not a quotation. */
.promote-editor { width: 100%; }
/* "Written in" and its picker on one line: a small correction, not a section. */
.promote-language {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    font-size: 0.875rem;
}
.file-input-hidden { display: none; }
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    border: 1px solid var(--border);
    border-radius: 50%;
    color: var(--text-muted);
    cursor: pointer;
    font-size: 1rem;
    margin: 0;
}
.icon-btn:hover { color: var(--brand); border-color: var(--brand); }
.selected-photos { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-bottom: 0.5rem; }
.selected-photo {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    max-width: 14rem;
    padding: 0.2rem 0.4rem 0.2rem 0.55rem;
    border: 1px solid var(--border);
    border-radius: 1rem;
    font-size: 0.8rem;
    color: var(--text-muted);
    background: var(--surface-muted);
}
.selected-photo-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.remove-photo {
    border: 0;
    background: none;
    padding: 0;
    line-height: 1;
    color: var(--text-muted);
    cursor: pointer;
}
.remove-photo:hover { color: var(--accent); }

/* Full-screen photo viewer (modal). */
.photo-viewer {
    position: fixed;
    inset: 0;
    z-index: 1050;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.85);
    cursor: zoom-out;
}
.viewer-image {
    max-width: 92vw;
    max-height: 88vh;
    object-fit: contain;
    border-radius: 0.4rem;
    cursor: default;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
.viewer-close, .viewer-nav {
    position: absolute;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.45);
    color: #fff;
    cursor: pointer;
}
.viewer-close { top: 1rem; right: 1rem; width: 2.6rem; height: 2.6rem; font-size: 1.1rem; }
.viewer-nav { top: 50%; transform: translateY(-50%); width: 3rem; height: 3rem; font-size: 1.4rem; }
.viewer-nav.prev { left: 1rem; }
.viewer-nav.next { right: 1rem; }
.viewer-close:hover, .viewer-nav:hover { background: rgba(0, 0, 0, 0.75); }

/* unread forum message (always from another owner): accent left edge + a "New" pill */
.message.unread {
    border-left: 3px solid var(--accent);
}
.unread-pill {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #fff;
    background: var(--brand);
    border-radius: 1rem;
    padding: 0.05rem 0.5rem;
}
.unread-badge {
    display: inline-block;
    min-width: 1.4rem;
    text-align: center;
    font-size: 0.75rem;
    font-weight: 700;
    color: #fff;
    background: var(--brand);
    border-radius: 1rem;
    padding: 0.05rem 0.45rem;
    margin-left: 0.4rem;
}

/* ---- enquiries (the board's questions to the owners) ---- */

/* The part that asks something of the reader, set off from the explanation above
   it so it reads as a form rather than as more text. */
.enquiry-form {
    margin-top: 1rem;
    padding: 1rem 1rem 0.75rem;
    border: 1px solid var(--border);
    border-left: 3px solid var(--brand);
    border-radius: 0.5rem;
    background: var(--surface-muted);
}

/* An answer already given, on the enquiry's own page. */
.enquiry-answers { margin: 0.75rem 0 0.25rem; }
.enquiry-answers dt { font-weight: 600; }
.enquiry-answers dd { margin: 0 0 0.5rem; }

/* The same answers on the list, as one line under the title: question, value,
   question, value — wrapping rather than running off the card. */
.enquiry-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem 0.9rem;
    margin-top: 0.2rem;
    font-size: 0.9rem;
}

.enquiry-summary-item strong { font-weight: 600; }

/* The translate control sits inline after a text and reserves room for itself.
   Here there is nothing to reserve — a question label carries no toggle of its
   own — and the gap would push the colon off its word. */
.enquiry-summary .ti-actions:empty { display: none; }

/* ---- polls ---- */
.poll-bar {
    background: var(--surface-muted);
    border-radius: 0.4rem;
    overflow: hidden;
    height: 1.5rem;
    position: relative;
    margin: 0.25rem 0 0.6rem;
}

.poll-bar-fill { background: var(--brand); opacity: 0.25; height: 100%; }

.poll-bar-label {
    position: absolute;
    inset: 0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 0.6rem;
    font-size: 0.85rem;
}

/* ---- misc ---- */
.preserve-lines { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Inline, icon-only translation controls shown at the end of translatable text:
   a bi-translate toggle (original ⇄ translation), an edit pencil for admins, and
   a spinner while a translation is being fetched. Kept subtle so they don't
   compete with the content. */
.ti-actions {
    display: inline-flex;
    align-items: center;
    gap: 0.1rem;
    margin-left: 0.3rem;
    vertical-align: baseline;
}
.ti-btn {
    border: 0;
    background: none;
    padding: 0 0.1rem;
    margin: 0;
    line-height: 1;
    font-size: 0.8rem;
    color: var(--text-muted);
    cursor: pointer;
    vertical-align: baseline;
}
.ti-btn:hover { color: var(--brand); }
.ti-btn.active { color: var(--brand); }     /* translation is currently shown */
.ti-spin {
    width: 0.7rem;
    height: 0.7rem;
    border-width: 0.12em;
    margin-left: 0.3rem;
    color: var(--text-muted);
    vertical-align: middle;
}

/* ---- document folder browser ---- */
.doc-breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 1rem;
    font-size: 0.95rem;
}
.doc-breadcrumb .sep { color: var(--text-muted); }
.doc-breadcrumb .current { font-weight: 600; color: var(--heading); }
.link-button {
    border: none;
    background: none;
    padding: 0;
    color: var(--brand);
    cursor: pointer;
}
.link-button:hover { text-decoration: underline; }
.file-list .file-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.6rem 0.25rem;
    border-bottom: 1px solid var(--border);
}
.file-list .file-row:last-child { border-bottom: none; }
.file-list .file-name { flex: 1; min-width: 12rem; }
.file-list .file-row:hover { background: var(--surface-muted); }
.file-list [role="button"] { cursor: pointer; }

/* ---- GDPR footer & cookie banner ---- */
.site-footer {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    margin-top: 2rem;
    padding: 1rem 1.5rem;
    border-top: 1px solid var(--border);
    font-size: 0.8rem;
    color: var(--text-muted);
}

.site-footer a { color: var(--text-muted); text-decoration: underline; }

/* ---- site alert (ActiveAlerts): a notice from management, in a red band above
       every page's content until it expires or the reader dismisses it. The red
       is fixed rather than taken from the colour scheme — "something is wrong"
       has to read the same on a sage-green site as on a burgundy one — but each
       theme gets its own pair, dark enough on light and light enough on dark. -- */
.site-alert {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.85rem 1rem;
    margin-bottom: 1rem;
    border: 1px solid rgba(179, 38, 30, 0.3);
    border-left: 4px solid #b3261e;
    border-radius: var(--radius);
    background: rgba(179, 38, 30, 0.08);
    color: #7f1d17;
    box-shadow: 0 1px 2px var(--shadow);
}
.site-alert-icon { flex: 0 0 auto; font-size: 1.15rem; line-height: 1.45; color: #b3261e; }
.site-alert-text { flex: 1 1 auto; min-width: 0; }

/* Links, e-mail addresses and @mentions inside the band wear the band's red, not
   the colour scheme's accent. The accent is picked to sit well on the page; on a
   warning it reads as a second signal competing with the first, and a teal link
   across a red notice looks like it belongs to something else. Same reasoning as
   the band's bold and its OK button. Underline and weight still mark them out —
   the colour was never what said "link". */
.site-alert a,
.site-alert .text-link,
.site-alert .mention { color: #b3261e; }
/* And the band's bold keeps the band's own text colour: the weight already says
   it, and a third colour across a warning says nothing. */
.site-alert .text-bold { color: inherit; }
.site-alert .mention-self { background: rgba(179, 38, 30, 0.18); }
/* The translate toggle sits inline at the end of the same sentence and turns
   brand-coloured once it is showing a translation, which left one accent-coloured
   speck in a band that is otherwise all red. Its resting muted grey is fine. */
.site-alert .ti-btn:hover,
.site-alert .ti-btn.active { color: #b3261e; }
.site-alert-dismiss {
    flex: 0 0 auto;
    padding: 0.1rem 0.3rem;
    border: 0;
    border-radius: 0.35rem;
    background: none;
    color: inherit;
    opacity: 0.7;
    cursor: pointer;
}
.site-alert-dismiss:hover,
.site-alert-dismiss:focus-visible { opacity: 1; background: rgba(179, 38, 30, 0.15); }

/* The OK / Agree button: pressing it is recorded, so it is a deliberate target
   rather than the quiet cross — outlined in the band's own red, never the
   colour scheme's, so it reads as part of the warning. */
.site-alert-confirm {
    flex: 0 0 auto;
    align-self: center;
    padding: 0.25rem 0.9rem;
    border: 1px solid #b3261e;
    border-radius: 0.35rem;
    background: transparent;
    color: #b3261e;
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}
.site-alert-confirm:hover,
.site-alert-confirm:focus-visible { background: #b3261e; color: #fff; }

html[data-theme="dark"] .site-alert {
    border-color: rgba(245, 169, 159, 0.3);
    border-left-color: #f5a99f;
    background: rgba(245, 169, 159, 0.12);
    color: #f3c3bc;
}
html[data-theme="dark"] .site-alert-icon { color: #f5a99f; }
html[data-theme="dark"] .site-alert a,
html[data-theme="dark"] .site-alert .text-link,
html[data-theme="dark"] .site-alert .mention { color: #f5a99f; }
html[data-theme="dark"] .site-alert .mention-self { background: rgba(245, 169, 159, 0.22); }
html[data-theme="dark"] .site-alert .ti-btn:hover,
html[data-theme="dark"] .site-alert .ti-btn.active { color: #f5a99f; }
html[data-theme="dark"] .site-alert-dismiss:hover,
html[data-theme="dark"] .site-alert-dismiss:focus-visible { background: rgba(245, 169, 159, 0.2); }
html[data-theme="dark"] .site-alert-confirm { border-color: #f5a99f; color: #f5a99f; }
html[data-theme="dark"] .site-alert-confirm:hover,
html[data-theme="dark"] .site-alert-confirm:focus-visible { background: #f5a99f; color: #3a0e0a; }

.cookie-banner {
    position: fixed;
    bottom: 1rem;
    left: 1rem;
    right: 1rem;
    margin: 0 auto;
    max-width: 60rem;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 1.25rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
    z-index: 1100;
}

.cookie-banner-text { flex: 1 1 20rem; }
.cookie-banner-text p { margin: 0.25rem 0 0; font-size: 0.85rem; color: var(--text-muted); }
.cookie-banner-actions { display: flex; gap: 0.5rem; flex-shrink: 0; }

#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;
    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: #b32121; padding: 1rem 1rem 1rem 3.7rem; color: white; }
.blazor-error-boundary::after { content: "An error has occurred."; }

/* ---- rich-text (Quill) editor & rendered blog content ---- */
.rich-text-editor .ql-container {
    min-height: 12rem;
    font-size: 1rem;
    border-bottom-left-radius: var(--radius);
    border-bottom-right-radius: var(--radius);
}

.rich-text-editor .ql-toolbar {
    border-top-left-radius: var(--radius);
    border-top-right-radius: var(--radius);
}

/* Keep typed text legible in dark mode (Quill ships light-only styles). */
html[data-theme="dark"] .rich-text-editor .ql-container,
html[data-theme="dark"] .rich-text-editor .ql-editor { background: var(--surface); color: var(--text); }
html[data-theme="dark"] .rich-text-editor .ql-toolbar,
html[data-theme="dark"] .rich-text-editor .ql-container { border-color: var(--border); }

/* Quill's toolbar icons are hardcoded dark grey (#444), which is invisible on
   the dark toolbar and makes every stock button look disabled. Recolour the
   icons and dropdown labels to the theme text colour; keep the brand colour for
   hover/active so toggled formats still read as "on". */
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-stroke { stroke: var(--text); }
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-fill { fill: var(--text); }
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-label { color: var(--text); }
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-label .ql-stroke { stroke: var(--text); }
html[data-theme="dark"] .rich-text-editor .ql-picker-options { background: var(--surface); border-color: var(--border); }
html[data-theme="dark"] .rich-text-editor .ql-picker-item { color: var(--text); }
html[data-theme="dark"] .rich-text-editor .ql-toolbar button:hover .ql-stroke,
html[data-theme="dark"] .rich-text-editor .ql-toolbar button.ql-active .ql-stroke,
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-label:hover .ql-stroke,
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-label.ql-active .ql-stroke { stroke: var(--brand); }
html[data-theme="dark"] .rich-text-editor .ql-toolbar button:hover .ql-fill,
html[data-theme="dark"] .rich-text-editor .ql-toolbar button.ql-active .ql-fill { fill: var(--brand); }
html[data-theme="dark"] .rich-text-editor .ql-toolbar button:hover,
html[data-theme="dark"] .rich-text-editor .ql-toolbar button.ql-active,
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-label:hover,
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-label.ql-active,
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-item:hover,
html[data-theme="dark"] .rich-text-editor .ql-toolbar .ql-picker-item.ql-selected { color: var(--brand); }

/* Blog title as a link to the article's own page: looks like the heading, hints on hover. */
.blog-title-link { color: inherit; text-decoration: none; }
.blog-title-link:hover, .blog-title-link:focus-visible { text-decoration: underline; }

.blog-content img { max-width: 100%; height: auto; border-radius: 0.4rem; }
.blog-content p { margin-bottom: 0.75rem; }

/* Author-applied bold (the editor's [B] button) is shown in the theme accent
   colour — in both the rendered post and the editor, so editing stays WYSIWYG. */
.blog-content strong, .blog-content b,
.rich-text-editor .ql-editor strong, .rich-text-editor .ql-editor b { color: var(--accent); }

/* Inline-image positioning, chosen in the rich-text editor and stored as a class
   on a wrapper around the <img> (rte-img-inline|left|right|full). Applied both in
   the editor (.ql-editor) and the rendered post (.blog-content). */
.blog-content .rte-img-inline,
.ql-editor .rte-img-inline { display: inline; }
.blog-content .rte-img-left,
.ql-editor .rte-img-left { float: left; max-width: 50%; margin: 0.25rem 1rem 0.5rem 0; }
.blog-content .rte-img-right,
.ql-editor .rte-img-right { float: right; max-width: 50%; margin: 0.25rem 0 0.5rem 1rem; }
.blog-content .rte-img-full,
.ql-editor .rte-img-full { display: block; width: 100%; margin: 0.5rem 0; }
/* The image fills its positioned wrapper. */
.blog-content .rte-img-left img, .blog-content .rte-img-right img, .blog-content .rte-img-full img,
.ql-editor .rte-img-left img, .ql-editor .rte-img-right img, .ql-editor .rte-img-full img {
    display: block; width: 100%; height: auto;
}
/* Contain the floats so they don't spill past the post/teaser. */
.blog-content::after,
.ql-editor::after { content: ""; display: block; clear: both; }
@media (max-width: 560px) {
    /* Floats get cramped on phones: let left/right images go full width instead. */
    .blog-content .rte-img-left,
    .blog-content .rte-img-right { float: none; max-width: 100%; margin: 0.5rem 0; }
}
.blog-content .ql-size-small { font-size: 0.75em; }
.blog-content .ql-size-large { font-size: 1.5em; }
.blog-content .ql-size-huge { font-size: 2.5em; }
.blog-content .ql-align-center { text-align: center; }
.blog-content .ql-align-right { text-align: right; }
.blog-content .ql-align-justify { text-align: justify; }

/* ---- "read more" cut ---- */
/* Toolbar button: a scissors icon, tinted with the accent colour so it stands
   out as a special insert among the stock formatting buttons. */
.rich-text-editor .ql-toolbar button.ql-read-more { color: var(--accent); }
.rich-text-editor .ql-toolbar button.ql-read-more:hover { color: var(--brand); }

/* The cut shown inside the editor: a labelled dashed divider. */
.rich-text-editor .ql-editor hr.rte-more {
    border: 0;
    border-top: 2px dashed var(--accent);
    margin: 1.25rem 0;
    height: 0;
    overflow: visible;
}
.rich-text-editor .ql-editor hr.rte-more::after {
    content: "✂";
    display: block;
    width: fit-content;
    margin: -0.8rem auto 0;
    padding: 0 0.5rem;
    background: var(--surface);
    color: var(--accent);
    font-size: 0.9rem;
}

/* Public "Read more / Show less" toggle. */
.btn-read-more {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-top: 0.25rem;
    padding: 0;
    border: 0;
    background: none;
    color: var(--brand);
    font-weight: 600;
    cursor: pointer;
}
.btn-read-more:hover { text-decoration: underline; }
