/* Everything the shell does not already do, plus the handful of places the shell hardcodes a colour
   it does not expose as a parameter.

   NO TAILWIND, NO FRAMEWORK, NO BUILD STEP. The BOS shell is deliberately dependency-free ("no
   Tailwind, no external deps" is the first line of bos-shell.css) and this file matches it. Theming
   is CSS custom properties and nothing else, which is also why it can theme a shared shell without
   forking it: the shell reads --bos-* variables, and those are set from here.

   The shell (_content/Bos.Template/bos-shell.css) is a SHARED file, hand-synced with the BauTalents
   and Tax & Purpose BOSes. Nothing here may be pushed back into it: an override that suits Autavi
   is a regression for the other two.

   ── HOW THE COLOURS WORK, because getting this wrong once already shipped a broken dark mode ──

   TWO LAYERS, and the split is the whole design.

   1. BRAND CONSTANTS (--a-*). Autavi's palette, COPIED FROM THE WEBSITE so the two look like one
      product: autavi/src/Autavi/wwwroot/css/tokens.css, the navy and red sampled from the wordmark
      (Marvelous's rebrand of 01 and 02.09; until 02.10 this file still carried the earlier cyan kit).
      These NEVER change with the theme. Repeated rather than imported because that file lives in
      another repository; the comment after each value names the website token it copies.

   2. ROLE TOKENS (--fg, --surface, --btn-bg, ...). What a colour is FOR, not what it is. Only these
      flip between light and dark, and every rule below is written against them.

   WHY THE SPLIT EXISTS. The first attempt at dark mode skipped it and inverted the brand constants
   directly: --a-secondary went from "the deep navy ink" to "the near-white text". That silently
   broke every rule that used it as a BACKGROUND while leaving every rule that used it as TEXT
   working, which is the worst possible failure because half the page looks right. The wordmark went
   white on a rail that had stayed light, so `autavi` simply disappeared; the primary button became
   white text on a white fill. A token named after a colour cannot be inverted. A token named after
   a role can. */
:root {
    /* ---- layer 1: Autavi's brand, fixed ---- */
    --a-primary: #204478;          /* --color-primary: the wordmark's navy blue */
    --a-primary-600: #1a3b68;      /* --primary-700: darker, for hovers */
    --a-primary-50: #eaf0f7;       /* --primary-50 */
    --a-primary-bright: #5c8cd0;   /* --color-primary-bright: the website's blue on dark */
    --a-primary-300: #7a9cc8;      /* --primary-300 */
    --a-secondary: #15243c;        /* --color-secondary: deep navy, the ink colour */
    --a-secondary-900: #0c1628;    /* --secondary-900 */
    --a-secondary-700: #2a3c58;    /* --secondary-700 */
    --a-secondary-400: #8896aa;    /* --secondary-400 */
    --a-secondary-100: #e8edf3;    /* --secondary-100 */
    --a-secondary-50: #f3f6f9;     /* --secondary-50 */
    --a-tertiary: #d40000;         /* --color-tertiary: the wordmark's red square, a spot colour only */
    --a-neutral-700: #454a4c;      /* --neutral-700 */
    --a-neutral-600: #71787a;      /* --neutral-600 */

    /* The website's typeface (--font there). The weights are the ones it loads (App.razor), so
       nothing renders in a faux-bold synthesised by the browser. */
    --a-font: "Kodchasan", system-ui, -apple-system, "Segoe UI", sans-serif;

    /* ---- layer 2: roles, LIGHT (the website's light aliases where it has one) ---- */
    --fg: var(--a-secondary);            /* body text (--text) */
    --fg-muted: var(--a-neutral-600);    /* secondary prose (--text-muted) */
    --fg-subtle: var(--a-secondary-400); /* labels, inactive icons */
    --surface: #ffffff;                  /* the stage, behind a tool (--bg) */
    --surface-sunken: var(--a-secondary-50); /* the rail (--bg-soft) */
    --surface-raised: #ffffff;           /* chips and buttons sitting ON the rail */
    --border: var(--a-secondary-100);
    --hover: var(--a-secondary-100);
    --accent: var(--a-primary);          /* the accent as a fill, an edge or a focus ring (--accent) */
    --accent-ink: var(--a-primary);      /* the accent used as TEXT or a hairline */
    --accent-wash: var(--a-primary-50);  /* the accent used as a large soft fill */
    --btn-bg: var(--a-primary);          /* primary button fill, as on the website */
    --btn-fg: #ffffff;                   /* text ON that fill */
    --btn-bg-hover: var(--a-primary-600);
    --brand-ink: var(--a-secondary);     /* the wordmark */
    --brand-logo-light: block;
    --brand-logo-dark: none;
    --rail-tab: linear-gradient(180deg, var(--a-primary) 0%, var(--a-secondary) 100%);
    --shadow: rgba(21, 36, 60, 0.12);
    --scrim: rgba(21, 36, 60, 0.45);
}

