/* ==========================================================================
   Theme
   ==========================================================================
   Everything in this file is about COLOR AND SHAPE, never about position or
   spacing (that's assets/css/site.css). Two kinds of rules live here:

   1. The couple of plain CSS rules a page needs regardless of components
      (link color, the <m3e-theme> host itself).

   2. Component re-skinning, done the way M3E is designed to be re-skinned:
      every component reads its colors from small, component-scoped CSS
      custom properties (e.g. --m3e-app-bar-container-color), each one
      falling back to a shared Material role token (e.g.
      --md-sys-color-surface) if you don't set it. Redefining one of
      those custom properties on an ancestor is enough to re-theme every
      instance below it - no need to fight the component's Shadow DOM,
      because custom properties are inherited straight through it.

      The exact property names below (--m3e-app-bar-*, --m3e-button-*,
      --m3e-icon-button-*) come straight from M3E's own source
      (packages/web/src/.../styles/...Token.ts on
      https://github.com/matraic/m3e) - if a future M3E upgrade renames
      one of these, that's the first place to check.
   ========================================================================== */

/* ==========================================================================
   Raw color tokens
   ==========================================================================
   Every literal color value used anywhere in this file - brand colors,
   plain black/white - is declared exactly once here, under a plain,
   easy-to-scan name. Nothing below this block ever writes a hex code (or
   a bare `white`/`black` keyword) directly into a rule again - it only
   ever references one of these by name, the same discipline the M3
   system tokens (--md-sys-color-*) already follow one layer up. Two
   concrete wins from that: a color only ever needs to change in one
   place, and this block alone IS the site's entire literal-color
   surface - grep it once, nothing is hiding three sections down inside a
   color-mix().
   --color-ivory is a plain hex like every other entry here, not a
   color-mix() result kept in a variable - #edeef0, white with only the
   faintest cool tinge of the brand slate (92/8), computed once by hand
   instead of asking the browser to recompute that same fixed blend on
   every paint. Deliberately mixed with slate, not cream - an earlier
   version leaned on cream instead and read as visibly yellow for a
   color meant to be "the light scheme's white/light-gray surface"; a
   whisper of the cool, near-neutral slate keeps it reading as white/
   light gray instead.
   --color-frost (#e9eeff) is the one genuinely new, freely-chosen brand
   color here rather than a derived/hand-computed one - a pale, cool
   near-white with a whisper of blue, used as the light scheme's own
   block3-end (.app-body__sidebar, below) so that fade lands somewhere
   more deliberate than "the same ivory surface as everywhere else."
   --color-graphite (#2e2e2e) is a true hue-less neutral gray, picked at
   roughly --color-slate's own perceived lightness - it only ever feeds
   --color-slate-muted, below, never referenced directly anywhere else.
   --color-slate-muted is slate itself pulled heavily toward graphite (20%
   slate, 80% graphite) - not a fresh hand-picked color, a genuine
   color-mix() of the two above, so it drifts if either of its ingredients
   ever does. Weighted this far toward graphite on purpose: at an even mix
   the result still read as a dark blue-gray, slate's own hue dominating
   enough to work against the "dark gray, not dark blue" look the dark
   scheme's surfaces are going for. Used in
   place of --color-slate for every dark-scheme surface-family role
   (background, surface, surface-dim/bright/variant, every surface-
   container step, outline/outline-variant, below) so the ENTIRE family
   - including things like the mobile nav popup's own background, which
   reads one of those same container roles - shifts together and keeps
   reading as one consistent surface, rather than some of those roles
   staying on slate's full blue accent while only background/surface
   moved off it. Kept as a separate token from plain --color-slate
   rather than overwriting that anchor's own value, since slate keeps
   its original, undiluted role elsewhere - the light scheme's tertiary
   accent group, further down, still wants the real, fully blue slate. */
:root {
    --color-white: #ffffff;
    --color-black: #000000;
    --color-slate: #212742;
    --color-graphite: #2e2e2e;
    --color-slate-muted: color-mix(in srgb, var(--color-slate) 20%, var(--color-graphite) 80%);
    --color-cream: #f7ecc4;
    --color-amethyst: #7884d1;
    --color-iris: #8066c2;
    --color-ivory: #edeef0;
    --color-frost: #e9eeff;
}

