/* styles/shell.css — app shell: sidebar, topbar, drawer, logo, buttons. Order in VulnerabilityCore.html is the cascade order. */

/* ============================================================ app shell */
.app {
  display: flex;
  min-height: 100vh;
  background: var(--bg);
  position: relative;
}
.app[data-theme="dark"] {
  background:
    radial-gradient(
      1200px 600px at 80% -10%,
      color-mix(in oklab, var(--accent) 7%, transparent),
      transparent 60%
    ),
    var(--bg);
}
/* In dark mode the cards/panels share the same indigo hue as the canvas, so a
   soft shadow (no border) is what makes them read as raised — the lighter
   --surface ramp does the rest. A right border on the sidebar separates it
   from the content without a hard line. */
.app[data-theme="dark"] .card,
.app[data-theme="dark"] .cov-strip,
.app[data-theme="dark"] .kpi {
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.02) inset,
    0 6px 20px rgba(0, 0, 0, 0.35);
}
/* The light theme's opposite problem, and the reason the Administration cards
   read as one undifferentiated sheet. --surface (the card), --bg-panel (the
   inset form and the table wrapper) are BOTH #ffffff in light, .card is
   border:none, and the shadow above is scoped to dark — so card-to-canvas
   measures 1.086:1 and card-to-form and card-to-table measure 1.000:1, three
   coincident whites separated by one 1.24:1 hairline. It is also what made any
   trailing space inside a card read as a rendering fault rather than as padding,
   which is the precondition for .admin-grid's switch from stretch to start.
   The light --shadow token already exists and ten rules use it; .entcard is this
   exact recipe. Scoped to the two console screens for now — un-scoping it to
   .card app-wide is the better design and a change to every screen, so it wants
   its own pass. */
.app[data-theme="light"] .admin-card,
.app[data-theme="light"] .plat-strip {
  box-shadow:
    var(--shadow),
    0 0 0 1px var(--line-2);
}
/* --btn-primary is #3a1d73 in BOTH themes and measures 1.34:1 against the dark
   card: the label is fine at 13.1:1, but the button has no boundary at all, on
   the page's only primary action, in the theme the app boots in. --focus gives a
   5.93:1 rim in dark and is visually inert in light. Do NOT lighten
   --btn-primary itself — the white ink depends on that fill staying dark. Scoped
   to the admin card rather than added to .btn-accent, which is on eleven other
   screens including the login card, where it has its own override. */
.admin-card .btn-accent {
  border-color: var(--focus);
}
/* Names a column whose header is deliberately empty (the row-actions cell), so
   the table still has a name for it in a screen reader. clip-path over the 1px
   box because the box alone leaves the text selectable and findable. */
