﻿/* ════════════════════════════════════════════════════════════════
   AURORA GLASS — design tokens
   Dark is the default; [data-theme="light"] overrides the same vars,
   so every existing page inherits the new palette automatically.
   ════════════════════════════════════════════════════════════════ */
:root {
    color-scheme: light;

    /* ---- Brand (raw) ----
       Named by role, not by hue, because these four are the whole brand: everything below is
       derived from them, and an alliance palette will later override exactly these. A name like
       --brand-coral goes stale the moment the brand moves, which is how the old set came to have
       a var called "sky" holding the colour of links, focus rings and info alike.

       Every ratio below is measured, not estimated. The pairing is deliberate: blue carries
       primary and informational meaning, gold carries the secondary highlight, and red is left to
       mean only "error" — the previous coral sat 1.11 away from the error red, close enough that
       a primary button and a failure read as the same colour. */
    --brand-primary:          #4C6A92;   /* white ink on this: 5.55:1  AA  */
    --brand-primary-strong:   #3D5A80;   /* on white:          7.06:1  AAA */
    --brand-secondary:        #C9A227;   /* dark ink on this:  7.29:1  AAA */
    --brand-secondary-strong: #7D6608;   /* on white:          5.56:1  AA  */

    /* ---- Surfaces ---- */
    --color-bg-primary:    #FAFAFC;
    --color-bg-secondary:  #FFFFFF;
    --color-bg-tertiary:   #F1F3F8;
    --color-bg-sunken:     #EEF1F7;
    --color-bg-inverse:    #1F2433;
    --color-surface-overlay: rgba(20, 24, 38, 0.45);

    /* ---- Text ---- */
    --color-text-primary:   #1F2433;
    --color-text-secondary: #5A6275;
    --color-text-tertiary:  #8A91A6;
    --color-text-inverse:   #FFFFFF;
    /* Links stay blue. They follow the primary rather than the secondary because a gold link on a
       white page reads as highlighted text, not as something to click. */
    --color-text-link:      var(--brand-primary-strong);

    /* ---- Borders ---- */
    --color-border-primary:   #E2E6F0;
    --color-border-secondary: #EDEFF6;
    --color-border-strong:    #CBD2E2;
    --color-border-focus:     var(--brand-primary-strong);

    /* ---- Accent / actions ---- */
    --color-accent-primary:        var(--brand-primary);
    /* Lighter than the base fill, but only as light as white ink allows: #5A7AA6 looked right and
       measured 4.4:1, so the label quietly failed AA on hover alone. */
    --color-accent-primary-hover:  #57769F;
    --color-accent-primary-active: #415C7E;
    --color-accent-strong:         var(--brand-primary-strong);
    --color-on-accent:             #FFFFFF;   /* white ink on the blue (5.55:1) */
    --color-accent-soft:           #EAEFF7;
    --color-accent-soft-border:    #CFDBEC;

    --color-accent-secondary:        var(--brand-secondary);
    --color-accent-secondary-strong: var(--brand-secondary-strong);
    --color-accent-secondary-soft:   #FAF3DC;
    --color-on-accent-secondary:     #1F1803;  /* dark ink on the gold (7.29:1) */

    /* ---- Status ----
       All three are darker than they were. Each is used as text both on a white card and on its
       own soft chip, and the old values cleared neither bar reliably: success was 4.16:1 on white,
       warning 4.24:1, and on the tinted chips all three fell to ~3.7-4.3:1. These values hold
       ≥4.5:1 in both places, which is the pair that actually gets rendered. */
    --color-status-success:      #187844;   /* 5.51 on white · 4.90 on its soft */
    --color-status-success-soft: #E3F6EC;
    --color-status-warning:      #9C5D00;   /* 5.28 on white · 4.73 on its soft */
    --color-status-warning-soft: #FFF1D6;
    --color-status-error:        #C4302A;   /* 5.52 on white · 4.62 on its soft */
    --color-status-error-soft:   #FCE6E5;
    --color-status-info:         var(--brand-primary-strong);
    --color-status-info-soft:    #EAEFF7;

    /* ---- Domain: troop colors (light, text-safe) ----
       Footman and cavalry carried the old success and warning hexes, and inherited their contrast
       failures with them (4.16 and 4.24 on white). They are written out rather than pointed at the
       status tokens: a footman is not a success, and coupling them would mean a future change to
       one silently repainting the other.

       Archer moved further than contrast alone required. At #1E78C8 it sat 6° of hue from the new
       brand blue, close enough that a troop tag read as an accent; #136B87 is 20° away and clearly
       its own colour. Hue is never the only signal here — every use is paired with an icon and a
       label — but it should not actively mislead. */
    --troop-archer:   #136B87;   /* 6.03 on white · 5.22 on its chip */
    --troop-footman:  #187844;   /* 5.51 on white · 4.90 on its chip */
    --troop-cavalry:  #9C5D00;   /* 5.28 on white · 4.73 on its chip */
    --troop-archer-soft:  #E4F0FB;
    --troop-footman-soft: #E3F6EC;
    --troop-cavalry-soft: #FFF1D6;

    /* ---- Spacing (4px base) ---- */
    --space-0: 0;
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4rem;
    --space-9: 6rem;

    /* ---- Typography ---- */
    /* Font fallback is per-character, so Latin text uses Nunito and only Han characters
       reach --font-cjk. The Noto faces are self-hosted subsets declared per-language in
       App.razor; the platform families after them catch anything outside the subset —
       user-typed member names and notes — so those degrade to the OS font, never to tofu. */
    --font-cjk: "Noto Sans SC", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;

    --font-family-body:    "Nunito Sans", "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", var(--font-cjk);
    --font-family-display: "Nunito", "Nunito Sans", -apple-system, "Segoe UI", var(--font-cjk);
    --font-family-mono:    "JetBrains Mono", ui-monospace, "Cascadia Code", monospace;

    --font-size-xs:   0.75rem;
    --font-size-sm:   0.875rem;
    --font-size-base: 1rem;
    --font-size-md:   1.125rem;
    --font-size-lg:   1.375rem;
    --font-size-xl:   1.75rem;
    --font-size-2xl:  2.25rem;
    --font-size-3xl:  3rem;

    --font-weight-normal:   400;
    --font-weight-medium:   600;
    --font-weight-semibold: 700;
    --font-weight-bold:     800;

    --line-height-tight:   1.2;
    --line-height-normal:  1.55;
    --line-height-relaxed: 1.7;

    --letter-spacing-tight:  -0.01em;
    --letter-spacing-normal: 0;
    --letter-spacing-wide:   0.04em;

    /* ---- Layout ---- */
    --max-width-content: 72ch;
    --max-width-wide:    1100px;
    --max-width-page:    1320px;
    --size-touch-target: 2.75rem;
    --sidebar-width:     264px;
    --header-height:     64px;
    --bottombar-height:  64px;

    /* Consolidated radius scale: everything is 16px, large containers 24px, pills stay round. */
    --border-radius-sm:   16px;
    --border-radius-md:   16px;
    --border-radius-lg:   16px;
    --border-radius-xl:   24px;
    --border-radius-full: 999px;

    /* ---- Shadows (soft, layered — no glass/blur) ---- */
    --shadow-sm:    0 1px 2px rgba(28, 33, 51, 0.06), 0 1px 3px rgba(28, 33, 51, 0.05);
    --shadow-md:    0 2px 6px rgba(28, 33, 51, 0.07), 0 6px 16px -6px rgba(28, 33, 51, 0.10);
    --shadow-lg:    0 8px 24px -8px rgba(28, 33, 51, 0.14), 0 16px 48px -16px rgba(28, 33, 51, 0.16);
    /* Mixed from the focus token rather than written as a literal rgba(). The literal here was
       rgba(22, 104, 214, 0.35) — #1668D6, the brand blue that was deleted in the recolour — so
       every focus ring in the app was still painted in a colour nothing else used, and no alliance
       override could reach it. Going through --color-border-focus means it follows the brand and
       the palette block picks it up for free. */
    --shadow-focus: 0 0 0 3px color-mix(in srgb, var(--color-border-focus) 35%, transparent);

    /* ---- Motion ---- */
    --duration-instant: 50ms;
    --duration-fast:    150ms;
    --duration-normal:  250ms;
    --duration-slow:    400ms;
    --easing-default: cubic-bezier(0.4, 0, 0.2, 1);
    --easing-in:      cubic-bezier(0.4, 0, 1, 1);
    --easing-out:     cubic-bezier(0, 0, 0.2, 1);
    --easing-bounce:  cubic-bezier(0.34, 1.56, 0.64, 1);

    /* ════════ LEGACY ALIASES — old names → new tokens (all 57 pages inherit) ════════ */
    --bg:      var(--color-bg-primary);
    --bg2:     var(--color-bg-secondary);
    --bg3:     var(--color-bg-tertiary);
    --bg4:     var(--color-bg-sunken);
    --border:  var(--color-border-primary);
    --border2: var(--color-border-strong);

    --text:  var(--color-text-primary);
    --text2: var(--color-text-secondary);
    --text3: var(--color-text-tertiary);

    --accent:      var(--color-accent-primary);
    --accent-2:    var(--color-accent-secondary);
    --accent-deep: var(--color-accent-strong);
    /* Nothing consumes this today, but it is an alias like its neighbours, so it points at the
       brand vars rather than repeating hexes — that way it cannot go stale the way it just did. */
    --accent-grad: linear-gradient(135deg, var(--brand-primary) 0%, var(--brand-secondary) 100%);
    --accent-soft: var(--color-accent-soft);
    --glow:        var(--shadow-focus);

    /* --gold used to live here, aliasing --color-accent-primary. It kept its name through the
       recolour, so a var called "gold" has been handing out blue, and three rules that paired it
       with a hardcoded black ink read as deliberate rather than broken. Its nine consumers now use
       --accent, which is the same value under an honest name. --teal went the same way and had no
       consumers at all, so it is simply gone. */
    --archer:  var(--troop-archer);
    --footman: var(--troop-footman);
    --cavalry: var(--troop-cavalry);
    --red:     var(--color-status-error);
    --purple:  #6A4DE0;
    --purple-soft: #EFEBFC;

    --surface-inset: var(--color-bg-tertiary);
    --ok-text:    var(--color-status-success);
    --ok-bg:      var(--color-status-success-soft);
    --ok-border:  #BFE6CE;
    --bad-text:   var(--color-status-error);
    --bad-bg:     var(--color-status-error-soft);
    --bad-border: #F4C7C4;
    --warn-bg:     var(--color-status-warning-soft);
    --warn-border: #ECD49B;

    /* Glass → solid card surfaces + soft shadow (blur removed) */
    --glass-bg:     var(--color-bg-secondary);
    --glass-bg-2:   var(--color-bg-tertiary);
    --glass-border: var(--color-border-primary);
    --glass-blur:   0px;
    --shadow:       var(--shadow-md);

    /* Animated aurora mesh → neutralized */
    --mesh-1: transparent;
    --mesh-2: transparent;
    --mesh-3: transparent;
    --mesh-base: var(--color-bg-primary);

    --radius:    var(--border-radius-lg);
    --radius-sm: var(--border-radius-sm);
    --radius-lg: var(--border-radius-xl);
    --font-body:    var(--font-family-body);
    --font-display: var(--font-family-display);
    --font-mono:    var(--font-family-mono);
}

/* Traditional Chinese. Simplified and Traditional share many codepoints but draw them
   differently, so serving Noto Sans SC to a zh-Hant reader looks subtly foreign. Noto Sans
   SC is kept as the second entry because untranslated strings fall back to Simplified, which
   Noto Sans TC cannot draw at all. Relies on <html lang> being set — see App.razor. */
html[lang="zh-Hant"] {
    --font-cjk: "Noto Sans TC", "Noto Sans SC", "PingFang TC", "Microsoft JhengHei", "Noto Sans CJK TC", sans-serif;
}

/* MudBlazor renders every typography variant from RocTheme, which is a static object and so
   cannot reorder SC/TC by language — its emitted stacks end at sans-serif with no CJK family.
   Point its variables at the stacks above instead, which carry both the CJK fallback and the
   zh-Hant swap. Every variant is listed because MudBlazor propagates Default.FontFamily to the
   variants RocTheme leaves unset, so they all need the same treatment.
   `html:root` outranks the plain `:root` MudThemeProvider emits, so this wins on specificity
   rather than document order (the provider renders from <body>, after this stylesheet). */
html:root {
    --mud-typography-default-family:   var(--font-body);
    --mud-typography-body1-family:     var(--font-body);
    --mud-typography-body2-family:     var(--font-body);
    --mud-typography-button-family:    var(--font-body);
    --mud-typography-caption-family:   var(--font-body);
    --mud-typography-overline-family:  var(--font-body);
    --mud-typography-subtitle1-family: var(--font-body);
    --mud-typography-subtitle2-family: var(--font-body);
    --mud-typography-h1-family:        var(--font-display);
    --mud-typography-h2-family:        var(--font-display);
    --mud-typography-h3-family:        var(--font-display);
    --mud-typography-h4-family:        var(--font-display);
    --mud-typography-h5-family:        var(--font-display);
    --mud-typography-h6-family:        var(--font-display);

    /* MudBlazor ships Material's uppercase button label at weight 500. The app's own .btn-*
       buttons are sentence case at 700, and nine pages put the two kinds side by side — so a row
       read "SHARE  Publish  DELETE ROW", which is what made the button text look wrong rather
       than any alignment problem. Matching the variables fixes every page at once; restyling
       nine pages' markup would have been the same result with far more risk. */
    --mud-typography-button-text-transform: none;
    --mud-typography-button-weight: 700;
}

/* Height and radius to match .btn-* as well, so a mixed row sits on one baseline instead of
   stepping up and down. Mud sets no min-height, which left its buttons ~10px shorter. */
.mud-button-root:not(.mud-icon-button) {
    min-height: 44px;
    border-radius: var(--border-radius-md);
}
.mud-button-root.mud-button-outlined-size-small,
.mud-button-root.mud-button-filled-size-small,
.mud-button-root.mud-button-text-size-small {
    min-height: 36px;
}

/* DARK — opt-in via toggle (night play). Warm-cool dark, not pure black. */
[data-theme="dark"] {
    color-scheme: dark;

    --color-bg-primary:   #12151D;
    --color-bg-secondary: #1A1F2B;
    --color-bg-tertiary:  #232938;
    --color-bg-sunken:    #0E1118;
    --color-bg-inverse:   #EDEFF5;
    --color-surface-overlay: rgba(0, 0, 0, 0.55);

    --color-text-primary:   #ECEEF5;
    --color-text-secondary: #AAB2C5;
    --color-text-tertiary:  #7A8198;
    --color-text-inverse:   #1F2433;
    --color-text-link:      #8FB0D8;

    --color-border-primary:   #2C3344;
    --color-border-secondary: #232938;
    --color-border-strong:    #3A4358;
    --color-border-focus:     #8FB0D8;

    /* Lifted off the light-theme values: #4C6A92 against a #1A1F2B card is far too close to read.
       #7FA0C8 clears 6.09:1 there and 6.75:1 on the page background. */
    --color-accent-primary:        #7FA0C8;
    --color-accent-primary-hover:  #8FB0D8;
    --color-accent-primary-active: #6E92BE;
    --color-accent-strong:         #7FA0C8;
    --color-on-accent:             #0E1626;   /* dark ink on the light blue (6.69:1) */
    --color-accent-soft:           rgba(127, 160, 200, 0.16);
    --color-accent-soft-border:    rgba(127, 160, 200, 0.34);

    --color-accent-secondary:        #D4AF37;
    --color-accent-secondary-strong: #D4AF37;
    --color-accent-secondary-soft:   rgba(212, 175, 55, 0.16);
    --color-on-accent-secondary:     #1F1803;

    --color-status-success:      #46C281;
    --color-status-success-soft: rgba(70, 194, 129, 0.16);
    --color-status-warning:      #E0A53A;
    --color-status-warning-soft: rgba(224, 165, 58, 0.16);
    --color-status-error:        #FF7A72;
    --color-status-error-soft:   rgba(255, 122, 114, 0.16);
    --color-status-info:         #8FB0D8;
    --color-status-info-soft:    rgba(143, 176, 216, 0.16);

    /* Same teal lean as light, and for a sharper reason: at #5BAAFF the archer tag sat 2° of hue
       from the dark primary, so a troop tag and an accent were the same colour. #63BBD0 is 21°
       away. Footman and cavalry keep their values; both clear AA here (7.29 and 7.54). */
    --troop-archer:  #63BBD0;   /* 7.49 on the card */
    --troop-footman: #46C281;
    --troop-cavalry: #E0A53A;
    --troop-archer-soft:  rgba(99, 187, 208, 0.16);
    --troop-footman-soft: rgba(70, 194, 129, 0.16);
    --troop-cavalry-soft: rgba(224, 165, 58, 0.16);

    --shadow-sm:    0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md:    0 2px 8px rgba(0, 0, 0, 0.45), 0 8px 24px -10px rgba(0, 0, 0, 0.5);
    --shadow-lg:    0 12px 36px -10px rgba(0, 0, 0, 0.6);
    /* Same reasoning as the light block: this was rgba(111, 182, 255, 0.45), the dark link colour
       this branch replaced. The alpha stays heavier here because the ring has less to work with
       against a dark surface. */
    --shadow-focus: 0 0 0 3px color-mix(in srgb, var(--color-border-focus) 45%, transparent);

    /* Legacy aliases needing explicit dark values */
    --accent-grad: linear-gradient(135deg, var(--color-accent-primary) 0%, var(--color-accent-secondary) 100%);
    --glow:        var(--shadow-focus);
    --shadow:      var(--shadow-md);
    --ok-border:   rgba(70, 194, 129, 0.4);
    --bad-border:  rgba(255, 122, 114, 0.4);
    --warn-border: rgba(224, 165, 58, 0.4);
    --purple:      #9B86FF;
    --purple-soft: rgba(155, 134, 255, 0.16);
    --mesh-base:   var(--color-bg-primary);
}

/* ── Spec Planner canvas (SVG nodes/links) — theme-adaptive ── */
:root {
    --sp-grid:           #E6E9F2;
    /* The three link tiers must stay separable against the canvas (--surface-inset,
       #F1F3F8) AND against the grid (#E6E9F2). --sp-link-dim used to be #EDEFF6 — lighter
       than the grid and a shade off the background — so the shape of an untouched tree was
       invisible until points were spent in it. Every tier now sits darker than the grid. */
    --sp-link:           #94A3BD;   /* both ends reachable, nothing invested yet */
    /* The node states were built from the old brand sky (#3B9EFF / #1668D6). Left alone they would
       have made /specs the one screen still painted in the previous brand — a brighter, bluer blue
       than everything around it, which reads as a bug rather than as a different subsystem. They
       now follow the same two blues as the rest of the app. */
    --sp-link-active:    #4C6A92;   /* on an invested path — set apart by hue and width */
    --sp-link-dim:       #B4C0D4;   /* an end is still locked — subordinate, not hidden */
    --sp-sel-ring:       #1F2433;
    --sp-hub-fill:       #FAF3DC;   /* gold-tinted, matching the hub dots below */
    --sp-locked-fill:    #F1F3F8;
    --sp-locked-stroke:  #CBD2E2;
    /* Low contrast on purpose: a locked node is inactive, which WCAG 1.4.3 exempts, and lifting it
       would undo the ranking the three fills exist to express. */
    --sp-locked-text:    #9097AB;
    --sp-avail-fill:     #FFFFFF;
    --sp-avail-stroke:   #4C6A92;
    --sp-avail-text:     #3D5A80;   /* 7.06 on its fill */
    --sp-partial-fill:   #EAEFF7;
    --sp-partial-stroke: #4C6A92;
    --sp-partial-text:   #3D5A80;   /* 6.12 on its fill */
    --sp-done-fill:      #3D5A80;
    --sp-done-stroke:    #2E4A73;
    --sp-done-text:      #FFFFFF;   /* 7.06 on its fill */
    --sp-dot-on:         #3D5A80;
    --sp-dot-on-stroke:  #2E4A73;
    --sp-dot-off:        #E2E6F0;
    --sp-dot-off-stroke: #CBD2E2;
    --sp-hubdot-on:        #C9A227;
    --sp-hubdot-on-stroke: #7D6608;
    --sp-hubdot-off:       #EEE7D6;
    --sp-hubdot-off-stroke:#D9C9A0;
}
[data-theme="dark"] {
    --sp-grid:           #1A2230;
    /* Canvas here is #232938, so links read as structure by sitting *lighter* than it.
       The old values were darker than the background and vanished into it. */
    --sp-link:           #6280AB;
    /* Dark used a cyan family (#00C8FF and friends) that belonged to no brand the app has ever
       had. Retoned onto the dark accents so the tree reads as the same product in either theme. */
    --sp-link-active:    #7FA0C8;
    --sp-link-dim:       #46536D;
    --sp-sel-ring:       #FFFFFF;
    --sp-hub-fill:       #1A3040;
    --sp-locked-fill:    #131C28;
    --sp-locked-stroke:  #2A3848;
    --sp-locked-text:    #6A7890;
    --sp-avail-fill:     #1B2A3D;
    --sp-avail-stroke:   #5A7EA8;
    --sp-avail-text:     #9BBBDE;   /* 7.30 on its fill */
    --sp-partial-fill:   #2E4A73;
    --sp-partial-stroke: #7FA0C8;
    --sp-partial-text:   #C3D6EC;   /* 6.04 on its fill */
    --sp-done-fill:      #3D5A80;
    --sp-done-stroke:    #8FB0D8;
    --sp-done-text:      #EAEFF7;   /* 6.12 on its fill */
    --sp-dot-on:         #8FB0D8;
    --sp-dot-on-stroke:  #B5CCE4;
    --sp-dot-off:        #1A2A3A;
    --sp-dot-off-stroke: #2A3848;
    --sp-hubdot-on:        #D4AF37;
    --sp-hubdot-on-stroke: #E0BC55;
    --sp-hubdot-off:       #1E2A18;
    --sp-hubdot-off-stroke:#2E3A28;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    font-family: var(--font-body);
    background: var(--color-bg-primary);
    color: var(--text);
    /* One notch down from the browser's 16px. The whole type scale is rem, and so is MudBlazor's,
       so this is the single knob that moves all of it together and keeps the ratios intact. Hit
       targets are px on purpose and stay where they are: text got smaller, buttons did not. */
    font-size: 15px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* Calm static page background (animated aurora mesh removed in the friendly overhaul). */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: var(--color-bg-primary);
}

a { color: var(--color-text-link); text-decoration: none; transition: color .15s; }
a:hover { color: var(--color-accent-strong); }

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    color: var(--text);
    margin: 0 0 0.5em 0;
    letter-spacing: -0.01em;
}

/* Mono numerics helper */
.mono { font-family: var(--font-mono); font-feature-settings: "tnum" 1; }

/* Reusable glass surface */
.glass {
    background: var(--glass-bg);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    border: 1px solid var(--glass-border);
}

/* Refined scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--border2) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb {
    background: var(--border2); border-radius: var(--border-radius-lg);
    border: 2px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--accent-deep); background-clip: content-box; }

@media (prefers-reduced-motion: reduce) {
    body::before { animation: none; }
}

/* ════════════════════════════════════════════════════════════════
   App shell — glass header + glass sidebar
   ════════════════════════════════════════════════════════════════ */
.roc-shell {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;   /* the sidebar + main scroll internally, so no body scrollbar runs past the header */
}

.roc-header {
    position: sticky;
    top: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    gap: var(--space-4);
    height: var(--header-height);
    padding: 0 var(--space-5);
    /* Left inset = sidebar margin + sidebar padding, so the brand's hover pill
       starts on the same line as the nav-link pills below it. The brand then
       carries the same 12px inner padding as a nav-link, so the ◈ also lines up
       with the menu icon rail. */
    padding-left: calc(var(--space-4) + var(--space-3));
    background: var(--color-bg-secondary);
    border-bottom: 1px solid var(--color-border-primary);
    box-shadow: var(--shadow-sm);
}

.roc-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--font-display);
    font-weight: 800;
    font-size: var(--font-size-md);
    letter-spacing: .01em;
    color: var(--color-text-primary);
    text-decoration: none;
    padding: 5px 10px 5px var(--space-3);
    border-radius: var(--border-radius-md);
    transition: background-color .15s;
}
.roc-brand:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
.roc-brand:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-brand-mark {
    width: 36px; height: 36px;
    border-radius: var(--border-radius-md);
    display: grid;
    place-items: center;
    background: var(--color-accent-primary);
    color: var(--color-on-accent);
    font-size: 17px;
    box-shadow: var(--shadow-sm);
}
/* The mark is the logo image itself, so it drops the accent tile it used to sit on: the artwork
   is sepia and transparent, and it clashed against the accent fill rather than reading as a mark.
   Sizing stays on .roc-brand-mark above so the header (36px) and auth pages (52px) keep their
   existing dimensions. */
img.roc-brand-mark {
    background: none;
    box-shadow: none;
    object-fit: contain;
}
.roc-brand-name { color: var(--color-text-primary); }

.roc-header-spacer { flex: 1; }
.roc-header-actions { display: flex; align-items: center; gap: var(--space-3); }

.roc-user {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    line-height: 1.2;
    gap: 3px;
}
.roc-user-name { font-size: var(--font-size-sm); font-weight: 700; color: var(--color-text-primary); }
.roc-user-role {
    font-size: 11px; font-weight: 700; line-height: 1;
    color: var(--color-accent-strong);
    background: var(--color-accent-soft);
    border: 1px solid var(--color-accent-soft-border);
    padding: 3px 9px;
    border-radius: var(--border-radius-full);
    text-transform: uppercase; letter-spacing: .06em;
}

.roc-icon-btn {
    width: 44px; height: 44px;
    border-radius: var(--border-radius-md);
    display: grid;
    place-items: center;
    cursor: pointer;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-strong);
    color: var(--color-text-secondary);
    transition: color .15s, border-color .15s, background-color .15s, box-shadow .15s;
}
.roc-icon-btn:hover { color: var(--color-text-primary); background: var(--color-bg-tertiary); }
.roc-icon-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-icon-btn svg { width: 20px; height: 20px; }

/* ── Notification bell + dropdown ── */
/* Game clock — the shared day and time, sized to sit level with the icon buttons beside it.
   Tabular figures so the minute ticking over does not shuffle the whole label sideways. */
.roc-gameclock {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    height: 44px;
    padding: 0 var(--space-3);
    border-radius: var(--border-radius-md);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-strong);
    font-size: var(--font-size-sm);
    line-height: 1;
    white-space: nowrap;
}
.roc-gameclock-day { color: var(--color-text-secondary); }
.roc-gameclock-offset {
    font-weight: 700;
    color: var(--color-text-primary);
    font-variant-numeric: tabular-nums;
}

