/* ============================================================================
 * Topbar — canonical strategy topbar, extracted verbatim (class names kept so
 * viz/ adopts it with no markup change): brand, Deck/Text switch, theme
 * toggle, version switcher, account menu.
 * Source of truth previously inlined in viz/topbar.mjs.
 * ========================================================================== */

.topbar {
  height: 52px;
  display: flex;
  align-items: center;
  background: var(--bg); /* code-bg: inherit — page background inherits root overrides. */
  border-bottom: 1px solid var(--border);
}
.topbar__in {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 clamp(14px, 2.4vw, 26px);
  display: flex;
  align-items: center;
  gap: 14px;
}
/* Brand lockup (mark + wordmark) — self-contained so the mark stays optically
   centered with the word, with an even gap, in ANY context (topbar, auth cards,
   consent). These rules used to be scoped to `.topbar`, so a lockup dropped into
   the consent/auth card fell back to inline layout: the mark rode the text
   baseline with no gap between it and the word. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  /* display: brand — a wordmark is the mark, not text. It is two words beside a
     glyph and it is what the product is recognised by, so it keeps the display
     face at every size it is set at, including the 13px below. */
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--strong);
  text-decoration: none;
}
.brand svg { display: block; flex: none; }
/* Topbar wordmark runs a touch smaller than the auth-card lockup. */
.topbar .brand { font-size: var(--text-sm); }
/* The lockup is a link, so it takes focus: the shared ring, with a radius of its
   own because the mark and the word have no box to borrow one from. #482 */
.brand:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); border-radius: var(--radius-xs); }
.topbar .spacer { flex: 1; }

/* Deck / Text (segmented, pill) */
.dtsw {
  display: none;
  align-items: center;
  gap: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 3px;
}
.dtsw.on { display: inline-flex; }
.dtsw a {
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: calc(var(--text-xs) + 1px);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text);
  padding: 4px 13px;
  border-radius: 24px;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.dtsw a:hover { color: var(--strong); }
.dtsw a.cur { color: var(--accent-contrast); background: var(--accent); }
:where(:root[data-theme="dark"]) .dtsw a.cur { color: var(--signal-contrast); }
@media (forced-colors: active) {
  .dtsw a.cur { forced-color-adjust: none; background: Highlight; color: HighlightText; --ring: var(--ring-width) solid CanvasText; }
}
.dtsw a:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }

/* Theme toggle — a single icon-only switch (no label). */
.toggle {
  display: inline-grid;
  place-items: center;
  width: 34px;
  height: 34px;
  cursor: pointer;
  background: var(--surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  font: inherit;
  transition:
    transform var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.toggle:hover { transform: translateY(-1px); border-color: var(--border-strong); }
.toggle:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
.toggle .ic { width: 17px; height: 17px; display: inline-block; color: var(--text); }
.toggle .ic svg { display: block; width: 100%; height: 100%; stroke-width: 2.2; }

/* Version switcher */
.vsw { position: relative; }
.vsw > .ui-dropdown { max-width: 100%; min-width: 0; }
.vsw__btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: calc(var(--text-sm) - 0.5px);
  font-weight: 500;
  color: var(--text);
  background: var(--surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 7px 11px 7px 13px;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
/* The hover edge steps aside under the band: an accent edge one pixel inside an
   accent band is the second band #578 round r34 took off the focus rules, and a
   pointer resting on a focused control would put it back. One indicator, which is
   what the kit's rows already chose. why: docs/foundations.md#the-focus-ring */
.vsw__btn:hover:not(:focus-visible) { border-color: var(--accent); }
.vsw__btn:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
.vsw__btn .cur { color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
.vsw__btn .car {
  width: 8px; height: 8px;
  border-right: 1.6px solid var(--muted);
  border-bottom: 1.6px solid var(--muted);
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--dur-fast) var(--ease);
}
.vsw.open .vsw__btn .car,
.vsw .ui-dropdown.open .vsw__btn .car { transform: translateY(2px) rotate(-135deg); }
.vsw__menu {
  position: absolute;
  top: calc(100% + 9px);
  left: 0;
  min-width: 266px;
  background: var(--bg-elevated);   /* the floating step — see dropdown.css. #295 */
  /* The two-step edge and the drop, through the level's own names: in DARK that is
     --border-strong outside and --border one pixel inside it, then the drop; in LIGHT
     both lines are transparent and the drop carries the whole separation.
     why: docs/foundations.md#elevation */
  border: 1px solid var(--float-edge);
  box-shadow: inset 0 0 0 1px var(--elev-edge, var(--float-edge-inner)), var(--elev-drop);
  border-radius: 16px;
  padding: 6px;
  /* The same cap `.ui-dropdown__panel` writes itself, in bespoke clothes: this
     menu is `wireDropdown()` too, so wireDropdown() measures it the same way and
     writes the same two properties. #489
     why: docs/components.md#the-dropdown-panel */
  max-height: min(var(--ui-dropdown-cap, 100dvh), var(--ui-dropdown-avail, calc(100dvh - 16px)));
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  /* Drawn for the whole of the fade out, so the rows do not vanish mid-fade — and a box
     that is drawn is a box that is hit. The open rule takes this back.
     why: docs/components.md#the-dropdown-panel */
  pointer-events: none;
  transform: translateY(-6px);
  /* Named properties, not the bare shorthand: `transition: 0.18s` is `all`, and `all`
     includes visibility — held at the OLD value for the whole duration, so the menu is
     still `hidden` in the frame it opens. */
  transition:
    opacity var(--dur-med) var(--ease),
    transform var(--dur-med) var(--ease),
    visibility var(--dur-med) linear;
  z-index: var(--z-dropdown);
}
/* Chrome makes a scroll container a keyboard stop on its own — no tabindex, no
   author rule — and #489's cap is what gave this menu the `overflow-y` that
   makes it one, the same way `.ui-dropdown__panel` got it. `.vopt` is
   `tabindex="-1"` and reached only by the arrow keys, so Tab lands on the menu
   itself rather than a row inside it, and the ring belongs here for the same
   reason `.ui-dropdown__panel` keeps it. why: docs/foundations.md#elevation */
.vsw__menu:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
/* Open, the menu is clickable and visible AT ONCE. Both menus in this file are
   `wireDropdown()` in bespoke clothes — same hooks, same keyboard, same fade — so they
   owe a panel's two rules as much as `.ui-dropdown__panel` does: `visibility` off the
   open transition, because the frame the open class lands is the frame the wiring moves
   focus onto a row in and a browser will not focus inside a hidden box; and
   `pointer-events` back on, because the closed rule turned it off.
   why: docs/components.md#the-dropdown-panel */
.vsw.open .vsw__menu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
  transition-property: opacity, transform;
}
.vopt {
  display: flex;
  gap: 11px;
  align-items: flex-start;
  padding: 10px 13px;
  border-radius: 10px;
  cursor: pointer;
  /* What a browser paints on a <button> before any author rule runs. A consumer
     reaches for <button> whenever the row has to work from the keyboard, so the
     row states these rather than taking them from the element. `font` and not
     `font-family`: Chrome's rule is the shorthand `font: 400 13.3333px Arial`,
     and answering the family alone leaves a row 2.25px shorter in type 1.17px
     smaller. Any font longhand a rule keeps goes after it. why: #251 */
  width: 100%;
  background: none;
  border: 0;
  text-align: left;
  font: inherit;
  transition: background var(--dur-fast) var(--ease);
}
/* Hover fills the row. Only keyboard focus draws its outline. */
.vopt:hover { background: var(--surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */ }
:where(:root[data-theme="light"]) .vopt:hover { background: var(--surface-3); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */ }
@media (forced-colors: active) {
  .vopt:hover { border: 1px solid Highlight; padding: 9px 12px; }
}
/* The row is a keyboard stop: react/src/Dropdown.tsx:758
   `els[next < 0 ? 0 : next]?.focus()` moves focus onto a `[data-dd-item]` with
   the arrow keys, and this is one. It takes the ring on its own 10px radius, the
   same way `.amenu a` above does. #482 */
.vopt:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
/* The selected version carries the kit's menu tick, as `.ui-dropdown__tick` does for
   every other menu; the badge keeps its own place beside it. */
.vopt .vtick { margin-left: 8px; flex: none; display: none; color: var(--accent); margin-top: 1px; }
.vopt .vtick svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 2.4; }
.vopt[aria-selected="true"] .vtick { display: inline-flex; }
.vopt .vname { font-family: var(--font-sans); font-size: calc(var(--text-sm) - 0.5px); color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
.vopt .vbadge {
  /* rank: chip */
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  padding: 2px 7px;
  border-radius: 999px;
  margin-left: auto;
  flex: none;
  margin-top: 1px;
}
.vbadge.live { color: var(--chip-success-ink); background: var(--chip-success-fill); /* code-bg: inherit — decorative or text-only surface has no nested chip. */ }
/* The archived version badge is the archive badge tone in a floating menu, so it takes
   the same neutral chip fill rather than the card surface — which, inside a panel that
   is itself lighter than a card, was darker than the ground it sat on. #453 */
.vbadge.arch { /* muted-ink: state — archived version */ color: var(--muted); background: var(--chip-neutral-fill); /* ring-gap: inherit — translucent wash retains the opaque containing gap. */ }
/* Anchor the menu to its right edge so a closed panel cannot widen the page. */
@media (max-width: 720px) {
  .vsw__menu { left: auto; right: 0; }
}

/* Keep the identity controls above the view controls on a phone.
   The toolbar pattern uses the same inset for both rows. */
@media (max-width: 560px) {
  .topbar { height: auto; min-height: 52px; }
  .topbar__in {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr) auto auto;
    grid-auto-rows: minmax(44px, auto);
    gap: var(--space-2) 0;
    /* 4px above and 3px below, because the band's 1px border is the fourth: one row is
       then 52px, as on a wide screen, and the shell offset below stays true. */
    padding: var(--space-1) var(--space-3) 3px;
  }
  .topbar__in > .brand { grid-area: 1 / 1 / 2 / 3; justify-self: start; }
  /* With the shell's menu control on the band it takes the first column and the brand
     the next, so the switch row below spans both. */
  .topbar__in:has(> .ui-app__menu) { grid-template-columns: max-content max-content minmax(0, 1fr) auto auto; }
  .topbar__in > .ui-app__menu { grid-area: 1 / 1; margin-inline-end: var(--space-2); }
  .topbar__in:has(> .ui-app__menu) > .brand { grid-area: 1 / 2 / 2 / 4; }
  .topbar__in:has(> .ui-app__menu) > .toggle { grid-area: 1 / 4; }
  .topbar__in:has(> .ui-app__menu) > .acct { grid-area: 1 / 5; }
  .topbar__in:has(> .ui-app__menu) > .dtsw { grid-area: 2 / 1 / 3 / 3; }
  .topbar__in:has(> .ui-app__menu) > .vsw { grid-area: 2 / 3 / 3 / -1; }
  .topbar__in > .toggle { grid-area: 1 / 3; justify-self: end; }
  /* The 8px beside the account is its own margin, so a hidden account leaves no gap and
     the theme switch ends on the inset. */
  .topbar__in > .acct { grid-area: 1 / 4; justify-self: end; margin-left: var(--space-2); }
  .topbar__in > .spacer { display: none; }
  .topbar__in > .dtsw { grid-area: 2 / 1; justify-self: start; }
  /* The first column is as wide as Deck/Text and no wider, because the brand spans past it.
     The version switch takes the flexible column and the two auto columns beside it, so its
     width never widens the columns that pin the theme switch and the account to the row's
     end inset. A long label gives way to an ellipsis, and 8px always stays between the
     two controls. */
  .topbar__in > .vsw { grid-area: 2 / 2 / 3 / -1; justify-self: end; min-width: 0; max-width: calc(100% - var(--space-2)); margin-left: var(--space-2); }
  .vsw__btn { max-width: 100%; min-width: 0; }
  .vsw__btn .cur { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .vsw__btn .car { flex: none; }
  /* Two 44px rows, an 8px gap, the 4px and 3px insets and the bottom border. */
  .ui-app-page:has(.topbar .dtsw.on),
  .ui-app-page:has(.topbar .vsw) { --ui-app-top: 104px; }
}

/* Account — avatar + dropdown. Gated: hidden until the app confirms a session
   and adds `.on` (mirrors the portal's /auth/me reveal). */
.acct { position: relative; display: none; }
.acct.on { display: inline-flex; }
.avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: linear-gradient(145deg, var(--accent-strong), var(--purple));
  color: var(--accent-contrast);
  font: inherit;
  font-weight: 600;
  font-size: calc(var(--text-sm) - 0.5px);
  display: grid;
  place-items: center;
  cursor: pointer;
  letter-spacing: 0.02em;
  transition: border-color var(--dur-fast) var(--ease);
}
/* The hover edge steps aside under the band: an accent edge one pixel inside an
   accent band is the second band #578 round r34 took off the focus rules, and a
   pointer resting on a focused control would put it back. One indicator, which is
   what the kit's rows already chose. why: docs/foundations.md#the-focus-ring */
/* The React menu puts the dropdown trigger's pill padding and flex row on the avatar. */
.acct .avatar { display: grid; padding: 0; }
.acct .avatar:hover:not(:focus-visible) { border-color: var(--accent); }
.avatar:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
/* The account button holding an Avatar: the part paints the circle in the reader's hue and
   the button keeps its edge, cursor, hover and ring. why: docs/components.md#avatar */
.avatar:has(> .ui-avatar) { background: none; padding: 0; overflow: hidden; }
.avatar > .ui-avatar { --ui-avatar-size: 100%; }
.amenu {
  position: absolute;
  right: 0; top: 44px;
  width: 252px;
  background: var(--bg-elevated);   /* the floating step — see dropdown.css. #295 */
  /* The two-step edge and the drop, through the level's own names: in DARK that is
     --border-strong outside and --border one pixel inside it, then the drop; in LIGHT
     both lines are transparent and the drop carries the whole separation.
     why: docs/foundations.md#elevation */
  border: 1px solid var(--float-edge);
  box-shadow: inset 0 0 0 1px var(--elev-edge, var(--float-edge-inner)), var(--elev-drop);
  border-radius: 14px;
  /* #519 chose no clip over padding: `overflow: hidden` with no room cut the ring down to
     the one pixel of border this panel had, so the rule took the clip away instead and let
     the ring cross the edge. #489's cap takes that choice away — `overflow-y: auto` clips
     again, because a menu that scrolls has to stop its content at its own edge — so this
     reserves the room instead, the same 6px `.vsw__menu` and `.ui-dropdown__panel` already
     keep rather than a new figure. why: docs/components.md#a-menu-panel-does-not-cut-off-its-rows-ring */
  padding: 6px;
  /* The same cap `.ui-dropdown__panel` writes itself — see .vsw__menu above. #489 */
  max-height: min(var(--ui-dropdown-cap, 100dvh), var(--ui-dropdown-avail, calc(100dvh - 16px)));
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Chromium scrolls a focused row flush with the scrollport edge, which is 3px
     short of the row's own ring: 1px of gap plus a 2px band. The padding above
     reserves that room while the menu stands still; this keeps it reserved once
     scrolling the menu — reaching the first row this PR made it do — puts a
     focused row at either end. #641's danger 2.
     why: docs/components.md#a-menu-panel-does-not-cut-off-its-rows-ring */
  scroll-padding-block: 3px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none; /* see .vsw__menu above */
  transform: translateY(-6px);
  /* Named properties — see .vsw__menu above. */
  transition:
    opacity var(--dur-med) var(--ease),
    transform var(--dur-med) var(--ease),
    visibility var(--dur-med) linear;
  z-index: var(--z-dropdown);
}
/* The same gap as `.vsw__menu` above, from the same cause: #489's cap. `.amenu
   a` is `tabindex="-1"` too, so Tab still lands on the menu and not a row, and
   the ring belongs here. why: docs/foundations.md#elevation */
.amenu:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
/* Clickable and visible at once — see .vsw.open .vsw__menu above. The row this one
   costs is `.aout`: a stray click in the fade out ends the reader's session. */
.acct.open .amenu {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
  transition-property: opacity, transform;
}
/* `margin` bleeds the head back through the 6px panel padding above, the way
   `.ui-dropdown__panel > .ui-dropdown__head` already does for its own panel:
   the border-bottom hairline reaches the panel's edge instead of stopping a
   notch short of it. #641's warning.
   why: docs/components.md#the-dropdown-panel */
.amenu .ahead {
  padding: 13px 15px;
  margin: -6px -6px 0;
  border-bottom: 1px solid var(--border);
  border-radius: 14px 14px 0 0;
  display: flex; align-items: center; gap: 11px;
}
.amenu .ui-dropdown__sep { margin: 5px 0; }
.amenu .ahead .avatar { cursor: default; }
.amenu .ahead .aw { min-width: 0; display: flex; flex-direction: column; gap: 1px; line-height: 1.35; }
.amenu .ahead .anm { color: var(--strong); font-size: var(--text-sm); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.amenu .ahead .aem { color: var(--text); font-size: calc(var(--text-xs) + 0.5px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* `scroll-margin-block` on the row, beside `scroll-padding-block` on the panel
   above: Chromium's own focus-follow scroll honoured the padding scrolling
   toward the end of the list and not the start, so the row carries its own
   reserve too rather than leaving the start of the list short. #641's danger 2.
   why: docs/components.md#a-menu-panel-does-not-cut-off-its-rows-ring */
.amenu a { display: flex; align-items: center; gap: 11px; padding: 10px 15px; color: var(--text); text-decoration: none; font-family: var(--font-sans); font-size: var(--text-sm); cursor: pointer; scroll-margin-block: 3px; }
.amenu a:hover { outline: 1px solid var(--control-edge); outline-offset: -1px; background: var(--surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */ }
.amenu a:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); border-radius: var(--radius-xs); }
.amenu a.cur { color: var(--accent); }
/* A custom account row keeps the Dropdown's disabled state without its row shape. */
.amenu a.is-disabled { /* muted-ink: state — unavailable account page */ color: var(--disabled-ink); cursor: not-allowed; pointer-events: none; }
/* The SVG takes the row's height; its React wrapper adds no line box. */
.amenu a > span[aria-hidden="true"] { display: contents; }
.amenu a svg { width: 19px; height: 19px; flex: none; stroke: currentColor; fill: none; stroke-width: 2; opacity: 0.85; }
.amenu .asep { height: 1px; background: var(--border); margin: 5px 0; }
.amenu a.aout { color: var(--text); }
.amenu a.aout:hover { color: var(--pink); }