/* ---- layer 2: roles, DARK -------------------------------------------------------------------
   Redefined in two places for the two ways dark is reached, and NOTHING but tokens appears in
   either: no component selector, no one-off override. A colour added later is themed by adding it
   here rather than by remembering to add a matching rule.

   The default is "follow the system", which is the bare media query. `[data-theme]` is only ever
   set when someone picks a side explicitly, and then it has to win in BOTH directions: hence
   `:not([data-theme="light"])` guarding the media query, and a separate `[data-theme="dark"]`
   block for forcing dark on a light OS. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        /* The website's [data-theme="dark"] palette. */
        --fg: #ffffff;
        --fg-muted: #a8b3bd;
        --fg-subtle: var(--a-secondary-400);
        --surface: #070b10;
        --surface-sunken: #0b1219;
        --surface-raised: #101820;
        --border: rgba(255, 255, 255, 0.1);
        --hover: rgba(255, 255, 255, 0.07);
        /* The navy disappears against a near-black ground, so on dark the accent is the website's
           bright blue, as it is there. The brand navy itself is untouched. */
        --accent: var(--a-primary-bright);
        --accent-ink: var(--a-primary-bright);
        --accent-wash: rgba(92, 140, 208, 0.16);
        /* The button inverts the way the website's does: bright blue with deep navy text. */
        --btn-bg: var(--a-primary-bright);
        --btn-fg: var(--a-secondary-900);
        --btn-bg-hover: var(--a-primary-300);
        --brand-ink: #ffffff;
        --brand-logo-light: none;
        --brand-logo-dark: block;
        --rail-tab: linear-gradient(180deg, var(--a-primary-600) 0%, #0b1219 100%);
        --shadow: rgba(0, 0, 0, 0.45);
        --scrim: rgba(0, 0, 0, 0.6);
    }
}