.roc-bell { position: relative; display: inline-flex; }
.roc-bell-btn { position: relative; }
.roc-bell-badge {
    position: absolute; top: -2px; right: -2px;
    min-width: 18px; height: 18px; padding: 0 4px;
    display: grid; place-items: center;
    font-size: 10px; font-weight: 800; line-height: 1;
    /* The ink comes from the palette rather than a literal, so it follows the accent it sits on.
       Hardcoded white measured 3.22:1 here in dark mode; the token is 6.87 light and 5.61 dark. */
    color: var(--color-on-accent); background: var(--color-accent-primary-active);
    border: 2px solid var(--color-bg-primary);
    border-radius: var(--border-radius-full);
}
.roc-bell-backdrop { position: fixed; inset: 0; z-index: 90; background: transparent; }
.roc-bell-panel {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 100;
    width: 340px; max-width: calc(100vw - 24px);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}
.roc-bell-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border-secondary);
    font-weight: 800; color: var(--color-text-primary);
}
.roc-bell-mark {
    background: none; border: none; cursor: pointer;
    color: var(--color-accent-secondary-strong); font-weight: 700; font-size: var(--font-size-sm);
}
.roc-bell-list { max-height: 360px; overflow-y: auto; }
.roc-bell-empty { padding: var(--space-5); text-align: center; color: var(--color-text-tertiary); font-size: var(--font-size-sm); }
.roc-bell-item {
    display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
    background: none; border: none; cursor: pointer;
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border-secondary);
}
.roc-bell-item:hover { background: var(--color-bg-tertiary); }
.roc-bell-item.is-unread { background: var(--color-accent-secondary-soft); }
.roc-bell-item.is-unread:hover { background: var(--color-accent-secondary-soft); }
.roc-bell-item-title { font-weight: 700; color: var(--color-text-primary); font-size: var(--font-size-sm); }
.roc-bell-item-body { color: var(--color-text-secondary); font-size: var(--font-size-sm); }
.roc-bell-item-time { color: var(--color-text-tertiary); font-size: 11px; }
.roc-bell-all {
    display: block; text-align: center; padding: var(--space-3);
    color: var(--color-accent-secondary-strong); font-weight: 700; font-size: var(--font-size-sm);
    text-decoration: none;
}
.roc-bell-all:hover { background: var(--color-bg-tertiary); }

/* ── User menu (avatar + dropdown) ── */
.roc-usermenu { position: relative; display: inline-flex; }
.roc-usermenu-trigger {
    display: inline-flex; align-items: center; gap: var(--space-2);
    min-height: 44px; padding: 4px 10px 4px 4px;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--border-radius-full);
    cursor: pointer; color: var(--color-text-primary);
    transition: background .15s, border-color .15s, box-shadow .15s;
}
.roc-usermenu-trigger:hover { background: var(--color-bg-tertiary); }
.roc-usermenu-trigger:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

.roc-avatar {
    width: 34px; height: 34px; flex: 0 0 auto;
    display: grid; place-items: center; border-radius: 50%;
    background: var(--color-accent-primary); color: var(--color-on-accent);
    font-weight: 800; font-size: var(--font-size-base); line-height: 1;
}
.roc-avatar-lg { width: 44px; height: 44px; font-size: var(--font-size-md); }

.roc-usermenu-name {
    font-weight: 700; font-size: var(--font-size-sm); color: var(--color-text-primary);
    max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.roc-usermenu-caret { width: 16px; height: 16px; color: var(--color-text-tertiary); flex: 0 0 auto; }

.roc-usermenu-panel {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 100;
    width: 250px; max-width: calc(100vw - 24px);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-2);
}
.roc-usermenu-head {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-2) var(--space-3) var(--space-3);
    border-bottom: 1px solid var(--color-border-secondary);
    margin-bottom: var(--space-2);
}
.roc-usermenu-id { display: flex; flex-direction: column; min-width: 0; }
.roc-usermenu-id .roc-usermenu-name { max-width: 150px; }
.roc-usermenu-role { font-size: var(--font-size-sm); color: var(--color-text-secondary); }

.roc-usermenu-item {
    display: flex; align-items: center; justify-content: flex-start; gap: var(--space-3);
    width: 100%; box-sizing: border-box;
    background: none; border: none; text-align: left; cursor: pointer;
    padding: 10px var(--space-3); border-radius: var(--border-radius-md);
    color: var(--color-text-primary); font-size: var(--font-size-base); font-weight: 600;
    font-family: inherit; line-height: 1.3;
    text-decoration: none; min-height: 44px;
    appearance: none; -webkit-appearance: none;
}
.roc-usermenu-item:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); text-decoration: none; }
.roc-usermenu-item svg { width: 18px; height: 18px; color: var(--color-text-secondary); flex: 0 0 auto; }
.roc-usermenu-danger { color: var(--color-status-error); }
.roc-usermenu-danger svg { color: var(--color-status-error); }
.roc-usermenu form { margin: 0; width: 100%; display: block; }

/* Language switcher inside the user menu */
.roc-usermenu-langs { display: flex; gap: 6px; padding: 6px var(--space-3); }
.roc-usermenu-lang {
    flex: 1; text-align: center; text-decoration: none;
    padding: 8px 6px; border-radius: var(--border-radius-md);
    border: 1px solid var(--color-border-primary);
    color: var(--color-text-secondary); font-weight: 700; font-size: var(--font-size-sm);
    min-height: 40px; display: flex; align-items: center; justify-content: center;
}
.roc-usermenu-lang:hover { color: var(--color-text-primary); background: var(--color-bg-tertiary); text-decoration: none; }

/* Language switcher on the login card */
.roc-login-langs { display: flex; justify-content: center; gap: var(--space-4); margin-top: var(--space-4); }
.roc-login-langs a { color: var(--color-text-tertiary); text-decoration: none; font-weight: 700; font-size: var(--font-size-sm); }
.roc-login-langs a:hover { color: var(--color-text-primary); }

@media (max-width: 560px) {
    .roc-usermenu-name, .roc-usermenu-caret { display: none; }
    .roc-usermenu-trigger { padding: 4px; }
    /* The header sheds text before it sheds controls. "Saturday" is the part a member can work out
       for themselves; the offset is the part they came for, so that is what survives. */
    .roc-gameclock-day { display: none; }
}

.roc-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 40px;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-strong);
    color: var(--color-text-primary);
    padding: 8px 16px;
    border-radius: var(--border-radius-md);
    font-size: var(--font-size-sm);
    font-weight: 700;
    cursor: pointer;
    font-family: var(--font-body);
    transition: color .15s, border-color .15s, background-color .15s, box-shadow .15s;
}
.roc-btn:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
.roc-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

/* ════════════════════════════════════════════════════════════════
   Button system (Task 5) — global, AA-safe, ≥44px targets.
   Named by role, not by hue: the fills come from the accent tokens, so an alliance palette
   repaints them and a hue in this comment would be wrong the moment it did.
   .btn-primary  accent fill + on-accent ink   .btn-secondary  outline
   .btn-ghost    transparent                   .btn-danger     error fill + white
   Add .btn-sm for a compact variant.
   ════════════════════════════════════════════════════════════════ */
.btn-primary, .btn-secondary, .btn-ghost, .btn-danger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 44px;
    padding: 10px 20px;
    border-radius: var(--border-radius-md);
    border: 1px solid transparent;
    font-family: var(--font-body);
    font-size: var(--font-size-base);
    font-weight: 700;
    line-height: 1.1;
    cursor: pointer;
    text-decoration: none;
    -webkit-appearance: none;
    transition: background-color .15s, color .15s, border-color .15s, box-shadow .15s, filter .15s;
}
.btn-primary:focus-visible, .btn-secondary:focus-visible,
.btn-ghost:focus-visible, .btn-danger:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus);
}
/* An <a> can match neither :disabled nor [disabled], so a disabled RocButton with an Href needs
   the aria form too. Without it the link is genuinely inert (RocButton drops the href) but still
   renders at full opacity and lights up on hover, which invites a click that does nothing. */
.btn-primary:disabled, .btn-secondary:disabled, .btn-ghost:disabled, .btn-danger:disabled,
.btn-primary[disabled], .btn-secondary[disabled], .btn-ghost[disabled], .btn-danger[disabled],
.btn-primary[aria-disabled="true"], .btn-secondary[aria-disabled="true"],
.btn-ghost[aria-disabled="true"], .btn-danger[aria-disabled="true"] {
    opacity: .55;
    cursor: not-allowed;
    box-shadow: none;
    filter: none;
}

.btn-primary {
    background: var(--color-accent-primary);
    color: var(--color-on-accent);
    box-shadow: var(--shadow-sm);
}
.btn-primary:hover { background: var(--color-accent-primary-hover); color: var(--color-on-accent); text-decoration: none; }
.btn-primary:active { background: var(--color-accent-primary-active); }

.btn-secondary {
    background: var(--color-bg-secondary);
    border-color: var(--color-border-strong);
    color: var(--color-text-primary);
}
.btn-secondary:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); text-decoration: none; }

.btn-ghost {
    background: transparent;
    color: var(--color-text-primary);
}
.btn-ghost:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); text-decoration: none; }

.btn-danger {
    background: var(--color-status-error);
    color: #fff;
    box-shadow: var(--shadow-sm);
}
.btn-danger:hover { filter: brightness(.95); color: #fff; text-decoration: none; }

.btn-sm { min-height: 36px; padding: 7px 14px; font-size: var(--font-size-sm); }

/* Icon-only buttons: square, at the 44px touch target (36px in the small size). */
.btn-icon { padding: 0; width: 44px; min-width: 44px; }
.btn-icon.btn-sm { width: 36px; min-width: 36px; }
.btn-icon-slot { display: inline-flex; align-items: center; }
.btn-icon-slot svg { width: 18px; height: 18px; display: block; }
.is-busy { cursor: progress; }

/* The variant :hover rules above are what actually light a button up, and an anchor keeps
   matching them while aria-disabled. One rule each, with the attribute selector for specificity,
   holds the disabled appearance through hover. */
.btn-primary[aria-disabled="true"]:hover { background: var(--color-accent-primary); color: var(--color-on-accent); }
.btn-secondary[aria-disabled="true"]:hover { background: var(--color-bg-secondary); color: var(--color-text-primary); }
.btn-ghost[aria-disabled="true"]:hover { background: transparent; color: var(--color-text-primary); }
.btn-danger[aria-disabled="true"]:hover { background: var(--color-status-error); color: #fff; filter: none; }

/* ════════════════════════════════════════════════════════════════
   Cards & grouped sections (Task 6) — Apple-Health-style building blocks
   .roc-card       soft content card (padded)
   .roc-section    muted label + .roc-group (rounded list of .roc-row)
   ════════════════════════════════════════════════════════════════ */
.roc-card {
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--space-5);
    /* Cards carried no margin, so every page invented its own and the ones that forgot had
       their cards touching. One default here, cancelled below wherever the container already
       supplies a gap, so the two can never add up. */
    margin-bottom: var(--space-5);
}

/* Vertical rhythm for stacked page content. Prefer this over per-page margins: a container
   with a gap spaces whatever it holds, cards or not. */
.roc-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}
.roc-stack > .roc-card,
.acct-grid > .roc-card,
.roc-stack > .mud-paper,
.roc-stack > .mud-card { margin-bottom: 0; }
.roc-card:last-child { margin-bottom: 0; }

/* A MudPaper or MudCard used as a page section gets the same rhythm as .roc-card. Pages mix the
   two — MudCard for the shieldwall rows, .roc-card elsewhere — and without this the Mud ones
   touched while the others were spaced. Scoped to direct children of the page shell so nested
   papers (a panel inside a card) keep their own spacing.
   `:where` keeps specificity at zero, so any page that wants something else still wins. */
:where(.roc-main) > :where(.mud-paper, .mud-card),
:where(.roc-main) > :where(div) > :where(.mud-paper, .mud-card) { margin-bottom: var(--space-5); }
:where(.roc-stack) > :where(.mud-paper, .mud-card):last-child,
:where(.roc-main) > :where(.mud-paper, .mud-card):last-child { margin-bottom: 0; }
.roc-card--flush { padding: 0; overflow: hidden; }
.roc-card--pad-sm { padding: var(--space-4); }
.roc-card--accent { background: var(--color-accent-soft); border-color: var(--color-accent-soft-border); }

.roc-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}
.roc-card-head--bordered {
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-border-secondary);
}
.roc-card-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: var(--font-size-lg);
    color: var(--color-text-primary);
    margin: 0;
    letter-spacing: -0.01em;
}
.roc-card-sub {
    margin: 2px 0 0;
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
}

.roc-section { margin-bottom: var(--space-6); }
.roc-section-title {
    font-size: var(--font-size-sm);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--color-text-tertiary);
    margin: 0 0 var(--space-3) var(--space-2);
}
/* RocSection header: the existing .roc-section-title, plus the description and action slot
   the component adds. Tokens only, no new values. */
.roc-section-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
}
.roc-section-head-text { min-width: 0; }
.roc-section-desc {
    font-size: var(--font-size-sm);
    color: var(--color-text-secondary);
    margin: 0 0 var(--space-2);
}
.roc-section-head-action { flex: 0 0 auto; }

.roc-group {
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.roc-row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-height: 56px;
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border-secondary);
}
.roc-row:last-child { border-bottom: none; }
.roc-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.roc-row-label { font-weight: 700; color: var(--color-text-primary); }
.roc-row-sub { font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.roc-row-value { color: var(--color-text-secondary); font-weight: 600; text-align: right; white-space: nowrap; }

a.roc-row, .roc-row--link {
    cursor: pointer;
    text-decoration: none;
    color: inherit;
    transition: background-color .15s;
}
a.roc-row:hover, .roc-row--link:hover { background: var(--color-bg-tertiary); text-decoration: none; }
a.roc-row:focus-visible, .roc-row--link:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-row-chevron { color: var(--color-text-tertiary); flex: 0 0 auto; }
.roc-row-chevron svg { width: 18px; height: 18px; display: block; }

/* ════════════════════════════════════════════════════════════════
   Page header (Task 7) — title + one-line purpose + optional action.
   Used at the top of every tool/list page via <PageHeader>.
   ════════════════════════════════════════════════════════════════ */
.roc-page-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}
.roc-page-head-text { min-width: 0; }
.roc-page-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: var(--font-size-xl);
    line-height: 1.15;
    letter-spacing: -0.02em;
    color: var(--color-text-primary);
    margin: 0;
}
/* <FocusOnNavigate> moves focus to this <h1> after each navigation so screen
   readers announce the new page. The heading is tabindex="-1" (never in the tab
   order), so it only ever receives that programmatic focus — a visible ring
   serves no keyboard-navigation purpose. Suppress it in every case (F5/Ctrl+R
   refresh registers keyboard modality, so :focus-visible would otherwise match). */
.roc-page-title:focus { outline: none; box-shadow: none; }
.roc-page-desc {
    margin: 6px 0 0;
    color: var(--color-text-secondary);
    font-size: var(--font-size-md);
    max-width: 70ch;
}
.roc-page-head-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}
@media (max-width: 600px) {
    .roc-page-head { align-items: flex-start; }
    /* A grid, not the wrapping flex row it is on desktop. Wrapping puts each button on a line of
       its own as soon as the labels are longer than half the screen, which on a phone turned five
       header buttons into five rows and ~200px of chrome above the content. Two tracks fit a
       320px screen, so the same five become three rows and every button keeps a full-width tap
       target. */
    .roc-page-head-actions {
        width: 100%;
        display: grid;
        /* 132px, not a rounder 150: the narrowest phone worth supporting leaves this row 280px
           after the main padding, and two tracks plus the 12px gap have to land inside that or
           auto-fit quietly falls back to one column and nothing changes. */
        grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
    }
    /* auto-fit collapses the tracks it does not need, so a header with one action would hand that
       button the whole row — a lone "New post" stretched 540px wide across a small tablet. Filling
       the track is only worth it when there is a second button to line up against. */
    .roc-page-head-actions > :only-child { justify-self: start; }
}

/* RocPage frame. The max width lives here rather than on each page, which is the whole point
   of the primitive being mandatory. */
.roc-page { max-width: 1100px; margin: 0 auto; }
.roc-page-loading {
    display: flex;
    justify-content: center;
    padding: var(--space-8) 0;
}
.roc-spinner {
    width: 28px;
    height: 28px;
    border: 3px solid var(--color-border-primary);
    border-top-color: var(--color-accent-primary);
    border-radius: 50%;
    animation: roc-spin .8s linear infinite;
}
@keyframes roc-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .roc-spinner { animation-duration: 3s; }
}

/* ════════════════════════════════════════════════════════════════
   Form fields (Task 8)
   Native controls use .roc-field / .roc-label / .roc-input / .roc-help.
   (.roc-input base lives in the Login block; here we extend it to
   select/textarea, add states, and lightly tune MudBlazor inputs.)
   ════════════════════════════════════════════════════════════════ */
.roc-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-4); }
.roc-label { font-size: var(--font-size-sm); font-weight: 700; color: var(--color-text-primary); }
.roc-label .roc-req { color: var(--color-status-error); margin-left: 2px; }
.roc-help { font-size: var(--font-size-sm); color: var(--color-text-secondary); margin: 0; }
.roc-field-error { font-size: var(--font-size-sm); color: var(--color-status-error); font-weight: 600; margin: 0; }
/* Buttons sitting under a field control. A row inside the column-flex .roc-field so the
   button keeps its natural width instead of stretching to the field. */
.roc-field-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* Toggles. The switch variant restyles the same checkbox rather than replacing it, so keyboard
   operation and the announced checked state survive. The input and its text sit inside one
   <label> so the whole 44px-tall row is the tap target, not just the 20x20 (or 42x24) visible
   box — the gap between a separate input and label would otherwise look tappable and not be. */
.roc-toggle { display: flex; min-height: 44px; }
.roc-toggle-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    font-weight: 600;
    color: var(--color-text-primary);
    cursor: pointer;
}
.roc-toggle-input { width: 20px; height: 20px; flex: 0 0 auto; accent-color: var(--color-accent-primary); }
.roc-toggle-input:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-toggle-input:disabled ~ .roc-toggle-text { color: var(--color-text-tertiary); cursor: not-allowed; }
/* The input sits inside .roc-toggle-label, so :has() is what reaches the whole 44px row —
   the box, the gap and the text — rather than just the text sibling above. Without it the
   label kept cursor: pointer over most of a disabled toggle's tap target. */
.roc-toggle-label:has(.roc-toggle-input:disabled) { cursor: not-allowed; }

.roc-toggle--switch .roc-toggle-input {
    appearance: none;
    -webkit-appearance: none;
    width: 42px;
    height: 24px;
    border-radius: 999px;
    background: var(--color-bg-tertiary);
    border: 1px solid var(--color-border-primary);
    position: relative;
    cursor: pointer;
    transition: background .15s ease;
}
.roc-toggle--switch .roc-toggle-input::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-bg-secondary);
    transition: transform .15s ease;
}
.roc-toggle--switch .roc-toggle-input:checked { background: var(--color-accent-primary); border-color: var(--color-accent-primary); }
.roc-toggle--switch .roc-toggle-input:checked::after { transform: translateX(18px); }
@media (prefers-reduced-motion: reduce) {
    .roc-toggle--switch .roc-toggle-input,
    .roc-toggle--switch .roc-toggle-input::after { transition: none; }
}

/* Security-log event badge (admin audit page). */
.roc-audit-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: var(--border-radius-sm);
    font-size: var(--font-size-sm);
    font-weight: 600;
    white-space: nowrap;
}
.roc-audit-badge.is-ok   { background: var(--color-status-success-soft); color: var(--color-text-primary); }
.roc-audit-badge.is-fail { background: var(--color-status-error-soft, var(--color-bg-tertiary)); color: var(--color-status-error); }

/* Chips. Colours come from the existing troop and status tokens, whose contrast ratios are
   measured in the :root block; nothing new is defined here. */
.roc-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: var(--font-size-sm);
    font-weight: 700;
    white-space: nowrap;
    background: var(--color-bg-tertiary);
    color: var(--color-text-primary);
}
.roc-chip--archer  { background: var(--troop-archer-soft);  color: var(--troop-archer); }
.roc-chip--footman { background: var(--troop-footman-soft); color: var(--troop-footman); }
.roc-chip--cavalry { background: var(--troop-cavalry-soft); color: var(--troop-cavalry); }
.roc-chip--success { background: var(--color-status-success-soft); color: var(--color-status-success); }
/* Warning takes the neutral ink, not --color-status-warning: that token is the same hex as
   --troop-cavalry in both themes (#9C5D00 light, #E0A53A dark), soft background included, so a
   warning chip drawn in its own strong colour would be pixel-identical to a cavalry troop chip,
   and this app renders both kinds of chip on the same surfaces. The neutral ink is what keeps
   them apart. */
.roc-chip--warning { background: var(--color-status-warning-soft); color: var(--color-text-primary); }
.roc-chip--error   { background: var(--color-status-error-soft);   color: var(--color-status-error); }

select.roc-input {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 38px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235A6275' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    cursor: pointer;
}
textarea.roc-input { min-height: 96px; padding: 11px 14px; resize: vertical; line-height: 1.5; }

.roc-input:disabled {
    background: var(--color-bg-tertiary);
    color: var(--color-text-tertiary);
    cursor: not-allowed;
}
.roc-input[aria-invalid="true"], .roc-input.is-invalid { border-color: var(--color-status-error); }
.roc-input[aria-invalid="true"]:focus, .roc-input.is-invalid:focus {
    box-shadow: 0 0 0 3px var(--color-status-error-soft);
}

/* MudBlazor inputs — friendlier sizing + readable labels/helper (low-risk tweaks) */
.mud-input-control .mud-input-root,
.mud-input-control input,
.mud-input-control .mud-select-input { font-size: var(--font-size-base); }
.mud-input-label { font-weight: 600; }
.mud-input-helper-text { font-size: var(--font-size-sm); color: var(--color-text-secondary); }

/* MudBlazor cards and tables sit flat.
   MudPaper, MudCard and MudTable all default to Elevation="1", so every one of them that does not
   say otherwise casts a drop shadow — a page of them is a page of soft grey halos. Flattened here
   rather than by passing Elevation="0" at each of the ~40 call sites that omit it, so a card added
   later lands flat too instead of depending on whoever writes it to remember.

   Only elevation 1 is touched. Popovers, menus and dialogs sit at 8 and above, where the lift is
   doing real work — telling you what floats over what — and they keep it. MudBlazor.min.css is
   linked ahead of this file (App.razor:52), so equal specificity is enough and !important is not. */
.mud-elevation-1 { box-shadow: none; }

/* The hairline replaces the shadow rather than just removing it. In the light theme a card surface
   (#FFFFFF) and the page behind it (#FAFAFC) are about two percent apart, so with nothing at the
   edge the card stops having one. This is the flat-plus-border treatment the fourteen call sites
   that already pass Elevation="0" Outlined="true" use, so the defaulted ones now agree with them.
   .mud-table needs naming separately — it carries its own surface and is not a .mud-paper. */
.mud-paper.mud-elevation-1,
.mud-table.mud-elevation-1 { border: 1px solid var(--mud-palette-lines-default); }

/* ════════════════════════════════════════════════════════════════
   Responsive data list (Task 9) — table on desktop, cards on phone.
   Custom tables: use .roc-table (wrap in .roc-table-wrap for framing).
   Give each <td data-label="Column"> so it stacks readably on mobile.
   MudTable: set Breakpoint + DataLabel on MudTd; styling below makes the
   stacked rows look like cards.
   ════════════════════════════════════════════════════════════════ */
.roc-table-wrap {
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
.roc-table { width: 100%; border-collapse: collapse; font-size: var(--font-size-base); }
.roc-table thead th {
    text-align: left;
    font-size: var(--font-size-sm);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-tertiary);
    padding: 12px var(--space-4);
    border-bottom: 1px solid var(--color-border-primary);
    white-space: nowrap;
}
.roc-table tbody td {
    padding: 13px var(--space-4);
    border-bottom: 1px solid var(--color-border-secondary);
    color: var(--color-text-primary);
    vertical-align: middle;
}
.roc-table tbody tr:last-child td { border-bottom: none; }
.roc-table tbody tr:hover { background: var(--color-bg-tertiary); }
.roc-table .roc-td-num { text-align: right; font-variant-numeric: tabular-nums; }
.roc-table .roc-td-actions { text-align: right; white-space: nowrap; }

/* This container query and the media query below it used to target the same selectors
   (.roc-table thead, tbody tr, tbody td, tbody td::before) at equal specificity, so file order
   alone decided the winner, and the media query was deliberately kept last so it won. Scoping
   every selector below to .roc-table-cq (see the next paragraph for why) added a second class to
   each one, raising this block from (0,1,x) to (0,2,x) while the media query's selectors stayed
   at (0,1,x). Higher specificity wins regardless of source order, so this block now wins every
   overlapping property against the media query below - padding, text-align, border-bottom, gap.

   That is accepted, not fought. A RocTable now collapses through the container query at every
   width instead of switching between "media query below 700px, container query between 768 and
   938" - simpler and more predictable, and it is exactly the behaviour this component keeps once
   the teardown plan deletes the media block below. Nothing renders differently today: no page
   outside Components/Ui uses any component of this layer, verified by grep. The cost lands during
   the page migration itself: a migrated table looks slightly different at phone width from an
   unmigrated one, card chrome versus a hairline list, for as long as both kinds of page exist.
   That is the "the app looks mixed while the migration runs" consequence the spec already names
   in section 11 and accepts.

   The container is .roc-table-cq, which only RocTable emits, and every selector inside the block
   below is scoped to it — precisely so the nine already-shipped pages that wrap a table in a bare
   .roc-table-wrap cannot match it. Their arithmetic is why: a card-wrapped table measures about
   viewport minus 378px (264px of sidebar plus var(--space-4), then var(--space-5) twice on
   .roc-main and twice on .roc-card), so it drops under 560px at roughly a 938px viewport. That
   is above the 767.98px where the sidebar hides and well above the 700px where the media query
   below takes over, so arming the query on .roc-table-wrap itself collapsed all nine into stacked
   lists on iPad portrait and half-screen desktop windows. No markup test can see that, because
   the change is pure CSS.

   It becomes the sole collapse, and phone rendering changes to match it, once the media query
   below is deleted during the MudBlazor teardown. The scoping is what has to be revisited then:
   either the legacy wrappers gain .roc-table-cq, or they have all become RocTable by then. */
.roc-table-cq { container-type: inline-size; }
.roc-table-caption {
    caption-side: top;
    text-align: left;
    padding: var(--space-3) var(--space-4) 0;
    font-size: var(--font-size-sm);
    font-weight: 700;
    color: var(--color-text-secondary);
}

@container (max-width: 560px) {
    .roc-table-cq .roc-table thead { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); border: 0; }
    .roc-table-cq .roc-table tbody tr {
        display: block;
        border-bottom: 1px solid var(--color-border-primary);
        padding: var(--space-3) 0;
    }
    .roc-table-cq .roc-table tbody tr:last-child { border-bottom: none; }
    .roc-table-cq .roc-table tbody td {
        display: flex;
        justify-content: space-between;
        gap: var(--space-3);
        border-bottom: none;
        padding: 4px var(--space-4);
        text-align: left;
    }
    .roc-table-cq .roc-table tbody td::before {
        content: attr(data-label);
        font-weight: 700;
        color: var(--color-text-secondary);
    }
    .roc-table-cq .roc-table tbody td.roc-td-num { text-align: right; }
}