/* ==========================================================================
   Custom color anchors
   ==========================================================================
   m3e-theme (_layouts/default.html) generates the site's whole Material
   color system - primary, secondary, tertiary, every surface/container/
   outline role, for both light AND dark - from the single seed color
   passed as its `color` attribute (#818dc0), using the standard M3
   dynamic-color (HCT) algorithm. That's still exactly where
   --md-sys-color-primary and everything derived from it (on-primary,
   primary-container, secondary, ...) comes from below - already correct,
   untouched here.

   What that algorithm can't do on its own is anchor the SURFACE and
   TERTIARY role groups to two further, specific brand colors instead of
   whatever it would otherwise derive by rotating that same single seed
   hue - m3e-theme only ever takes one seed color, so the other two below
   are anchored by hand instead:
     - --color-slate       - the dark scheme's surface/background group.
     - --color-cream       - the dark scheme's tertiary (accent) group.
   (The seed itself, #818dc0 above, has no CSS variable of its own here -
   it's only ever read from that HTML attribute, never referenced from a
   CSS rule, so it never earned a place in the raw color tokens block.)
   The light scheme (freely chosen) reuses the same two colors with their
   roles SWAPPED - slate reused as light's own tertiary accent - so the
   two schemes read as two sides of one design instead of unrelated
   palettes, with primary constant across both. The light scheme's own
   SURFACE group, however, anchors to --color-ivory rather than
   --color-cream directly - see that variable's own comment above for
   why.

   Every "on-X"/"X-container" role below is hand-derived from its anchor
   with color-mix() for comfortable contrast, rather than a true HCT
   tonal palette - computing exact Material tonal steps needs the same
   color-science this library itself runs in JS, and this project has no
   build step to run that kind of computation ahead of time.

   Every rule below needs !important, and has to target m3e-theme
   directly (not :root): m3e-theme recomputes its entire palette into a
   real stylesheet (a CSSStyleSheet, adopted onto the document) every
   time its color/scheme/contrast properties change - confirmed in its
   source - and per the Constructable Stylesheets spec, an adopted
   stylesheet cascades AFTER the document's own <link> stylesheets
   (this one included) regardless of selector specificity, so a plain
   override would silently lose to the library's own freshly-generated
   value every time it recomputes. And since a role like
   --md-sys-color-surface is set BY THE LIBRARY directly on the
   m3e-theme element itself, a value merely inherited from an ancestor
   like :root would still lose to it too - a value set directly on an
   element always wins over one it only inherited, independent of
   specificity. Targeting m3e-theme directly, with !important, is what
   actually wins both of those fights.

   Each color is repeated once for the automatic case (OS preference,
   under scheme="auto" - the default) and once for an explicit user
   choice (the theme toggle in assets/js/app.js sets scheme="light"/"dark"
   directly, which should apply regardless of the OS's own preference).
   ========================================================================== */

@media (prefers-color-scheme: dark) {
    m3e-theme:not([scheme="light"]) {
        --md-sys-color-background: var(--color-slate-muted) !important;
        --md-sys-color-on-background: color-mix(
            in srgb,
            var(--color-cream) 12%,
            var(--color-white) 88%
        ) !important;
        --md-sys-color-surface: var(--color-slate-muted) !important;
        --md-sys-color-on-surface: color-mix(
            in srgb,
            var(--color-cream) 12%,
            var(--color-white) 88%
        ) !important;
        --md-sys-color-surface-dim: color-mix(
            in srgb,
            var(--color-slate-muted) 85%,
            var(--color-black) 15%
        ) !important;
        --md-sys-color-surface-bright: color-mix(
            in srgb,
            var(--color-slate-muted) 85%,
            var(--color-white) 15%
        ) !important;
        --md-sys-color-surface-variant: color-mix(
            in srgb,
            var(--color-slate-muted) 75%,
            var(--color-black) 25%
        ) !important;
        --md-sys-color-on-surface-variant: color-mix(
            in srgb,
            var(--color-slate-muted) 15%,
            var(--color-white) 85%
        ) !important;
        --md-sys-color-surface-container-lowest: color-mix(
            in srgb,
            var(--color-slate-muted) 82%,
            var(--color-black) 18%
        ) !important;
        --md-sys-color-surface-container-low: color-mix(
            in srgb,
            var(--color-slate-muted) 92%,
            var(--color-black) 8%
        ) !important;
        --md-sys-color-surface-container: var(--color-slate-muted) !important;
        --md-sys-color-surface-container-high: color-mix(
            in srgb,
            var(--color-slate-muted) 88%,
            var(--color-white) 12%
        ) !important;
        --md-sys-color-surface-container-highest: color-mix(
            in srgb,
            var(--color-slate-muted) 76%,
            var(--color-white) 24%
        ) !important;
        --md-sys-color-outline: color-mix(
            in srgb,
            var(--color-slate-muted) 45%,
            var(--color-white) 55%
        ) !important;
        --md-sys-color-outline-variant: color-mix(
            in srgb,
            var(--color-slate-muted) 68%,
            var(--color-white) 32%
        ) !important;
        --md-sys-color-tertiary: var(--color-cream) !important;
        --md-sys-color-on-tertiary: color-mix(
            in srgb,
            var(--color-slate) 88%,
            var(--color-black) 12%
        ) !important;
        --md-sys-color-tertiary-container: var(--color-amethyst) !important;
        /* Was a flat var(--color-white) - the one dark-scheme text role
           still using pure stark white while on-background/on-surface
           right above both already use this exact cream-tinted-white
           color-mix() instead. .app-titlebar (site.css) and this
           panel's own name/description (.profile-panel__name/
           __description, site.css - both already read this same token,
           not a literal white) now land on the same softer off-white
           as the rest of the page's dark-mode text instead of standing
           out as the one pure-white surface on it. */
        --md-sys-color-on-tertiary-container: color-mix(
            in srgb,
            var(--color-cream) 12%,
            var(--color-white) 88%
        ) !important;
        --color-block3-end: var(--md-sys-color-primary-container);
        --color-block3-glow: var(--color-iris);
    }
}