.vc-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.sidebar {
  width: 248px;
  flex: none;
  position: sticky;
  top: 0;
  height: 100vh;
  background: var(--bg-panel);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  padding: 0 14px 18px;
  z-index: 20;
}
.sidebar-top {
  /* The vulnerability.core wordmark (an SVG lockup, swapped per theme in app.jsx)
     in the top-left of the sidebar. The CyMobility logo sits separately at the
     foot of the sidebar and is unrelated to this.

     Height is the topbar's, minus the 1px divider that follows — so block + rule
     together occupy exactly --topbar-h and the rule's bottom edge meets the
     topbar's. The wordmark is centred in that band rather than pushed down by a
     top padding, which also lines it up with the breadcrumb beside it.

     Padding is 0 on both sides on purpose. Any asymmetry here (it used to be 6px
     left / 8px right) shifts the centred wordmark off the sidebar's true midline,
     which is the whole point of centring it. The sidebar's own 14px side padding
     is symmetric, so it doesn't disturb this. */
  height: calc(var(--topbar-h) - 1px);
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* No gap above the first rule — .sidebar-top has already reserved the exact
   height, so any top margin here would push the line back off the topbar's
   level. The footer rule keeps the normal 12px breathing room. */
.sidebar-top + .sidebar-rule {
  margin-top: 0;
}
.brand-logo {
  /* viewBox 68.301 668.262 1360 200 (ratio 6.8), so height alone sizes it; width follows.

     The box is NOT cropped to the ink: the glyphs occupy only 1295.0 x 112.5 of those
     1360 x 200 units, and the leftover margin was lopsided on BOTH axes — 12/75.5 units
     vertically, 20.8/44.2 horizontally. Flex centring lines up the BOX, so that padding
     became visible error: the mark sat 4.1px high and 1.5px left of where it belonged.

     Both offsets are now (ink centre - half the box), so the box centre and the ink
     centre coincide and `align-items: center` / `justify-content: center` land the
     VISIBLE mark where they claim to. Measured after the fix in a real engine: 0.03px
     vertical error, 0.00px horizontal.

     If either wordmark is re-exported these offsets are lost — a fresh export goes back
     to an uncentred window. Re-derive them from the glyph outlines rather than nudging
     the CSS, or the same bug comes back wearing a different hat. */
  display: block;
  height: 26px;
  width: auto;
}
/* Thin divider between the sidebar's sections (top / nav / footer).
   The rule spans the FULL sidebar width, edge to edge — it has to cancel the
   sidebar's own 14px side padding with negative margins to escape it, then add
   that width back. Without this it floats with a gap at each end and reads as a
   stray line rather than a section break. Every .sidebar-rule gets this (under
   the wordmark, above the CyMobility logo), so the treatment is identical on
   every page of the app. */
.sidebar-rule {
  height: 1px;
  margin: 12px -14px;
  width: calc(100% + 28px);
  background: var(--line);
}
/* On a short screen (a phone's drawer on its side) only the nav gives: it scrolls,
   the wordmark band keeps its exact height and the account row stays reachable. */
.sidebar > * {
  flex-shrink: 0;
}
.sidebar > .sidenav {
  flex-shrink: 1;
  min-height: 0;
  overflow-y: auto;
}

/* ---- workspace plate -------------------------------------------------------
   WHERE you are and in what capacity: the company this account belongs to and your
   role in it. It sits in the account menu's header, under "Signed in as"; it used
   to be a block at the top of the sidebar. Recessed to --bg (a step
   below the menu surface in both themes) so it reads as an ID plate, not a control;
   no hover, because it does nothing. */
.ws-plate {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px 5px 5px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
}
.ws-mark {
  flex: none;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 6px;             /* concentric with the plate: 10 - 4 */
  /* Tinted like the account avatar, not filled. --focus on this tint over --bg is
     5.7:1 dark, 7.5:1 light. */
  background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--line-2);
  color: var(--focus);
  font: 700 12px/1 var(--font-display);
}
.ws-id {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.ws-name,
.ws-role {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ws-name {
  font: 600 12.5px/1.15 var(--font);
  color: var(--text);
}
.ws-role {
  font: 500 11px/1.2 var(--font);
  color: var(--text-faint);       /* 8.0:1 on the dark plate */
}
/* --text-faint on the light plate (#f5f5fb) is 4.3:1, under AA; --text-dim is 4.65:1. */
.app[data-theme="light"] .ws-role {
  color: var(--text-dim);
}
/* Under 520px tall (a phone on its side) a row the nav cuts fades out rather than
   ending on a sliver, because an overlay scrollbar shows nothing, and the padding
   lets the last row clear the fade at the end of the scroll. */
@media (max-height: 520px) {
  .sidebar > .sidenav {
    padding-bottom: 16px;
    mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent);
  }
}

/* ---- nav ---------------------------------------------------------------------- */
.sidenav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sidenav-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sidenav-group + .sidenav-group {
  margin-top: 16px;
}
/* Only "Manage" has a caption; the plate heads the daily work. The hairline runs to
   the column's edge so it reads as a section break, not a label. */
.sidenav-lab {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 0 8px 9px;           /* on the icon tiles' left edge */
  font: 600 10.5px/1 var(--font);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);       /* 7.5:1 dark, 4.7:1 light */
}
.sidenav-lab::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}
.sidenav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 38px;
  padding: 0 10px 0 9px;
  border: none;
  border-radius: 10px;
  background: transparent;
  color: var(--text-faint);       /* 7.5:1 dark, 4.7:1 light */
  font: 500 13.5px/1 var(--font);
  text-align: left;
  cursor: pointer;
  transition: background 0.14s, color 0.14s;
}
.sidenav-ic {
  flex: none;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-2);
  border-radius: 7px;
  transition: background 0.14s, border-color 0.14s, color 0.14s;
}
.sidenav-txt {
  flex: 1;
  min-width: 0;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidenav-item:hover {
  background: var(--sidebar-hover);
  color: var(--text);
}
.sidenav-item:hover .sidenav-ic {
  background: var(--bg-panel);    /* the tile stays cut out of the hover fill */
}
/* "You are here" is the tile: solid --focus, 5.9:1 on the dark panel and 10.2:1 on
   the light one, never raw --accent (1.5:1 in dark). The row's --accent-soft (the
   admin rail's fill) is only a hint: it is about as light as the hover fill, so the
   tile alone tells selected from hovered.
   The weight stays 500: 600 would ellipsise "Vulnerability ledger" beside 99+. */
.sidenav-item.is-on {
  background: var(--accent-soft);
  color: var(--text);
}
.sidenav-item.is-on .sidenav-ic {
  background: var(--focus);
  border-color: var(--focus);
  color: var(--bg-panel);
}
/* Inset: the nav is a scroll container and would clip a ring drawn outside it. */
.sidenav-item:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}
.sidenav-sub {
  margin-top: 20px;
  padding: 0 8px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.sidenav-sub-lab {
  font: 600 10px/1 var(--font);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: 5px;
}
.sidenav-link {
  background: none;
  border: none;
  text-align: left;
  color: var(--text-dim);
  font: 13px/1 var(--font);
  padding: 5px 0;
  cursor: pointer;
}
.sidenav-link:hover {
  color: var(--accent);
}
.sidebar-foot {
  margin-top: auto;
  padding-top: 12px;              /* keeps the foot off a scrolled nav */
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* CyMobility logo at the foot of the sidebar. Both assets are now tightly-cropped
   wordmarks of matching ratio (dark 960x61 ≈15.7:1, light 3000x199 ≈15.1:1), so a
   single fixed width renders them at the same size AND height in both themes — the
   surrounding layout stays identical. (The dark PNG was auto-trimmed of its large
   transparent margins; the untrimmed original is kept as *.original.png.) */
.foot-cymo {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  padding: 4px 2px 0 9px;         /* 9px puts the wordmark on the icon tiles' left edge (x = 23). */
  opacity: 0.85;
  transition: opacity 0.2s;
}
.foot-cymo:hover {
  opacity: 1;
}
.foot-cymo img {
  /* Fixed box + object-fit:contain pins the footprint so the layout never shifts,
     even if the browser serves a stale-cached copy of either PNG. 150x10 ≈ the
     ~15:1 wordmark ratio, so the logo fills the box with no distortion. */
  width: 150px;
  height: 10px;
  object-fit: contain;
  object-position: left center;
  display: block;
}
/* ---- account (foot) ------------------------------------------------------------
   One flat row that opens the account menu. The avatar sits on the icon axis and
   the name on the label column, like the plate at the top. */
.sb-me {
  position: relative;
}
.sb-me-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 44px;
  padding: 0 9px 0 6px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: var(--text);
  font: 600 13px/1.2 var(--font);
  text-align: left;
  cursor: pointer;
  transition: background 0.14s, border-color 0.14s;
}
.sb-me-btn:hover,
.sb-me-btn.is-open {
  background: var(--sidebar-hover);
}
.sb-me-btn.is-open {
  border-color: var(--line-2);
}
/* On My account the row takes the nav's selected fill... */
.sb-me-btn.is-on {
  background: var(--accent-soft);
}
.sb-me-btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}
.sb-me-avatar {
  position: relative;
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--focus);            /* 5.3:1 dark, 8.1:1 light; v1's --accent was 1.5:1 in dark */
  font: 700 11.5px/1 var(--font-mono);
  /* The tint alone is 1.1:1 on the dark panel; the edge keeps it a disk, like the tiles. */
  box-shadow: inset 0 0 0 1px var(--line-2);
}
/* ...and the avatar becomes the nav's "you are here" tile. */
.sb-me-btn.is-on .sb-me-avatar {
  background: var(--focus);
  color: var(--bg-panel);
  box-shadow: none;
}
/* Something on My account needs this person (atlNeedsYou in app.jsx). A dot, not a
   row, so the sidebar stays navigation. Amber is 10.7:1 on the dark panel and 3.2:1
   on the light one: enough for a non-text cue, and the trigger's name carries it
   for screen readers. The ring cuts it out of the avatar and follows the row's fill. */