/* MudTable — roomier + tokenized headers (desktop) */
.mud-table-root { font-size: var(--font-size-base); }
.mud-table-root .mud-table-head .mud-table-cell {
    font-size: var(--font-size-sm);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-tertiary);
}
.mud-table-root .mud-table-body .mud-table-cell { padding-top: 13px; padding-bottom: 13px; }

/* Phone: collapse custom tables into stacked cards via data-label */
@media (max-width: 700px) {
    .roc-table thead {
        position: absolute;
        width: 1px; height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }
    .roc-table, .roc-table tbody, .roc-table tr, .roc-table td { display: block; width: 100%; }
    .roc-table tbody tr {
        background: var(--color-bg-secondary);
        border: 1px solid var(--color-border-primary);
        border-radius: var(--border-radius-md);
        box-shadow: var(--shadow-sm);
        margin-bottom: var(--space-3);
        padding: 4px var(--space-3);
    }
    .roc-table tbody tr:hover { background: var(--color-bg-secondary); }
    .roc-table tbody td {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: var(--space-4);
        padding: 10px 0;
        border-bottom: 1px solid var(--color-border-secondary);
        text-align: right;
    }
    .roc-table tbody tr td:last-child { border-bottom: none; }
    .roc-table tbody td::before {
        content: attr(data-label);
        font-size: var(--font-size-sm);
        font-weight: 700;
        color: var(--color-text-secondary);
        text-align: left;
        margin-right: var(--space-3);
    }
    .roc-table tbody td:not([data-label])::before,
    .roc-table tbody td[data-label=""]::before { content: none; }
    .roc-table .roc-td-num, .roc-table .roc-td-actions { text-align: right; }

    /* MudTable stacked rows → card look */
    .mud-table-root.mud-table-smalldevice .mud-table-body .mud-table-row {
        border: 1px solid var(--color-border-primary);
        border-radius: var(--border-radius-md);
        box-shadow: var(--shadow-sm);
        margin-bottom: var(--space-3);
        background: var(--color-bg-secondary);
    }
}

/* ════════════════════════════════════════════════════════════════
   Dialogs (Task 10) — light, rounded, roomy. MudBlazor provides the
   focus-trap, Esc-to-close, and backdrop; we restyle the surface + copy.
   ════════════════════════════════════════════════════════════════ */
.mud-dialog {
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-lg);
    border: 1px solid var(--color-border-primary);
}
.mud-dialog-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: var(--font-size-lg);
    color: var(--color-text-primary);
}
.mud-dialog-content { font-size: var(--font-size-base); color: var(--color-text-primary); }
.roc-dialog-message { line-height: var(--line-height-relaxed); }
.mud-dialog-actions { gap: var(--space-2); padding: var(--space-4) var(--space-5) var(--space-5); }

/* ════════════════════════════════════════════════════════════════
   Empty states (Task 11) — friendly "nothing here yet" via <EmptyState>:
   soft icon + one-line explanation + a clear next action.
   ════════════════════════════════════════════════════════════════ */
.roc-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-3);
    padding: var(--space-7) var(--space-5);
    max-width: 440px;
    margin: 0 auto;
}
.roc-empty-icon {
    width: 64px;
    height: 64px;
    display: grid;
    place-items: center;
    border-radius: var(--border-radius-full);
    background: var(--color-accent-soft);
    color: var(--color-accent-strong);
    margin-bottom: var(--space-1);
}
.roc-error-icon {
    background: var(--color-status-warning-soft);
    color: var(--color-status-warning);
}
.roc-empty-icon svg { width: 30px; height: 30px; }
.roc-empty-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: var(--font-size-lg);
    color: var(--color-text-primary);
    margin: 0;
}
.roc-empty-desc {
    color: var(--color-text-secondary);
    font-size: var(--font-size-base);
    margin: 0;
    max-width: 42ch;
    line-height: var(--line-height-relaxed);
}
.roc-empty-action { margin-top: var(--space-2); }

/* ════════════════════════════════════════════════════════════════
   Feedback (Task 12) — toasts, tooltips, inline tips.
   Snackbar/tooltip severity colors come from the theme; here we tokenize
   shape/shadow/typography. <Tip> gives inline guidance (info/tip/success/warn).
   ════════════════════════════════════════════════════════════════ */
.mud-snackbar .mud-snackbar-surface {
    border-radius: var(--border-radius-md);
    box-shadow: var(--shadow-lg);
    font-family: var(--font-body);
    font-size: var(--font-size-base);
    font-weight: 600;
}
.mud-snackbar .mud-snackbar-content-message { font-family: var(--font-body); }

/* Calmer progress bar */
.mud-progress-linear {
    height: 8px !important;
    border-radius: var(--border-radius-full);
    overflow: hidden;
    background: var(--color-bg-tertiary);
}
.mud-progress-linear .mud-progress-linear-bar { border-radius: var(--border-radius-full); }

.mud-tooltip {
    background: var(--color-bg-inverse);
    color: var(--color-text-inverse);
    border-radius: var(--border-radius-sm);
    font-family: var(--font-body);
    font-size: var(--font-size-sm);
    font-weight: 600;
    padding: 6px 10px;
    box-shadow: var(--shadow-md);
}

.roc-tip {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--border-radius-md);
    background: var(--color-accent-secondary-soft);
    color: var(--color-text-primary);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
}
.roc-tip strong { font-weight: 800; }
.roc-tip-icon { flex: 0 0 auto; color: var(--color-accent-secondary-strong); margin-top: 1px; }
.roc-tip-icon svg { width: 18px; height: 18px; display: block; }
.roc-tip-body { min-width: 0; }
.roc-tip-body :last-child { margin-bottom: 0; }

.roc-tip--tip { background: var(--color-accent-soft); }
.roc-tip--tip .roc-tip-icon { color: var(--color-accent-strong); }
.roc-tip--success { background: var(--color-status-success-soft); }
.roc-tip--success .roc-tip-icon { color: var(--color-status-success); }
.roc-tip--warning { background: var(--color-status-warning-soft); }
.roc-tip--warning .roc-tip-icon { color: var(--color-status-warning); }

/* Dashboard account card (GroupCard child element — styled globally) */
.home-account { max-width: 560px; }

/* Header crowding relief on small screens (full mobile nav lands in Task 4) */
@media (max-width: 480px) {
    .roc-header { padding: 0 var(--space-4); gap: var(--space-3); }
    .roc-user-name { display: none; }
}

.roc-layout { display: flex; flex: 1; min-height: 0; }

.roc-sidebar {
    width: var(--sidebar-width);
    min-width: var(--sidebar-width);
    margin: var(--space-4) 0 var(--space-4) var(--space-4);
    padding: var(--space-3);
    border-radius: var(--border-radius-lg);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    box-shadow: var(--shadow-sm);
    align-self: flex-start;
    position: sticky;
    top: calc(var(--header-height) + var(--space-3));
    max-height: calc(100vh - var(--header-height) - var(--space-6));
    overflow-y: auto;
}

.roc-main { flex: 1; padding: var(--space-5); overflow: auto; min-width: 0; }

/* Nav */
.roc-nav { display: flex; flex-direction: column; gap: var(--space-4); }
.roc-nav-section { display: flex; flex-direction: column; gap: 2px; }
.roc-nav-heading {
    padding: var(--space-2) var(--space-3) 6px;
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--color-text-tertiary);
}
.roc-nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 44px;
    padding: 10px 12px;
    border-radius: var(--border-radius-md);
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    font-weight: 700;
    transition: background .15s, color .15s;
}
.roc-nav-link svg { width: 19px; height: 19px; flex: 0 0 auto; opacity: .8; }
.roc-nav-link:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); text-decoration: none; }
.roc-nav-link:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-nav-link.active { color: var(--color-text-primary); background: var(--color-accent-soft); }
.roc-nav-link.active::before {
    content: "";
    position: absolute;
    left: calc(-1 * var(--space-3)); top: 8px; bottom: 8px;
    width: 3px;
    border-radius: 0 16px 16px 0;
    background: var(--color-accent-primary);
}
.roc-nav-link.active svg { opacity: 1; color: var(--color-accent-primary); }

/* Disabled "coming soon" nav items */
.roc-nav-link-soon {
    cursor: default;
    color: var(--color-text-tertiary);
}
.roc-nav-link-soon:hover { background: transparent; color: var(--color-text-tertiary); }
.roc-nav-link-soon svg { opacity: .55; }
.roc-soon {
    margin-left: auto;
    font-size: 10px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--color-text-tertiary);
    background: var(--color-bg-tertiary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-full);
    padding: 2px 8px;
}

/* ════════════════════════════════════════════════════════════════
   Mobile bottom navigation + "More" sheet (Task 4)
   Desktop: hidden. Phone (<768px): sidebar hides, bottom bar shows.
   ════════════════════════════════════════════════════════════════ */
.roc-bottombar { display: none; }
.roc-bottom-link {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: var(--bottombar-height);
    padding: 6px 2px;
    border: none;
    background: none;
    cursor: pointer;
    color: var(--color-text-secondary);
    font-family: var(--font-body);
    font-size: 11px;
    font-weight: 700;
    text-decoration: none;
    transition: color .15s;
}
.roc-bottom-link svg { width: 24px; height: 24px; }
.roc-bottom-link:hover { color: var(--color-text-primary); text-decoration: none; }
.roc-bottom-link:focus-visible { outline: none; box-shadow: var(--shadow-focus); border-radius: var(--border-radius-sm); }
.roc-bottom-link.active { color: var(--color-accent-strong); }
.roc-bottom-link.active svg { color: var(--color-accent-primary); }

.roc-sheet-backdrop {
    position: fixed; inset: 0; z-index: 60;
    background: var(--color-surface-overlay);
    animation: roc-fade-in .15s var(--easing-out);
}
.roc-sheet {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 61;
    max-height: 80vh; overflow-y: auto;
    background: var(--color-bg-secondary);
    border-radius: var(--border-radius-lg) var(--border-radius-lg) 0 0;
    border-top: 1px solid var(--color-border-primary);
    box-shadow: var(--shadow-lg);
    padding: var(--space-2) var(--space-4) calc(var(--space-5) + env(safe-area-inset-bottom, 0px));
    animation: roc-slide-up .22s var(--easing-out);
}
.roc-sheet-handle {
    width: 40px; height: 4px; border-radius: 999px;
    background: var(--color-border-strong);
    margin: 4px auto var(--space-3);
}
.roc-sheet-head {
    display: flex; align-items: center; justify-content: space-between;
    font-family: var(--font-display);
    font-weight: 800; font-size: var(--font-size-md);
    margin-bottom: var(--space-2);
}
@keyframes roc-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes roc-slide-up { from { transform: translateY(100%); } to { transform: translateY(0); } }

@media (max-width: 767.98px) {
    .roc-sidebar { display: none; }
    .roc-main {
        padding: var(--space-4);
        padding-bottom: calc(var(--bottombar-height) + var(--space-5) + env(safe-area-inset-bottom, 0px));
    }
    .roc-bottombar {
        display: flex;
        position: fixed;
        left: 0; right: 0; bottom: 0;
        z-index: 50;
        padding-bottom: env(safe-area-inset-bottom, 0px);
        background: var(--color-bg-secondary);
        border-top: 1px solid var(--color-border-primary);
        box-shadow: 0 -2px 12px -6px rgba(28, 33, 51, .18);
    }
}

/* Safety: never show the mobile sheet/bar on desktop, even if state lingers. */
@media (min-width: 768px) {
    .roc-sheet, .roc-sheet-backdrop { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .roc-sheet, .roc-sheet-backdrop { animation: none; }
}

/* Login page */
.roc-login-wrap {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg-primary);
    padding: var(--space-5);
}

.roc-login-card {
    width: 100%;
    max-width: 400px;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-6) var(--space-6) var(--space-7);
}

.roc-login-brand {
    display: flex;
    justify-content: center;
    margin-bottom: var(--space-4);
}
.roc-login-brand .roc-brand-mark {
    width: 52px; height: 52px;
    font-size: 24px;
    border-radius: var(--border-radius-lg);
}

.roc-login-title {
    font-family: var(--font-display);
    font-weight: 800;
    font-size: var(--font-size-xl);
    letter-spacing: -0.01em;
    text-align: center;
    color: var(--color-text-primary);
    margin: 0 0 6px;
}

.roc-login-sub {
    text-align: center;
    color: var(--color-text-secondary);
    font-size: var(--font-size-base);
    margin-bottom: var(--space-6);
}
.roc-system-ref {
    text-align: center;
    color: var(--color-text-tertiary);
    font-size: var(--font-size-sm);
    margin: 0 0 var(--space-2);
}
.roc-system-ref code {
    font-family: var(--font-mono, monospace);
    color: var(--color-text-secondary);
    word-break: break-all;
}

/* ---- First-visit onboarding ---- */
.roc-onboard-overlay {
    position: fixed; inset: 0; z-index: 1400;
    background: rgba(20, 14, 12, .45);
    display: flex; align-items: center; justify-content: center;
    padding: var(--space-4);
    animation: roc-onboard-fade .2s ease;
}
@keyframes roc-onboard-fade { from { opacity: 0; } to { opacity: 1; } }
.roc-onboard-card {
    position: relative;
    width: 100%; max-width: 420px;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-xl);
    box-shadow: var(--shadow-lg);
    padding: var(--space-7) var(--space-6) var(--space-6);
    text-align: center;
    animation: roc-onboard-pop .22s ease;
}
@keyframes roc-onboard-pop { from { transform: translateY(8px) scale(.98); opacity: 0; } to { transform: none; opacity: 1; } }
.roc-onboard-skip {
    position: absolute; top: 10px; right: 10px;
    background: transparent; border: none;
    color: var(--color-text-tertiary); cursor: pointer;
    font-size: var(--font-size-sm); font-weight: 700;
    padding: 8px 12px; border-radius: var(--border-radius-md);
    min-height: 44px;
}
.roc-onboard-skip:hover { color: var(--color-text-primary); background: var(--color-bg-tertiary); }
.roc-onboard-skip:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-onboard-art {
    width: 64px; height: 64px; margin: 0 auto var(--space-4);
    display: grid; place-items: center;
    border-radius: 50%;
    background: var(--color-accent-soft);
    color: var(--color-accent-strong);
}
.roc-onboard-art svg { width: 32px; height: 32px; }
.roc-onboard-title {
    font-size: var(--font-size-xl); font-weight: 800;
    color: var(--color-text-primary);
    margin: 0 0 var(--space-2);
}
.roc-onboard-body {
    font-size: var(--font-size-base);
    color: var(--color-text-secondary);
    line-height: var(--line-height-normal);
    margin: 0 0 var(--space-5);
}
.roc-onboard-dots {
    display: flex; gap: 7px; justify-content: center;
    margin-bottom: var(--space-5);
}
.roc-onboard-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--color-border-strong);
    transition: background .15s, transform .15s;
}
.roc-onboard-dot.is-active {
    background: var(--color-accent-primary);
    transform: scale(1.25);
}
.roc-onboard-actions {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3);
}
.roc-onboard-actions .btn-primary { min-width: 132px; text-align: center; }

/* "Coming soon" dashboard tile */
.home-tile--soon { cursor: default; opacity: .72; }
.home-tile--soon .home-tile-icon { opacity: .85; }

.roc-input-group {
    margin-bottom: var(--space-4);
}

/* A label with something beside it — an info tip, usually. The label keeps its own margin, so the
   row aligns on the text rather than on the box below it. */
.roc-label-row { display: flex; align-items: baseline; gap: 2px; }
.roc-label-row .roc-input-label { margin-bottom: 6px; }

.roc-input-label {
    display: block;
    font-size: var(--font-size-sm);
    color: var(--color-text-secondary);
    font-weight: 700;
    margin-bottom: 6px;
}

.roc-input {
    width: 100%;
    min-height: 46px;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-strong);
    color: var(--color-text-primary);
    padding: 11px 14px;
    border-radius: var(--border-radius-md);
    font-size: var(--font-size-base);
    outline: none;
    font-family: inherit;
    transition: border-color .15s, box-shadow .15s;
}
.roc-input::placeholder { color: var(--color-text-tertiary); }
.roc-input:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }

/* Keep me signed in. Sits under the password rather than beside the submit button, so the
   consequence is read before the button is pressed rather than after. */
.roc-remember {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    font-size: var(--font-size-sm);
    color: var(--color-text-secondary);
}
.roc-remember input {
    width: 16px;
    height: 16px;
    margin: 0;
    accent-color: var(--color-accent-primary);
    cursor: pointer;
}
.roc-remember-help {
    margin: var(--space-1) 0 0 24px;
    font-size: var(--font-size-xs);
    color: var(--color-text-tertiary);
}

.roc-submit {
    width: 100%;
    min-height: 48px;
    margin-top: var(--space-2);
    background: var(--color-accent-primary);
    border: none;
    color: var(--color-on-accent);
    border-radius: var(--border-radius-md);
    font-family: var(--font-body);
    font-size: var(--font-size-md);
    font-weight: 800;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: background-color .15s, box-shadow .15s;
}
.roc-submit:hover { background: var(--color-accent-primary-hover); }
.roc-submit:active { background: var(--color-accent-primary-active); }
.roc-submit:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
/* button-styled anchor (e.g. "Go to sign in" after a successful reset) */
.roc-submit-link { display: block; text-align: center; text-decoration: none; line-height: 48px; }

.roc-login-link {
    display: block; text-align: center; margin-top: var(--space-4);
    color: var(--color-accent-secondary-strong); font-size: var(--font-size-sm);
    font-weight: 700; text-decoration: none;
}
.roc-login-link:hover { text-decoration: underline; }

.roc-error {
    color: var(--color-status-error);
    font-size: var(--font-size-sm);
    font-weight: 600;
    text-align: center;
    margin-bottom: var(--space-4);
    padding: 10px 12px;
    background: var(--color-status-error-soft);
    border: 1px solid var(--color-status-error);
    border-radius: var(--border-radius-md);
}

/* Error UI */
#blazor-error-ui {
    background: var(--bg2);
    border-top: 1px solid var(--red);
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0,0,0,0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
    color: var(--text);
}

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

/* ──────────────────────────────────────────────────────────────────
   Royal Tech (and, later, Normal Tech) — class names ported verbatim
   from the old HTML tool so the port stays readable side-by-side.
   ────────────────────────────────────────────────────────────────── */

.rtp-wrap { display: flex; flex-direction: column; min-height: calc(100vh - var(--header-height) - 150px); gap: var(--space-3); }

.rtp-toolbar {
    display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
    padding: var(--space-3);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
}

.rtp-kpi {
    background: var(--color-bg-tertiary); border: 1px solid var(--color-border-primary);
    border-radius: 999px; padding: 5px 14px; font-size: var(--font-size-sm);
    color: var(--color-text-secondary);
}
.rtp-kpi strong { color: var(--color-accent-strong); font-weight: 800; margin-left: 4px; }

.rtp-legend { font-size: var(--font-size-sm); color: var(--color-text-tertiary); }

.rtp-search {
    flex: 1; min-width: 180px; max-width: 320px;
    min-height: 40px;
    background: var(--color-bg-secondary); border: 1px solid var(--color-border-strong); color: var(--color-text-primary);
    padding: 8px 12px; border-radius: var(--border-radius-md); font-size: var(--font-size-base); font-family: inherit;
    outline: none;
}
.rtp-search::placeholder { color: var(--color-text-tertiary); }
.rtp-search:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }

.rtp-btn {
    display: inline-flex; align-items: center; gap: 6px;
    min-height: 40px;
    background: var(--color-bg-secondary); border: 1px solid var(--color-border-strong);
    color: var(--color-text-primary); padding: 8px 14px; border-radius: var(--border-radius-md);
    font-size: var(--font-size-sm); font-weight: 700; cursor: pointer; font-family: inherit;
    transition: background .15s, color .15s, border-color .15s, box-shadow .15s;
}
.rtp-btn:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
.rtp-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.rtp-btn.active { color: var(--color-text-primary); border-color: var(--color-accent-primary); background: var(--color-accent-soft); }
.rtp-btn.rtp-btn-todo.active { background: var(--color-accent-soft); }
.rtp-btn-danger { color: var(--color-status-error); border-color: var(--color-status-error); }
.rtp-btn-danger:hover { background: var(--color-status-error-soft); color: var(--color-status-error); }
.rtp-btn-save { color: var(--color-accent-strong); border-color: var(--color-accent-primary); }

/* The stat chips are what makes this bar tall on a phone: each one is a single line of prose
   ("FM to max all ≥ 12,759,357") too long to sit beside its neighbour, so three of them cost three
   rows and leave a third of the width empty. Breaking the label onto its own line above the number
   makes each chip narrow enough for two per row — 331px of toolbar becomes 254px on a 320px screen,
   which is the difference between seeing a tech on the first screen and not.

   Deliberately NOT a grid: equal tracks force the long labels to wrap and every candidate grid
   measured *taller* than the flex row it replaced. The legend joins in as a half-width item because
   it is the one remaining chip-sized thing in the bar. */
@media (max-width: 600px) {
    .rtp-toolbar { gap: 8px; padding: 10px; }
    .rtp-toolbar .rtp-kpi {
        flex: 1 1 calc(50% - 6px);
        min-width: 0;
        display: flex; flex-direction: column;
        padding: 5px 10px;
        font-size: 11px; line-height: 1.25;
    }
    .rtp-toolbar .rtp-kpi strong { margin-left: 0; }
    .rtp-toolbar .rtp-legend {
        flex: 1 1 calc(50% - 6px);
        min-width: 0;
        align-self: center;
        font-size: 11px;
    }
}

/* Royal/Normal segmented switch (Tech page header) */
.tech-switch {
    display: inline-flex;
    background: var(--color-bg-tertiary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-full);
    padding: 3px;
    gap: 2px;
}
.tech-switch-tab {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; padding: 9px 18px;
    border-radius: var(--border-radius-full);
    font-size: var(--font-size-sm); font-weight: 700;
    color: var(--color-text-secondary);
    text-decoration: none;
    transition: background .15s, color .15s;
}
.tech-switch-tab:hover { color: var(--color-text-primary); text-decoration: none; }
.tech-switch-tab.active { background: var(--color-accent-primary); color: var(--color-on-accent); }
.tech-switch-tab:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

/* The z-index here is load-bearing, and not for the reason it looks like.
   The Aurora glass layer at the bottom of this file gives .rtp-filterbar a backdrop-filter, and a
   backdrop-filter creates a stacking context. That traps the open dropdown inside the filter bar:
   its own z-index: 50 can only order it against its siblings *within* the bar, so the stat bar and
   the books — later siblings, each with a glass stacking context of their own — paint straight over
   it however high that number goes. Lifting the whole bar is what actually gets the panel out.
   Kept under .roc-header (40) so a dropdown never covers the sticky header. */
.rtp-filterbar {
    position: relative; z-index: 30;
    display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
    padding: 8px 12px; background: var(--bg2);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
}
.rtp-dd-wrap { position: relative; }
.rtp-dd-btn {
    background: var(--bg3); border: 1px solid var(--border);
    color: var(--text2); padding: 6px 10px; border-radius: var(--border-radius-lg);
    font-size: 12px; cursor: pointer; font-family: inherit; min-width: 140px;
    text-align: left;
}
.rtp-dd-btn:hover { background: var(--bg4); color: var(--text); }
.rtp-dd-panel {
    position: absolute; z-index: 50; top: calc(100% + 4px); left: 0;
    background: var(--bg2); border: 1px solid var(--border2); border-radius: var(--border-radius-lg);
    padding: 8px; display: flex; flex-direction: column; gap: 4px;
    min-width: 200px; max-height: 320px; overflow-y: auto;
    box-shadow: 0 6px 22px rgba(0,0,0,0.4);
}
.rtp-dd-row {
    display: flex; align-items: center; gap: 6px; font-size: 12px;
    color: var(--text); cursor: pointer; padding: 3px 4px; border-radius: var(--border-radius-lg);
}
.rtp-dd-row:hover { background: var(--bg3); }
.rtp-dd-row input[type=checkbox] { margin: 0; accent-color: var(--color-accent-strong); }
.rtp-dd-clear {
    margin-top: 4px; padding-top: 6px; border-top: 1px solid var(--border);
    font-size: 11px; color: var(--red); cursor: pointer; text-align: center;
}

/* Troop / buff / context badges */
.rtp-badge {
    display: inline-block; padding: 1px 6px; border-radius: var(--border-radius-lg);
    font-size: 10px; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.04em; border: 1px solid transparent;
    vertical-align: middle;
}
.bd-all { color: var(--text2); border-color: var(--border); }
.bd-footman { color: var(--footman); border-color: rgba(63, 185, 80, 0.4); }
.bd-archer { color: var(--archer); border-color: rgba(56, 139, 253, 0.4); }
.bd-cavalry { color: var(--cavalry); border-color: rgba(227, 179, 65, 0.4); }
.bd-calamity { color: var(--color-status-error); border-color: rgba(255, 132, 132, 0.4); }
.bd-judicator { color: var(--purple); border-color: rgba(163, 113, 247, 0.4); }
.bd-destroyer { color: var(--color-status-warning); border-color: rgba(255, 158, 100, 0.4); }
.bd-evilbuster { color: var(--color-accent-secondary-strong); border-color: rgba(118, 200, 255, 0.4); }
.bd-terminator { color: var(--color-status-error); border-color: rgba(255, 110, 110, 0.4); }
.bd-punisher { color: var(--purple); border-color: rgba(199, 138, 240, 0.4); }

.bt-might, .bt-damageout { color: var(--color-status-error); border-color: rgba(248, 81, 73, 0.4); }
.bt-resist, .bt-damagered { color: var(--color-status-success); border-color: rgba(86, 211, 100, 0.4); }
.bt-hp { color: var(--purple); border-color: rgba(163, 113, 247, 0.4); }
.bt-tactmight { color: var(--cavalry); border-color: rgba(227, 179, 65, 0.4); }
.bt-tactresist { color: var(--color-accent-secondary-strong); border-color: rgba(88, 166, 255, 0.4); }
.bt-combatspeed { color: var(--color-text-secondary); border-color: rgba(139, 148, 158, 0.4); }
.bt-cap, .bt-research, .bt-costred, .bt-training, .bt-heal {
    color: var(--text2); border-color: var(--border);
}

/* "General" — applies wherever you fight, so it reads neutral like the All troop badge. */
.ctx-all { color: var(--text2); border-color: var(--border); }
.ctx-field { color: var(--color-status-success); border-color: rgba(86, 211, 100, 0.4); }
.ctx-siege, .ctx-siegeatk, .ctx-siegedef { color: var(--color-status-warning); border-color: rgba(255, 158, 100, 0.4); }