m3e-theme[scheme="dark"] {
    --md-sys-color-background: var(--color-slate-muted) !important;
    --md-sys-color-on-background: color-mix(
        in srgb,
        var(--color-cream) 12%,
        var(--color-white) 88%
    ) !important;
    --md-sys-color-surface: var(--color-slate-muted) !important;
    --md-sys-color-on-surface: color-mix(
        in srgb,
        var(--color-cream) 12%,
        var(--color-white) 88%
    ) !important;
    --md-sys-color-surface-dim: color-mix(
        in srgb,
        var(--color-slate-muted) 85%,
        var(--color-black) 15%
    ) !important;
    --md-sys-color-surface-bright: color-mix(
        in srgb,
        var(--color-slate-muted) 85%,
        var(--color-white) 15%
    ) !important;
    --md-sys-color-surface-variant: color-mix(
        in srgb,
        var(--color-slate-muted) 75%,
        var(--color-black) 25%
    ) !important;
    --md-sys-color-on-surface-variant: color-mix(
        in srgb,
        var(--color-slate-muted) 15%,
        var(--color-white) 85%
    ) !important;
    --md-sys-color-surface-container-lowest: color-mix(
        in srgb,
        var(--color-slate-muted) 82%,
        var(--color-black) 18%
    ) !important;
    --md-sys-color-surface-container-low: color-mix(
        in srgb,
        var(--color-slate-muted) 92%,
        var(--color-black) 8%
    ) !important;
    --md-sys-color-surface-container: var(--color-slate-muted) !important;
    --md-sys-color-surface-container-high: color-mix(
        in srgb,
        var(--color-slate-muted) 88%,
        var(--color-white) 12%
    ) !important;
    --md-sys-color-surface-container-highest: color-mix(
        in srgb,
        var(--color-slate-muted) 76%,
        var(--color-white) 24%
    ) !important;
    --md-sys-color-outline: color-mix(
        in srgb,
        var(--color-slate-muted) 45%,
        var(--color-white) 55%
    ) !important;
    --md-sys-color-outline-variant: color-mix(
        in srgb,
        var(--color-slate-muted) 68%,
        var(--color-white) 32%
    ) !important;
    --md-sys-color-tertiary: var(--color-cream) !important;
    --md-sys-color-on-tertiary: color-mix(
        in srgb,
        var(--color-slate) 88%,
        var(--color-black) 12%
    ) !important;
    --md-sys-color-tertiary-container: var(--color-amethyst) !important;
    --md-sys-color-on-tertiary-container: var(--color-white) !important;
    --color-block3-end: var(--md-sys-color-primary-container);
    --color-block3-glow: var(--color-iris);
}

@media (prefers-color-scheme: light) {
    m3e-theme:not([scheme="dark"]) {
        --md-sys-color-background: var(--color-ivory) !important;
        --md-sys-color-on-background: color-mix(
            in srgb,
            var(--color-slate) 85%,
            var(--color-black) 15%
        ) !important;
        --md-sys-color-surface: var(--color-ivory) !important;
        --md-sys-color-on-surface: color-mix(
            in srgb,
            var(--color-slate) 85%,
            var(--color-black) 15%
        ) !important;
        --md-sys-color-surface-dim: color-mix(
            in srgb,
            var(--color-slate) 12%,
            var(--color-ivory) 88%
        ) !important;
        --md-sys-color-surface-bright: var(--color-ivory) !important;
        --md-sys-color-surface-variant: color-mix(
            in srgb,
            var(--color-slate) 12%,
            var(--color-ivory) 88%
        ) !important;
        --md-sys-color-on-surface-variant: color-mix(
            in srgb,
            var(--color-slate) 65%,
            var(--color-black) 10%
        ) !important;
        --md-sys-color-surface-container-lowest: var(--color-white) !important;
        --md-sys-color-surface-container-low: color-mix(
            in srgb,
            var(--color-slate) 4%,
            var(--color-ivory) 96%
        ) !important;
        --md-sys-color-surface-container: var(--color-ivory) !important;
        --md-sys-color-surface-container-high: color-mix(
            in srgb,
            var(--color-slate) 9%,
            var(--color-ivory) 91%
        ) !important;
        --md-sys-color-surface-container-highest: color-mix(
            in srgb,
            var(--color-slate) 15%,
            var(--color-ivory) 85%
        ) !important;
        --md-sys-color-outline: color-mix(
            in srgb,
            var(--color-slate) 55%,
            var(--color-ivory) 45%
        ) !important;
        --md-sys-color-outline-variant: color-mix(
            in srgb,
            var(--color-slate) 28%,
            var(--color-ivory) 72%
        ) !important;
        --md-sys-color-tertiary: var(--color-slate) !important;
        --md-sys-color-on-tertiary: var(--color-cream) !important;
        /* Was its own pale slate/ivory mix - .app-titlebar (site.css)
           and this panel share their fill (tertiary-container) with
           dark scheme too (see the comment on m3e-theme[scheme="dark"]
           below), so both schemes now paint that strip in the exact
           same --color-amethyst rather than two different colors that
           only happened to play the same ROLE. on-tertiary-container
           follows suit - a mid-tone purple needs light text either way,
           so light scheme now reads with the same off-white the dark
           scheme's own text already uses here, not the dark ink that
           made sense against the old, much paler fill. */
        --md-sys-color-tertiary-container: var(--color-amethyst) !important;
        --md-sys-color-on-tertiary-container: color-mix(
            in srgb,
            var(--color-cream) 12%,
            var(--color-white) 88%
        ) !important;
        --color-block3-end: var(--color-frost);
        --color-block3-glow: var(--md-sys-color-primary-container);
    }
}