:root[data-theme="dark"] {
    --fg: #ffffff;
    --fg-muted: #a8b3bd;
    --fg-subtle: var(--a-secondary-400);
    --surface: #070b10;
    --surface-sunken: #0b1219;
    --surface-raised: #101820;
    --border: rgba(255, 255, 255, 0.1);
    --hover: rgba(255, 255, 255, 0.07);
    --accent: var(--a-primary-bright);
    --accent-ink: var(--a-primary-bright);
    --accent-wash: rgba(92, 140, 208, 0.16);
    --btn-bg: var(--a-primary-bright);
    --btn-fg: var(--a-secondary-900);
    --btn-bg-hover: var(--a-primary-300);
    --brand-ink: #ffffff;
    --brand-logo-light: none;
    --brand-logo-dark: block;
    --rail-tab: linear-gradient(180deg, var(--a-primary-600) 0%, #0b1219 100%);
    --shadow: rgba(0, 0, 0, 0.45);
    --scrim: rgba(0, 0, 0, 0.6);
}

/* Tells the browser to theme its own furniture (scrollbars, form controls, the flash before paint)
   to match. Without it a dark page keeps light scrollbars on its own elements. */
:root { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { color-scheme: dark; } }

/* THE FIX THAT MATTERS MOST. Until 2026-08-21 nothing in this app set a font: the shell's
   FontFamily parameter defaults to `inherit`, .nav-item said `font: inherit`, and so the rail
   inherited the BROWSER default, which is a serif. The workspace rendered as a bulleted list in
   Times while the right-hand pane, which set its own font, looked fine. */
body {
    font-family: var(--a-font);
    color: var(--fg);
    background: var(--surface);
    margin: 0;
}

/* ---- the app is a console, not a document -----------------------------------------------------
   THE "WHY DOES THE PAGE HAVE A MARGIN" FIX, and the "why is the sidebar scrolling" fix, which
   turned out to be the same bug.

   The shell is a general-purpose page chrome: `min-height: 100vh` on the shell and `padding: 2rem`
   on the content, which is right for a BOS whose pages are documents that scroll. This app is not
   that. Its content is a full-bleed iframe, so a 100dvh stage inside a 2rem-padded main made the
   document 100dvh + 4rem tall, the page scrolled for no content, the frame sat inset by 2rem, and
   scrolling slid the page out from under a rail that is only 100vh tall. */
.bos-shell {
    height: 100vh;
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
}

/* The shell sizes the rail to 100vh at the desktop breakpoint. A landscape tablet
   is often wider than 1024px and much shorter than 100vh, so that footer was
   clipped. Match the shell's own dvh box and let the nav list shrink. */
@media (min-width: 1024px) {
    .bos-rail {
        height: 100%;
        max-height: 100%;
        align-self: stretch;
    }
}

.bos-content { min-height: 0; }   /* a flex child defaults to min-height:auto and refuses to shrink */

.bos-content__main {
    padding: 0;
    min-height: 0;
    overflow: hidden;
    /* The shell fades and lifts the content on load. Fine for a page of text, wrong for a
       full-bleed frame: it animates a 12px gap at the bottom edge on every tool switch. */
    animation: none;
}

/* The topbar carries only the mobile hamburger, which the shell already hides above 1024px. It
   still occupies its padding box, so it is collapsed rather than left as a stray strip. */
@media (min-width: 1024px) {
    .bos-content__topbar { display: none; }
}

/* ---- the rail's own chrome --------------------------------------------------------------------
   The shell exposes four colours as parameters and hardcodes slate for everything else: the
   tagline, the pin button, the close button, the footer rule. Against Autavi's palette that slate
   is visibly foreign, and in dark mode it is illegible. Each rule below is one of those hardcodes,
   restated as a role token.

   The rail's own background is NOT here: it is the shell's SidebarBg parameter, and Workspace.razor
   passes `var(--surface-sunken)` so it follows the theme like everything else. Passing a literal
   hex there is what left the rail light while the text went white. */
.bos-shell { --bos-rail-border: var(--border); }

.bos-rail__tagline {
    color: var(--fg-subtle);
    letter-spacing: 0.14em;
    font-weight: 700;
}

.bos-rail__footer { border-top-color: var(--border); }

.bos-rail__pin {
    color: var(--fg-subtle);
    background: var(--surface-raised);
    border-color: var(--border);
}

/* Pinned. The shell fills this with the brand colour, which puts a bright cyan chip in the corner
   of an otherwise calm rail, for the one piece of information nobody is looking for. */
.bos-rail__pin[aria-pressed="true"] {
    color: var(--fg);
    background: var(--hover);
    border-color: var(--border);
}

.bos-rail__pin:hover,
.bos-rail__pin[aria-pressed="true"]:hover {
    color: var(--fg);
    background: var(--hover);
    border-color: var(--fg-subtle);
    box-shadow: 0 2px 8px var(--shadow);
}

.bos-rail__close { color: var(--fg-subtle); }
.bos-rail__close:hover { background: var(--hover); color: var(--fg); }

.bos-shell__backdrop { background: var(--scrim); }

.bos-hamburger {
    background: var(--surface-raised);
    border-color: var(--border);
    color: var(--fg);
}

/* The drawer tab, the one piece of chrome that is unmissable on a wide screen. The shell builds it
   from --bos-brand, and Autavi's brand colour is a bright cyan: as a large filled shape carrying
   white glyphs it both fails contrast and shouts over everything near it. */
.bos-drawer-toggle {
    background: var(--rail-tab);
    border-color: rgba(255, 255, 255, 0.28);
    box-shadow: 2px 0 12px var(--shadow);
}

.bos-drawer-toggle:hover {
    background: var(--rail-tab);
    filter: brightness(1.25);
    box-shadow: 2px 0 16px var(--shadow);
}

/* ---- the brand wordmark ---------------------------------------------------------------------
   Which file is visible is a role token, flipped in the same two dark blocks as the colours.
   A descendant selector here would desync from a forced theme (moon on a light OS). */
.brand-logo {
    height: 2.25rem;
    width: auto;
    max-width: 100%;
    object-fit: contain;
}

.brand-logo--light {
    display: var(--brand-logo-light);
}

.brand-logo--dark {
    display: var(--brand-logo-dark);
}

/* A customer's name in place of the logo (BrandMark.razor), set like Leila's dashboard sets hers.
   ponytail: one serif for every customer; a setting when a second one wants another. */
.bos-auth__logo { text-decoration: none; } /* the sign-in card's logo link; an image never showed it */
.brand-wordmark {
    font-family: Georgia, "Times New Roman", serif;
    font-size: 1.35rem;
    line-height: 2.25rem;
    color: var(--accent);
    white-space: nowrap;
}

/* ---- the signed-out page ----------------------------------------------------------------------
   bos-auth.css ships light-only surfaces; point them at the role tokens so the page follows the
   theme like everything else. Loaded after bos-auth.css, so these win. */
.bos-auth {
    --bos-surface: var(--surface-sunken);
    --bos-card: var(--surface-raised);
    --bos-border: var(--border);
    --bos-text-strong: var(--fg);
    --bos-text: var(--fg);
    --bos-text-muted: var(--fg-muted);
    background: var(--surface-sunken);
}

.bos-auth__btn--primary,
.bos-auth__btn--primary:hover {
    color: var(--btn-fg);
}

/* ---- the rail ---------------------------------------------------------------------------------
   The shell deliberately ships NO nav-item styles: what an item looks like is the consuming app's
   business, and its demo.html says so in as many words. This is that. */
.bos-rail__nav {
    display: flex;
    flex-direction: column;
    gap: .125rem;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

.bos-rail__nav::-webkit-scrollbar { width: 6px; }
.bos-rail__nav::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

.nav-item[data-tool],
.nav-group-toggle,
.nav-item.logout {
    display: flex;
    align-items: center;
    gap: .625rem;
    width: 100%;
    /* border-box, and it is load-bearing rather than tidiness. This sheet has NO global
       `* { box-sizing: border-box }` reset, so the default content-box applies: `width: 100%` plus
       .75rem of padding on each side made every item 1.5rem WIDER than .bos-rail__nav, which clips
       with overflow-x: hidden. The 1.5rem that got cut off is exactly where the trailing icons live,
       so the info button and the external-link arrows were sliced down their right edge while the
       labels looked fine. Reported 2026-09-10 by the owner, who could see it and could not name it.

       The real fix is a global border-box reset, and this is the THIRD place to opt in by hand
       (.bos-rail__brand-block and .launch got there first, each presumably after the same
       half-hour). That reset is worth doing deliberately, with someone watching the layout, rather
       than slipped in beside a bug fix. */
    box-sizing: border-box;
    padding: .5625rem .75rem;
    border: 0;
    border-radius: .625rem;
    background: none;
    font: 500 .9375rem/1.35 var(--a-font);
    color: var(--fg);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    /* Colour and background only. Transitioning `all` would animate the inset rule the active state
       draws, which reads as the item sliding rather than being chosen. */
    transition: background-color .14s ease, color .14s ease;
}

/* A rail group (Showcases): a native <details> dropdown. The toggle looks like any other item, with
   a chevron that turns when open; its tools sit indented underneath. */
.nav-group > summary {
    list-style: none;
}
.nav-group > summary::-webkit-details-marker {
    display: none;
}
.nav-group-toggle::after {
    content: "";
    flex: none;
    width: .45rem;
    height: .45rem;
    margin-left: auto;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform .14s ease;
    opacity: .6;
}
.nav-group[open] > .nav-group-toggle::after {
    transform: rotate(45deg);
}
.nav-group-items {
    padding-left: .75rem;
}

.nav-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: .2rem;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
}

.nav-row .nav-item[data-tool] {
    flex: 1 1 auto;
    min-width: 0;
    width: auto;
}

.nav-item[data-tool] .tool-name,
.nav-group-toggle .tool-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tool-icon {
    flex: 0 0 auto;
    color: var(--fg-subtle);
    transition: color .14s ease;
}

.nav-item[data-tool]:hover,
.nav-group-toggle:hover,
.nav-item.logout:hover {
    background: var(--hover);
    color: var(--fg);
}

.nav-item[data-tool]:hover .tool-icon { color: var(--fg); }

.nav-item[data-tool]:focus-visible,
.nav-group-toggle:focus-visible,
.nav-item.logout:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* THE SELECTED ITEM, and why it once never showed: workspace.js has always set the class `active`
   (the template's demo.html uses the same word) and this file styled `.is-active`, which nothing
   ever adds. */
.nav-item[data-tool].active {
    background: var(--accent-wash);
    color: var(--fg);
    font-weight: 700;
    /* The wash alone is too soft to read as "this one", so it is joined by a solid edge. Drawn with
       box-shadow rather than border-left so the text does not shift by 3px when an item is chosen. */
    box-shadow: inset 3px 0 0 0 var(--accent);
}

.nav-item[data-tool].active .tool-icon { color: var(--accent-ink); }

.nav-item.logout {
    color: var(--fg-subtle);
    font-weight: 500;
}

/* Marks the tools that open in a new tab instead of rendering here, which is the one thing about
   this rail that surprises people. */
.tool-external {
    flex: 0 0 auto;
    color: var(--fg-subtle);
    opacity: .55;
}

.nav-item[data-tool]:hover .tool-external,
.nav-item[data-tool].active .tool-external { opacity: 1; }

/* The unread mark: a red dot over the tool's icon. It stays until this person marks the note
   read. The button is larger than the dot so it can be clicked and focused; the dot itself is
   the radial fill. Positioned from the row, over the icon's top-right (icon starts after the
   item's .75rem padding and is 18px). */
.tool-dot {
    position: absolute;
    z-index: 2;
    left: 1.15rem;
    top: .15rem;
    width: 1.15rem;
    height: 1.15rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: radial-gradient(circle at 70% 35%, #e11d2e 0 4.5px, transparent 5px);
    cursor: pointer;
}

.tool-dot[hidden] { display: none; }

.tool-dot:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.change-popover {
    position: fixed;
    z-index: 80;
    width: min(22rem, calc(100vw - 1.5rem));
    padding: .9rem 1rem 1rem;
    background: var(--surface-raised);
    color: var(--fg);
    border: 1px solid var(--border);
    border-radius: .75rem;
    box-shadow: 0 12px 32px var(--shadow);
    font: 500 .8125rem/1.4 var(--a-font);
}

.change-popover[hidden] { display: none; }

/* Over the tool AND the rail (the rail is z-index 50, the content is a stacking context at 1,
   so a scrim inside the stage can never cover a click on the frame). Under the popover (80). */
.change-popover-scrim {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: transparent;
}

.change-popover-scrim[hidden] { display: none; }

.change-popover__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .5rem;
    margin: 0 0 .65rem;
}

.change-popover__kicker {
    margin: 0;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--fg-subtle);
}

.change-popover__close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    margin: -.25rem -.35rem 0 0;
    padding: 0;
    border: 0;
    border-radius: .4rem;
    background: transparent;
    color: var(--fg-subtle);
    cursor: pointer;
}