/* Stat bar */
.rtp-statbar {
    background: var(--bg2); border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 10px 12px;
}
.rtp-statbar-hdr {
    display: flex; justify-content: space-between; align-items: baseline;
    font-size: 11px; color: var(--text3); text-transform: uppercase;
    letter-spacing: 0.06em; margin-bottom: 8px;
}
.rtp-statbar-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
/* Four stat cards with large numbers overflow a phone; go 2×2 on narrow screens. */
@media (max-width: 560px) { .rtp-statbar-grid { grid-template-columns: repeat(2, 1fr); } }
.rtp-statcard {
    background: var(--bg3); border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 8px 10px;
}
.rtp-statcard-title {
    font-size: 12px; font-weight: 700; margin-bottom: 6px;
    display: flex; align-items: center; gap: 6px;
}
.rtp-statcard-title.foot { color: var(--footman); }
.rtp-statcard-title.arch { color: var(--archer); }
.rtp-statcard-title.cav { color: var(--cavalry); }
.rtp-statcard-title.gen { color: var(--color-accent-strong); }
.rtp-statrow {
    display: flex; justify-content: space-between; font-size: 11px;
    padding: 1px 0; color: var(--text2);
}
.rtp-statrow .v { color: var(--text); font-weight: 600; }
.rtp-statrow.zero .v { color: var(--text3); font-weight: 400; }

/* Books / sections / techs */
.rtp-books { display: flex; flex-direction: column; gap: 10px; }
.rtp-book {
    background: var(--bg2); border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    overflow: hidden;
}
.rtp-book-hdr {
    display: flex; align-items: center; gap: 12px; padding: 10px 14px;
    background: var(--surface-inset); cursor: pointer;
    border-bottom: 1px solid var(--border);
}
.rtp-book-hdr:hover { background: var(--bg4); }
.rtp-book-name { color: var(--color-accent-strong); font-weight: 700; font-size: 14px; flex: 1; }
.rtp-book-meta { display: flex; align-items: center; gap: 12px; font-size: 11px; color: var(--text2); }
.rtp-book-meta .chev { color: var(--text3); transition: transform 0.2s; }
/* Fill or empty a whole book. Inside the header, which is itself the collapse toggle, so these
   stop the click travelling — and they are the only interactive things in a bar that is otherwise
   one big button, hence the gap that keeps them off the numbers. */
.rtp-book-actions { display: inline-flex; align-items: center; gap: 6px; margin-left: 4px; }
@media (max-width: 767.98px) {
    /* On a phone the header already wraps; the buttons go with the numbers rather than pushing the
       chevron off the edge. */
    .rtp-book-actions { margin-left: 0; }
}
.rtp-book.rtp-book-collapsed .rtp-book-body { display: none; }
.rtp-book.rtp-book-collapsed .chev { transform: rotate(-90deg); }

.rtp-book-body { padding: 6px 10px 10px 10px; }
/* Rows sat flush against each other — measured 151px tall and 151px apart, so nothing separated
   one tech from the next. A gap on the section spaces them without touching the row itself. */
.rtp-section { margin-top: 6px; display: flex; flex-direction: column; gap: 6px; }
.rtp-section-name {
    font-size: 10px; color: var(--color-accent-secondary-strong); text-transform: uppercase;
    letter-spacing: 0.08em; padding: 6px 2px 4px 2px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 4px;
}

.rtp-tech {
    display: flex; align-items: center; gap: 10px;
    padding: 4px 6px; border: 1px solid transparent; border-radius: var(--border-radius-lg);
    min-height: 28px;
}
.rtp-tech:hover { border-color: var(--border); background: var(--color-bg-tertiary); }

/* A tech waiting on another. Dimmed rather than hidden: knowing it is there, and what it needs, is
   the point of showing the tree at all. The spec planner already reserves --sp-locked-* for exactly
   this state, so the two pages agree on what "locked" looks like. */
.rtp-tech-locked { opacity: 0.55; }
.rtp-tech-locked .rtp-tech-name { color: var(--sp-locked-text); }
.rtp-tech-locked .rtp-tech-slider { cursor: not-allowed; }
.rtp-lock { font-size: 10px; flex: 0 0 auto; line-height: 1; }
.ntp-locked-note { font-size: 10px; color: var(--sp-locked-text); }
.rtp-tech-cb { accent-color: var(--color-accent-strong); margin: 0; cursor: pointer; }
.rtp-tech-name {
    font-size: 12px; color: var(--text); flex: 0 0 200px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    display: flex; align-items: center; gap: 6px;
}
.rtp-tech-desc {
    flex: 1; font-size: 11px; color: var(--text3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rtp-tech-troops { display: flex; gap: 3px; }
.rtp-tech-slider {
    flex: 0 0 140px; accent-color: var(--color-accent-secondary); height: 3px;
}
.rtp-tech-lv { font-size: 11px; color: var(--text2); flex: 0 0 auto; min-width: 52px; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rtp-tech-cost { font-size: 11px; color: var(--text3); width: 90px; text-align: right; }
.rtp-tech.rtp-tech-maxed .rtp-tech-name,
.rtp-tech.rtp-tech-maxed .rtp-tech-lv { color: var(--color-status-success); }
.rtp-tech.rtp-tech-done {
    opacity: 0.45;
}
.rtp-tech.rtp-tech-done .rtp-tech-name,
.rtp-tech.rtp-tech-done .rtp-tech-desc { text-decoration: line-through; color: var(--text3); }

.rtp-loading {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: var(--space-4);
    padding: 56px 24px;
    text-align: center;
    color: var(--color-text-secondary);
    font-size: var(--font-size-base);
    font-weight: 600;
}
.rtp-loading::before {
    content: "";
    width: 38px; height: 38px;
    border-radius: 50%;
    border: 3px solid var(--color-border-strong);
    border-top-color: var(--color-accent-primary);
    animation: roc-spin .8s linear infinite;
}
@keyframes roc-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .rtp-loading::before { animation-duration: 2.4s; }
}

/* ──────────────────────────────────────────────────────────────────
   Normal Tech — grid of node cards + per-node modal
   ────────────────────────────────────────────────────────────────── */

.ntp-rows { display: flex; flex-direction: column; gap: 14px; }
/* Padding sits on the header and body rather than the row, so the header's own background reaches
   the edges the way the Royal Tech book headers do. */
.ntp-row {
    background: var(--bg2); border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    overflow: hidden;
}
.ntp-row-hdr {
    display: flex; align-items: center; gap: 12px; padding: 10px 12px;
    background: var(--surface-inset); cursor: pointer;
    border-bottom: 1px solid var(--border);
}
.ntp-row-hdr:hover { background: var(--bg4); }
.ntp-row-name {
    color: var(--color-accent-strong); font-weight: 700; font-size: 13px;
    letter-spacing: 0.04em; text-transform: uppercase;
    flex: 1;
}
.ntp-row-meta {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    font-size: 11px; color: var(--text2);
}
.ntp-row-meta .chev { color: var(--text3); transition: transform 0.2s; }
.ntp-row-body { padding: 10px 12px; }
.ntp-row.ntp-row-collapsed .ntp-row-body { display: none; }
.ntp-row.ntp-row-collapsed .chev { transform: rotate(-90deg); }
.ntp-row-grid {
    display: grid; gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr));
}

.ntp-node {
    background: var(--bg3); border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 10px 12px; cursor: pointer;
    transition: border-color 0.12s, transform 0.12s;
    display: flex; flex-direction: column; gap: 6px;
    min-height: 100px;
}
.ntp-node:hover { border-color: var(--border2); transform: translateY(-1px); }
.ntp-node.ntp-done { border-color: rgba(63, 185, 80, 0.35); }
.ntp-node.ntp-done .ntp-node-name { color: var(--footman); }
.ntp-node.ntp-partial { border-color: rgba(227, 179, 65, 0.4); }
.ntp-node.ntp-hidden { display: none; }

.ntp-node-hdr { display: flex; align-items: center; gap: 6px; }
.ntp-node-status { font-size: 12px; }
.ntp-node-name { font-size: 13px; font-weight: 700; color: var(--text); }
.ntp-node-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ntp-season { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.04em; }
.ntp-node-stats {
    display: flex; gap: 10px; font-size: 11px; color: var(--text2);
}

.ntp-medal { padding: 1px 6px; font-size: 10px; font-weight: 700; border-radius: var(--border-radius-lg); border: 1px solid transparent; }
.ntp-medal-wb  { color: var(--cavalry); border-color: rgba(227, 179, 65, 0.4); }
.ntp-medal-cm  { color: var(--archer);  border-color: rgba(56, 139, 253, 0.4); }
.ntp-medal-res { color: var(--footman); border-color: rgba(63, 185, 80, 0.4); }
.ntp-medal-mix { color: var(--purple);  border-color: rgba(163, 113, 247, 0.4); }
/* Fearless Medals — Royal Tech's currency, so it needs a colour of its own even though no
   Normal Tech section is ever paid for in it. */
.ntp-medal-fm  { color: var(--color-status-error); border-color: rgba(248, 81, 73, 0.4); }

.ntp-progress-bar {
    height: 4px; background: var(--bg); border-radius: var(--border-radius-lg); overflow: hidden;
    margin-top: auto;
}
.ntp-progress-fill {
    height: 100%; background: linear-gradient(90deg, var(--accent), var(--cavalry));
    transition: width 0.2s;
}
.ntp-progress-text { font-size: 10px; color: var(--text3); }

/* Modal */
.ntp-modal-backdrop {
    position: fixed; inset: 0; background: rgba(0,0,0,0.65);
    z-index: 1000;
}
.ntp-modal-box {
    position: fixed; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: min(760px, 92vw); max-height: 85vh;
    background: var(--bg2); border: 1px solid var(--border2); border-radius: var(--border-radius-lg);
    box-shadow: 0 18px 60px rgba(0,0,0,0.6);
    display: flex; flex-direction: column;
    z-index: 1001;
}
.ntp-modal-hdr {
    display: flex; align-items: center; justify-content: space-between;
    padding: 12px 16px; border-bottom: 1px solid var(--border);
    background: var(--surface-inset);
    border-radius: 16px 16px 0 0;
}
.ntp-modal-title {
    color: var(--color-accent-strong); font-weight: 700; font-size: 14px;
    display: flex; align-items: center; gap: 8px;
}
.ntp-modal-close {
    background: none; border: none; color: var(--text2);
    font-size: 16px; cursor: pointer; padding: 4px 8px;
}
.ntp-modal-close:hover { color: var(--red); }
.ntp-modal-body {
    flex: 1; overflow-y: auto; padding: 8px 12px;
    display: flex; flex-direction: column; gap: 6px;
}
.ntp-modal-footer {
    display: flex; gap: 8px; justify-content: flex-end;
    padding: 10px 16px; border-top: 1px solid var(--border);
}

.ntp-mtech {
    display: flex; flex-direction: column; gap: 4px;
    padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    background: var(--bg3);
}
.ntp-mtech.rtp-tech-maxed { border-color: rgba(63, 185, 80, 0.4); }
.ntp-mtech-row1 { display: flex; justify-content: space-between; gap: 10px; }
.ntp-mtech-name { font-size: 12px; font-weight: 700; color: var(--text); }
.ntp-mtech-desc { font-size: 11px; color: var(--text3); flex: 1; text-align: right; }
.ntp-mtech-row2 { display: flex; align-items: center; gap: 8px; }
.ntp-mtech-row2 .rtp-tech-slider { flex: 1; }
.ntp-mtech-lv { font-size: 11px; color: var(--text2); width: 44px; text-align: right; }
.ntp-mtech-qq { padding: 2px 8px; font-size: 11px; }
.ntp-mtech-row3 { display: flex; flex-direction: column; gap: 2px; font-size: 11px; }
.ntp-next-cost { font-weight: 700; color: var(--color-accent-secondary-strong); }
.ntp-max-cost { color: var(--text3); }

/* ──────────────────────────────────────────────────────────────────
   Hero Combos
   ────────────────────────────────────────────────────────────────── */

.hc-wrap { display: flex; flex-direction: column; gap: 12px; min-height: calc(100vh - 97px); }

.hc-ctrls {
    display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
    padding: 10px 12px; background: var(--bg2);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
}
.hc-sel {
    background: var(--color-bg-secondary); border: 1px solid var(--color-border-strong);
    color: var(--color-text-primary); padding: 8px 12px; border-radius: var(--border-radius-md);
    font-size: var(--font-size-sm); font-family: inherit; outline: none;
    min-width: 140px; min-height: 40px;
}
.hc-sel:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
.hc-count { font-size: 11px; color: var(--text2); margin-left: auto; }

/* min-width: 140px is what a select needs to show "✦ Combo partners only" on a desktop, and it is
   also what stops two of them sharing a line on a phone — 140 + 140 + the gap does not fit the
   280px this row gets at 320px, so four filters cost four rows. Dropping the floor lets them pair
   up; the longest option text is clipped in the closed state, which is what a native select does
   everywhere and costs nothing, since opening it shows the full list regardless. */
@media (max-width: 600px) {
    .hc-ctrls { gap: 8px; }
    .hc-ctrls .hc-sel { flex: 1 1 calc(50% - 4px); min-width: 0; }
    .hc-ctrls .rtp-search { flex: 1 1 100%; min-width: 0; max-width: none; }
}
.hc-banner { color: var(--color-accent-strong); font-style: italic; margin-left: 6px; }

.hc-selbar {
    display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
    padding: 8px 12px; background: var(--bg2);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    min-height: 42px;
}
.hc-sellabel { font-size: 11px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.06em; }
.hc-selhint { font-size: 12px; color: var(--text2); }
.hc-chip {
    background: var(--bg3); border: 1px solid var(--accent);
    color: var(--text); font-size: 12px; font-family: inherit;
    padding: 3px 10px; border-radius: 999px; cursor: pointer;
    display: inline-flex; align-items: center; gap: 6px;
}
.hc-chip:hover { background: var(--color-accent-soft); }
.hc-chip-x { color: var(--text3); font-size: 10px; }
.hc-clear { margin-left: auto; }

/* Results */
/* One column since the SX22/SX28 split was merged into a single combined list. */
.hc-results {
    display: grid; grid-template-columns: 1fr; gap: 10px;
}
.hc-result-col {
    background: var(--bg2); border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 10px 12px;
    display: flex; flex-direction: column; gap: 6px;
}
.hc-result-hdr {
    font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--color-status-warning);
    padding-bottom: 6px; border-bottom: 1px solid var(--border);
}
.hc-combo {
    display: flex; flex-direction: column; gap: 2px;
    padding: 6px 8px; border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    background: var(--bg3);
}
.hc-combo-hdr { display: flex; justify-content: space-between; font-size: 10px; }
.hc-rank { font-weight: 700; }
.hc-combo-body { font-size: 12px; color: var(--text); }
.hc-member-sel { color: var(--color-accent-strong); font-weight: 700; }
.hc-skin { color: var(--text3); font-size: 11px; }
.hc-sep { color: var(--text3); }
.hc-more { text-align: center; color: var(--text3); font-size: 11px; padding: 6px 0; }
.hc-empty { grid-column: 1 / -1; }

.hc-cat-cavalry     { color: var(--color-accent-secondary-strong); border-color: var(--color-accent-secondary-strong); }
.hc-cat-archer      { color: var(--color-status-success); border-color: var(--color-status-success); }
.hc-cat-footmen     { color: var(--color-status-warning); border-color: var(--color-status-warning); }
.hc-cat-mixed       { color: var(--purple); border-color: var(--purple); }
.hc-cat-poisontiler { color: var(--color-accent-strong); border-color: var(--color-accent-strong); }
.hc-cat-other       { color: var(--text2); border-color: var(--border); }

/* Hero grid */
.hc-grid {
    display: grid; gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
}
.hcard {
    background: var(--bg2); border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 10px 12px; cursor: pointer;
    display: flex; flex-direction: column; gap: 6px;
    transition: border-color 0.12s, transform 0.12s;
    position: relative;
    min-height: 108px;
}
.hcard:hover { border-color: var(--border2); transform: translateY(-1px); }
.hcard-selected { border-color: var(--color-accent-strong); box-shadow: 0 0 0 1px var(--accent); }
.hcard-hdr { display: flex; justify-content: space-between; align-items: baseline; }
.hcard-name { font-size: 13px; font-weight: 700; color: var(--text); }
.hcard-season { font-size: 10px; color: var(--text3); }
.hcard-badges { display: flex; gap: 4px; flex-wrap: wrap; }
.hcard-troop-foot { color: var(--footman); border-color: rgba(63, 185, 80, 0.4); }
.hcard-troop-arch { color: var(--archer); border-color: rgba(56, 139, 253, 0.4); }
.hcard-troop-cav  { color: var(--cavalry); border-color: rgba(227, 179, 65, 0.4); }
.hcard-type-imperial  { color: var(--cavalry); border-color: rgba(227, 179, 65, 0.4); }
.hcard-type-season    { color: var(--archer); border-color: rgba(56, 139, 253, 0.4); }
.hcard-type-exclusive { color: var(--purple); border-color: rgba(163, 113, 247, 0.4); }
.hcard-excels { color: var(--text2); border-color: var(--border); }
.hcard-stats { display: flex; gap: 10px; font-size: 11px; color: var(--text2); }
.hcard-sel-indicator {
    position: absolute; top: 6px; right: 8px;
    color: var(--color-accent-strong); font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
}

/* ─── Spec Planner ───────────────────────────────────────────────── */
.sp-wrap {
    display: flex; flex-direction: column;
    height: calc(100vh - var(--header-height) - 178px);
    min-height: 480px;
    overflow: hidden;
    overflow-anchor: none;
}

.sp-toolbar {
    display: flex; align-items: center; gap: var(--space-4);
    padding: var(--space-3);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    margin-bottom: var(--space-3);
    flex: 0 0 auto;
}

.sp-tabs { display: flex; gap: var(--space-2); flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.sp-tabs::-webkit-scrollbar { display: none; }
.sp-tab {
    --sp-tab-accent: var(--color-border-strong);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 1px; padding: 8px 18px;
    min-width: 116px; min-height: 44px; flex: 0 0 auto;
    background: var(--color-bg-tertiary);
    border: 2px solid transparent;
    border-radius: var(--border-radius-md);
    color: var(--color-text-secondary);
    font-family: var(--font-body);
    font-size: var(--font-size-sm); font-weight: 700;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.sp-tab:hover { background: var(--color-bg-sunken); color: var(--color-text-primary); }
.sp-tab:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.sp-tab-active {
    background: color-mix(in srgb, var(--sp-tab-accent) 14%, var(--color-bg-secondary));
    border-color: var(--sp-tab-accent);
    color: var(--color-text-primary);
}
.sp-tab-icon  { font-size: 18px; line-height: 1; }
.sp-tab-label { font-size: var(--font-size-sm); letter-spacing: 0.01em; line-height: 1.2; }
.sp-tab-pts   { font-size: 11px; color: var(--color-text-tertiary); font-weight: 600; }
.sp-tab-active .sp-tab-pts { color: var(--color-text-secondary); }

.sp-actions { display: flex; gap: var(--space-2); align-items: center; flex: 0 0 auto; }
.sp-zoom { display: inline-flex; gap: 4px; }
.sp-zoom-btn {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--border-radius-md);
    color: var(--color-text-primary);
    font-size: var(--font-size-md); font-weight: 700;
    cursor: pointer;
    transition: background .15s, border-color .15s;
}
.sp-zoom-btn:hover { background: var(--color-bg-tertiary); }
.sp-zoom-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

.sp-body {
    display: flex; flex: 1 1 auto; gap: 8px;
    min-height: 0;
}

.sp-canvas-wrap {
    flex: 1 1 auto;
    min-width: 0; min-height: 0;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
.sp-canvas-scroll {
    height: 100%;
    overflow: auto;
    overflow-anchor: none;
    background: var(--surface-inset);
}
.sp-svg { display: block; }

.sp-side {
    flex: 0 0 360px;
    display: flex; flex-direction: column; gap: var(--space-3);
    min-height: 0;
}
.sp-side-section {
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    display: flex; flex-direction: column;
    min-height: 0;
}
.sp-side-detail  { flex: 0 0 auto; max-height: 50%; overflow: auto; }
.sp-side-summary { flex: 1 1 auto; overflow: auto; }
.sp-side-hdr {
    flex: 0 0 auto;
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-tertiary);
    border-bottom: 1px solid var(--color-border-primary);
    color: var(--color-text-tertiary);
    font-family: var(--font-body);
    font-size: var(--font-size-sm); font-weight: 800;
    letter-spacing: 0.06em; text-transform: uppercase;
}

/* Spec Planner — stack canvas + side panel on small screens */
@media (max-width: 860px) {
    .sp-wrap { height: auto; min-height: 0; overflow: visible; }
    .sp-body { flex-direction: column; }
    .sp-canvas-wrap { height: 60vh; flex: 0 0 auto; }
    .sp-side { flex: 1 1 auto; width: 100%; }
    .sp-side-detail { max-height: none; }
}

/* Detail panel */
.sp-side-detail > div:not(.sp-side-hdr),
.sp-side-detail > pre,
.sp-side-detail > ul { padding-left: 12px; padding-right: 12px; }
.sp-side-detail .sp-empty { padding: 14px 12px; color: var(--text3); }
.sp-side-detail .sp-tip   { padding: 0 12px 12px; color: var(--text3); font-size: 11px; line-height: 1.5; }
.sp-name {
    margin-top: 10px;
    color: var(--text);
    font-family: var(--font-display);
    font-size: 16px; font-weight: 600;
}
.sp-lvl-row { display: flex; align-items: center; justify-content: space-between; margin-top: 6px; }
.sp-lvl     { color: var(--color-accent-secondary-strong); font-weight: 600; font-size: 13px; }
.sp-lvl-btns { display: inline-flex; gap: 4px; }
.sp-lvl-btn {
    min-width: 36px; height: 36px;
    background: var(--bg); color: var(--text);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    cursor: pointer; font-weight: 700; font-size: 14px;
}
.sp-lvl-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.sp-lvl-plus:not(:disabled):hover  { border-color: var(--color-accent-secondary-strong); color: var(--color-accent-secondary-strong); }
.sp-lvl-minus:not(:disabled):hover { border-color: var(--color-status-error); color: var(--color-status-error); }

.sp-status {
    display: inline-block; margin-top: 8px;
    padding: 2px 8px; font-size: 11px; font-weight: 600;
    border-radius: var(--border-radius-lg); letter-spacing: 0.04em;
}
.sp-status-locked  { background: var(--color-bg-tertiary); color: var(--color-text-tertiary); }
.sp-status-avail   { background: var(--color-accent-secondary-soft);  color: var(--color-accent-secondary-strong); }
.sp-status-partial { background: var(--color-accent-secondary-soft);  color: var(--color-accent-secondary-strong); }
.sp-status-done    { background: var(--color-status-success-soft);   color: var(--color-status-success); }
.sp-status-hub     { background: var(--color-accent-soft);  color: var(--color-accent-strong); }

.sp-desc {
    margin: 8px 0 6px; padding: 8px 10px;
    background: var(--color-bg-tertiary); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
    color: var(--text2); font-size: 12px; line-height: 1.5;
    white-space: pre-wrap; font-family: inherit;
}
.sp-pre-label { margin-top: 6px; color: var(--text3); font-size: 11px; }
.sp-pre-list  { list-style: none; padding: 0; margin: 4px 0 8px; }
.sp-pre-list li { padding: 2px 0; font-size: 12px; }
.sp-pre-done { color: var(--text2); }
.sp-pre-todo { color: var(--text3); }
.sp-cost-line { margin-top: 6px; font-size: 12px; color: var(--color-accent-strong); }
.sp-cost-note { color: var(--text3); }

/* Summary panel */
.sp-side-summary > * { padding-left: 12px; padding-right: 12px; }
.sp-side-summary > .sp-side-hdr,
.sp-side-summary .sp-section-hdr.sp-section-inner { padding-left: 12px; padding-right: 12px; }
.sp-side-summary .sp-empty { padding: 14px 12px; color: var(--text3); }

.sp-section-hdr {
    margin-top: 10px;
    color: var(--text2);
    font-family: var(--font-display);
    font-size: 11px; font-weight: 600;
    letter-spacing: 0.08em; text-transform: uppercase;
}

.sp-tree-pts { list-style: none; padding-left: 0; margin: 6px 0; }
.sp-tree-pts li {
    padding: 4px 8px; margin: 4px 0;
    background: rgba(255,255,255,0.03);
    font-size: 12px; color: var(--text2);
}
.sp-tree-pts-total { color: var(--color-accent-strong) !important; font-weight: 600; border-left: 3px solid var(--accent) !important; }

.sp-stats, .sp-abilities { list-style: none; padding-left: 0; margin: 6px 0; }
.sp-stats li {
    padding: 3px 0;
    border-bottom: 1px dotted var(--border);
    font-size: 12px; color: var(--text2);
}
.sp-stats li:last-child { border-bottom: none; }
.sp-abilities li {
    padding: 4px 8px; margin: 4px 0;
    background: rgba(255,255,255,0.03);
    font-size: 12px; color: var(--text2);
}

/* SVG node interactions */
.sp-node { cursor: default; }
.sp-clickable { cursor: pointer; }
.sp-clickable:hover circle:nth-of-type(1) { filter: brightness(1.3); }

/* ─── Eden Calculator ────────────────────────────────────────────── */
.ec-wrap { display: flex; flex-direction: column; gap: var(--space-4); padding-bottom: var(--space-4); }

.ec-toolbar {
    display: flex; align-items: center; gap: var(--space-4);
    padding: var(--space-3);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    flex-wrap: wrap;
}
/* Scrollable chip-row of tabs */
.ec-tabs {
    display: flex; gap: var(--space-2);
    flex: 1 1 auto;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 2px;
}
.ec-tabs::-webkit-scrollbar { display: none; }
.ec-tab {
    display: inline-flex; align-items: center; gap: 7px;
    flex: 0 0 auto;
    min-height: 44px;
    padding: 10px 16px;
    background: var(--color-bg-tertiary);
    border: 1px solid transparent;
    border-radius: var(--border-radius-full);
    color: var(--color-text-secondary);
    font-family: var(--font-body);
    font-size: var(--font-size-sm); font-weight: 700;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.ec-tab:hover { background: var(--color-bg-sunken); color: var(--color-text-primary); }
.ec-tab:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.ec-tab-active {
    background: var(--color-accent-primary);
    border-color: var(--color-accent-primary);
    color: var(--color-on-accent);
}
.ec-tab-active:hover { background: var(--color-accent-primary-hover); color: var(--color-on-accent); }
.ec-tab-icon  { font-size: 15px; line-height: 1; }
.ec-tab-label { letter-spacing: 0.01em; }
/* "Later" tabs (Loyalty/Fortress): de-emphasised until selected */
.ec-tab-later:not(.ec-tab-active) { background: transparent; color: var(--color-text-tertiary); }
.ec-tab-later:not(.ec-tab-active) .ec-tab-icon { opacity: 0.6; }
.ec-tab-later:not(.ec-tab-active):hover { background: var(--color-bg-tertiary); color: var(--color-text-secondary); }

/* Tab group labels ("Set up" / "Results") in the Eden tab bar */
.ec-tabgroup {
    display: inline-flex; align-items: center; flex: 0 0 auto; align-self: center;
    font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--color-text-tertiary);
    white-space: nowrap;
}
.ec-tabgroup:not(:first-child) {
    margin-left: 6px; padding-left: 12px;
    border-left: 1px solid var(--color-border-secondary);
}

/* Guided-flow hero on the Dashboard (start-here guide / plain-language summary) */
.ec-hero {
    display: flex; gap: var(--space-4); align-items: flex-start;
    padding: var(--space-4) var(--space-5);
    border-radius: var(--border-radius-lg);
}
.ec-hero-start   { background: var(--color-accent-soft); border: 1px solid var(--color-accent-soft-border); }
.ec-hero-summary { background: var(--color-bg-secondary); border: 1px solid var(--color-border-primary); }
.ec-hero-emoji   { font-size: 28px; line-height: 1.2; flex: 0 0 auto; }
.ec-hero-text    { min-width: 0; }
.ec-hero-title   { font-family: var(--font-display); font-weight: 800; font-size: var(--font-size-lg); color: var(--color-text-primary); margin-bottom: 2px; }
.ec-hero-sub     { margin: 0 0 var(--space-3); color: var(--color-text-secondary); font-size: var(--font-size-md); line-height: 1.5; max-width: 68ch; }
.ec-hero-summary .ec-hero-sub { margin-bottom: 0; }
.ec-hero-sub b   { color: var(--color-text-primary); }
.ec-hero-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* Tile cards read as tappable (they open the assign dialog) */
.ec-tile-card { transition: border-color .15s, box-shadow .15s, transform .15s; }
.ec-tile-card:hover { border-color: var(--color-accent-soft-border); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.ec-tile-card-count { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.ec-tile-card-edit { color: var(--color-accent-secondary-strong); font-weight: 700; white-space: nowrap; }

/* Workshop slots read as tappable too */
.ec-ws-slot:not([disabled]) { transition: border-color .15s, box-shadow .15s, transform .15s; }
.ec-ws-slot:not([disabled]):hover { border-color: var(--color-accent-soft-border); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.ec-ws-slot-hint { display: block; margin-top: 2px; font-size: 10px; font-weight: 700; color: var(--color-accent-secondary-strong); }

/* Password field with a show/hide eye toggle */
.roc-pw { position: relative; display: block; }
.roc-pw .roc-input { width: 100%; padding-right: 44px; }
.roc-pw-toggle {
    position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
    display: grid; place-items: center; width: 32px; height: 32px;
    background: none; border: none; cursor: pointer; padding: 0;
    color: var(--color-text-tertiary); border-radius: var(--border-radius-md);
    transition: background .15s, color .15s;
}
.roc-pw-toggle:hover { color: var(--color-text-secondary); background: var(--color-bg-tertiary); }
.roc-pw-toggle:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-pw-toggle svg { width: 18px; height: 18px; }
/* Login form uses two SVGs toggled by a class (JS-driven); the Blazor field swaps its own. */
.roc-pw-toggle .pw-eye-off { display: none; }
.roc-pw-toggle.is-showing .pw-eye { display: none; }
.roc-pw-toggle.is-showing .pw-eye-off { display: block; }

/* Inline info marker (hover/focus for a plain-language explanation) */
.roc-infotip {
    display: inline-grid; place-items: center;
    width: 15px; height: 15px; margin-left: 5px; vertical-align: middle;
    border-radius: 50%;
    background: var(--color-bg-tertiary); color: var(--color-text-secondary);
    font-size: 10px; font-weight: 800; font-style: italic; font-family: var(--font-display);
    line-height: 1; cursor: help; user-select: none;
    transition: background .15s, color .15s;
}
.roc-infotip:hover, .roc-infotip:focus-visible {
    background: var(--color-accent-soft); color: var(--color-accent-strong); outline: none;
}

/* The native title tooltip needs a mouse to hover, which is exactly what a phone has not got. On
   focus — which is what a tap gives this span — the same text appears as a bubble instead. Hover is
   left to the browser so the two never show at once. */
.roc-infotip { position: relative; }
.roc-infotip::after {
    content: attr(title);
    /* Anchored to the marker's left edge rather than centred on it. A marker generally sits just
       after a label, near the left of its column, and a centred bubble hangs off the side of a
       phone screen — which is the one place this bubble exists to serve. */
    position: absolute; top: calc(100% + 6px); left: 0;
    z-index: 30;
    width: max-content; max-width: min(220px, calc(100vw - 3rem));
    padding: 8px 10px;
    border-radius: var(--radius-sm); border: 1px solid var(--color-border-primary);
    background: var(--color-bg-inverse); color: var(--color-text-inverse);
    font-size: var(--font-size-sm); font-style: normal; font-weight: 400; font-family: var(--font-body);
    line-height: 1.35; text-align: left; white-space: normal;
    box-shadow: var(--shadow-sm);
    /* Removed from layout rather than merely made invisible. A hidden-but-laid-out absolute box
       still counts towards its scroll container's width, so a 220px bubble anchored anywhere right
       of centre gave the page a permanent sideways scroll that no amount of not-hovering removed —
       /eden scrolled 163px on a phone with every tooltip shut. */
    display: none;
}
.roc-infotip:focus::after { display: block; }

/* Progressive-disclosure block (native <details>) for advanced/optional content.
   No own margin — spacing comes from the parent's gap so rhythm stays uniform. */
.roc-collapsible { margin-top: 0; }
.roc-collapsible-summary {
    display: inline-flex; align-items: center; gap: var(--space-2);
    cursor: pointer; user-select: none; list-style: none;
    padding: 9px 14px; min-height: 40px;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    font-weight: 700; font-size: var(--font-size-sm); color: var(--color-text-secondary);
    transition: background .15s, color .15s;
}
.roc-collapsible-summary::-webkit-details-marker { display: none; }
.roc-collapsible-summary::marker { content: ""; }
.roc-collapsible-summary:hover { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
.roc-collapsible-summary:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.roc-collapsible-chevron { width: 16px; height: 16px; flex: 0 0 auto; transition: transform .15s; }
.roc-collapsible[open] > .roc-collapsible-summary .roc-collapsible-chevron { transform: rotate(90deg); }
.roc-collapsible-body { padding-top: var(--space-4); }

/* Plain-language "here's the result" line at the top of a tab */
.ec-takeaway {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    font-size: var(--font-size-md); color: var(--color-text-secondary); line-height: 1.5;
}
.ec-takeaway b { color: var(--color-text-primary); font-weight: 800; }
.ec-takeaway .pos { color: var(--color-status-success); font-weight: 800; }
.ec-takeaway .neg { color: var(--color-status-error); font-weight: 800; }

@media (prefers-reduced-motion: reduce) {
    .roc-collapsible-chevron { transition: none; }
}

.ec-toolbar-right {
    display: flex; align-items: center; gap: var(--space-4); flex: 0 0 auto; flex-wrap: wrap;
}
.ec-date-field { display: inline-flex; align-items: center; gap: var(--space-2); }
.ec-date-label { font-size: var(--font-size-sm); font-weight: 700; color: var(--color-text-secondary); }
.ec-date {
    background: var(--color-bg-secondary); color: var(--color-text-primary);
    border: 1px solid var(--color-border-strong); border-radius: var(--border-radius-md);
    padding: 9px 12px; font-size: var(--font-size-sm);
    font-family: var(--font-body);
    color-scheme: light;
}
[data-theme="dark"] .ec-date { color-scheme: dark; }
.ec-date:focus { outline: none; border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
.ec-phase {
    color: var(--color-text-secondary); font-size: var(--font-size-sm); font-weight: 600;
}
.ec-save-state { font-size: var(--font-size-sm); font-weight: 700; padding: 4px 10px; border-radius: var(--border-radius-full); }
.ec-save-pending { color: var(--color-status-warning); background: var(--color-status-warning-soft); }
.ec-save-ok      { color: var(--color-status-success); background: var(--color-status-success-soft); }

/* Panel content */
.ec-panel-content {
    /* One consistent vertical rhythm for every block in a tab. Children must NOT
       add their own vertical margins — that stacks on top of this gap and makes
       the spacing uneven. */
    display: flex; flex-direction: column; gap: var(--space-4);
}

.ec-section {
    background: var(--bg2); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 12px;
}
.ec-section-hdr {
    color: var(--color-accent-strong);
    font-family: var(--font-display);
    font-size: 12px; font-weight: 600;
    letter-spacing: 0.06em; text-transform: uppercase;
    margin-bottom: 8px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
}

.ec-grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: 10px; }
.ec-grid3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: 10px; }
.ec-grid4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); gap: 10px; }

.ec-form-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; }
.ec-label { color: var(--text2); font-size: 12px; min-width: 130px; }
.ec-hint  { color: var(--text3); font-size: 11px; }

.ec-input {
    background: var(--bg); color: var(--text);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 4px 8px; font-size: 12px;
    flex: 1 1 auto; min-width: 0;
}
.ec-input-sm { max-width: 120px; }

.ec-select {
    background: var(--bg); color: var(--text);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 4px 8px; font-size: 12px;
    flex: 1 1 auto; min-width: 0;
}
.ec-select:disabled { opacity: 0.4; cursor: not-allowed; }

.ec-check-row { cursor: pointer; padding: 4px 0; user-select: none; }

/* KPI strip */
.ec-kpi-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 8px;
}
.ec-kpi-card {
    background: var(--bg); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 8px 10px;
    text-align: center;
}
.ec-kpi-card-bold { border-color: var(--color-accent-strong); }
.ec-kpi-val {
    color: var(--text);
    font-family: var(--font-display);
    font-size: 18px; font-weight: 700;
}
.ec-kpi-label {
    color: var(--text3); font-size: 10px;
    letter-spacing: 0.06em; text-transform: uppercase;
    margin-top: 2px;
}