m3e-theme[scheme="light"] {
    --md-sys-color-background: var(--color-ivory) !important;
    --md-sys-color-on-background: color-mix(
        in srgb,
        var(--color-slate) 85%,
        var(--color-black) 15%
    ) !important;
    --md-sys-color-surface: var(--color-ivory) !important;
    --md-sys-color-on-surface: color-mix(
        in srgb,
        var(--color-slate) 85%,
        var(--color-black) 15%
    ) !important;
    --md-sys-color-surface-dim: color-mix(
        in srgb,
        var(--color-slate) 12%,
        var(--color-ivory) 88%
    ) !important;
    --md-sys-color-surface-bright: var(--color-ivory) !important;
    --md-sys-color-surface-variant: color-mix(
        in srgb,
        var(--color-slate) 12%,
        var(--color-ivory) 88%
    ) !important;
    --md-sys-color-on-surface-variant: color-mix(
        in srgb,
        var(--color-slate) 65%,
        var(--color-black) 10%
    ) !important;
    --md-sys-color-surface-container-lowest: var(--color-white) !important;
    --md-sys-color-surface-container-low: color-mix(
        in srgb,
        var(--color-slate) 4%,
        var(--color-ivory) 96%
    ) !important;
    --md-sys-color-surface-container: var(--color-ivory) !important;
    --md-sys-color-surface-container-high: color-mix(
        in srgb,
        var(--color-slate) 9%,
        var(--color-ivory) 91%
    ) !important;
    --md-sys-color-surface-container-highest: color-mix(
        in srgb,
        var(--color-slate) 15%,
        var(--color-ivory) 85%
    ) !important;
    --md-sys-color-outline: color-mix(
        in srgb,
        var(--color-slate) 55%,
        var(--color-ivory) 45%
    ) !important;
    --md-sys-color-outline-variant: color-mix(
        in srgb,
        var(--color-slate) 28%,
        var(--color-ivory) 72%
    ) !important;
    --md-sys-color-tertiary: var(--color-slate) !important;
    --md-sys-color-on-tertiary: var(--color-cream) !important;
    --md-sys-color-tertiary-container: var(--color-amethyst) !important;
    --md-sys-color-on-tertiary-container: color-mix(
        in srgb,
        var(--color-cream) 12%,
        var(--color-white) 88%
    ) !important;
    --color-block3-end: var(--color-frost);
    --color-block3-glow: var(--md-sys-color-primary-container);
}

a {
    color: var(--md-sys-color-primary);
}

/* m3e-theme wraps the entire page (see _layouts/default.html once step 2
   wires Jekyll back in). It already paints background/text color once
   the custom element upgrades; this just avoids a flash of an unstyled,
   zero-height page before that happens. */
m3e-theme {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background-color: var(--md-sys-color-background);
    color: var(--md-sys-color-on-background);
}

/* ==========================================================================
   Nav bar: transparent over the header banner
   ==========================================================================
   "Transparent" means the bar shows the header's own banner image through
   it instead of painting its usual solid surface color - both at rest and
   once scrolling would otherwise switch it to --container-color-on-scroll
   (m3e-app-bar's built-in scroll-elevation behavior, see the `for`
   attribute in its docs). Buttons/icons inside default to on-surface(-
   variant) colors, made for a solid surface - re-pointed to white here so
   they stay legible over an arbitrary photo.
   ========================================================================== */

/* Shared "resting state" fallback for every button/icon-button variant
   used anywhere over the banner photo - the nav bar's text buttons and
   icon buttons (menu/toc/theme toggles) below, AND the header's own tag
   buttons (.app-header__tags, in .app-header__content - a sibling of
   .app-header__bar, not a descendant of it, hence setting this on their
   common ancestor instead of repeating it in both places). See
   ButtonVariantToken.ts and IconButtonVariantToken.ts - every variant-
   specific token falls back to one of these before falling back to the
   theme's own default (on-surface, made for a plain surface - wrong over
   an arbitrary photo). Hover/focus state-layer color is white too, so a
   "borderless" text-variant button (the tag buttons) still shows a
   visible, translucent-white wash on hover instead of nothing at all.
   Label/icon color is pinned white for every interaction state a text
   button has (idle, unselected, hover, focus, pressed) - each one is
   its own separate token with its own separate default (confirmed in
   m3e-web's own source), so leaving any single one unset would have let
   the nav-link text/icon flash to that state's own default color the
   moment that state became active, even though the state-layer
   backgrounds above (hover/focus) are still deliberately left free to
   change for interaction feedback - only the text/icon color itself is
   frozen. */