.change-popover__close:hover {
    background: var(--hover);
    color: var(--fg);
}

.change-popover__close:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.change-popover__list {
    list-style: none;
    margin: 0 0 .85rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .65rem;
    max-height: 14rem;
    overflow-y: auto;
}

.change-popover__item {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    color: var(--fg);
    font-weight: 500;
}

.change-popover__when {
    font-size: .6875rem;
    font-weight: 600;
    color: var(--fg-subtle);
}

.change-popover__text { white-space: pre-wrap; }

.change-popover__read {
    width: 100%;
    padding: .5rem .75rem;
    border: 0;
    border-radius: .5rem;
    background: var(--btn-bg);
    color: var(--btn-fg);
    font: 700 .8125rem/1.2 var(--a-font);
    cursor: pointer;
}

.change-popover__read:hover { background: var(--btn-bg-hover); }

.change-popover__read:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---- the stage --------------------------------------------------------------------------------
   The surface behind a tool. It shows only in the moment before a frame paints and around a launch
   card, but if it stays white in dark mode that is exactly the flash the theme exists to remove. */
.stage {
    position: relative;
    height: 100%;
    overflow: hidden;
    background: var(--surface);
}

.pane {
    position: absolute;
    inset: 0;
    z-index: 1;
    overflow: auto;
}