/* All three parts follow the info token. They used to name two different variables that happened
   to hold the same blue; now that the secondary is gold, that coincidence would have shown up as
   gold text on a blue panel. */
.ec-banner {
    padding: 8px 12px;
    background: var(--color-status-info-soft); border: 1px solid var(--color-status-info);
    border-radius: var(--border-radius-lg);
    color: var(--color-status-info); font-size: 12px;
}
.ec-banner-warn {
    background: rgba(212,168,67,0.12); border-color: var(--color-accent-strong); color: var(--color-accent-strong);
}

.ec-result-row {
    display: flex; justify-content: space-between;
    padding: 3px 0;
    font-size: 12px;
}
.ec-result-key { color: var(--text2); }
.ec-result-val { color: var(--text); font-weight: 600; }
.ec-result-pos { color: var(--color-status-success) !important; }
.ec-result-neg { color: var(--color-status-error) !important; }
.ec-result-block { padding: 4px; }

/* Camp / fortress / workshop cards */
.ec-camp-card,
.ec-fort-card,
.ec-ws-card {
    background: var(--bg); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 8px;
    display: flex; flex-direction: column; gap: 4px;
}
.ec-camp-name,
.ec-fort-title,
.ec-ws-card-title {
    color: var(--text);
    font-family: var(--font-display);
    font-size: 13px; font-weight: 600;
}
.ec-camp-cost,
.ec-ws-card-info {
    color: var(--text3); font-size: 10px;
}
.ec-fort-totals {
    margin-top: 8px; padding-top: 6px;
    border-top: 1px solid var(--border);
    color: var(--text); font-size: 12px;
}

/* Spec slot tiles */
.ec-spec-row { display: flex; gap: 8px; flex-wrap: wrap; }
.ec-spec-node {
    width: 60px; height: 60px;
    background: var(--bg); border: 2px solid var(--border);
    border-radius: var(--border-radius-lg);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    cursor: pointer;
    color: var(--text2);
    transition: border-color 0.12s, background 0.12s;
}
.ec-spec-node:hover { border-color: var(--color-accent-secondary-strong); }
.ec-spec-node-lit  { background: var(--color-accent-secondary-soft); border-color: var(--color-accent-secondary-strong); color: var(--color-accent-secondary-strong); }
.ec-spec-node-lv   { font-family: var(--font-display); font-size: 14px; font-weight: 700; }
.ec-spec-node-sub  { font-size: 10px; color: var(--text3); }
.ec-spec-node-lit .ec-spec-node-sub { color: var(--color-accent-secondary-strong); }

/* Tile cards */
.ec-tile-card {
    background: var(--bg); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 10px;
    text-align: left; cursor: pointer;
    display: flex; flex-direction: column; gap: 4px;
    color: var(--text); font: inherit;
}
.ec-tile-card:hover { border-color: var(--color-accent-strong); }
.ec-tile-card-resource { font-size: 14px; font-weight: 700; }
.ec-tile-card-name     { color: var(--text2); font-size: 11px; }
.ec-tile-card-rate     { font-family: var(--font-display); font-size: 18px; font-weight: 700; color: var(--color-accent-secondary-strong); }
.ec-tile-card-unit     { font-size: 11px; color: var(--text3); }
.ec-tile-card-count    { color: var(--text3); font-size: 11px; }

/* Workshop slot grid */
.ec-slots-grid {
    display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; margin-top: 8px;
}
.ec-ws-slot {
    background: var(--bg); border: 1.5px solid var(--border); border-radius: var(--border-radius-lg);
    padding: 14px 10px; min-height: 90px;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px;
    color: var(--text2); cursor: pointer; font: inherit;
}
.ec-ws-slot:hover:not(:disabled) { border-color: var(--color-accent-secondary-strong); }
.ec-ws-slot:disabled { cursor: default; }
.ec-ws-slot-active { background: var(--color-accent-secondary-soft); border-color: var(--color-accent-secondary-strong); color: var(--color-accent-secondary-strong); }
.ec-ws-slot-locked { opacity: 0.4; }
.ec-ws-slot-title  { font-size: 11px; color: var(--text3); }
.ec-ws-slot-icon   { font-size: 22px; }
.ec-ws-slot-label  { font-size: 12px; font-weight: 700; color: var(--color-accent-secondary-strong); }
.ec-ws-slot-empty  { font-size: 11px; color: var(--text3); }
.ec-ws-slot-lock   { font-size: 16px; }

.ec-slot-grid-modal {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; padding: 8px;
}
.ec-slot-pick {
    background: var(--color-bg-tertiary); border: 1.5px solid var(--color-border-strong);
    border-radius: var(--border-radius-lg); padding: 12px;
    color: var(--color-text-secondary);
    font-size: 12px; font-weight: 700;
    cursor: pointer; font-family: inherit;
}
.ec-slot-pick:hover           { border-color: var(--color-accent-secondary-strong); color: var(--color-accent-secondary-strong); }
.ec-slot-pick-active          { background: var(--color-accent-secondary-soft); border-color: var(--color-accent-secondary-strong); color: var(--color-accent-secondary-strong); }

/* Period toggle */
.ec-toolbar-inner { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; padding: 4px 0; }
.ec-vt-tabs { display: flex; gap: 4px; }
.ec-vt-btn {
    background: var(--bg); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 4px 12px;
    color: var(--text2); font-size: 12px; cursor: pointer;
}
.ec-vt-active { background: var(--color-accent-secondary-soft); border-color: var(--color-accent-secondary-strong); color: var(--color-accent-secondary-strong); }

/* Badges */
.ec-badges  { display: flex; gap: 4px; flex-wrap: wrap; margin: 4px 0; }
.ec-badge {
    background: var(--bg); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 2px 8px;
    font-size: 11px; color: var(--text2);
}
.ec-badge-max { background: rgba(212,168,67,0.18); border-color: var(--color-accent-strong); color: var(--color-accent-strong); }

.ec-detail { font-size: 11px; color: var(--text3); margin: 2px 0; }

/* Table */
.ec-table-wrap { overflow-x: auto; }
.ec-table {
    width: 100%; border-collapse: collapse;
    font-size: 12px; color: var(--text);
}
.ec-table th, .ec-table td {
    padding: 6px 10px;
    border-bottom: 1px solid var(--border);
    text-align: left;
}
.ec-table th { color: var(--text3); font-weight: 600; font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; }
/* Phone: stack ec-tables into label:value cards (they're too wide to read as a side-scroll strip).
   Each <td> must carry data-label="Column". */
@media (max-width: 560px) {
    .ec-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    .ec-table, .ec-table tbody, .ec-table tr, .ec-table td { display: block; width: 100%; }
    .ec-table tbody tr { border: 1px solid var(--border); border-radius: var(--border-radius-md); margin-bottom: 8px; }
    .ec-table tbody td { display: flex; justify-content: space-between; gap: 12px; text-align: right; border-bottom: 1px solid var(--border); }
    .ec-table tbody tr td:last-child { border-bottom: none; }
    .ec-table tbody td::before { content: attr(data-label); font-weight: 600; color: var(--text3); text-align: left; }
    .ec-table tbody td:not([data-label])::before { content: none; }
}

/* Progress bar */
.ec-progress {
    background: var(--color-bg-tertiary); border-radius: var(--border-radius-lg);
    height: 6px; margin-top: 6px; overflow: hidden;
}
.ec-progress-bar { background: var(--color-accent-secondary); height: 100%; border-radius: var(--border-radius-lg); }

/* Flow cards (dashboard) */
.ec-flow-card {
    background: var(--bg); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 10px;
}
.ec-flow-title {
    color: var(--color-accent-strong);
    font-family: var(--font-display);
    font-size: 11px; font-weight: 600;
    letter-spacing: 0.06em; text-transform: uppercase;
    margin-bottom: 6px;
}
.ec-flow-row { font-size: 11px; color: var(--text2); padding: 2px 0; }

/* Tile dialog */
.ec-tile-modal-body {
    padding: 8px 12px; max-height: 50vh; overflow-y: auto;
    display: flex; flex-direction: column; gap: 4px;
}
.ec-tile-modal-totals {
    display: flex; justify-content: space-between;
    padding: 8px 4px 0;
    border-top: 1px solid var(--border);
    margin-top: 6px;
    font-size: 12px; color: var(--text2);
}

/* ── Virtue planner ───────────────────────────────────────────────────── */
.vp-outer {
    display: flex; flex-direction: column;
    height: calc(100vh - 64px); overflow: hidden;
}
.vp-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 8px 14px; background: var(--bg2);
    border-bottom: 1px solid var(--border);
    flex-shrink: 0; flex-wrap: wrap; gap: 8px;
}
.vp-controls {
    display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
}
.vp-troop-sel, .vp-path-sel {
    display: flex; align-items: center; gap: 5px;
}
.vp-label {
    font-size: var(--font-size-sm); font-weight: 700; color: var(--color-text-secondary);
    text-transform: uppercase; letter-spacing: .05em;
    margin-right: 4px;
}
.vp-troop-btn, .vp-path-btn {
    min-height: 44px;
    padding: 7px 14px; font-size: var(--font-size-sm); font-weight: 700;
    background: var(--color-bg-tertiary); border: 1px solid var(--color-border-strong);
    color: var(--color-text-secondary); border-radius: var(--border-radius-md); cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.vp-troop-btn:hover, .vp-path-btn:hover {
    color: var(--color-text-primary); background: var(--color-bg-sunken);
}
.vp-troop-btn:focus-visible, .vp-path-btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.vp-troop-active, .vp-path-active {
    background: var(--color-accent-primary); color: var(--color-on-accent);
    border-color: var(--color-accent-primary);
}
.vp-troop-active:hover, .vp-path-active:hover { background: var(--color-accent-primary-hover); color: var(--color-on-accent); }
.vp-path-clear { color: var(--color-status-error); }
.vp-totals {
    display: flex; align-items: center; gap: 6px; font-size: 12px;
}
.vp-total-label { color: var(--text2); }
.vp-total-val { color: var(--color-accent-strong); font-weight: 700; }
.vp-save-state {
    margin-left: 12px; font-size: 11px;
    padding: 2px 8px; border-radius: var(--border-radius-lg);
}
.vp-save-pending { background: var(--warn-bg); color: var(--color-accent-strong); }
.vp-save-ok { background: var(--ok-bg); color: var(--ok-text); }

.vp-body {
    display: flex; flex: 1; overflow: hidden;
    background: transparent;
}
.vp-columns {
    flex: 1; overflow-x: auto; overflow-y: auto;
    display: flex; gap: 8px; padding: 10px; align-items: flex-start;
}
.vp-col {
    display: flex; flex-direction: column; gap: 6px;
    flex: 0 0 220px;
    width: 220px; min-width: 220px; max-width: 220px;
}
.vp-col-hdr {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 6px 10px; background: var(--bg2);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
}
.vp-col-title { color: var(--color-accent-strong); font-weight: 700; font-size: 13px; }
.vp-col-cost { color: var(--text2); font-size: 11px; }
.vp-col-prog {
    font-size: 11px; padding: 1px 7px; border-radius: var(--border-radius-lg);
    background: var(--bg3); color: var(--text2);
    border: 1px solid var(--border);
}
.vp-prog-partial { color: var(--color-accent-strong); border-color: var(--color-accent-strong); }
.vp-prog-done { color: var(--ok-text); border-color: var(--ok-border); background: var(--ok-bg); }

.vp-tree-card {
    position: relative;
    padding: 8px 10px; background: var(--bg2);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    cursor: pointer; transition: background .1s, border-color .1s;
}
.vp-tree-step {
    position: absolute; top: 5px; right: 5px;
    min-width: 20px; height: 20px;
    padding: 0 5px;
    /* 11px bold is not large text, so this pair owes a full 4.5:1. A literal black opted the rule
       out of the palette's ink guarantee and fell to 3.78:1 when the brand moved to blue. */
    background: var(--accent); color: var(--color-on-accent);
    font-weight: 700; font-size: 11px;
    border-radius: var(--border-radius-lg);
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 1px 4px rgba(0,0,0,.4);
}
.vp-tree-card:hover {
    background: var(--bg3); border-color: var(--color-accent-strong);
}
.vp-tree-partial {
    border-color: var(--color-accent-strong); background: var(--warn-bg);
}
.vp-tree-done {
    border-color: var(--ok-border); background: var(--ok-bg);
}
.vp-tree-selected {
    outline: 2px solid var(--accent); outline-offset: -2px;
}
.vp-tree-name { font-weight: 700; font-size: 12px; padding-right: 28px; }
.vp-tree-cost { color: var(--text2); font-size: 11px; margin-bottom: 4px; }
.vp-tree-bar-wrap {
    height: 4px; background: var(--bg3);
    border-radius: var(--border-radius-lg); overflow: hidden; margin-bottom: 3px;
}
.vp-tree-bar {
    height: 100%; background: var(--accent);
    transition: width .15s;
}
.vp-tree-prog { font-size: 10px; color: var(--text2); }

.vp-legion {
    padding: 6px 8px; background: var(--surface-inset);
    border: 1px dashed var(--border2); border-radius: var(--border-radius-lg);
    margin-top: 4px;
}
.vp-legion-done {
    border-color: var(--ok-border); background: var(--ok-bg);
}
.vp-legion-title {
    font-size: 10px; font-weight: 700; color: var(--color-accent-strong);
    text-transform: uppercase; letter-spacing: .05em;
}
.vp-legion-name { font-size: 12px; font-weight: 600; }
.vp-legion-desc { font-size: 10px; color: var(--text2); line-height: 1.3; }

/* Detail panel */
.vp-detail {
    width: 320px; min-width: 320px;
    display: flex; flex-direction: column; overflow: hidden;
    background: var(--bg2);
    border-left: 1px solid var(--border);
}
.vp-detail-top {
    flex: 1; overflow-y: auto; padding: 10px;
}
.vp-detail-empty {
    padding: 20px 10px; text-align: center; color: var(--text2);
    font-style: italic;
}
.vp-detail-empty-sm {
    padding: 6px 0; color: var(--text2); font-style: italic; font-size: 11px;
}
.vp-detail-title {
    font-size: 14px; font-weight: 700; color: var(--color-accent-strong);
}
.vp-detail-cost {
    font-size: 11px; color: var(--text2); margin-bottom: 8px;
}
.vp-detail-section {
    font-size: 10px; font-weight: 700; color: var(--text2);
    text-transform: uppercase; letter-spacing: .05em;
    margin: 8px 0 4px; padding-bottom: 3px;
    border-bottom: 1px solid var(--border);
}
.vp-detail-nodes, .vp-detail-milestones {
    display: flex; flex-direction: column; gap: 3px;
}
.vp-detail-node, .vp-detail-ms {
    display: flex; align-items: flex-start; gap: 6px;
    padding: 5px 6px; border-radius: var(--border-radius-lg); cursor: pointer;
    border: 1px solid transparent; background: var(--surface-inset);
}
.vp-detail-node:hover, .vp-detail-ms:hover {
    background: var(--color-bg-tertiary); border-color: var(--color-border-strong);
}
.vp-detail-node-checked, .vp-detail-ms-done {
    background: var(--ok-bg); border-color: var(--ok-border);
}
.vp-detail-node-passive {
    border-left: 2px solid var(--accent);
}
.vp-detail-node-check {
    width: 14px; height: 14px; min-width: 14px; border-radius: var(--border-radius-lg);
    border: 1.5px solid var(--color-border-strong);
    display: flex; align-items: center; justify-content: center;
    font-size: 9px; font-weight: 700; color: var(--ok-text);
    background: var(--color-bg-tertiary); margin-top: 1px;
}
.vp-detail-node-checked .vp-detail-node-check,
.vp-detail-ms-done .vp-detail-node-check {
    background: var(--color-status-success); border-color: var(--color-status-success); color: #fff;
}
.vp-detail-node-info {
    display: flex; flex-direction: column; gap: 1px; flex: 1;
}
.vp-detail-node-name { font-size: 11px; font-weight: 600; }
.vp-detail-node-effect { font-size: 10px; color: var(--text2); line-height: 1.3; }
.vp-detail-ms-pct {
    width: 32px; min-width: 32px; padding: 1px 0;
    background: var(--bg3); border-radius: var(--border-radius-lg);
    text-align: center; font-size: 10px; font-weight: 700;
    color: var(--color-accent-strong);
}
.vp-detail-ms-effect { flex: 1; font-size: 10px; line-height: 1.3; }
.vp-detail-ms-check {
    font-size: 12px; color: var(--ok-text); min-width: 14px; text-align: center;
}
.vp-detail-actions {
    display: flex; gap: 6px; margin-top: 10px;
}
.vp-btn-all, .vp-btn-clear {
    flex: 1; min-height: 40px; padding: 6px 10px; font-size: 11px; font-weight: 600;
    border-radius: var(--border-radius-lg); cursor: pointer;
}
.vp-btn-all {
    background: var(--ok-bg); border: 1px solid var(--ok-border); color: var(--ok-text);
}
.vp-btn-clear {
    background: var(--bad-bg); border: 1px solid var(--bad-border); color: var(--bad-text);
}
.vp-btn-all:hover { background: var(--color-status-success-soft); border-color: var(--color-status-success); }
.vp-btn-clear:hover { background: var(--color-status-error-soft); border-color: var(--color-status-error); }

.vp-detail-summary {
    border-top: 1px solid var(--border);
    padding: 10px; max-height: 40%; overflow-y: auto;
    background: var(--color-bg-tertiary);
}
.vp-sum-rows { display: flex; flex-direction: column; }
.vp-sum-row {
    display: flex; justify-content: space-between; align-items: baseline;
    padding: 2px 0; border-bottom: 1px solid var(--color-border-secondary);
    font-size: 11px;
}
.vp-sum-name { color: var(--text2); }
.vp-sum-val { font-weight: 700; font-variant-numeric: tabular-nums; }
.vp-sum-pos { color: var(--ok-text); }
.vp-sum-neg { color: var(--bad-text); }

@media (max-width: 900px) {
    .vp-detail {
        width: 100%; min-width: unset;
        border-left: none; border-top: 1px solid var(--border);
    }
    .vp-body { flex-direction: column; overflow: visible; }

    /* Once the two panes stack, the fixed-height app shell stops working. .vp-outer's
       calc(100vh - 64px) had to cover a header that wraps to ~200px on a phone plus a detail panel
       that sizes to its content, and the tree columns are what lost: flex:1 shrank them to 10px,
       so the thing the page exists for was invisible. Stacked panes want an ordinary page that
       scrolls, not a viewport-locked shell. */
    .vp-outer { height: auto; overflow: visible; }

    /* Label above its buttons rather than beside them, and let the buttons wrap. Inline, the label
       ate ~110px of a 280px row and neither row could wrap, so the third troop type and most of the
       recommended paths sat outside a container with overflow: hidden — not merely cramped,
       unreachable. */
    .vp-troop-sel, .vp-path-sel { flex-wrap: wrap; align-items: flex-start; }
    .vp-troop-sel .vp-label, .vp-path-sel .vp-label { flex: 1 1 100%; margin-right: 0; }
}

/* ── Hero Roster ──────────────────────────────────────────────────────── */
.hr-wrap {
    display: flex; flex-direction: column; gap: var(--space-4);
}
.hr-section {
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--space-4);
}
.hr-section-hdr {
    display: flex; align-items: center; justify-content: space-between;
    font-size: var(--font-size-sm); font-weight: 800; color: var(--color-text-tertiary);
    text-transform: uppercase; letter-spacing: .06em;
    margin-bottom: var(--space-3);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border-secondary);
}
.hr-save-state {
    font-size: var(--font-size-sm); font-weight: 700; padding: 4px 10px; border-radius: var(--border-radius-full);
    text-transform: none; letter-spacing: 0;
}
.hr-save-pending { background: var(--color-status-warning-soft); color: var(--color-status-warning); }
.hr-save-ok { background: var(--color-status-success-soft); color: var(--color-status-success); }