.app-header {
    --m3e-button-label-text-color: var(--color-white);
    --m3e-button-icon-color: var(--color-white);
    --m3e-button-unselected-label-text-color: var(--color-white);
    --m3e-button-unselected-icon-color: var(--color-white);
    --m3e-button-hover-label-text-color: var(--color-white);
    --m3e-button-hover-icon-color: var(--color-white);
    --m3e-button-focus-label-text-color: var(--color-white);
    --m3e-button-focus-icon-color: var(--color-white);
    --m3e-button-pressed-label-text-color: var(--color-white);
    --m3e-button-pressed-icon-color: var(--color-white);
    --m3e-button-hover-state-layer-color: var(--color-white);
    --m3e-button-focus-state-layer-color: var(--color-white);
    --m3e-icon-button-icon-color: var(--color-white);
    --m3e-icon-button-unselected-icon-color: var(--color-white);
}

/* [Mobile] The three icon-buttons unique to the mobile bar (table of
   contents, search, hamburger menu) plus .theme-toggle (shown at every
   width, not just mobile, but needing the exact same fix). The rule
   above only ever covers these at REST: hover/focus/pressed each read
   from their OWN separate icon-color token (confirmed in
   m3e-icon-button's source), which falls back to the library's own
   default - tuned for a plain surface, not this photo backdrop - the
   moment any of those states become active. On mobile that "moment" is
   every single tap (a touch interaction passes through hover AND
   pressed before the click even registers), so the icon visibly flashed
   to a different, wrong color on every tap without this. --selected here
   also covers the menu button (id="nav-toggle") staying white once
   opened, and the theme-toggle icon staying white regardless of which of
   its two icons (dark_mode/light_mode, swapped by assets/js/app.js) is showing. */
.app-header__toc-toggle,
.app-header__search-toggle,
.app-header__menu-toggle,
.theme-toggle {
    --m3e-icon-button-hover-icon-color: var(--color-white);
    --m3e-icon-button-hover-selected-icon-color: var(--color-white);
    --m3e-icon-button-hover-unselected-icon-color: var(--color-white);
    --m3e-icon-button-focus-icon-color: var(--color-white);
    --m3e-icon-button-focus-selected-icon-color: var(--color-white);
    --m3e-icon-button-focus-unselected-icon-color: var(--color-white);
    --m3e-icon-button-pressed-icon-color: var(--color-white);
    --m3e-icon-button-pressed-selected-icon-color: var(--color-white);
    --m3e-icon-button-pressed-unselected-icon-color: var(--color-white);
    --m3e-icon-button-selected-icon-color: var(--color-white);
}

.app-header__bar {
    --m3e-app-bar-container-color: transparent;
    --m3e-app-bar-container-color-on-scroll: transparent;
    --m3e-app-bar-container-elevation: none;
    --m3e-app-bar-container-elevation-on-scroll: none;
    --m3e-app-bar-title-text-color: var(--color-white);
    --m3e-app-bar-subtitle-text-color: var(--color-white);
}

/* Search bar: transparent background everywhere, rather than the filled
   pill it draws by default - it then blends into whatever surface it's
   placed on (the header banner on desktop, the mobile search popup's own
   surface color below) instead of floating on top of it as a separate
   box. */
m3e-search-bar {
    --m3e-search-bar-container-color: transparent;
}

/* Sitting directly on the header's banner photo, the input/placeholder/
   icon colors (tuned for a plain surface) need to flip to white for the
   same legibility reason as the surrounding bar's own buttons/icons
   above. With a fully transparent fill, the field would otherwise have
   no visible boundary at all over a busy photo - m3e-search-bar has no
   built-in outlined variant (confirmed in its source: it only exposes a
   container *color*, not a border/outline token), so the outline here is
   a plain CSS border added from outside, in the same white the rest of
   the bar's content already uses, sized to match the field's own fully-
   rounded pill shape. */
.app-header__bar-search m3e-search-bar {
    --m3e-search-bar-input-color: var(--color-white);
    --m3e-search-bar-supporting-text-color: color-mix(
        in srgb,
        var(--color-white) 80%,
        transparent
    );
    --m3e-search-bar-leading-icon-color: var(--color-white);
    --m3e-search-bar-trailing-icon-color: var(--color-white);
    border: 0.0625rem solid
        color-mix(in srgb, var(--color-white) 70%, transparent);
    border-radius: var(--md-sys-shape-corner-full, 999px);
}

/* Once the page has scrolled, the banner image is no longer right behind
   the bar - a solid fill reads much better there than a flat transparent
   strip over arbitrary scrolled-past content. m3e-app-bar does track its
   own scroll state internally (for the --m3e-app-bar-container-color-on-
   scroll swap above), but that state stays private to its shadow DOM -
   there's no CSS part or reflected attribute for it. assets/js/app.js keeps
   that state anyway (see comment there), applied here as a plain host-
   element class.
   Text/icons are fixed white here too, same as .app-header/.app-header
   __bar's own resting-state defaults above. No color custom properties
   need redeclaring here any more: .app-header's white fallback already
   applies underneath this class regardless, so there's nothing to
   override.
   tertiary-container, not a translucent dark wash - the same flat color
   .app-titlebar (site.css) fills its own strip with, so the bar reads as
   one consistent surface with the titlebar the moment it appears rather
   than a blurred, semi-transparent view of whatever content happens to
   be scrolled past underneath it. No blur/backdrop-filter here any more
   for the same reason: a fully opaque fill has nothing to blur - there's
   no longer any scrolled-past content showing through to soften. */