.pane[hidden] { display: none; }

/* A tool's frame on its way: the shared spinner (BosLoading), centred, in the accent, until it loads. */
.pane-loading { position: absolute; inset: 0; z-index: 0; --bos-loading-accent: var(--accent); pointer-events: none; }
.pane.is-loaded .pane-loading { display: none; }

/* No border and no scrollbar of our own: the tool owns the whole area, so the frame should be
   invisible rather than look like a widget on a page. Above the pane's spinner: the frame is see-through
   until the tool paints, so the spinner shows until then and is covered the moment the page appears (the
   load event, which hides it, waits for every picture and script). */
.tool-frame {
    position: relative;
    z-index: 1;
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

/* ---- the card shown instead of a frame ------------------------------------------------------- */
.launch {
    max-width: 34rem;
    margin: 0 auto;
    padding: 3rem 2rem;
    min-height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    font: 400 1rem/1.65 var(--a-font);
    color: var(--fg-muted);
    box-sizing: border-box;
}

.launch h1 {
    font-size: 1.6rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.25;
    margin: 0 0 .75rem;
    color: var(--fg);
}

.launch p { margin: 0 0 1rem; }
.launch .why { color: var(--fg-muted); }

.launch .fixable {
    background: var(--accent-wash);
    border-left: 3px solid var(--accent);
    padding: .85rem 1rem;
    border-radius: 0 .375rem .375rem 0;
    color: var(--fg);
}

.launch .note { color: var(--fg-subtle); font-size: .95rem; }

.launch code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .875em;
    background: var(--hover);
    color: var(--fg);
    padding: .1em .35em;
    border-radius: .25rem;
}