.hr-legion-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
    gap: 10px;
}
.hr-legion {
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
    padding: 8px;
    display: flex; flex-direction: column; gap: 6px;
}
.hr-legion-hdr {
    display: flex; align-items: center; justify-content: space-between;
    font-size: 11px; color: var(--text2);
    text-transform: uppercase; letter-spacing: .05em;
}
.hr-legion-title { color: var(--color-accent-strong); font-weight: 700; }
.hr-clear-btn {
    background: transparent; border: 1px solid var(--border2);
    color: var(--bad-text); font-size: 10px; padding: 2px 8px;
    border-radius: var(--border-radius-lg); cursor: pointer;
}
.hr-clear-btn:hover { background: var(--bad-bg); border-color: var(--bad-border); }

.hr-slots {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px;
}
.hr-slot {
    min-height: 78px;
    background: var(--surface-inset); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 6px;
    display: flex; flex-direction: column; gap: 4px;
    cursor: pointer; text-align: left;
    color: var(--text2);
    transition: background .1s, border-color .1s;
}
.hr-slot:hover {
    background: var(--color-bg-tertiary); border-color: var(--color-accent-strong);
}
.hr-slot-empty {
    align-items: center; justify-content: center;
    border-style: dashed;
}
.hr-slot-plus {
    font-size: 22px; color: var(--text2); font-weight: 700; line-height: 1;
}
.hr-slot-empty-label {
    font-size: 10px; color: var(--text2);
    text-transform: uppercase; letter-spacing: .05em;
}
.hr-slot-filled {
    background: var(--ok-bg);
    border-color: var(--ok-border);
    color: var(--color-text-primary);
}
.hr-slot-filled:hover {
    background: var(--color-status-success-soft);
    border-color: var(--color-status-success);
}
.hr-slot-name {
    font-size: 12px; font-weight: 700;
    overflow: hidden; text-overflow: ellipsis;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.hr-slot-badges {
    display: flex; flex-wrap: wrap; gap: 3px;
    margin-top: auto;
}
.hr-slot-season {
    background: var(--color-accent-soft); color: var(--color-accent-strong);
    border: 1px solid var(--color-accent-soft-border);
}

.hr-ctrls {
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
    margin-bottom: 10px;
}
.hr-count {
    margin-left: auto; font-size: var(--font-size-sm); color: var(--color-text-secondary);
}
.hr-count b { color: var(--color-accent-strong); }

.hr-card {
    position: relative;
    transition: opacity .1s;
}
.hr-card.hr-owned {
    border-color: var(--ok-border);
    box-shadow: 0 0 0 1px rgba(108, 208, 125, .12) inset;
}
.hr-card.hr-assigned {
    opacity: .55; cursor: not-allowed;
}
.hr-card-tag {
    margin-top: 6px;
    font-size: 10px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em;
    text-align: center;
    padding: 3px 4px; border-radius: var(--border-radius-lg);
}
.hr-card-tag-owned {
    color: var(--ok-text); background: var(--color-status-success-soft);
    border: 1px solid var(--ok-border);
}
.hr-card-tag-assigned {
    color: var(--color-accent-strong); background: var(--color-accent-soft);
    border: 1px solid var(--color-accent-soft-border);
}

/* Picker dialog */
.hr-pick-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr));
    gap: 8px;
    margin-top: 10px;
    max-height: 60vh; overflow-y: auto;
}
.hr-pick-card {
    background: var(--bg3); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: 8px;
    text-align: left; cursor: pointer;
    display: flex; flex-direction: column; gap: 5px;
}
.hr-pick-card:hover {
    border-color: var(--color-accent-strong); background: var(--bg2);
}
.hr-pick-name { font-weight: 700; font-size: 13px; }
.hr-pick-meta { display: flex; flex-wrap: wrap; gap: 3px; }
.hr-empty-pick {
    padding: 16px; text-align: center;
    color: var(--text2); font-style: italic;
}

/* ── Dragon Master ────────────────────────────────────────────────────── */
.dm-wrap {
    padding: 14px;
    display: flex; flex-direction: column;
    gap: 14px;
}
.dm-subtabs {
    display: flex; align-items: center; gap: 0;
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 16px 16px 0 0;
    padding: 0 4px;
    flex-wrap: wrap;
}
.dm-stab {
    background: transparent; border: none;
    color: var(--color-text-secondary); cursor: pointer;
    min-height: 44px; padding: 12px 18px; font-size: var(--font-size-base); font-weight: 700;
    border-bottom: 3px solid transparent;
    transition: color .15s, border-color .15s;
}
.dm-stab:hover { color: var(--color-text-primary); }
.dm-stab:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.dm-stab-active {
    color: var(--color-accent-strong);
    border-bottom-color: var(--color-accent-primary);
}
.dm-save-state {
    margin-left: auto;
    font-size: var(--font-size-sm); font-weight: 700; padding: 4px 10px; border-radius: var(--border-radius-full);
    margin-right: 10px;
}
.dm-save-pending { background: var(--color-status-warning-soft); color: var(--color-status-warning); }
.dm-save-ok { background: var(--color-status-success-soft); color: var(--color-status-success); }

.dm-section {
    background: var(--bg2);
    border: 1px solid var(--border);
    border-radius: 0 0 16px 16px;
    padding: 18px 20px;
    display: flex; flex-direction: column; gap: 10px;
}
.dm-h1 {
    font-size: 18px; font-weight: 700; color: var(--color-accent-strong);
    margin-bottom: 4px;
}
.dm-h2 {
    font-size: 14px; font-weight: 700; color: var(--color-accent-strong);
    margin: 14px 0 4px;
    text-transform: uppercase; letter-spacing: .04em;
    border-bottom: 1px solid var(--border);
    padding-bottom: 5px;
}
.dm-p {
    font-size: 12px; color: var(--text2); line-height: 1.5;
}

/* Cost cards */
.dm-cost-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 12px;
}
.dm-cost-card {
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
    padding: 14px 16px;
    display: flex; flex-direction: column; gap: 5px;
}
.dm-cost-blue {
    border-left: 4px solid var(--color-accent-secondary);
    background: var(--color-accent-secondary-soft);
}
.dm-cost-purple {
    border-left: 4px solid var(--purple);
    background: var(--purple-soft);
}
.dm-cost-title {
    font-size: 13px; font-weight: 700; color: var(--color-text-primary);
    margin-bottom: 6px;
}
.dm-cost-row {
    display: flex; justify-content: space-between; align-items: baseline;
    font-size: 12px;
    padding: 3px 0;
}
.dm-cost-row span { color: var(--text2); }
.dm-cost-row b { color: var(--color-text-primary); font-weight: 600; }
.dm-cost-row-total {
    border-top: 1px solid var(--border);
    margin-top: 4px; padding-top: 8px;
    font-size: 13px;
}
.dm-cost-row-total b { color: var(--color-accent-strong); }
.dm-cost-vs b { color: var(--ok-text); }

.dm-warn {
    background: var(--color-status-warning-soft); border: 1px solid var(--warn-border);
    border-left: 4px solid var(--color-status-warning);
    color: var(--color-text-primary); padding: 10px 14px;
    border-radius: var(--border-radius-lg); font-size: 12px; line-height: 1.5;
}
.dm-tip {
    background: var(--color-status-success-soft); border: 1px solid var(--ok-border);
    border-left: 4px solid var(--ok-text);
    color: var(--color-text-primary); padding: 10px 14px;
    border-radius: var(--border-radius-lg); font-size: 12px; line-height: 1.5;
}

/* Tracker grid */
.dm-tracker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 10px;
    margin-bottom: 12px;
}
.dm-piece {
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
    padding: 12px;
    display: flex; flex-direction: column; gap: 6px;
    transition: border-color .12s, background .12s;
}
.dm-piece-ready {
    border-color: var(--ok-text);
    background: var(--color-status-success-soft);
}
.dm-piece-gold {
    border-color: var(--color-accent-strong);
    background: var(--color-accent-soft);
    opacity: .85;
}
.dm-piece-hdr {
    display: flex; align-items: center; gap: 8px;
    margin-bottom: 4px;
}
.dm-piece-icon { font-size: 22px; }
.dm-piece-name {
    font-size: 14px; font-weight: 700; color: var(--color-text-primary);
    flex: 1;
}
.dm-piece-tag {
    font-size: 10px; font-weight: 700;
    padding: 3px 7px; border-radius: var(--border-radius-lg);
    text-transform: uppercase; letter-spacing: .04em;
}
.dm-piece-tag-ready {
    background: var(--ok-bg); color: var(--ok-text);
    border: 1px solid var(--ok-border);
}
.dm-piece-tag-gold {
    background: var(--color-accent-soft); color: var(--color-accent-strong);
    border: 1px solid var(--color-accent-soft-border);
}
.dm-piece-row {
    display: flex; align-items: center; gap: 8px;
    font-size: 12px;
}
.dm-piece-lbl {
    color: var(--text2); width: 60px; flex-shrink: 0;
}
.dm-piece-inp {
    width: 64px; background: var(--bg);
    border: 1px solid var(--border);
    color: var(--color-text-primary); border-radius: var(--border-radius-lg);
    padding: 4px 6px; font-size: 12px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.dm-piece-inp:focus {
    border-color: var(--color-accent-strong);
    outline: none;
}
.dm-piece-target {
    color: var(--text2); font-size: 11px;
}
.dm-piece-prog-wrap {
    height: 8px; background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); overflow: hidden;
    margin-top: 4px;
}
.dm-piece-prog-bar {
    height: 100%;
    background: linear-gradient(90deg, #58a6ff 0%, #a371f7 50%, var(--ok-text) 100%);
    transition: width .15s;
}
.dm-piece-prog-text {
    font-size: 11px; color: var(--text2);
    text-align: center;
}
.dm-piece-need {
    color: var(--color-status-warning);
}
.dm-piece-actions {
    margin-top: 4px;
}
.dm-piece-btn {
    width: 100%; min-height: 40px; padding: 8px;
    font-size: 11px; font-weight: 600;
    border-radius: var(--border-radius-lg); cursor: pointer;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--text2);
    transition: background .1s, border-color .1s;
}
.dm-piece-btn:disabled {
    opacity: .5; cursor: not-allowed;
}
.dm-piece-btn-gold:not(:disabled) {
    background: var(--color-accent-soft); border-color: var(--color-accent-strong);
    color: var(--color-accent-strong);
}
.dm-piece-btn-gold:not(:disabled):hover {
    background: var(--accent); color: var(--color-on-accent);
}
.dm-piece-btn-undo {
    color: var(--bad-text); border-color: var(--bad-border);
}
.dm-piece-btn-undo:hover {
    background: var(--bad-bg);
}

/* Totals row */
.dm-totals {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 10px;
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
    padding: 12px;
}
.dm-total-card {
    display: flex; flex-direction: column; gap: 4px;
    align-items: center; text-align: center;
    padding: 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
}
.dm-total-val {
    font-size: 18px; font-weight: 700; color: var(--color-accent-strong);
    font-variant-numeric: tabular-nums;
}
.dm-total-lbl {
    font-size: 11px; color: var(--text2);
    text-transform: uppercase; letter-spacing: .04em;
}

/* Sequence diagrams */
.dm-seq {
    display: flex; align-items: center; gap: 6px;
    flex-wrap: wrap;
    padding: 10px;
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
}
.dm-seq-item {
    display: flex; flex-direction: column; align-items: center;
    gap: 4px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
    padding: 8px 12px;
    min-width: 80px;
}
.dm-seq-num {
    font-weight: 700; font-size: 11px;
    text-transform: uppercase; letter-spacing: .04em;
}
.dm-seq-num-1 { color: var(--color-status-error); }
.dm-seq-num-2 { color: var(--color-status-warning); }
.dm-seq-num-3 { color: var(--color-accent-strong); }
.dm-seq-num-4 { color: var(--color-status-success); }
.dm-seq-num-5 { color: var(--color-accent-secondary-strong); }
.dm-seq-num-6 { color: var(--purple); }
.dm-seq-icon { font-size: 24px; }
.dm-seq-label {
    font-size: 11px; color: var(--text2);
    font-weight: 600;
}
.dm-seq-arrow {
    font-size: 18px; color: var(--text2);
    font-weight: 700;
}

/* Fusion flow */
/* Blue and Purple routes as two clearly-separated cards */
.dm-routes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: var(--space-4);
    margin-top: var(--space-3);
}
.dm-route {
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    padding: var(--space-4);
}
.dm-route-b { border-left: 4px solid var(--color-accent-secondary); background: var(--color-accent-secondary-soft); }
.dm-route-p { border-left: 4px solid var(--purple); background: var(--purple-soft); }

.dm-route-title {
    font-size: 12px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
    margin: 0 0 var(--space-2);
}
.dm-route-blue { color: var(--color-accent-secondary-strong); }
.dm-route-purple { color: var(--purple); }
.dm-flow {
    display: flex; align-items: center; gap: 6px;
    flex-wrap: wrap;
    padding: 10px;
    background: var(--bg3);
    border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
}
.dm-flow-node {
    display: flex; flex-direction: column; align-items: center;
    gap: 2px;
    border-radius: var(--border-radius-lg);
    padding: 8px 14px;
    min-width: 80px;
    border: 1px solid;
}
.dm-flow-blue   { background: var(--color-accent-secondary-soft); border-color: var(--color-accent-secondary); color: var(--color-accent-secondary-strong); }
.dm-flow-purple { background: var(--purple-soft); border-color: var(--purple); color: var(--purple); }
.dm-flow-orange { background: var(--color-status-warning-soft); border-color: var(--color-status-warning); color: var(--color-status-warning); }
.dm-flow-gold   { background: var(--color-accent-soft); border-color: var(--color-accent-strong); color: var(--color-accent-strong); }
.dm-flow-qty {
    font-size: 16px; font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.dm-flow-tier {
    font-size: 10px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
}
.dm-flow-arrow {
    display: flex; flex-direction: column; align-items: center;
    gap: 2px;
    color: var(--text2);
    font-size: 18px; font-weight: 700;
}
.dm-flow-cost {
    font-size: 10px; color: var(--text2);
    font-weight: 600;
}

/* Decomp table */
.dm-table-wrap { overflow-x: auto; }
.dm-table {
    width: 100%; border-collapse: collapse;
    font-size: 12px;
}
.dm-table th, .dm-table td {
    text-align: left; padding: 8px 12px;
    border-bottom: 1px solid var(--border);
}
.dm-table th {
    background: var(--bg3);
    color: var(--color-accent-strong); font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
    font-size: 11px;
}
.dm-table td {
    font-variant-numeric: tabular-nums;
}
.dm-gain { color: var(--ok-text); font-weight: 700; }

/* ── Tile War ─────────────────────────────────────────────────────────── */
.tw-wrap {
    background: #0b0e1a;            /* intentional dark tactical console (both themes) */
    color: #e8eaf0;
    padding: 16px 18px;
    margin: -22px;                  /* bleed to the edges of .roc-main padding */
    min-height: calc(100vh - 60px);
    display: flex; flex-direction: column;
    gap: 10px;
}
.tw-tabs {
    display: flex; gap: var(--space-2); flex-wrap: wrap;
}
.tw-tab {
    background: var(--color-bg-tertiary); border: 1px solid transparent;
    border-radius: var(--border-radius-md);
    color: var(--color-text-secondary);
    min-height: 40px;
    padding: 8px 18px; font-size: var(--font-size-sm); font-weight: 700;
    letter-spacing: .01em;
    cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.tw-tab:hover { color: var(--color-text-primary); background: var(--color-bg-sunken); }
.tw-tab:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.tw-tab-active {
    background: var(--color-bg-secondary); color: var(--color-text-primary);
    border-color: var(--color-border-primary);
    box-shadow: var(--shadow-sm);
}
.tw-tab-bad.tw-tab-active   { border-top: 3px solid var(--bad-text); }
.tw-tab-good.tw-tab-active  { border-top: 3px solid var(--archer); }
.tw-tab-play.tw-tab-active  { border-top: 3px solid var(--cavalry); }

.tw-topbar {
    background: var(--color-bg-secondary); border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--space-3) var(--space-4);
    display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-5);
    align-items: center;
}
.tw-mode-title {
    flex: 1 1 auto;
    font-size: var(--font-size-base); font-weight: 800;
    color: var(--color-text-primary);
    letter-spacing: .01em;
}
.tw-spd-row {
    display: flex; align-items: center; gap: 5px;
    font-size: var(--font-size-sm); color: var(--color-text-secondary);
}
.tw-spd-lbl { margin-right: 4px; font-weight: 700; }
.tw-spd-btn {
    background: var(--color-bg-secondary); border: 1px solid var(--color-border-strong);
    color: var(--color-text-secondary); border-radius: var(--border-radius-sm);
    min-height: 44px; padding: 5px 14px; font-size: var(--font-size-sm); font-weight: 700;
    cursor: pointer;
}
.tw-spd-btn:hover { color: var(--color-text-primary); background: var(--color-bg-tertiary); }
.tw-spd-on {
    background: var(--color-accent-secondary-soft); color: var(--color-accent-secondary-strong);
    border-color: var(--color-accent-secondary-strong);
}
.tw-ctrl {
    background: var(--color-bg-secondary); border: 1px solid var(--color-border-strong);
    color: var(--color-text-primary); border-radius: var(--border-radius-md);
    min-height: 40px; padding: 7px 16px; font-size: var(--font-size-sm); font-weight: 700;
    cursor: pointer;
    transition: background .15s;
}
.tw-ctrl:hover { background: var(--color-bg-tertiary); }
.tw-time {
    font-size: var(--font-size-md); font-weight: 800;
    color: var(--color-text-primary);
    font-variant-numeric: tabular-nums;
    min-width: 64px;
}
.tw-winner {
    margin-left: auto;
    padding: 6px 14px; border-radius: var(--border-radius-full);
    font-weight: 800; font-size: var(--font-size-sm);
    text-transform: uppercase; letter-spacing: .05em;
}
.tw-winner-blue {
    background: var(--color-accent-secondary-soft);
    color: var(--color-accent-secondary-strong);
    border: 1px solid var(--color-accent-secondary-strong);
}
.tw-winner-red {
    background: var(--color-status-error-soft);
    color: var(--color-status-error);
    border: 1px solid var(--color-status-error);
}

.tw-instructions {
    background: var(--color-accent-secondary-soft);
    border: 1px solid var(--color-accent-secondary-soft);
    border-left: 3px solid var(--color-accent-secondary-strong);
    color: var(--color-text-primary);
    padding: var(--space-3) var(--space-4); border-radius: var(--border-radius-md);
    font-size: var(--font-size-sm); line-height: var(--line-height-normal);
}