.app-header__bar--scrolled {
    background-color: var(--md-sys-color-tertiary-container);
}

/* ==========================================================================
   Sidebar panel
   ==========================================================================
   The only surface on the page with genuinely square corners (everything
   else - cards, chips, buttons - keeps M3's default rounding). Reads as a
   flush, full-height rail rather than a floating card; see
   .app-body__sidebar in site.css for its (square, edge-to-edge) geometry.
   ========================================================================== */

/* FOUR independent pieces this time, split across the element, two
   pseudos, and one small real element specifically so each can solve
   its own problem without fighting the others:
   1) THIS element - a flat fill plus grain, nothing else. Since it's a
      plain background-color (not a gradient with a 0%-stop that merely
      HAPPENS to equal the target color), the top edge is unconditionally
      --md-sys-color-tertiary-container, pixel-identical to .app-titlebar,
      regardless of whatever the pieces above it are doing - the
      simplest possible way to guarantee that invariant. Grain: the same
      inline-SVG turbulence tile used elsewhere on this surface, tiled
      small and blended at low opacity via "overlay" - no network
      request, and what keeps the wash below from reading as flat,
      plasticky color.
   2) ::before - the calm wash that actually solves "still looks good on
      a long post": FOUR color stops so a several-screens-tall column
      keeps crossing distinct hues start to finish instead of resolving
      into one flat color for most of the scroll. Angled at 200deg, not
      180deg/"to bottom" - a deliberately NOT-perfectly-horizontal fade,
      its iso-color lines run diagonally instead of stacking as flat
      horizontal bands. A mask fades it in only a few rem down, so this
      pseudo contributes nothing whatsoever near the top edge, whatever
      angle it's drawn at - simpler and more robust than trying to prove
      a tilted gradient's 0%-stop stays flat across the entire top edge
      width by construction (it doesn't, once you tilt it - the two top
      corners sit at different points along a diagonal gradient line).
   3) .profile-panel__sign-accent (a real element in sidebar.html, not a
      pseudo) - assets/img/sign.svg, masked into a 20rem-tall strip across the
      panel's top. See git history for how this got here: first a single
      circular-arc curve on its own, then three different attempts at
      combining it with the sign entirely through CSS masking on the
      SAME ::after as the arc below (centered above the curve with no
      separation; nested inside the curve's own first bend; sharing
      ::after via a THIRD mask-image layer - a hard-edged step gradient
      with mask-composite: intersect meant to cut the arc's tiling out
      of the sign's reserved space, plus legacy -webkit-mask-composite
      keywords for Blink) - all three proved unreliable in practice
      across browsers. Giving the sign its own real box sidesteps the
      whole problem: it's just an element positioned above the arc's own
      box (3, below), no mask-layer ordering or cross-browser
      compositing quirks involved at all.
   4) ::after - the earlier hand-drawn circular-arc curve (see git
      history for the straight-line zigzag it started as), tiling from
      top: 8rem - not inset: 0 - so its own box doesn't exist above
      where the sign's box (3) ends. That's what actually guarantees
      "curve starts below the sign": a box that isn't there above 8rem
      has nothing for mask-repeat to tile INTO up there, independent of
      any tiling-phase or layer-compositing math.
      A clip-path polygon was the very first attempt at this shape - a
      polygon is a FIXED, finite list of points - stretch it (percentage
      Y) and short panels squash the angle; give it fixed-rem Y instead
      and it holds its angle but now simply runs out of points on a tall
      desktop column, ending well short of the real bottom edge either
      way. Neither version can cover "however tall this box turns out to
      be" on its own. A repeating MASK is what actually does that: the
      arc is tiled with mask-repeat: repeat-y at mask-size: 100% 16rem -
      CSS's own tiling machinery repeats that one bend for exactly as
      many 16rem increments as the box turns out to be tall, however
      tall that is, the same way background-repeat already tiles the
      grain tile elsewhere on this surface - no finite point list to run
      out of.
      Both (3) and (4) share the exact same background-image (a 165deg
      diagonal gradient) and mix-blend-mode: soft-light - the mask in
      each decides WHERE its own shape is, this gradient decides what
      color shows through it at each point, so the sign and the arc read
      as one continuous accent shifting color top-to-bottom rather than
      two unrelated pieces that just happen to sit near each other. Both
      stops are color-mix()ed toward --md-sys-color-tertiary-container
      rather than left fully saturated - a fully saturated --color-cream
      mid-stop, painted solid, could land directly behind the panel's
      own description text (.profile-panel__description, on-tertiary-
      container - white in dark scheme) with genuinely poor contrast
      wherever the mark happened to cross it. Muting the color and
      letting it modulate the surface instead of replacing it keeps the
      mark visible as a shifting accent without ever producing a patch
      saturated enough to fight foreground text.
      (The ::before wash still fades in from 3rem down on its own -
      unrelated to either of these - so the flat tertiary-container
      still reads first at the very top corners; only the sign's own
      mark touches y0, and softened as above, that's no longer a visible
      mismatch against .app-titlebar the way a fully saturated color
      would be.)
   None of --color-block3-glow/--color-cream/--color-block3-end ever
   overlaps --md-sys-color-secondary/-secondary-container, what
   m3e-icon-button's tonal variant uses (confirmed in its source) for the
   social buttons in .profile-panel__links on this same panel - those
   buttons stay a distinct color against whatever sits behind them.
   Both pseudos AND .profile-panel__sign-accent: z-index: 0, behind
   .profile-panel's own z-index: 1 (site.css) - the real avatar/name/
   buttons always render in front.
   position: relative on the element itself (no overflow: hidden ON IT)
   for the usual reason: it's also .profile-panel's own positioning
   ancestor, and overflow != visible HERE would hijack its desktop
   sticky behavior, a regression that happened once before. */