.sb-me-dot {
  position: absolute;
  top: -1px;
  right: -1px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--st-prog);
  box-shadow: 0 0 0 2px var(--bg-panel);
}
.sb-me-btn:hover .sb-me-dot,
.sb-me-btn.is-open .sb-me-dot {
  box-shadow: 0 0 0 2px var(--sidebar-hover);
}
/* The row's --accent-soft is translucent: laid over the panel, the ring is its exact
   composite. (The first shadow paints on top.) */
.sb-me-btn.is-on .sb-me-dot {
  box-shadow: 0 0 0 2px var(--accent-soft), 0 0 0 2px var(--bg-panel);
}
.sb-me-name {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sb-me-caret {
  flex: none;
  color: var(--text-faint);
  transform: rotate(180deg);      /* points up: the menu opens upward */
  transition: transform 0.16s;
}
.sb-me-btn.is-open .sb-me-caret {
  transform: none;
}

/* ---- account menu ---------------------------------------------------------------
   The .stmenu recipe (--line-2 edge, --shadow, 6px pad), lifted one step in dark,
   where a panel-coloured popover over the panel would barely read. Anchored to
   .sb-me and 6px past it on each side: 232px in the 248px sidebar and 248px in the
   264px drawer, so it never leaves either. That width cannot hold the workspace plate
   BESIDE "Signed in as" ("Company administrator" alone is about 118px), so the plate
   takes its own row under it in the header. No ancestor up to .sidebar clips it (the
   scrolling nav is a sibling). */
.sb-menu {
  position: absolute;
  left: -6px;
  right: -6px;
  bottom: calc(100% + 8px);
  z-index: 5;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: 12px;
  box-shadow: var(--shadow);
  transform-origin: 50% 100%;
  animation: sb-menu-in 0.14s ease-out;
}
.app[data-theme="dark"] .sb-menu {
  background: var(--surface-2);
}
@keyframes sb-menu-in {
  from {
    opacity: 0;
    transform: translateY(4px) scale(0.98);
  }
}
@media (prefers-reduced-motion: reduce) {
  .sb-menu { animation: none; }
  .sb-me-caret { transition: none; }
}
.sb-menu-head {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 6px 4px 8px;
}
.sb-menu-who {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 0 6px;                 /* the item text column; the plate below runs wider */
}
.sb-menu-cap {
  font: 500 11px/1.2 var(--font);
  color: var(--text-faint);       /* 6.3:1 on dark --surface-2, 4.7:1 on light white */
}
.sb-menu-who strong {
  font: 600 13px/1.2 var(--font);
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sb-menu-sep {
  height: 1px;
  margin: 5px -6px;
  background: var(--line);
}
/* --line is about 1.1:1 on dark --surface-2. */
.app[data-theme="dark"] .sb-menu-sep {
  background: var(--line-2);
}
.sb-menu-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  height: 34px;
  padding: 0 10px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-dim);         /* 12.7:1 dark, 5.05:1 light */
  font: 500 13px/1 var(--font);
  text-align: left;
  cursor: pointer;
}
.sb-menu-item .ic {
  flex: none;
  color: var(--text-faint);
}
.sb-menu-item:hover,
.sb-menu-item:focus-visible {
  background: var(--surface-2);
  color: var(--text);
}
.app[data-theme="dark"] .sb-menu-item:hover,
.app[data-theme="dark"] .sb-menu-item:focus-visible {
  background: var(--surface-3);
}
.sb-menu-item:hover .ic,
.sb-menu-item:focus-visible .ic {
  color: currentColor;
}
.sb-menu-item:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}
.sb-menu-item.is-on {
  color: var(--text);
}
.sb-menu-item.is-on .ic {
  color: var(--focus);
}
.sb-menu-txt {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The avatar's dot, repeated on the item it points to. */
.sb-menu-flag {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--st-prog);
}
/* Off is an outline and on is a --focus fill: in dark, --text-faint and --focus are
   near the same lilac, so two filled tracks would leave only the knob's position. */