.tw-legend {
    display: flex; flex-wrap: wrap;
    gap: 6px 14px;
}
.tw-leg {
    display: flex; align-items: center; gap: 5px;
    font-size: var(--font-size-sm); color: var(--color-text-secondary);
}
.tw-leg-sw {
    width: 13px; height: 13px;
    border-radius: var(--border-radius-lg); flex-shrink: 0;
    border: 1px solid transparent;
}
.tw-leg-cb { background: #4a90d9; border-color: #88ccff; }
.tw-leg-cr { background: #d94a4a; border-color: #ff9999; }
.tw-leg-blue { background: #185fa5; }
.tw-leg-red { background: #a32d2d; }
.tw-leg-blue-p { background: #185fa5; border: 2px dashed #6ab4ff; }
.tw-leg-red-p { background: #a32d2d; border: 2px dashed #ff8888; }
.tw-leg-dot {
    width: 12px; height: 12px; border-radius: 50%;
    background: #ff9966; border: 1.5px solid #fff;
}
.tw-leg-dot-ret { opacity: .4; border-color: rgba(255,255,255,.4); }

.tw-body {
    display: flex; gap: 12px;
    align-items: flex-start;
}
.tw-grid-scroll {
    flex: 1 1 auto;
    overflow-x: auto; overflow-y: hidden;
    padding-bottom: 8px;
}
.tw-grid-host {
    /* Sized inline by JS module via canvas + tile rows */
}

/* Tile colors painted by JS via class assignment */
.tw-tile {
    /* Base sized inline by JS; styled here */
    cursor: default;
}
.tw-tile.tw-blue   { background: #185fa5; color: #b5d4f4; border-color: #2a7fcc; }
.tw-tile.tw-red    { background: #a32d2d; color: #f7c1c1; border-color: #cc4444; }
.tw-tile.tw-blue-p { background: #185fa5; color: #b5d4f4; border: 2px dashed #6ab4ff; }
.tw-tile.tw-red-p  { background: #a32d2d; color: #f7c1c1; border: 2px dashed #ff8888; }
.tw-tile.tw-cb     { background: #4a90d9; color: #fff; border: 2px solid #88ccff; }
.tw-tile.tw-cr     { background: #d94a4a; color: #fff; border: 2px solid #ff9999; }
.tw-castle-burning {
    background: #d94a4a; color: #fff; border: 2px solid #ff9999;
    animation: tw-burn .7s ease-in-out infinite;
}
@keyframes tw-burn {
    0%, 100% { box-shadow: 0 0 4px 2px rgba(220,60,60,.5); }
    50%      { box-shadow: 0 0 14px 6px rgba(255,80,80,.95); }
}

/* Click affordance in play mode */
.tw-wrap[data-mode="play"] .tw-tile { cursor: crosshair; }
.tw-tile:hover { filter: brightness(1.15); }

/* Side panel (legions + log) */
.tw-side {
    flex: 0 0 280px;
    display: flex; flex-direction: column; gap: 10px;
}
.tw-side-section {
    background: #22252e;
    border: 1px solid #2e3140;
    border-radius: var(--border-radius-lg);
    padding: 10px 12px;
}
.tw-side-hdr {
    font-size: 11px; font-weight: 700;
    color: var(--color-accent-strong);
    text-transform: uppercase; letter-spacing: .04em;
    margin-bottom: 6px;
    padding-bottom: 4px;
    border-bottom: 1px solid #2e3140;
}
.tw-leg-row {
    display: flex; align-items: center; gap: 6px;
    padding: 5px 6px; border-radius: var(--border-radius-lg);
    font-size: 11px;
    margin-bottom: 3px;
    background: #1a1d24;
    border: 1px solid #2a2e38;
}
.tw-leg-row-blue { border-left: 3px solid #4a90d9; }
.tw-leg-row-red  { border-left: 3px solid #d94a4a; }
.tw-leg-id {
    font-weight: 700; color: #e8eaf0;
    width: 38px; flex-shrink: 0;
}
.tw-leg-status { flex: 1; color: #9aa0b4; font-size: 10.5px; }
.tw-leg-status-idle      { color: #6a7080; }
.tw-leg-status-out       { color: #ffb070; }
.tw-leg-status-ret       { color: #9aa0b4; }
.tw-leg-status-capturing { color: #f7a0a0; font-weight: 700; }
.tw-leg-eta {
    color: var(--color-accent-strong); font-weight: 700;
    font-variant-numeric: tabular-nums;
    font-size: 10.5px;
}

.tw-log {
    display: flex; flex-direction: column; gap: 3px;
    max-height: 240px; overflow-y: auto;
}
.tw-log-entry {
    font-size: 10.5px; line-height: 1.4;
    padding: 4px 6px; border-radius: var(--border-radius-lg);
    background: #1a1d24;
}
.tw-log-neutral { color: #9aa0b4; }
.tw-log-good    { color: #a0f7c0; }
.tw-log-warn    { color: #f7c1a0; }
.tw-log-bad     { color: #f7a0a0; }
.tw-log-defeat  { color: #ff6060; font-weight: 700; }
.tw-log-win     { color: #60ff90; font-weight: 700; }

.tw-verdict {
    background: #22252e; border: 1px solid #2e3140;
    border-left: 3px solid #4a90d9;
    border-radius: var(--border-radius-lg);
    padding: 10px 14px;
    transition: border-color .4s;
}
.tw-verdict-defeat {
    border-left-color: #ff3333;
    background: #2e1a1a;
}
.tw-verdict-t {
    font-size: 11px; color: #9aa0b4;
    margin-bottom: 4px;
}
.tw-verdict-msg { font-size: 13px; line-height: 1.55; }
.tw-vm-neutral { color: #9aa0b4; }
.tw-vm-good    { color: #a0f7c0; }
.tw-vm-warn    { color: #f7c1a0; }
.tw-vm-bad     { color: #f7a0a0; }
.tw-vm-defeat  { color: #ff6060; font-weight: 700; font-size: 14px; }
.tw-vm-win     { color: #60ff90; font-weight: 700; font-size: 14px; }

@media (max-width: 1100px) {
    .tw-body { flex-direction: column; }
    .tw-side { flex: 0 0 auto; width: 100%; }
}

/* ════════════════════════════════════════════════════════════════
   AURORA GLASS — feature-page rollout layer
   Upgrades the top-level panels of every feature namespace to the
   glass treatment + Aurora radii. Declared last so it wins over the
   original (lower-specificity, earlier) rules. Nested cards keep
   their solid surfaces for readability over the glass.
   ════════════════════════════════════════════════════════════════ */

/* Top-level panels / toolbars / sections → frosted glass */
.rtp-toolbar, .rtp-filterbar, .rtp-statbar, .rtp-book,
.ntp-row,
.hc-ctrls, .hc-selbar, .hc-result-col,
.sp-toolbar, .sp-canvas-wrap, .sp-side-section,
.ec-toolbar, .ec-section,
.vp-header,
.hr-section,
.dm-section, .dm-subtabs {
    background: var(--glass-bg);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* Dragon Master keeps its tab-strip + body visually joined */
.dm-subtabs { border-radius: var(--radius) var(--radius) 0 0; border-bottom: none; box-shadow: none; }
.dm-section { border-radius: 0 0 var(--radius) var(--radius); }

/* Virtue header spans full width — square it, keep the glass */
.vp-header { border-radius: 0; box-shadow: none; }

/* Modals & dialogs → glass + soft depth, with a blurred backdrop */
.ntp-modal-box {
    background: var(--glass-bg);
    -webkit-backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(160%);
    backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(160%);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}
.ntp-modal-backdrop,
.mud-overlay-dialog {
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
}
.mud-dialog {
    background: var(--glass-bg) !important;
    -webkit-backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(160%);
    backdrop-filter: blur(calc(var(--glass-blur) + 6px)) saturate(160%);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-lg) !important;
    box-shadow: var(--shadow-lg) !important;
}

/* Rounder, friendlier controls across the feature pages */
.rtp-btn, .rtp-dd-btn, .rtp-search,
.hc-sel,
.ec-input, .ec-select, .ec-vt-btn, .ec-date,
.vp-troop-btn, .vp-path-btn, .vp-col-prog,
.dm-piece-btn, .dm-piece-inp {
    border-radius: var(--radius-sm);
}

/* Accent glow on focus for the main search/inputs (gold == accent now) */
.rtp-search:focus, .hc-sel:focus, .ec-input:focus, .ec-select:focus,
.dm-piece-inp:focus {
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Lift cards a touch more on hover, with an Aurora edge */
.ntp-node:hover, .hcard:hover, .hr-pick-card:hover,
.dm-piece:hover, .ec-tile-card:hover, .vp-tree-card:hover {
    box-shadow: var(--shadow), 0 0 0 1px var(--glass-border);
}

@media (prefers-reduced-motion: reduce) {
    .ntp-node, .hcard, .hr-pick-card, .dm-piece, .ec-tile-card, .vp-tree-card { transition: none; }
}

/* ── Settings pages (/account and /castle) ────────────────────────────────────
   Global rather than scoped: /account and /castle share this layout and these
   controls, and Blazor's scoped CSS is per-component, so a .razor.css on one page
   would leave the other unstyled. Same reasoning as the .sp-* planner styles above. */
/* One centred column of segments.
   .roc-main is unconstrained, so on a wide screen the old auto-fit grid spread these six
   cards over three or four columns of uneven height — settings ended up scattered rather
   than read in order. A single column keeps each segment one measure wide and the reading
   order top to bottom; the cap is what stops a full-width monitor stretching a form field
   to 1500px. Below the cap the width rule is inert, so narrow screens are unaffected. */
.acct-page {
    --acct-col: 760px;
    max-width: var(--acct-col);
    margin-inline: auto;
}

.acct-grid {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

/* Castle profile: active-hours grid (+0..+23) and castle-type chips.
   Column counts are fixed rather than auto-filled, and every one of them divides 24 exactly,
   so the last row is always full. auto-fill used to be fine in a ~350px card, but in the
   wider single column it fitted 15 across and left a ragged 9 on the second row. */
.acct-hours {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));   /* 4 x 6 */
    gap: 6px;
    margin-top: var(--space-2);
}
@media (min-width: 480px) {
    .acct-hours { grid-template-columns: repeat(8, minmax(0, 1fr)); }   /* 8 x 3 */
}
@media (min-width: 760px) {
    .acct-hours { grid-template-columns: repeat(12, minmax(0, 1fr)); }  /* 12 x 2 */
}
.acct-hour {
    min-height: 40px; padding: 6px 4px;
    border: 1px solid var(--color-border-strong);
    background: var(--color-bg-tertiary);
    color: var(--color-text-secondary);
    border-radius: var(--border-radius-md);
    font-weight: 700; font-size: var(--font-size-sm);
    font-variant-numeric: tabular-nums; cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.acct-hour:hover { color: var(--color-text-primary); }
.acct-hour.is-on {
    background: var(--color-accent-soft);
    border-color: var(--color-accent-strong);
    color: var(--color-accent-strong);
}
.acct-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.acct-chip {
    min-height: 40px; padding: 8px 14px;
    border: 1px solid var(--color-border-strong);
    background: var(--color-bg-tertiary);
    color: var(--color-text-secondary);
    border-radius: var(--border-radius-full);
    font-weight: 700; font-size: var(--font-size-sm); cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.acct-chip:hover { color: var(--color-text-primary); }
.acct-chip.is-on {
    background: var(--color-accent-secondary-soft);
    border-color: var(--color-accent-secondary-strong);
    color: var(--color-accent-secondary-strong);
}

/* The glossary under the key hero skills: only the terms the skills on screen actually use. */
.hs-glossary { margin-top: var(--space-5); border-top: 1px solid var(--border); padding-top: var(--space-4); }
.hs-glossary-h { font-family: var(--font-display); font-size: var(--font-size-md); font-weight: 800; margin: 0 0 var(--space-3); }
.hs-glossary-list { display: grid; grid-template-columns: auto 1fr; gap: 6px var(--space-4); margin: 0; max-width: 80ch; }
.hs-glossary-list dt { font-weight: 700; }
.hs-glossary-list dd { margin: 0; color: var(--color-text-secondary); }

/* Ask the alliance. A card per request, and a two-column review: what they have on the left,
   what people said on the right. Stacks on a phone, because a review is read there as often. */
.adv-ask { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.adv-list { display: flex; flex-direction: column; gap: var(--space-3); }
.adv-card {
    background: var(--bg2); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg); padding: var(--space-3);
}
.adv-card.adv-closed { opacity: .62; }
.adv-card-head { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.adv-who { font-weight: 700; }
.adv-when { margin-left: auto; color: var(--color-text-tertiary); font-size: var(--font-size-sm); }
.adv-note { margin: 6px 0 0; color: var(--color-text-secondary); max-width: 68ch; }
.adv-note-lead { margin-bottom: var(--space-4); }
.adv-card-foot { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-3); }
.adv-count { color: var(--color-text-tertiary); font-size: var(--font-size-sm); margin-right: auto; }

.adv-scope-tech { color: var(--color-accent-strong); border-color: var(--color-accent-primary); }
.adv-scope-heroes { color: var(--purple); border-color: rgba(163, 113, 247, 0.4); }
.adv-stale { color: var(--color-text-tertiary); border-color: var(--border); }

.adv-review { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: var(--space-5); }
.adv-h { font-family: var(--font-display); font-size: var(--font-size-md); font-weight: 800; margin: var(--space-4) 0 2px; }
.adv-targets { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.adv-target {
    display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
    background: var(--bg2); border: 1px solid var(--rule, var(--border));
    border-radius: var(--border-radius-md); padding: 8px 10px;
}
.adv-target-name { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-right: auto; }
.adv-mark-btns { display: flex; gap: 6px; }
.adv-btn-focus.active { color: var(--color-status-success); border-color: var(--color-status-success); }
.adv-btn-skip.active { color: var(--color-status-warning); border-color: var(--color-status-warning); }
.adv-legions { margin: var(--space-2) 0 0; padding-left: 20px; color: var(--color-text-secondary); }

.adv-marks { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.adv-mark { display: flex; gap: 8px; flex-wrap: wrap; font-size: var(--font-size-sm); }
.adv-mark-what { font-weight: 700; }
.adv-mark-who, .adv-mark-note { color: var(--color-text-tertiary); }
.adv-focus { color: var(--color-status-success); }
.adv-skip { color: var(--color-status-warning); }

.adv-reply { border-left: 3px solid var(--border); padding-left: var(--space-3); margin-top: var(--space-3); }
.adv-reply-head { font-weight: 700; font-size: var(--font-size-sm); }
.adv-reply-text { margin: 2px 0 0; color: var(--color-text-secondary); white-space: pre-wrap; }

/* What the alliance said, under a move on the plan. */
.mp-said { display: flex; flex-wrap: wrap; gap: 10px; margin: 6px 0 0; font-size: var(--font-size-sm); }
.mp-said-stale { opacity: .6; }
.mp-said-note { color: var(--color-text-tertiary); }

/* Plan page — two ranked lists side by side on a wide screen, stacked on a phone. Each move is a
   row rather than a card: the list is read top to bottom, and cards invite comparison sideways. */
.mp-lists {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: var(--space-5);
}
.mp-list-hdr { margin-bottom: var(--space-3); }
.mp-list-title { font-family: var(--font-display); font-size: var(--font-size-md); font-weight: 800; margin: 0 0 2px; }
.mp-none, .mp-account { margin-top: var(--space-3); }

.mp-moves { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); counter-reset: mp; }
.mp-move {
    position: relative;
    background: var(--bg2); border: 1px solid var(--border);
    border-radius: var(--border-radius-lg);
    padding: var(--space-3) var(--space-3) var(--space-3) 44px;
}
/* The rank, because "best first" is the whole claim the list is making. */
.mp-move::before {
    counter-increment: mp; content: counter(mp);
    position: absolute; left: 14px; top: var(--space-3);
    font-weight: 800; color: var(--color-text-tertiary);
}
.mp-move-head { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.mp-move-name { font-weight: 700; }
.mp-move-lv { color: var(--color-text-secondary); font-size: var(--font-size-sm); }
.mp-move-code { font-family: var(--font-mono, monospace); font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
.mp-move-book { margin-left: auto; color: var(--color-text-tertiary); font-size: var(--font-size-sm); }
.mp-locked { color: var(--color-status-warning); border-color: rgba(255, 158, 100, 0.4); }

.mp-gains { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.mp-cost { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); margin-top: 6px; }
.mp-cost-main { font-weight: 700; color: var(--color-accent-strong); }
.mp-caveat { font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
.mp-res { font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.mp-unlocks, .mp-desc { margin: 6px 0 0; font-size: var(--font-size-sm); color: var(--color-text-secondary); }

/* Goal page — the three currency boxes sit side by side where there is room and stack where
   there is not, because they are one answer ("what have I got") rather than three questions. */
.mg-stocks {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: var(--space-4);
}
.mg-stocks .roc-field { margin-bottom: 0; }

/* Castle buddies picker */
.acct-buddies {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr));
    gap: 6px;
    margin-top: var(--space-2);
}
.acct-buddy {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px;
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-md);
    cursor: pointer; font-size: var(--font-size-sm);
}
.acct-buddy.is-on { background: var(--color-accent-secondary-soft); border-color: var(--color-accent-secondary-strong); }
.acct-buddy-name { font-weight: 700; color: var(--color-text-primary); }
.acct-buddy-coord { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--color-text-tertiary); font-size: 12px; }
.acct-toggle {
    display: flex; align-items: center; gap: 10px;
    margin: var(--space-3) 0;
    font-size: var(--font-size-sm); color: var(--color-text-secondary); cursor: pointer;
}

.acct-coords {
    display: flex;
    gap: var(--space-4);
}

.acct-coords .roc-field {
    flex: 1;
}

.acct-push-status {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--border-radius-lg);
    font-size: var(--font-size-sm);
    line-height: 1.5;
    margin-bottom: var(--space-3);
}
.acct-push-status.is-on   { background: var(--color-status-success-soft); color: var(--color-text-primary); }
.acct-push-status.is-off  { background: var(--color-bg-tertiary); color: var(--color-text-secondary); }
.acct-push-status.is-info { background: var(--color-accent-secondary-soft); color: var(--color-text-primary); }
.acct-push-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.acct-qr {
    display: block;
    width: 200px;
    height: 200px;
    border-radius: var(--border-radius-lg);
    background: #fff;
    padding: var(--space-3);
    margin: var(--space-3) 0;
}

.acct-recovery {
    list-style: none;
    padding: var(--space-3) var(--space-4);
    margin: var(--space-3) 0;
    background: var(--color-bg-tertiary);
    border-radius: var(--border-radius-lg);
    columns: 2;
    column-gap: var(--space-5);
}
.acct-recovery li { padding: 2px 0; }
.acct-recovery code {
    font-size: var(--font-size-sm);
    letter-spacing: 0.05em;
}

/* Deletion is irreversible, so the card that holds it is marked out rather than sitting as
   just another section in the stack. */
.acct-danger { border-color: var(--color-status-error); }

/* A shieldwall row opened from a notification. Filled rather than outlined so the row the link
   named is obvious without scrolling hunting for it. */
.sw-highlight { border: 2px solid var(--color-accent-secondary-strong); }

/* Countdown on a notification. Amber inside the last hour, because that is when the pad deadline
   stops being information and starts being a problem. */
.roc-bell-countdown {
    margin-left: 8px;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--color-accent-secondary-strong);
}
.roc-bell-countdown.is-urgent { color: var(--color-status-warning); }

/* Unread dot on the What's new menu entry. A dot rather than a count: there is only ever one
   thing to say — there is something you have not read. */
.roc-usermenu-dot {
    width: 8px;
    height: 8px;
    margin-left: auto;
    border-radius: 50%;
    background: var(--color-accent-primary);
    flex: none;
}

/* What's new. The kind chip is fixed-width so the change text starts on the same column down the
   whole list and can be skimmed. */
.wn-kind { min-width: 76px; justify-content: center; }
.wn-item { padding-left: 0; padding-right: 0; }
.wn-timeline { padding-left: 0; }

/* Shieldwall row times: the three moments, game time and the member's own day beside it.
   Borderless because it is a label-value list rather than data to compare across rows. */

/* Currency-tinted KPI chips, so the War Badge figure and the Courage Medal figure are not two
   identical-looking numbers side by side. */
.rtp-kpi-wb  { color: var(--cavalry); }
.rtp-kpi-cm  { color: var(--archer); }
.rtp-kpi-res { color: var(--footman); }
.rtp-kpi-mix { color: var(--purple); }
.rtp-kpi-fm  { color: var(--color-status-error); }

/* The smaller "spent" figure inside a currency chip: present but subordinate to what is left,
   which is the number being planned against. */
.rtp-kpi-sub { font-size: 10px; opacity: 0.75; font-weight: 400; margin-left: 4px; }

/* Shieldwall timeline: the row%s five moments side by side. Was five two-line table rows, which
   pushed the castle list off the screen on every wall. Wraps rather than scrolls, so a narrow phone
   gets two or three per line instead of a sideways scrollbar. */
.sw-times { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.sw-time {
    display: flex; flex-direction: column; gap: 1px;
    padding: 4px 8px; min-width: 132px;
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    background: var(--color-bg-tertiary);
}
/* Wide enough for the words. The tiles used to read "T", "T+1", "T+9" and 96px was generous for
   that; they now say what the moment is ("Free to charge again" is the longest), so the tile is
   sized to the label instead of to the notation it replaced. */
.sw-time-t     { font-size: 11px; font-weight: 700; color: var(--text2); }
.sw-time-game  { font-size: 12px; color: var(--text); white-space: nowrap; }
.sw-time-local { font-size: 11px; color: var(--text3); white-space: nowrap; }

/* Spec preset menu and the "loaded from" line. Presets used to be invisible until you opened a
   dialog; the point of these is that the page says what you have and what you are looking at. */
.sp-preset-item { display: flex; flex-direction: column; gap: 1px; min-width: 180px; }
.sp-preset-name { font-size: 13px; color: var(--text); }
.sp-preset-when { font-size: 11px; color: var(--text3); }
.sp-loaded-preset {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
    margin-bottom: var(--space-3); padding: 6px 10px;
    font-size: 12px; color: var(--text2);
    background: var(--color-bg-tertiary);
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
}

/* ── Reference pages: buildings, hero skins, dragon ─────────────────────────────────────────
   Plain layout on purpose. These are catalogues you look something up in, so the styling stays out
   of the way of the numbers. */
.bld-name  { font-weight: 600; }
.bld-sub   { font-size: 11px; color: var(--text3); margin-left: 6px; }

/* Heroes three across. There are 75 of them and, once the catalogue's duplicate rows are collapsed,
   most own a single skin — so a full-width card per hero was a page of mostly margin, about eight
   screens of it. Three columns is roughly two. Heights are left alone rather than equalised: a hero
   with one skin next to one with three would otherwise take the taller of the two, and the
   whitespace that saves the eye on a six-card week costs real screens across seventy-five. */
.skin-heroes { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); align-items: start; }
.skin-hero   { margin: 0; padding: var(--space-4); }
.skin-hero .roc-card-head  { margin-bottom: var(--space-3); }
.skin-hero .roc-card-title { font-size: var(--font-size-md); }

/* One skin per line inside a third-width card; the old auto-fill wanted 280px and would give a
   second column back only by squeezing the effects onto four lines each. */
.skin-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-2); }
.skin {
    display: flex; flex-direction: column; gap: 3px;
    padding: 8px 10px;
    border: 1px solid var(--border); border-radius: var(--border-radius-lg);
    background: var(--color-bg-tertiary);
}
.skin-hdr    { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
/* Box and name together, so the name is part of what you can tap to own a skin. */
.skin-own    { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.skin-name   { font-weight: 600; font-size: 13px; }
.skin-power  { font-size: 11px; color: var(--text3); margin-left: auto; }
.skin-sub    { font-size: 11px; color: var(--text3); font-style: italic; }
.skin-effects{ display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.skin-effect { font-size: 10px; }
.skin-cost   { font-size: 11px; color: var(--text2); }
.skin-cost-label {
    display: inline-block; min-width: 52px;
    font-size: 10px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text3);
}

.drg-row    { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-2); }
.drg-select { max-width: 190px; }

.bld-level  { max-width: 90px; }
/* An owned skin is tinted rather than moved, so the grid does not reflow as you tick things. */
.skin-owned { border-color: var(--color-accent-strong); background: var(--color-bg-secondary); }

/* Two columns before the effect badges start wrapping one to a line, then one — the same two steps
   the hourlies grid takes, and the phone rule below already sets .skin-own a thumb-sized target. */
@media (max-width: 1180px) { .skin-heroes { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767.98px) {
    .skin-heroes { grid-template-columns: 1fr; gap: var(--space-2); }
    .skin-hero   { padding: var(--space-4) var(--space-3); }
}

/* ════════════════════════════════════════════════════════════════
   MudBlazor overlays on a phone

   MudBlazor sizes pickers, dialogs and popovers for a desktop viewport and nothing in the library
   scales them down. The numbers below are read out of MudBlazor.min.css rather than guessed, so
   what is being corrected is on the record:

     .mud-dialog-width-full     width: calc(100% - 64px)   32px of margin each side
     .mud-dialog-content        padding: 8px 24px          48px more
     .mud-picker-calendar-container  width: 310px          fixed, not a minimum
     .mud-picker-content        min-width: 310px
     .mud-picker-toolbar        height: 100px

   On a 360px screen a dialog therefore spends 112px — nearly a third of the width — on chrome
   before any content, and a picker panel cannot become narrower than 310px whatever it is put in.
   ════════════════════════════════════════════════════════════════ */

/* Game-time entry: a day and two numbers, the same three controls the target planner uses.
   Desktop keeps them compact and side by side. The row wraps rather than forcing one line, which
   is what used to put a horizontal scrollbar on /shieldwalls. */
.gt-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: flex-end;
}
.gt-row .roc-field { margin: 0; }
.gt-date { max-width: 170px; }
.gt-num { max-width: 110px; }
.gt-caption {
    font-size: var(--font-size-sm);
    color: var(--color-text-secondary);
    padding-bottom: var(--space-2);
}

@media (max-width: 767.98px) {
    /* Full width and stacked: a 170px field is reachable with a thumb, but three of them plus a
       caption on one line is not, and the row is what caused the page to scroll sideways. */
    .gt-date, .gt-num { max-width: none; flex: 1 1 100%; }

    /* Give the dialog back the width the phone has. 8px of margin instead of 32, and content
       padding down from 24px, which leaves a 360px screen 328px to work with — enough for the
       310px picker panel that MudBlazor will not shrink. */
    .mud-dialog-width-full { width: calc(100% - var(--space-2)); }
    .mud-dialog { max-width: calc(100% - var(--space-2)) !important; }
    .mud-dialog .mud-dialog-title { padding: var(--space-3) var(--space-4); }
    .mud-dialog .mud-dialog-content { padding: var(--space-2) var(--space-3); }
    .mud-dialog-actions { padding: var(--space-3) var(--space-3) var(--space-4); }

    /* A tall dialog has to scroll inside itself rather than push its buttons off the screen. */
    .mud-dialog { max-height: calc(100vh - var(--space-4)); }

    /* Select and menu popovers are placed from their anchor with no cap on how far right they
       may extend, so a long option name runs off the edge. */
    .mud-popover { max-width: calc(100vw - var(--space-4)); }
}

/* ════════════════════════════════════════════════════════════════
   Tech pages on a phone

   A Royal Tech row is a flex line of fixed widths that cannot shrink:

     .rtp-tech-name    flex: 0 0 200px
     .rtp-tech-slider  flex: 0 0 140px
     .rtp-tech-lv      min-width: 52px
     .rtp-tech-cost    width: 90px

   With the checkbox and six 10px gaps that is 567px before the description is given a single
   pixel, against the 328px a 360px screen has after .roc-main's padding. The row cannot wrap, so
   every one of them overflowed.

   The slider is the other half of it: .rtp-tech-slider is 3px tall, which is the whole hit area
   for a thumb. Normal Tech's modal stacks and flexes properly already, but its slider is the same
   element and was just as hard to hit.
   ════════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
    /* The row becomes a small card: name and badges, then the description, then the control. */
    .rtp-tech {
        flex-wrap: wrap;
        gap: 6px 8px;
        padding: 8px;
        border-color: var(--border);
    }
    .rtp-tech-name   { flex: 1 1 auto; min-width: 0; }
    .rtp-tech-desc   { flex: 1 1 100%; white-space: normal; }
    /* Its own line, and wrapping. Royal Academy carries nine troop badges and the strip does not
       wrap, so on a phone the tail was cut off rather than moved down — measured in Chrome at 52px
       past the edge on a 360 screen and 92px on a 320. */
    .rtp-tech-troops { flex: 1 1 100%; flex-wrap: wrap; }
    .rtp-tech-slider { flex: 1 1 100%; }
    .rtp-tech-lv     { min-width: 0; }
    .rtp-tech-cost   { flex: 1 1 auto; width: auto; text-align: left; }

    /* A thumb needs something to aim at. The track stays thin; the input's box grows to 28px so
       there is a target, and the thumb is drawn at 22px rather than left to the platform.
       Styling the thumb means opting out of the native appearance, which also drops accent-color,
       so both engines' track and thumb are spelled out here.

       pan-y, not none: a vertical swipe still scrolls the page, a horizontal drag moves the value.
       Without it the slider swallows the gesture and the page will not scroll past a tech row. */
    .rtp-tech-slider {
        height: 36px;
        -webkit-appearance: none;
        appearance: none;
        background: transparent;
        touch-action: pan-y;
    }
    .rtp-tech-slider::-webkit-slider-runnable-track {
        height: 4px; border-radius: 2px; background: var(--border2);
    }
    .rtp-tech-slider::-moz-range-track {
        height: 4px; border-radius: 2px; background: var(--border2);
    }
    .rtp-tech-slider::-webkit-slider-thumb {
        -webkit-appearance: none; appearance: none;
        width: 22px; height: 22px; margin-top: -9px;
        border: none; border-radius: 50%;
        background: var(--color-accent-secondary);
    }
    .rtp-tech-slider::-moz-range-thumb {
        width: 22px; height: 22px;
        border: none; border-radius: 50%;
        background: var(--color-accent-secondary);
    }
    .rtp-tech-slider:disabled::-webkit-slider-thumb { background: var(--text3); }
    .rtp-tech-slider:disabled::-moz-range-thumb     { background: var(--text3); }
}

/* ════════════════════════════════════════════════════════════════
   Touch targets

   Measured in Chrome at 360x800 across every page a member can reach. Nothing overflowed — width
   is fine app-wide. What failed was the size of the things you have to hit:

     input[type=checkbox]            13x13   the browser default, and there are over a thousand:
                                             899 on Royal Tech, 202 on Hero Skins, more in filters
     .mud-input-numeric-spin button  20x20   360 of them on Buildings
     .dm-piece-inp                   29 tall on Dragon Master
     .rtp-tech-slider                28 tall

   32px is the floor used here. Apple asks 44 and Material 48; 32 is low enough that anything
   still failing it is genuinely fiddly rather than merely tight.
   ════════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
    /* The browser default is 13px square. On a skin card the box is not wrapped in a label, so it
       is the entire target — there is nothing else to tap. */
    input[type="checkbox"],
    input[type="radio"] {
        width: 22px;
        height: 22px;
        flex: 0 0 auto;
    }

    /* A filter row is a <label>, so the row is the real target and only needs to be tall enough.
       Padding rather than a min-height, so the text stays centred against the bigger box. */
    .rtp-dd-row { padding: var(--space-2); }

    /* MudBlazor stacks increment above decrement at 20px each, beside a field that is 90px wide.
       Growing them would make a 64px-tall column next to a one-line input; instead they go away on
       a phone, where the numeric keyboard is the faster way in anyway. Delete this to bring the
       arrows back.

       The long selector is MudBlazor's own, matched rather than beaten with !important: it sets
       display:inline-flex at .mud-input-control.mud-input-number-control .mud-input-numeric-spin,
       which outranks a bare class and quietly won the first time this was written. */
    .mud-input-control.mud-input-number-control .mud-input-numeric-spin { display: none; }

    /* The only way to set a piece count, at 29px tall. */
    .dm-piece-inp {
        padding: var(--space-2);
        font-size: var(--font-size-sm);
    }

    /* A checkbox that is the whole target gets the height from its label rather than by growing
       into something out of scale with the text beside it. The negative margin keeps the row
       looking as it did while the area you can actually hit is 36px. */
    .rtp-cb-hit {
        min-width: 36px;
        min-height: 36px;
        margin: -7px 0 -7px -7px;
    }
    .skin-own { min-height: 32px; }

    /* MudBlazor's small icon button is 3px of padding around an 18px glyph, so 26px square. It is
       the row action on Manage Users and on each shieldwall row. The doubled class matches their
       own .mud-icon-button.mud-button, which sets min-width: unset and would otherwise win. */
    .mud-icon-button,
    .mud-icon-button.mud-button {
        min-width: 36px;
        min-height: 36px;
    }
}

/* Wrapper whose only job is to be big enough to hit; see the touch-target block above. */
.rtp-cb-hit { display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }

/* ── Season scores ───────────────────────────────────────────────────────
   A wide table of numbers, so the numbers get the room: names left and everything else right, in
   tabular figures, which is what lets a column be read down rather than across. */
.ss-pick { display: flex; flex-direction: column; gap: 4px; min-width: 150px; }

.ss-table td, .ss-edit-table td { vertical-align: middle; }
.ss-num  { text-align: right; }
.ss-name { font-weight: 600; }
.ss-total { font-weight: 700; }
.ss-table .mono, .ss-edit-table .mono { font-variant-numeric: tabular-nums; }

/* A guest is somebody outside the alliance, marked because their score counting is the surprising
   part, not something to hide. */
.ss-guest { margin-left: 6px; font-size: 10px; text-transform: uppercase; letter-spacing: .04em; }
.ss-sub   { display: block; font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
.ss-foot  { margin-top: var(--space-3); }

.ss-panel { margin-bottom: var(--space-4); }
.ss-paste { font-family: var(--font-family-mono); font-size: var(--font-size-sm); resize: vertical; }
.ss-file  { display: inline-flex; align-items: center; cursor: pointer; }

/* The add-a-castle form: four short fields that sit on one line while there is room for them. */
.ss-add { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-3); }

.ss-cell { width: 100%; min-width: 80px; text-align: right; padding: 6px 8px; }
.ss-row-actions { text-align: right; white-space: nowrap; }
.ss-save { margin-top: var(--space-3); align-items: center; gap: var(--space-3); }

.ss-import-result { margin-top: var(--space-3); font-size: var(--font-size-sm); }
.ss-problems { margin: var(--space-2) 0 0; padding-left: var(--space-4); color: var(--color-status-error); }
.ss-problems li { margin-bottom: 2px; }

@media (max-width: 767.98px) {
    /* The editing grid keeps its own scroll rather than shrinking six number boxes to nothing. */
    .ss-edit-table { min-width: 640px; }
    .ss-cell { min-width: 64px; }
}

/* ── Alliance board ──────────────────────────────────────────────────────
   Two pages, one prefix: /board reads, /board/manage writes.

   Posts are a single column with a comfortable measure rather than the card grid the reference
   pages use. These are paragraphs, and a paragraph 1100px wide is one nobody finishes — the eye
   loses the start of the next line. 68ch is the usual reading measure and the reason this page
   ignores the width it is given. */
.bd-posts { display: flex; flex-direction: column; gap: var(--space-4); max-width: 68ch; }
.bd-post  { margin: 0; }

.bd-post-head  { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.bd-post-title { font-family: var(--font-display); font-weight: 800; font-size: var(--font-size-md); margin: 0; }
.bd-post-topic { flex: 0 0 auto; }
.bd-post-by    { margin: 2px 0 var(--space-3); font-size: var(--font-size-sm); color: var(--color-text-secondary); }
.bd-post-when  { color: var(--color-text-tertiary); }

/* The one line that makes plain text safe to store and still readable: line breaks are the
   author's, and they survive to the page without anything being parsed. */
.bd-post-body {
    margin: 0; white-space: pre-wrap; overflow-wrap: anywhere;
    line-height: 1.55; color: var(--color-text-primary);
}

.bd-links { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); }
.bd-link {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px; min-height: 36px;
    border: 1px solid var(--color-border-primary); border-radius: var(--radius-pill, 999px);
    background: var(--color-bg-tertiary);
    font-size: var(--font-size-sm); font-weight: 600;
    color: var(--color-text-link); text-decoration: none;
}
.bd-link:hover { background: var(--color-bg-secondary); border-color: var(--color-border-strong); text-decoration: none; }
.bd-link-icon  { font-size: 12px; }
.bd-link-label { overflow-wrap: anywhere; }

/* Topic filter. Buttons rather than a dropdown: six of them, and on a phone a row of taps beats
   a select that hides five of the six behind a menu. */
.bd-topics { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.bd-topic {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 14px; min-height: 36px;
    border: 1px solid var(--color-border-primary); border-radius: var(--radius-pill, 999px);
    background: var(--color-bg-secondary); color: var(--color-text-secondary);
    font-size: var(--font-size-sm); font-weight: 600; cursor: pointer;
}
.bd-topic:hover     { background: var(--color-bg-tertiary); color: var(--color-text-primary); }
.bd-topic.is-active { background: var(--color-accent-soft); border-color: var(--color-accent-strong); color: var(--color-accent-strong); }
.bd-topic-n         { font-variant-numeric: tabular-nums; opacity: .7; font-weight: 700; }

/* Writing side. */
.bd-form { max-width: 68ch; }
.bd-body { font-family: inherit; line-height: 1.5; resize: vertical; min-height: 180px; }
.bd-topic-select { max-width: 100%; }

.bd-link-rows { display: flex; flex-direction: column; gap: var(--space-2); }
/* Label, address, remove. The address gets the room because it is the long one. */
.bd-link-row  { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto; gap: var(--space-2); align-items: center; }

.bd-form-actions { display: flex; gap: var(--space-2); margin-top: var(--space-4); }
.bd-notify-note  { margin-top: var(--space-2); }

.bd-manage-list { display: flex; flex-direction: column; }
.bd-manage-row {
    display: flex; align-items: center; gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--color-border-secondary);
}
.bd-manage-row:last-child { border-bottom: 0; }
.bd-manage-row.is-editing { background: var(--color-accent-soft); border-radius: var(--radius-sm); padding-inline: var(--space-2); }
.bd-manage-main   { min-width: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
.bd-manage-title  { font-weight: 600; overflow-wrap: anywhere; }
.bd-manage-meta   { font-size: var(--font-size-sm); color: var(--color-text-tertiary); }
.bd-manage-actions { display: flex; gap: var(--space-2); flex: 0 0 auto; }

@media (max-width: 767.98px) {
    /* The address needs the whole width on a phone, so the row becomes two lines: what it is and
       the remove button on the first, the address across the second. Placement is explicit —
       auto-placement would push the button below the address instead. */
    .bd-link-row      { grid-template-columns: minmax(0, 1fr) auto; }
    .bd-link-label-in { grid-column: 1; grid-row: 1; }
    .bd-link-row > .btn-ghost { grid-column: 2; grid-row: 1; }
    .bd-link-url-in   { grid-column: 1 / -1; grid-row: 2; }
    .bd-manage-row { flex-wrap: wrap; }
}

/* ── Hourlies ────────────────────────────────────────────────────────────
   Prefixed hrly-, not hr-: the hero roster already owns hr- (hr-slot, hr-day, hr-hours and a dozen
   more). Sharing it meant .hr-slot's flex-direction: column landed on these rows and stacked the
   tick box, the hours and the description on top of each other — which looked like a layout bug in
   this file and was not.

   Six days, three across and two down. Stacked one per row the page was 48 slots long and nothing
   could be compared with anything; three columns puts a whole week on one screen.

   A slot is a label so the whole row toggles rather than just the box, which would otherwise be a
   22px target repeated 48 times.

   The six cards are one size, not six. `align-items: start` let each day stop wherever its longest
   description happened to wrap, so Thursday (eight three-line rows) stood a head taller than Friday
   and the grid read as ragged. `grid-auto-rows: 1fr` gives every row the height of the tallest card
   and the card stretches into it; the head and the slot rows below are equalised the same way, so
   the tick boxes land on a shared baseline across all six. The page is no taller than it already
   was — the tallest day set the height before and still does. */
.hrly-week { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 1fr; gap: var(--space-3); }
.hrly-day  { margin: 0; height: 100%; display: flex; flex-direction: column; }

/* Five themes are one line and Saturday's "Crush your enemies (In a different Province)" is two.
   Reserving two everywhere starts every day's first slot at the same height; the clamp is there so
   a longer translation pads the head rather than growing it. */
.hrly-day .roc-card-head { margin-bottom: var(--space-3); }
.hrly-day .roc-card-sub {
    line-height: 1.35;
    min-height: calc(2 * 1.35 * var(--font-size-sm));
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}

/* The eight rows split what the head leaves equally, so a one-line slot occupies as much as a
   three-line one and row n is level in every card. The label fills its row so the hover and the
   followed tint are full bands rather than text-height slivers. */
.hrly-slots { flex: 1; display: grid; grid-auto-rows: 1fr; gap: 2px; }
.hrly-row   { display: flex; flex-direction: column; min-height: 0; }

.hrly-slot {
    display: flex; align-items: center; gap: var(--space-2);
    padding: 3px var(--space-2); border-radius: var(--radius-sm);
    border: 1px solid transparent;
    cursor: pointer; min-height: 30px; flex: 1 1 auto;
}
.hrly-slot:hover { background: var(--color-bg-tertiary); }
.hrly-slot.is-on { background: var(--color-bg-secondary); border-color: var(--color-accent-strong); }
.hrly-hours { flex: 0 0 76px; font-size: 10px; color: var(--text3); font-variant-numeric: tabular-nums; }
.hrly-what  { font-size: 11px; color: var(--text); line-height: 1.25; }
.hrly-tip   { font-size: 10px; color: var(--text3); font-style: italic; padding: 0 var(--space-2) var(--space-2); }

/* Two columns before the descriptions start wrapping to four lines each. */
@media (max-width: 1180px) { .hrly-week { grid-template-columns: repeat(2, 1fr); } }

@media (max-width: 767.98px) {
    /* One day at a time, and back to a thumb-sized row: the hours and the description each want
       the full width once the card is 320px wide.

       The equalising goes with it. Nothing sits beside anything to be levelled against, so matching
       heights would only pad five cards out to Thursday's length and add scrolling for nothing. */
    .hrly-week  { grid-template-columns: 1fr; grid-auto-rows: auto; gap: var(--space-2); }
    .hrly-day   { height: auto; }
    .hrly-slots { display: block; }
    .hrly-day .roc-card-sub { display: block; min-height: 0; overflow: visible; }
    .hrly-slot  { flex-wrap: wrap; gap: var(--space-1) var(--space-2); min-height: 36px; }
    .hrly-hours { flex: 0 0 auto; }
    .hrly-what  { flex: 1 1 100%; font-size: 12px; }
}


/* ── QoL fixes ───────────────────────────────────────────────────────────
   Three reports from a phone, two of them measured and one taken on trust.
   ──────────────────────────────────────────────────────────────────────── */
@media (max-width: 767.98px) {
    /* Royal Tech rows are cards on a phone, so they want a little more air than on desktop. */
    .rtp-section { gap: var(--space-2); }

    /* Normal Tech's section modal: the level and the 0/Max buttons go under the slider rather than
       beside it. The row cannot actually overflow — the slider is flex:1 and the rest is 133px of
       fixed width against a 283px modal — but it is cramped, and asked for. */
    .ntp-mtech-row2 { flex-wrap: wrap; row-gap: var(--space-2); }
    .ntp-mtech-row2 .rtp-tech-slider { flex: 1 1 100%; }
    .ntp-mtech-lv { width: auto; min-width: 44px; text-align: left; }

    /* Spec planner toolbar: the zoom buttons are 140px of a 360px bar, which left the tree tabs
       146px against a 373px scroll width — one tab visible of three at 360px, none at 320px. The
       tabs take their own row so all three can be reached. */
    .sp-toolbar { flex-wrap: wrap; gap: var(--space-2); }
    .sp-tabs    { flex: 1 1 100%; order: 1; }
    .sp-actions { flex: 1 1 100%; order: 2; justify-content: flex-end; }

    /* Three tabs at 116px plus gaps want 365px and the row has 302, so the third stayed behind a
       scroll. Narrower padding fits all three without dropping below the 44px touch height. */
    .sp-tab { min-width: 0; flex: 1 1 0; padding: 8px var(--space-2); }
    .sp-tab-label { font-size: 12px; }
}

/* ── Hero Combos: owned filter, suggestions, assigning to a legion ───────── */
.hc-owned-toggle {
    display: inline-flex; align-items: center; gap: var(--space-2);
    font-size: 12px; color: var(--text); cursor: pointer;
    padding: var(--space-2); border-radius: var(--radius-sm); min-height: 36px;
}
.hc-owned-toggle:hover { background: var(--color-bg-tertiary); }
.hc-suggest-btn { white-space: nowrap; }

.hc-suggest {
    border: 1px solid var(--color-accent-strong); border-radius: var(--border-radius-lg);
    padding: var(--space-3); margin-bottom: var(--space-3);
    background: var(--color-bg-secondary);
}
.hc-suggest-note { font-size: 11px; color: var(--text3); margin-bottom: var(--space-2); }
.hc-suggest-close { float: right; padding: 0 8px; }
.hc-suggest-row { margin-bottom: var(--space-2); }

/* One button per legion. They wrap rather than shrink, because five of them beside a line-up name
   is more than a phone has room for on one line. */
.hc-assign { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.hc-assign-btn { font-size: 10px; padding: 4px 8px; min-height: 32px; }

@media (max-width: 767.98px) {
    .hc-assign-btn { flex: 1 1 auto; min-height: 36px; }
}

/* ── Plan gate ───────────────────────────────────────────────────────────
   Shown in place of a page's content when the plan does not cover it. Sized
   and centred like an EmptyState, because that is what it is: the page has
   nothing to show, and the reason happens to be commercial. */
.plan-gate {
    max-width: 34rem;
    margin: 3rem auto;
    padding: 2rem;
    text-align: center;
    background: var(--color-bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.plan-gate-badge {
    display: inline-block;
    padding: 0.15rem 0.6rem;
    margin-bottom: 0.9rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-accent-strong);
    background: var(--color-accent-soft);
    border-radius: 999px;
}
.plan-gate-title { margin: 0 0 0.6rem; font-size: 1.25rem; font-weight: 700; }
.plan-gate-body { margin: 0 0 1.4rem; color: var(--color-text-secondary); line-height: 1.55; }
.plan-gate-hint { margin: 0; font-size: 0.9rem; color: var(--color-text-secondary); }

/* ── Billing ─────────────────────────────────────────────────────────── */
.bill-status { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: 0.5rem; }
.bill-status li { display: flex; align-items: center; gap: 0.6rem; }
.bill-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.bill-dot.on { background: var(--color-status-success); }
.bill-dot.off { background: var(--border); }

.bill-plans { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.bill-plan {
    padding: 1.5rem;
    background: var(--color-bg-secondary);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    display: flex; flex-direction: column;
}
.bill-plan--accent { border-color: var(--color-accent-primary); }
.bill-plan-name { margin: 0 0 0.4rem; font-size: 1.05rem; font-weight: 700; }
.bill-plan-price { font-size: 2rem; font-weight: 800; line-height: 1.1; }
.bill-plan-price span { font-size: 0.9rem; font-weight: 500; color: var(--color-text-secondary); }
.bill-plan-alt { margin-bottom: 0.9rem; font-size: 0.85rem; color: var(--color-text-secondary); }
.bill-plan-lead { margin: 0 0 0.9rem; color: var(--color-text-secondary); line-height: 1.5; }
.bill-plan-list { margin: 0 0 1.2rem; padding-left: 1.1rem; display: grid; gap: 0.35rem; }
.bill-plan-list li { line-height: 1.45; }
.bill-plan .btn-primary, .bill-plan .btn-secondary { margin-top: auto; text-align: center; }
.bill-plan-have {
    margin-top: auto; padding: 0.5rem; text-align: center; font-weight: 600;
    color: var(--color-status-success); background: var(--color-status-success-soft);
    border-radius: var(--radius-lg);
}
.bill-plan-hint { margin-top: auto; font-size: 0.9rem; color: var(--color-text-secondary); }

/* ── Public pages (landing, terms, privacy, FAQ, contact) ───────────────
   These render under PublicLayout, which forces the dark theme, so they are
   written dark-first and need no light variant. The case being designed for
   is a visitor arriving from a Discord link, on a phone, at night. */
.pub { display: flex; flex-direction: column; min-height: 100vh; }

.pub-head {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: 1rem clamp(1rem, 5vw, 3rem);
    border-bottom: 1px solid var(--border);
}
.pub-brand { display: flex; align-items: center; gap: 0.6rem; font-weight: 800; font-size: 1.05rem;
             color: var(--color-text-primary); text-decoration: none; }
.pub-brand-mark { width: 30px; height: 30px; }
.pub-nav { display: flex; align-items: center; gap: 1.25rem; }
.pub-nav a { color: var(--color-text-secondary); text-decoration: none; font-weight: 600; font-size: 0.95rem; }
.pub-nav a:hover { color: var(--color-text-primary); }
.pub-nav .pub-signin {
    padding: 0.45rem 1rem; color: var(--color-text-primary);
    border: 1px solid var(--border); border-radius: 999px;
}
.pub-main { flex: 1 1 auto; }

.pub-foot { border-top: 1px solid var(--border); padding: 2.5rem clamp(1rem, 5vw, 3rem) 1.5rem; }
.pub-foot-inner {
    max-width: 68rem; margin: 0 auto; display: flex; flex-wrap: wrap; gap: 2rem;
    justify-content: space-between;
}
.pub-foot-brand { display: grid; gap: 0.4rem; max-width: 26rem; color: var(--color-text-secondary); }
.pub-foot-brand strong { color: var(--color-text-primary); }
.pub-foot-fine { font-size: 0.8rem; color: var(--color-text-tertiary); }
.pub-foot-links { display: flex; flex-wrap: wrap; gap: 1.25rem; align-content: start; }
.pub-foot-links a { color: var(--color-text-secondary); text-decoration: none; font-size: 0.95rem; }
.pub-foot-links a:hover { color: var(--color-text-primary); }
.pub-foot-legal {
    max-width: 68rem; margin: 1.5rem auto 0; padding-top: 1rem; border-top: 1px solid var(--border);
    display: flex; flex-wrap: wrap; gap: 1rem; font-size: 0.8rem; color: var(--color-text-tertiary);
}

/* Hero */
.lp-hero {
    padding: clamp(3rem, 9vw, 6.5rem) clamp(1rem, 5vw, 3rem);
    background:
        radial-gradient(70rem 30rem at 15% -10%, rgba(255, 107, 92, 0.16), transparent 60%),
        radial-gradient(60rem 28rem at 90% 0%, rgba(59, 158, 255, 0.14), transparent 60%);
}
.lp-hero-inner { max-width: 52rem; margin: 0 auto; text-align: center; }
.lp-eyebrow {
    margin: 0 0 1rem; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.09em;
    text-transform: uppercase; color: var(--color-accent-strong);
}
.lp-title {
    margin: 0 0 1.1rem; font-family: var(--font-family-display);
    font-size: clamp(2rem, 5.5vw, 3.4rem); line-height: 1.08; font-weight: 800; letter-spacing: -0.02em;
}
.lp-lead {
    margin: 0 auto 2rem; max-width: 40rem; font-size: clamp(1rem, 2vw, 1.15rem);
    line-height: 1.6; color: var(--color-text-secondary);
}
.lp-cta { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; }
.lp-cta-note { margin: 1rem 0 0; font-size: 0.85rem; color: var(--color-text-tertiary); }

.lp-btn {
    display: inline-block; padding: 0.8rem 1.6rem; border-radius: 999px;
    font-weight: 700; text-decoration: none; border: 1px solid transparent; transition: all 0.15s ease;
}
.lp-btn--primary { background: var(--color-accent-primary); color: var(--color-on-accent); }
.lp-btn--primary:hover { background: var(--color-accent-primary-hover); text-decoration: none; }
.lp-btn--ghost { border-color: var(--border); color: var(--color-text-primary); }
.lp-btn--ghost:hover { background: var(--color-bg-tertiary); text-decoration: none; }

/* Stat band */
.lp-band { padding: 1.5rem clamp(1rem, 5vw, 3rem); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.lp-band-inner {
    max-width: 68rem; margin: 0 auto; display: grid; gap: 1.5rem;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); text-align: center;
}
.lp-stat { display: grid; gap: 0.2rem; }
.lp-stat strong { font-size: 1.6rem; font-weight: 800; font-family: var(--font-family-display); }
.lp-stat span { font-size: 0.85rem; color: var(--color-text-secondary); }

/* Sections */
.lp-section { padding: clamp(3rem, 7vw, 5rem) clamp(1rem, 5vw, 3rem); }
.lp-section--alt { background: var(--color-bg-tertiary); }
.lp-h2 {
    max-width: 68rem; margin: 0 auto 0.75rem; text-align: center;
    font-family: var(--font-family-display); font-size: clamp(1.5rem, 3.5vw, 2.2rem);
    font-weight: 800; letter-spacing: -0.015em;
}
.lp-sub {
    max-width: 42rem; margin: 0 auto 2.5rem; text-align: center;
    color: var(--color-text-secondary); line-height: 1.6;
}
.lp-grid {
    max-width: 68rem; margin: 2.5rem auto 0; display: grid; gap: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}
.lp-card {
    padding: 1.6rem; background: var(--color-bg-secondary);
    border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.lp-card h3 { margin: 0.9rem 0 0.5rem; font-size: 1.08rem; font-weight: 700; }
.lp-card p { margin: 0; color: var(--color-text-secondary); line-height: 1.55; }
.lp-icon { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 10px;
           background: var(--color-accent-soft); color: var(--color-accent-strong); }
.lp-icon svg { width: 22px; height: 22px; }

/* Pricing */
/* The cards stretch to the tallest of them rather than each sizing to its own contents. They are
   a comparison, and three boxes of three heights read as three unrelated things — the Free card
   is shortest, having no annual price line, so it looked like the afterthought rather than the
   deliberate offer. It is also what makes margin-top: auto on the buttons below do anything:
   without a card taller than its contents there is no space for it to take up, and the buttons
   sit at whatever height each list happens to end at. */
.lp-plans {
    max-width: 68rem; margin: 0 auto; display: grid; gap: 1.25rem;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}
.lp-plan {
    position: relative; padding: 1.75rem; background: var(--color-bg-secondary);
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    display: flex; flex-direction: column; gap: 0.5rem;
}
.lp-plan--accent { border-color: var(--color-accent-primary); box-shadow: var(--shadow-lg); }
.lp-tag {
    position: absolute; top: -0.7rem; left: 1.75rem; padding: 0.2rem 0.7rem;
    font-size: 0.7rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--color-on-accent); background: var(--color-accent-primary); border-radius: 999px;
}
.lp-plan h3 { margin: 0; font-size: 1.05rem; font-weight: 700; }
.lp-price { font-family: var(--font-family-display); font-weight: 800; font-size: 1.4rem; line-height: 1; }
.lp-amount { font-size: 2.6rem; }
.lp-per { font-size: 0.95rem; font-weight: 500; color: var(--color-text-secondary); }
.lp-plan-alt { margin: 0; font-size: 0.85rem; color: var(--color-text-secondary); }
.lp-plan-lead { margin: 0.4rem 0 0.6rem; color: var(--color-text-secondary); line-height: 1.5; }
.lp-plan ul { margin: 0 0 1.4rem; padding-left: 1.1rem; display: grid; gap: 0.4rem; }
.lp-plan li { line-height: 1.45; }
.lp-plan .lp-btn { margin-top: auto; text-align: center; }
.lp-fine { max-width: 68rem; margin: 1.5rem auto 0; text-align: center;
           font-size: 0.85rem; color: var(--color-text-tertiary); }

/* Closing */
.lp-closing { padding: clamp(3rem, 7vw, 5rem) clamp(1rem, 5vw, 3rem); text-align: center; }
.lp-closing h2 { margin: 0 0 0.5rem; font-family: var(--font-family-display);
                 font-size: clamp(1.4rem, 3.5vw, 2.1rem); font-weight: 800; }
.lp-closing p { margin: 0 0 1.75rem; color: var(--color-text-secondary); }

/* Long-form public documents: terms, privacy, FAQ, contact */
.doc { max-width: 46rem; margin: 0 auto; padding: clamp(2rem, 6vw, 4rem) clamp(1rem, 5vw, 2rem); }
.doc h1 { margin: 0 0 0.4rem; font-family: var(--font-family-display);
          font-size: clamp(1.8rem, 4vw, 2.4rem); font-weight: 800; letter-spacing: -0.02em; }
.doc .doc-meta { margin: 0 0 2.5rem; color: var(--color-text-tertiary); font-size: 0.9rem; }
.doc h2 { margin: 2.5rem 0 0.75rem; font-size: 1.2rem; font-weight: 700; }
.doc h3 { margin: 1.75rem 0 0.5rem; font-size: 1.02rem; font-weight: 700; }
.doc p, .doc li { line-height: 1.7; color: var(--color-text-secondary); }
.doc p { margin: 0 0 1rem; }
.doc ul, .doc ol { margin: 0 0 1rem; padding-left: 1.3rem; display: grid; gap: 0.45rem; }
.doc strong { color: var(--color-text-primary); }
.doc a { color: var(--color-text-link); }
.doc-lead { font-size: 1.05rem; color: var(--color-text-primary); }
.doc-note {
    margin: 0 0 1.5rem; padding: 1rem 1.2rem; border-left: 3px solid var(--color-accent-primary);
    background: var(--color-bg-tertiary); border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
}
.doc-note p:last-child { margin-bottom: 0; }
.doc-faq { margin-bottom: 2rem; }
.doc-faq h3 { margin-top: 0; }

/* Monthly / yearly toggle on the plans page. */
.bill-cycle {
    display: inline-flex;
    align-self: center;
    padding: 4px;
    gap: 4px;
    background: var(--color-bg-tertiary);
    border: 1px solid var(--border);
    border-radius: 999px;
}
.bill-cycle button {
    padding: 0.45rem 1.1rem;
    font: inherit;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--color-text-secondary);
    background: transparent;
    border: 0;
    border-radius: 999px;
    cursor: pointer;
}
.bill-cycle button.on {
    color: var(--color-on-accent);
    background: var(--color-accent-primary);
}
.bill-cycle button:focus-visible { outline: none; box-shadow: var(--shadow-focus); }

/* A field positioned where nobody can see it, for the honeypot. Not display:none, which some
   form-fillers skip; off-screen, which they generally do not. */
.visually-offscreen {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* Toasts. Fixed to the bottom on phones and the bottom-right on wider screens, above the
   bottom bar's safe-area inset. The close button is a RocButton (IconOnly, Ghost, Small),
   not bespoke markup here, so it carries the shared button/focus/disabled styling already
   defined above rather than a second copy of it. */
.roc-toast-host {
    position: fixed;
    right: var(--space-4);
    bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
    left: var(--space-4);
    z-index: 1200;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    pointer-events: none;
}
@media (min-width: 768px) {
    .roc-toast-host { left: auto; max-width: 420px; }
}
.roc-toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--border-radius-lg);
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    box-shadow: var(--shadow-lg);
    color: var(--color-text-primary);
    pointer-events: auto;
}
.roc-toast--success { border-color: var(--color-status-success); }
.roc-toast--warning { border-color: var(--color-status-warning); }
.roc-toast--error   { border-color: var(--color-status-error); }
.roc-toast-text { flex: 1; min-width: 0; }

/* Dialogs. */
.roc-dialog-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1300;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    background: rgba(0, 0, 0, .45);
}
.roc-dialog {
    width: 100%;
    max-width: 480px;
    max-height: calc(100vh - var(--space-8));
    overflow-y: auto;
    background: var(--color-bg-secondary);
    border: 1px solid var(--color-border-primary);
    border-radius: var(--border-radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-5);
}
.roc-dialog:focus { outline: none; }
.roc-dialog-title {
    margin: 0 0 var(--space-3);
    font-size: var(--font-size-lg);
    font-weight: 800;
    color: var(--color-text-primary);
}
.roc-dialog-body { color: var(--color-text-primary); }
.roc-dialog-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-5);
}