.app-body__sidebar,
.app-body__mobile-profile {
    position: relative;
    background-color: var(--md-sys-color-tertiary-container);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
    background-repeat: repeat;
    background-size: 100px 100px;
    background-blend-mode: overlay;
    color: var(--md-sys-color-on-tertiary-container);
}

/* Experimental: a big soft radial glow layered on top of the wash, on
   request, to see how it reads - fixed rem radius (not a percentage of
   the box) anchored a fixed distance down, same reasoning as the halo
   tried earlier in this rule's history: a percentage/farthest-corner
   radius balloons into a huge, nearly-flat wash of its own on a very
   tall long-post column, which is exactly the "flat color" problem this
   whole panel exists to avoid. --color-block3-glow (already the wash's
   own accent token, scheme-reactive) keeps it part of the same palette
   rather than adding a new one-off color; soft-light so it modulates the
   wash under it instead of sitting on top as a flat disc. */
.app-body__sidebar::before,
.app-body__mobile-profile::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        radial-gradient(
            circle 18rem at 50% 8rem,
            color-mix(in srgb, var(--color-block3-glow) 70%, transparent) 0%,
            transparent 70%
        ),
        linear-gradient(
            200deg,
            var(--md-sys-color-tertiary-container) 0%,
            color-mix(
                    in srgb,
                    var(--color-block3-glow) 55%,
                    var(--md-sys-color-tertiary-container)
                )
                50%,
            var(--color-block3-end) 100%
        );
    background-blend-mode: soft-light, normal;
    mask-image: linear-gradient(
        to bottom,
        transparent 0,
        transparent 3rem,
        black 7rem
    );
    -webkit-mask-image: linear-gradient(
        to bottom,
        transparent 0,
        transparent 3rem,
        black 7rem
    );
}

/* Light-scheme-only recolor of the wash above - same angle, same stop
   positions, same color-mix() shape, ONLY the source tokens differ.
   Nudged one step darker from the first, paler pass at this: this
   panel's text (.profile-panel__name/__description, site.css) reads
   --md-sys-color-on-tertiary-container, which is now the same near-
   white both schemes use for .app-titlebar's own text (see
   m3e-theme[scheme="light"] above) - fine against the 0%-stop's
   --color-amethyst (a real mid-tone), but the too-pale accent stops
   tried first left near-white text sitting on near-white background
   for most of the panel's height. Still deliberately softer than the
   fully saturated --color-iris/--color-slate first attempt (this is a
   light touch, not a reversal) - just enough darker to read.
   Down to 3 stops, not 4 - the mid-point is --md-sys-color-primary (the
   site's own blue, same token `a { color }` uses site-wide) mixed with
   --color-ivory, the sole accent between start and end now that the
   separate --color-iris stop tried right before this is gone. Same
   radial glow as the shared rule above, added here too since this whole
   background-image list is a full override, not a merge - it wouldn't
   otherwise inherit the base rule's glow layer.
   Dark scheme is untouched - only the light-scheme selectors below
   exist. */
@media (prefers-color-scheme: light) {
    m3e-theme:not([scheme="dark"]) .app-body__sidebar::before,
    m3e-theme:not([scheme="dark"]) .app-body__mobile-profile::before {
        background-image:
            radial-gradient(
                circle 18rem at 50% 8rem,
                color-mix(in srgb, var(--color-block3-glow) 70%, transparent) 0%,
                transparent 70%
            ),
            linear-gradient(
                200deg,
                var(--md-sys-color-tertiary-container) 0%,
                color-mix(in srgb, var(--md-sys-color-primary) 60%, var(--color-ivory) 40%)
                    50%,
                color-mix(in srgb, var(--color-slate) 45%, var(--color-ivory) 55%)
                    100%
            );
        background-blend-mode: soft-light, normal;
    }
}