/* Navy on white, bright blue on dark. The pairing inverts because a navy fill vanishes on a
   near-black page; the website uses the same inverse. Both halves come from
   tokens, so neither can end up the same colour as the other, which is what happened when this
   said `background: var(--a-secondary); color: #fff` and --a-secondary was inverted to near-white. */
.launch-button {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin-top: .5rem;
    padding: .7rem 1.4rem;
    background: var(--btn-bg);
    color: var(--btn-fg);
    border-radius: .5rem;
    text-decoration: none;
    font-weight: 700;
    transition: background-color .14s ease;
}

.launch-button:hover { background: var(--btn-bg-hover); }

.launch-button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ---- the appearance control -------------------------------------------------------------------
   WHAT IT CAN AND CANNOT DO, because the limit is the reason it exists rather than a shortcoming.
   It themes THIS SHELL. It cannot theme the tools: Directus, VaultWarden and Excalidraw render in
   cross-origin iframes and choose their own appearance from their own settings and the OS, and a
   parent page cannot reach in to restyle them. This is here so the rail stops being a bright white
   frame around apps that came up dark. Each tool's own theme setting is where its look is chosen. */
.theme-toggle {
    display: flex;
    align-items: center;
    gap: .25rem;
    margin-bottom: .5rem;
    padding: .25rem;
    background: var(--hover);
    border-radius: .625rem;
}