.sb-switch {
  flex: none;
  position: relative;
  width: 28px;
  height: 16px;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1.5px var(--text-faint);
  transition: background 0.14s, box-shadow 0.14s;
}
.sb-switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-faint);
  transition: transform 0.14s, background 0.14s;
}
.sb-menu-item[aria-checked="true"] .sb-switch {
  background: var(--focus);
  box-shadow: none;
}
.sb-menu-item[aria-checked="true"] .sb-switch::after {
  transform: translateX(12px);
  background: var(--bg-panel);    /* 5.9:1 on dark --focus, 10.2:1 on light */
}
@media (prefers-reduced-motion: reduce) {
  .sb-switch, .sb-switch::after { transition: none; }
}

.main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.topbar {
  position: sticky;
  top: 0;
  z-index: 15;
  height: var(--topbar-h);
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 26px;
  background: color-mix(in oklab, var(--bg-panel) 82%, transparent);
  backdrop-filter: blur(12px);
}
.topbar-crumb {
  display: flex;
  align-items: center;
  gap: 8px;
  font: 13px/1 var(--font);
  color: var(--text-dim);
}
.topbar-crumb .ic {
  color: var(--accent);
}
.topbar-crumb span {
  font-size: 13px;
}
.topbar-crumb strong {
  color: var(--text);
  font-weight: 600;
}
.crumb-sep {
  color: var(--text-faint);
}
.topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.sync {
  display: flex;
  align-items: center;
  gap: 7px;
  font: 11.5px/1 var(--font);
  color: var(--text-dim);
  padding: 6px 11px;
  border-radius: 20px;
  border: 1px solid var(--line);
  background: var(--surface);
  white-space: nowrap;
}
.sync-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 0 var(--ok);
  animation: pulse 2.4s infinite;
}
@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 color-mix(in oklab, var(--ok) 60%, transparent);
  }
  70% {
    box-shadow: 0 0 0 6px transparent;
  }
  100% {
    box-shadow: 0 0 0 0 transparent;
  }
}
.iconbtn {
  position: relative;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-dim);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: 0.14s;
}
.iconbtn:hover {
  color: var(--text);
  border-color: var(--line-2);
  background: var(--surface-2);
}
/* Row-level destructive action (delete a user / a company on the Admin page). Stays
   quiet until hovered: these sit one per row in a dense table, and a row of red
   buttons reads as a table full of errors rather than a table with an action. */