m3e-theme[scheme="light"] .app-body__sidebar::before,
m3e-theme[scheme="light"] .app-body__mobile-profile::before {
    background-image:
        radial-gradient(
            circle 18rem at 50% 8rem,
            color-mix(in srgb, var(--color-block3-glow) 70%, transparent) 0%,
            transparent 70%
        ),
        linear-gradient(
            200deg,
            var(--md-sys-color-tertiary-container) 0%,
            color-mix(in srgb, var(--md-sys-color-primary) 60%, var(--color-ivory) 40%)
                50%,
            color-mix(in srgb, var(--color-slate) 45%, var(--color-ivory) 55%)
                100%
        );
    background-blend-mode: soft-light, normal;
}

/* The sign, on its own real element (.profile-panel__sign-accent,
   sidebar.html) rather than a third mask-image layer sharing ::after
   with the arc below - two different mask-composite attempts at doing
   this purely in CSS (add+intersect, then that plus legacy -webkit-
   mask-composite keywords for Blink) both proved unreliable in
   practice. A real box sidesteps the whole problem: it's simply
   positioned above the arc's own box (below), no mask-layer ordering or
   cross-browser compositing quirks involved. inset defines an 8rem-tall
   strip across the panel's full width; the mask (sign.svg, no-repeat,
   centered, 8rem auto - its own 1:1 intrinsic ratio, preserved by
   leaving one axis auto) draws the actual mark within it. Same
   background-image/mix-blend-mode as the arc below (see its own
   comment) for the same reason: one continuous gradient the mask cuts
   shapes out of, so the sign's color genuinely matches where the arc
   picks up beneath it instead of looking like an unrelated accent. */
.profile-panel__sign-accent {
    position: absolute;
    inset: 0 0 auto 0;
    height: 20rem;
    z-index: 0;
    pointer-events: none;
    mask-image: url("../img/sign.svg");
    -webkit-mask-image: url("../img/sign.svg");
    mask-mode: alpha;
    -webkit-mask-mode: alpha;
    mask-repeat: no-repeat;
    -webkit-mask-repeat: no-repeat;
    mask-position: center top;
    -webkit-mask-position: center top;
    mask-size: 20rem auto;
    -webkit-mask-size: 20rem auto;
    background-image: linear-gradient(
        165deg,
        color-mix(
                in srgb,
                var(--color-block3-glow) 70%,
                var(--md-sys-color-tertiary-container)
            )
            0%,
        color-mix(
                in srgb,
                var(--color-cream) 55%,
                var(--md-sys-color-tertiary-container)
            )
            50%,
        color-mix(
                in srgb,
                var(--color-block3-glow) 70%,
                var(--md-sys-color-tertiary-container)
            )
            100%
    );
    mix-blend-mode: soft-light;
}

/* The arc, tiling from exactly where the sign's own box (above) ends -
   top: 20rem (matching .profile-panel__sign-accent's own height above),
   not inset: 0, is what actually guarantees "curve starts below the
   sign" now: a real box that doesn't exist above that line has nothing
   for mask-repeat to tile INTO up there, no matter how the tiling math
   works out, unlike the mask-position/mask-composite tricks tried
   first. */
.app-body__sidebar::after,
.app-body__mobile-profile::after {
    content: "";
    position: absolute;
    top: 20rem;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 0;
    pointer-events: none;
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 200' preserveAspectRatio='none'%3E%3Cpath d='M18,0 A190,190 0 0 1 18,200' fill='none' stroke='white' stroke-width='8' stroke-linecap='round'/%3E%3C/svg%3E");
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 200' preserveAspectRatio='none'%3E%3Cpath d='M18,0 A190,190 0 0 1 18,200' fill='none' stroke='white' stroke-width='8' stroke-linecap='round'/%3E%3C/svg%3E");
    mask-repeat: repeat-y;
    -webkit-mask-repeat: repeat-y;
    mask-size: 100% 16rem;
    -webkit-mask-size: 100% 16rem;
    background-image: linear-gradient(
        165deg,
        color-mix(
                in srgb,
                var(--color-block3-glow) 70%,
                var(--md-sys-color-tertiary-container)
            )
            0%,
        color-mix(
                in srgb,
                var(--color-cream) 55%,
                var(--md-sys-color-tertiary-container)
            )
            50%,
        color-mix(
                in srgb,
                var(--color-block3-glow) 70%,
                var(--md-sys-color-tertiary-container)
            )
            100%
    );
    mix-blend-mode: soft-light;
}

/* ==========================================================================
   Footer
   ==========================================================================
   Plain flat color, no texture image, no overlay. The same
   --md-sys-color-tertiary-container .app-titlebar uses (site.css) - both
   --color-iris (tried first) and --color-slate before that were one-off
   choices; this instead points at the exact role token .app-titlebar
   already reads, so the two strips that bookend the page (site.css's own
   comment on .app-titlebar) share their color by referencing the same
   token rather than by two separate values that merely happen to
   currently match. Reads as --color-amethyst in both schemes today (the
   custom color anchors, above, set tertiary-container to that literal
   color in both) - but follows automatically if that ever changes,
   unlike a hardcoded color would. */

.app-footer {
    background-color: var(--md-sys-color-tertiary-container);
}