.theme-toggle button {
    flex: 1 1 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .35rem 0;
    border: 0;
    border-radius: .45rem;
    background: none;
    color: var(--fg-subtle);
    cursor: pointer;
    transition: background-color .14s ease, color .14s ease;
}

.theme-toggle button:hover { color: var(--fg); }

/* aria-pressed IS the state, so the styling reads off the same attribute a screen reader does
   rather than a second class that could disagree with it. */
.theme-toggle button[aria-pressed="true"] {
    background: var(--surface-raised);
    color: var(--fg);
    box-shadow: 0 1px 2px var(--shadow);
}

.theme-toggle button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* BACK, START OVER, IN-CONTEXT, AND SEND TO PROD. Compact equal squares, centred as a group. */
.rail-actions {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: .3rem;
    width: 100%;
    box-sizing: border-box;
    padding-bottom: .5rem;
    margin-bottom: .5rem;
    border-bottom: 1px solid var(--border);
}

.rail-action {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.85rem;
    height: 1.85rem;
    padding: 0;
    box-sizing: border-box;
    border: 1.5px solid var(--border);
    border-radius: .5rem;
    background: none;
    color: var(--fg);
    cursor: pointer;
    transition: background-color .14s ease, color .14s ease, border-color .14s ease;
}

.rail-action .tool-icon {
    color: inherit;
    stroke-width: 2.15;
}

.rail-action .tool-icon [stroke="none"] { stroke: none; }

.rail-action:hover:not(:disabled) {
    background: var(--hover);
    border-color: var(--fg-subtle);
    color: var(--fg);
}
.rail-action:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rail-action[hidden] { display: none; }

.rail-action[aria-pressed="true"] {
    background: var(--accent-wash);
    border-color: var(--accent-ink);
    color: var(--accent-ink);
}

/* Disabled means "there is nothing to go back to inside the workspace", which is the moment the
   browser's own Back would have taken you out of it. */
.rail-action:disabled { opacity: .4; cursor: default; }

/* WHO IS SIGNED IN, at the foot of the rail, with Sign out behind a click (Notion, 26.09). A native
   <details>: the panel opens upward because the menu sits at the very bottom of the rail. */
.user-menu {
    position: relative;
}

.user-chip {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .4rem .5rem;
    border-radius: .625rem;
    color: var(--fg);
    cursor: pointer;
    list-style: none;
    transition: background-color .14s ease;
}

.user-chip::-webkit-details-marker { display: none; }
.user-chip:hover,
.user-menu[open] .user-chip { background: var(--hover); }
.user-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.user-avatar {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    object-fit: cover;
    background: var(--accent-wash);
    color: var(--accent-ink);
    font-size: .8rem;
    font-weight: 700;
}

.user-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .875rem;
    font-weight: 600;
}

.user-menu-panel {
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(100% + .35rem);
    box-sizing: border-box;
    padding: .5rem;
    border: 1px solid var(--border);
    border-radius: .75rem;
    background: var(--surface-raised);
    box-shadow: 0 8px 24px var(--shadow);
    z-index: 5;
}

.user-email {
    display: block;
    padding: .25rem .5rem .5rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .8rem;
    color: var(--fg-muted);
}