.iconbtn-danger {
  width: 28px;
  height: 28px;
  border-radius: 7px;
  border-color: transparent;
  background: transparent;
}
.iconbtn-danger:hover {
  color: #ffffff;
  background: var(--sev-critical);
  border-color: var(--sev-critical);
}

.iconbtn-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  font: 600 9px/1 var(--font-mono);
  background: var(--sev-critical);
  color: #fff;
  padding: 2px 4px;
  border-radius: 10px;
  min-width: 14px;
  text-align: center;
}
.content {
  padding: 26px;
  max-width: 1320px;
  width: 100%;
  margin: 0 auto;
}

/* Hamburger toggle + drawer scrim — hidden on desktop, enabled in the
   responsive block below once the sidebar collapses into an off-canvas drawer. */
.nav-toggle {
  display: none;
  width: 38px;
  height: 38px;
  margin-right: 4px;
  flex: none;
  border-radius: 9px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text-dim);
  place-items: center;
  cursor: pointer;
  transition: 0.14s;
}
.nav-toggle:hover {
  color: var(--text);
  border-color: var(--line-2);
  background: var(--surface-2);
}
.nav-scrim {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 25;
  background: color-mix(in oklab, #000 50%, transparent);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s ease;
}

/* ============================================================ logo */
.logo {
  display: inline-flex;
  align-items: center;
  gap: 9px;
}
.logo-mark {
  display: grid;
  place-items: center;
  filter: drop-shadow(
    0 0 8px color-mix(in oklab, var(--accent) 35%, transparent)
  );
}
.logo-word {
  font: 700 17px/1 var(--font-display);
  letter-spacing: -0.01em;
  color: var(--text);
}
.logo-word-2 {
  color: var(--accent);
}
/* In dark mode the base --accent (#49299a) sits invisibly on the navy sidebar
   (--bg-panel #00003b). Use the lighter on-brand variant for readable contrast. */
.app[data-theme="dark"] .logo-word-2 {
  color: var(--accent-3);
}

/* ============================================================ buttons */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: 600 13px/1 var(--font);
  padding: 9px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--btn-secondary);
  background: var(--btn-secondary);
  color: var(--btn-secondary-ink);
  cursor: pointer;
  transition: 0.14s;
  white-space: nowrap;
}
.btn:hover {
  filter: brightness(1.12);
}
.btn-accent {
  background: var(--btn-primary);
  border-color: var(--btn-primary);
  color: var(--btn-primary-ink);
}
.app[data-theme="light"] .btn-accent {
  color: var(--btn-primary-ink);
}
.btn-accent:hover {
  filter: brightness(1.08);
  background: var(--btn-primary);
}
.btn-ghost {
  background: var(--btn-secondary);
  border-color: var(--btn-secondary);
  color: var(--btn-secondary-ink);
}
.btn-ghost:hover {
  filter: brightness(1.12);
  background: var(--btn-secondary);
  border-color: var(--btn-secondary);
}
.btn-block {
  width: 100%;
  justify-content: center;
}
.btn-sm {
  padding: 7px 11px;
  font-size: 12px;
}
.btn-spin {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 2px solid color-mix(in oklab, var(--accent-ink) 30%, transparent);
  border-top-color: var(--accent-ink);
  animation: spin 0.7s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ============================================================ 404 */
/* An address that names no page. Rendered inside the shell (sidebar, topbar) so the
   way out is the same nav as everywhere else; the failed address is quoted as typed. */
.notfound {
  align-items: center;
  justify-content: center;
  min-height: 60vh;
  text-align: center;
  gap: 10px;
}
.notfound-code {
  font: 700 64px/1 var(--font-display, var(--font));
  letter-spacing: -0.04em;
  color: var(--accent);
  opacity: 0.85;
}
.app[data-theme="dark"] .notfound-code { color: var(--accent-3); }
.notfound-title {
  margin: 0;
  font: 600 20px/1.25 var(--font);
  color: var(--text);
}
.notfound-sub {
  margin: 0;
  max-width: 52ch;
  font: 14px/1.6 var(--font);
  color: var(--text-dim);
}
.notfound-path {
  display: inline-block;
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--bg-panel);
  color: var(--text);
  font-size: 12.5px;
  word-break: break-all;
}
.notfound-acts {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 8px;
}
