/* Vendored verbatim from @fikar/design@1.4.0 by scripts/fetch-design.sh; do not edit. */
/* @fikar/design — Ink component layer. Ported from docs/mockups/fikar.css +
   fikar-components.html (Phase 2 plan §A1); those files now import THIS.
   Requires tokens.css (roles + --fk-* ramp) to be imported first.

   Import order matters: in Tailwind apps, @import this at the TOP of your global
   stylesheet, before the @tailwind directives, so Tailwind utilities win ties by
   source order. Deliberately NOT wrapped in `@layer components` — Tailwind v3's
   PostCSS plugin intercepts that at-rule in any file it processes and errors
   without a matching @tailwind directive.

   Conventions: ramp refs use --fk-*; role refs (--primary, --warning, --destructive…)
   stay unprefixed so .dark overrides apply. Status tints (hsl(var(--warning)/.13))
   MUST stay on role vars. Rules needing an explicit dark variant double-key
   `.dark` and `[data-theme='dark']` like logo.css. */

/* ── utilities ── */
.mono { font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; }
.muted { color: hsl(var(--muted-foreground)); }

/* ── buttons ── */
.btn { height: 36px; padding: 0 14px; border-radius: var(--r-sm); font-size: 14px; font-weight: 500; font-family: inherit; display: inline-flex;
  align-items: center; justify-content: center; gap: 7px; cursor: pointer; border: 1px solid transparent; white-space: nowrap;
  transition: background-color var(--fast) var(--ease), border-color var(--fast) var(--ease), transform var(--fast) var(--ease); }
.btn:active { transform: translateY(1px); }
.btn svg { width: 16px; height: 16px; }
.btn-primary { background: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
.btn-primary:hover { background: hsl(var(--fk-blue700)); }
.btn-ghost { background: hsl(var(--card)); border-color: hsl(var(--border)); color: hsl(var(--foreground)); }
.btn-ghost:hover { background: hsl(var(--muted)); }
.btn-secondary { background: hsl(var(--muted)); color: hsl(var(--foreground)); border-color: hsl(var(--border)); }
.btn-secondary:hover { background: hsl(var(--fk-n200)); }
.btn-destructive { background: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); }
.btn-link { background: transparent; color: hsl(var(--fk-blue700)); text-decoration: underline; text-underline-offset: 2px; padding: 0 4px; }
.dark .btn-link, [data-theme='dark'] .btn-link { color: hsl(var(--fk-blue400)); }
.btn-lg { height: 44px; padding: 0 22px; font-size: 15px; }
.btn-block { width: 100%; }
.iconbtn { width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid hsl(var(--border));
  border-radius: var(--r-sm); background: hsl(var(--card)); color: hsl(var(--muted-foreground)); cursor: pointer; transition: .15s var(--ease); }
.iconbtn:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.iconbtn svg { width: 17px; height: 17px; }

/* ── inputs ── */
.field { display: block; }
.field label { font-size: 13px; font-weight: 500; display: block; margin-bottom: 6px; }
.inp { width: 100%; height: 38px; padding: 0 12px; border: 1px solid hsl(var(--input)); border-radius: var(--r-sm); background: hsl(var(--card));
  color: inherit; font-size: 14px; font-family: inherit; outline: none; transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease); }
.inp::placeholder { color: hsl(var(--fk-n400)); }
.inp:focus { border-color: hsl(var(--ring)); box-shadow: 0 0 0 3px hsl(var(--ring) / .18); }
.hint { font-size: 12px; color: hsl(var(--muted-foreground)); margin-top: 6px; }

/* ── card ── */
.card { background: hsl(var(--card)); border: 1px solid hsl(var(--border)); border-radius: var(--r-md); }
.panel-head { display: flex; align-items: center; gap: 12px; padding: 16px 18px; border-bottom: 1px solid hsl(var(--border)); }
.panel-head h2 { font-size: 15px; font-weight: 600; }
.count { font-size: 12px; color: hsl(var(--muted-foreground)); background: hsl(var(--muted)); padding: 2px 8px; border-radius: 999px; }

/* ── badges + pills ── */
.badge { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: var(--r-sm); display: inline-flex; align-items: center; gap: 5px; }
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
/* theme-neutral: light resolves to blue700-on-blue50, dark to the .dark accent pair */
.b-decision { color: hsl(var(--accent-foreground)); background: hsl(var(--accent)); }
.b-risk { color: hsl(var(--warning)); background: hsl(var(--warning) / .13); }
.b-action { color: hsl(var(--success)); background: hsl(var(--success) / .13); }
.b-incident { color: hsl(var(--destructive)); background: hsl(var(--destructive) / .12); }
/* entity types (KAN-340): the same colours as chat's Sources graph, from the --entity-* roles so dark is automatic */
.b-person { color: hsl(var(--entity-person)); background: hsl(var(--entity-person) / .13); }
.b-organization { color: hsl(var(--entity-organization)); background: hsl(var(--entity-organization) / .13); }
.b-event { color: hsl(var(--entity-event)); background: hsl(var(--entity-event) / .13); }
.b-geo { color: hsl(var(--entity-geo)); background: hsl(var(--entity-geo) / .13); }
/* status (KAN-342): a dot in the status colour and the word in the text colour. The signal badges above put the role colour on the text as well, which is 2.5:1 for
   --warning in light, so a status word is plain text and only the dot carries the role. .badge::before is the dot, and each status sets its colour. */
.b-success, .b-warning, .b-destructive, .b-muted { padding: 0; gap: 6px; background: none; color: hsl(var(--foreground)); font-size: 12px; font-weight: 500; }
.b-success::before, .b-warning::before, .b-destructive::before, .b-muted::before { width: 8px; height: 8px; }
.b-success::before { background: hsl(var(--success)); }
.b-warning::before { background: hsl(var(--warning)); }
.b-destructive::before { background: hsl(var(--destructive)); }
.b-muted::before { background: hsl(var(--muted-foreground)); }
.pill { font-size: 12px; font-weight: 500; padding: 3px 10px; border-radius: 999px; white-space: nowrap; }
.p-open { color: hsl(var(--warning)); background: hsl(var(--warning) / .13); }
.p-acted, .p-active { color: hsl(var(--success)); background: hsl(var(--success) / .13); }
.p-tracked, .p-pending { color: hsl(var(--muted-foreground)); background: hsl(var(--muted)); }
/* filter chip (KAN-340): a pill that is a button, on or off by aria-pressed. The border keeps the off state visible on a card, and the padding is 1px less on
   each side so the chip is as tall as a plain pill. */
.p-toggle { padding: 2px 9px; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); color: hsl(var(--muted-foreground)); font-family: inherit; cursor: pointer;
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease), border-color var(--fast) var(--ease); }
.p-toggle:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.p-toggle[aria-pressed="true"] { background: hsl(var(--accent)); border-color: hsl(var(--accent-foreground) / .35); color: hsl(var(--accent-foreground)); }
.p-toggle:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }

/* ── avatar ── */
.avatar { width: 28px; height: 28px; border-radius: 999px; background: hsl(var(--fk-blue600)); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; flex: 0 0 auto; }
.av { width: 24px; height: 24px; border-radius: 999px; background: hsl(var(--muted)); color: hsl(var(--fk-n600)); display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 600; }
.dark .av, [data-theme='dark'] .av { color: hsl(var(--fk-n300)); }

/* ── switch ── */
.switch { width: 40px; height: 23px; border-radius: 999px; background: hsl(var(--fk-n300)); position: relative; cursor: pointer; border: 0; flex: 0 0 auto; transition: background-color var(--fast) var(--ease); }
.switch.on { background: hsl(var(--primary)); }
.switch::after { content: ""; position: absolute; top: 2px; left: 2px; width: 19px; height: 19px; border-radius: 999px; background: #fff; transition: left var(--fast) var(--ease); }
.switch.on::after { left: 19px; }

/* ── table (opt-in: class-scoped so it never restyles a host app's tables) ── */
.fk-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.fk-table thead th { text-align: left; font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: hsl(var(--muted-foreground)); padding: 11px 18px; border-bottom: 1px solid hsl(var(--border)); }
.fk-table thead th.num, .fk-table td.num { text-align: right; }
.fk-table tbody td { padding: 13px 18px; border-bottom: 1px solid hsl(var(--border)); vertical-align: middle; }
.fk-table tbody tr:last-child td { border-bottom: 0; }
.fk-table tbody tr { transition: background-color var(--fast) var(--ease); }
.fk-table tbody tr:hover { background: hsl(var(--muted) / .5); }
.who { display: flex; align-items: center; gap: 8px; }

/* ── app shell: sidebar family ── */
.shell { display: grid; grid-template-columns: 248px 1fr; height: 100vh; }
.sidebar { background: hsl(var(--card)); border-right: 1px solid hsl(var(--border)); display: flex; flex-direction: column; padding: 20px 14px; }
.brand { display: flex; align-items: center; gap: 10px; padding: 6px 8px 18px; }
.brand .word { font-weight: 600; font-size: 18px; letter-spacing: -.01em; }
.brand img { height: 18px; width: auto; display: block; }
nav.side { display: flex; flex-direction: column; gap: 2px; }
.nav-item { display: flex; align-items: center; gap: 11px; padding: 8px 10px; border-radius: var(--r-sm); color: hsl(var(--muted-foreground));
  font-size: 14px; font-weight: 500; cursor: pointer; border: 0; background: none; font-family: inherit; text-align: left; width: 100%; text-decoration: none;
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease); }
.nav-item:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.nav-item.active { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.nav-item svg { width: 18px; height: 18px; flex: 0 0 auto; }
.side-foot { margin-top: auto; display: flex; align-items: center; gap: 10px; padding: 8px; border-top: 1px solid hsl(var(--border)); }
.side-foot .meta > b { font-size: 13px; font-weight: 600; }
.side-foot .meta > span { font-size: 11px; color: hsl(var(--muted-foreground)); }
.main { display: flex; flex-direction: column; overflow: hidden; }
.topbar { display: flex; align-items: center; gap: 14px; padding: 18px 28px; border-bottom: 1px solid hsl(var(--border)); }
.topbar h1 { font-size: 20px; font-weight: 600; letter-spacing: -.015em; }
.topbar .sub { font-size: 13px; color: hsl(var(--muted-foreground)); }
.grow { margin-left: auto; }
.content { padding: 24px 28px; overflow: auto; }
.search { position: relative; width: 280px; }
.search svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: hsl(var(--muted-foreground)); }
.search .inp { padding-left: 34px; height: 36px; }

/* ── app shell: responsive (KAN-292) ──
   The shell owns its own collapse. Below 1024px the sidebar becomes an
   off-canvas drawer, opened by toggling .nav-open on .shell (behaviour is
   the host's: click, Escape, scrim click, focus return), and .shell-topbar
   is a sticky bar with the menu button, the brand and the right-hand
   cluster (launcher, avatar). Above 1024px the sidebar shows as before and
   the top bar keeps only the cluster. ── */
.shell-topbar { position: sticky; top: 0; z-index: 30; display: flex; align-items: center; gap: 10px; min-height: 56px; padding: 0 16px;
  border-bottom: 1px solid hsl(var(--border)); background: hsl(var(--card)); }
.shell-topbar .brand { padding: 0; }
.shell-topbar .cluster { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.shell-menu { width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: var(--r-sm);
  background: none; color: hsl(var(--muted-foreground)); cursor: pointer; }
.shell-menu:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.shell-menu svg { width: 20px; height: 20px; }
.shell-scrim { display: none; position: fixed; inset: 0; z-index: 40; background: hsl(var(--foreground) / .35); }
.shell-close { display: none; }
@media (min-width: 1024px) {
  .shell-topbar .shell-menu, .shell-topbar .brand { display: none; }
  .shell.with-topbar { grid-template-rows: auto 1fr; }
  .shell.with-topbar .sidebar { grid-row: 1 / 3; }
  .shell.with-topbar .shell-topbar { grid-column: 2; }
}
@media (max-width: 1023px) {
  .shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; height: auto; min-height: 100vh; min-height: 100dvh; }
  .sidebar { position: fixed; top: 0; bottom: 0; left: 0; z-index: 50; width: min(280px, 85vw); border-right: 1px solid hsl(var(--border));
    transform: translateX(-100%); transition: transform var(--fast) var(--ease); overflow-y: auto; }
  .shell.nav-open .sidebar { transform: none; box-shadow: var(--shadow-md); }
  .shell.nav-open .shell-scrim { display: block; }
  .shell-close { display: inline-flex; margin-left: auto; }
  .main { overflow: visible; }
  .content { padding: 16px; }
  .topbar { padding: 14px 16px; }
  nav.side { flex-direction: column; }
}

/* ── app shell: closed drawer leaves the tab order (KAN-322) ──
   translateX moves the drawer out of sight but not out of the tab order, so Tab
   from the top of a phone-width page landed on an invisible close button. Visibility
   removes focus and the accessibility tree while it is closed. The delay on the way
   out lets the slide finish first, and opening is immediate so the first focus target
   is already focusable. Kept as its own block so the 0.4.0 rules above stay verbatim. ── */
@media (max-width: 1023px) {
  .sidebar { visibility: hidden; transition: transform var(--fast) var(--ease), visibility 0s linear var(--fast); }
  .shell.nav-open .sidebar { visibility: visible; transition-delay: 0s; }
}

/* ── app shell: desktop collapse (KAN-313) ──
   Opt-in and desktop-only. Nothing changes unless .is-collapsed is on .shell,
   and every collapsed rule sits inside the min-width query, so a 0.4.0 surface
   renders as before and the phone drawer never sees the state. Labels are
   clipped, not display:none, so they stay in the accessibility tree and give
   the host's tooltip its text. The collapse control is the last child of the
   sidebar, so it holds the same place open or collapsed. ── */
.shell-collapse { display: none; }
@media (min-width: 1024px) {
  .shell-collapse { display: flex; align-items: center; gap: 11px; width: 100%; margin-top: 8px; padding: 8px 10px; border: 0; border-radius: var(--r-sm);
    background: none; color: hsl(var(--muted-foreground)); font-family: inherit; font-size: 14px; font-weight: 500; text-align: left; cursor: pointer;
    transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease); }
  .shell-collapse:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
  .shell-collapse svg { width: 18px; height: 18px; flex: 0 0 auto; }
  .shell.is-collapsed { grid-template-columns: 72px 1fr; }
  .shell.is-collapsed .nav-item, .shell.is-collapsed .shell-collapse { position: relative; justify-content: center; padding-left: 0; padding-right: 0; }
  .shell.is-collapsed .nav-label, .shell.is-collapsed .brand .word, .shell.is-collapsed .side-foot .meta {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
  .shell.is-collapsed .sidebar .brand, .shell.is-collapsed .side-foot { justify-content: center; padding-left: 0; padding-right: 0; }
  .shell.is-collapsed .shell-collapse svg { transform: rotate(180deg); }
}

/* ── app shell: theme-aware logo (KAN-322) ──
   A brand holds both variants and shows one:
     <img class="logo-light" src=".../fikar-logo.svg" alt="">
     <img class="logo-dark" src=".../fikar-logo-reversed.svg" alt="">
   Each selector has two classes, which outranks `.brand img` (one class, one type), so this works
   without touching that 0.4.0 rule; a utility such as Tailwind's `hidden` never could. Works on an
   inline <svg> too. Dark follows the same two switches the tokens and logo.css use, the `.dark`
   class and [data-theme='dark'] on any ancestor. There is no prefers-color-scheme rule because the
   tokens have none: a host that offers a "system" choice resolves it to one of those two switches. ── */
.brand .logo-dark { display: none; }
.brand svg.logo-light, .brand svg.logo-dark { height: 18px; width: auto; }
.dark .brand .logo-light, [data-theme='dark'] .brand .logo-light { display: none; }
.dark .brand .logo-dark, [data-theme='dark'] .brand .logo-dark { display: block; }

/* ── app shell: top app bar ── */
.appbar { display: flex; align-items: center; gap: 24px; height: 58px; padding: 0 28px; border-bottom: 1px solid hsl(var(--border)); background: hsl(var(--card)); }
.appbar nav { display: flex; gap: 4px; margin-left: 8px; }
.appbar nav a { padding: 7px 12px; border-radius: var(--r-sm); font-size: 14px; font-weight: 500; color: hsl(var(--muted-foreground)); text-decoration: none; }
.appbar nav a.active { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }

/* ── app launcher (KAN-285): the grid button + panel every app renders ──
   Markup and behaviour live in the host (see README "App launcher recipe");
   this layer only styles it, so React apps can wrap their own popover and
   plain-JS hosts can toggle .open. Hidden-not-disabled: an app the person
   cannot use is simply not in the list. ── */
.launcher { position: relative; display: inline-flex; }
.launcher-btn { width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border: 0;
  border-radius: var(--r-sm); background: none; color: hsl(var(--muted-foreground)); cursor: pointer;
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease); }
.launcher-btn:hover, .launcher-btn[aria-expanded="true"] { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.launcher-btn svg { width: 18px; height: 18px; }
.launcher-panel { position: absolute; top: calc(100% + 6px); right: 0; z-index: 40; min-width: 260px; padding: 6px;
  border: 1px solid hsl(var(--border)); border-radius: var(--r-md); background: hsl(var(--popover)); color: hsl(var(--popover-foreground));
  box-shadow: var(--shadow-md); }
.launcher:not(.open) > .launcher-panel { display: none; }
.launcher-item { display: flex; flex-direction: column; gap: 1px; padding: 8px 10px; border-radius: var(--r-sm);
  color: hsl(var(--foreground)); text-decoration: none; font-size: 14px; font-weight: 500; }
.launcher-item:hover, .launcher-item:focus-visible { background: hsl(var(--muted)); outline: none; }
.launcher-item.current { background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.launcher-item .hint { margin: 0; font-size: 12px; font-weight: 400; color: hsl(var(--muted-foreground)); }
.launcher-item.current .hint { color: inherit; opacity: .8; }
/* Keyboard focus (KAN-331). The button had only the browser's own ring, and a focused current row showed nothing because its accent
   background wins over the focus background. The ring is the one the user menu rows use. It styles a state the browser already renders on the
   button and one that showed no mark on the row, so it is the one change here to a rule that existed before 1.2.0. */
.launcher-btn:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.launcher-item[aria-current]:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }
/* The launcher panel on a phone (KAN-331). The panel is right aligned to its button, and first in the cluster that button is 92px from the viewport
   edge (16px of bar padding, the avatar, the theme button and three gaps of 4px). The panel is about 274px wide, so below 366px its left edge left the screen.
   It gives way from there and keeps 16px on its own side, as the user menu panel does. The panel's 6px padding and 1px border on each side (14px in all)
   come off the content width, so the limit is 108px plus 14px. The user menu and the theme menu panels have their own widths. */
.cluster > .launcher:first-child > .launcher-panel:not(.user-menu-panel):not(.theme-menu-panel) { min-width: min(260px, calc(100vw - 122px)); max-width: calc(100vw - 122px); }

/* ── user menu (KAN-327): the account avatar and its panel, last in the top bar cluster ──
   The root is a .launcher and the panel a .launcher-panel, so it opens the same way (.open on the root) and its right
   edge sits on the trigger's right edge. The initials or picture reuse .avatar, sized to 32px here only, so .avatar
   itself is unchanged. Rows are .launcher-item plus .user-menu-item, which adds the resets a <button> row needs
   (Sign out by handler) and a focus ring. The names are new on purpose: a host that still styles .avatar-btn,
   .menu-id or .menu-sep locally is not touched by this layer. Markup and behaviour: README "User menu". ── */
.user-menu-btn { display: inline-flex; width: 32px; height: 32px; padding: 0; border: 0; border-radius: 999px; background: none; cursor: pointer; }
.user-menu-btn .avatar { width: 32px; height: 32px; font-size: 13px; object-fit: cover; transition: box-shadow var(--fast) var(--ease); }
.user-menu-btn:hover .avatar, .user-menu-btn[aria-expanded="true"] .avatar { box-shadow: 0 0 0 3px hsl(var(--ring) / .18); }
.user-menu-btn:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
/* Right aligned to the trigger, so on a phone the panel's left edge is the risk. The bar keeps 16px on each side, so the panel may be
   32px narrower than the viewport, and the panel's 6px padding and 1px border on each side (14px in all) come off the content width. */
.user-menu-panel { min-width: min(260px, calc(100vw - 46px)); max-width: calc(100vw - 46px); }
.user-menu-id { display: flex; flex-direction: column; gap: 1px; padding: 8px 10px; min-width: 0; overflow-wrap: anywhere; }
.user-menu-name { font-size: 14px; font-weight: 600; color: hsl(var(--foreground)); }
.user-menu-email { font-size: 12px; color: hsl(var(--muted-foreground)); }
.user-menu-sep { height: 1px; margin: 4px 0; background: hsl(var(--border)); }
.user-menu-item:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }
button.user-menu-item { width: 100%; border: 0; background: none; font-family: inherit; text-align: left; cursor: pointer; }

/* ── theme menu (KAN-332): the sun or moon button and its panel of three choices, second in the top bar cluster ──
   The trigger is a .launcher-btn, so its size and hover come from there. Both icons are in the markup and the theme on the root picks
   one, with the same two switches the logo rules use (.dark or [data-theme='dark']). That makes the right icon show on first paint with no script.
   The tokens have no prefers-color-scheme rule, so a host that leaves "system" to CSS resolves it to one of the two switches itself.
   Rows are .launcher-item plus .theme-menu-item, with the resets a <button> row needs, and the chosen one has .current.
   Markup and behaviour: README "Theme menu". ── */
.theme-menu-btn .theme-icon-moon { display: none; }
.dark .theme-menu-btn .theme-icon-sun, [data-theme='dark'] .theme-menu-btn .theme-icon-sun { display: none; }
.dark .theme-menu-btn .theme-icon-moon, [data-theme='dark'] .theme-menu-btn .theme-icon-moon { display: block; }
.theme-menu-panel { min-width: 160px; }
button.theme-menu-item { width: 100%; border: 0; background: none; font-family: inherit; text-align: left; cursor: pointer; }
.theme-menu-item:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }

/* ── metrics row ── */
.metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; }
.metric { padding: 16px 18px; }
.metric .label { font-size: 12px; color: hsl(var(--muted-foreground)); font-weight: 500; }
.metric .val { font-size: 28px; font-weight: 600; letter-spacing: -.02em; margin-top: 6px; font-variant-numeric: tabular-nums; }
.delta { font-size: 12px; font-weight: 500; margin-top: 4px; }
.delta.up { color: hsl(var(--success)); }
.delta.down { color: hsl(var(--destructive)); }
.delta.flat { color: hsl(var(--muted-foreground)); }

/* ── centered layouts ── */
.center { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.page-h { margin-bottom: 18px; }
.page-h h2 { font-size: 16px; font-weight: 600; }
.page-h p { font-size: 13px; color: hsl(var(--muted-foreground)); margin-top: 2px; }

/* ── empty state: the mark as a quiet illustration + two-beat voice ── */
.empty { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px; padding: 44px 24px; }
.empty .em-mark { width: 60px; color: hsl(var(--muted-foreground)); opacity: .55; margin-bottom: 10px; line-height: 0; }
.empty .em-mark svg { width: 100%; height: auto; }
.empty h3 { font-size: 16px; font-weight: 600; }
.empty p { font-size: 14px; color: hsl(var(--muted-foreground)); max-width: 340px; line-height: 1.5; }
.empty .btn { margin-top: 14px; }

/* ── brand loader: the C draws in, the dots cascade out, then it reverses
   ("…thinking", ping-pong). Needs the INLINE SVG from brand/fikar-loader.svg —
   an <img> can't be animated by these rules. currentColor, defaults to primary. ── */
.fikar-loader { display: inline-block; width: 64px; line-height: 0; color: hsl(var(--primary)); }
.fikar-loader.xs { width: 18px; }
.fikar-loader.sm { width: 26px; }
.fikar-loader.lg { width: 92px; }
.btn .fikar-loader { color: inherit; }
.fikar-loader svg { width: 100%; height: auto; overflow: visible; }
/* arc easing is a sanctioned brand-motion exception (not --ease): the drawn-in signature */
.fikar-loader .fkl-arc { stroke-dasharray: 100; animation: fkl-draw 1.54s cubic-bezier(.4, 0, .2, 1) infinite alternate; }
.fikar-loader .fkl-d { animation: fkl-pop1 1.54s cubic-bezier(.2, 0, 0, 1) infinite alternate; }
.fikar-loader .fkl-d2 { animation: fkl-pop2 1.54s cubic-bezier(.2, 0, 0, 1) infinite alternate; }
.fikar-loader .fkl-d3 { animation: fkl-pop3 1.54s cubic-bezier(.2, 0, 0, 1) infinite alternate; }
@keyframes fkl-draw { 0% { stroke-dashoffset: 100; } 42%, 100% { stroke-dashoffset: 0; } }
@keyframes fkl-pop1 { 0%, 45% { r: 0; opacity: 0; } 60%, 100% { r: 82px; opacity: 1; } }
@keyframes fkl-pop2 { 0%, 60% { r: 0; opacity: 0; } 75%, 100% { r: 69px; opacity: 1; } }
@keyframes fkl-pop3 { 0%, 82% { r: 0; opacity: 0; } 96%, 100% { r: 61px; opacity: 1; } }

/* full-screen brand splash — fades out via CSS so it can never block the page */
.fikar-splash { position: fixed; inset: 0; z-index: 500; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  background: hsl(var(--background)); animation: fkl-splashout .5s var(--ease) 1.3s forwards; }
.fikar-splash .lbl { font-size: 13px; color: hsl(var(--muted-foreground)); letter-spacing: .01em; }
@keyframes fkl-splashout { to { opacity: 0; visibility: hidden; } }

/* scoped reduced-motion: the loader freezes to the fully drawn state */
@media (prefers-reduced-motion: reduce) {
  .fikar-loader .fkl-arc, .fikar-loader .fkl-d, .fikar-loader .fkl-d2, .fikar-loader .fkl-d3 { animation: none; }
  .fikar-loader .fkl-arc { stroke-dashoffset: 0; }
  .fikar-splash { animation-delay: .3s; }
}

/* ── app shell: opt-in pieces (KAN-315) ──
   Each one does nothing until the markup uses it, so a 0.6.0 surface renders as before.
   The rail rules sit inside the 1024px query like the collapse block, and clip the
   heading instead of removing it so it stays in the accessibility tree. ── */
.fk-tooltip { z-index: 60; max-width: 240px; padding: 6px 10px; border-radius: var(--r-sm); background: hsl(var(--foreground)); color: hsl(var(--background));
  font-size: 12px; font-weight: 500; line-height: 1.3; box-shadow: var(--shadow-md); pointer-events: none; }
.shell-topbar-start { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; }
.nav-heading { padding: 14px 10px 4px; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.nav-badge { margin-left: auto; padding: 1px 6px; border: 1px solid hsl(var(--border)); border-radius: 999px; font-size: 10px; font-weight: 500;
  letter-spacing: .04em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
/* Scroll gutter (KAN-326). The gutter is reserved so items keep one width whether or not the list scrolls, and
   the 4px padding gives a classic scrollbar a gap. The equal negative margin pulls the nav into the sidebar's own
   14px right padding, so with overlay scrollbars (which reserve nothing) items keep the width and place they had
   at 1.0.0. There are no spacing tokens yet, so 4px is a literal, kept equal in padding and margin. */
nav.side.is-scroll { flex: 1; min-height: 0; overflow-y: auto; scrollbar-gutter: stable; padding-right: 4px; margin-right: -4px; }
.sidebar:has(> nav.side.is-scroll) { min-height: 0; overflow: hidden; }
@media (min-width: 1024px) {
  /* The 72px rail has 44px of content width, too little to give a classic scrollbar a gutter and a gap. */
  .shell.is-collapsed nav.side.is-scroll { scrollbar-gutter: auto; padding-right: 0; margin-right: 0; }
  .shell.is-collapsed .nav-heading {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
  .shell.is-collapsed .nav-badge { display: none; }
}

/* ── app shell: rail utilities (KAN-322) ──
   .rail-hidden is for content the rail should not show, such as a list of recent items.
   It is clipped like .nav-label and also visibility:hidden, so it drops out of the tab order
   and the accessibility tree along with the picture. Plain clipping would leave any link or
   button inside it reachable by Tab, which puts focus on something nobody can see. The cost is
   that a screen reader no longer hears it in the rail. Text that must still be announced there
   belongs in .nav-label. It is fully visible open, and in the drawer.
   .rail-only is the opposite: shown only in the rail, and display:none otherwise so it takes no
   space. It hides itself outside the rail and sets no display value when collapsed, so
   it keeps whatever display the host gave it. Both hiding rules are scoped under .shell so a host's
   own single-class display rule cannot win the tie by coming later. ── */
@media (min-width: 1024px) {
  .shell.is-collapsed .rail-hidden {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; visibility: hidden; }
  .shell:not(.is-collapsed) .rail-only { display: none; }
}
@media (max-width: 1023px) {
  .shell .rail-only { display: none; }
}

/* ── app shell: back link (KAN-322) ──
   The item that leads out of the current surface, back to the app the person came from.
   Smaller and lighter than the items below it, with a rule between them. ── */
.nav-item-back { position: relative; margin-bottom: 10px; font-size: 13px; font-weight: 400; }
.nav-item-back svg { width: 16px; height: 16px; }
.nav-item-back::after { content: ""; position: absolute; left: 10px; right: 10px; bottom: -6px; height: 1px; background: hsl(var(--border)); }

/* ── app shell: grid children may shrink (KAN-322) ──
   A grid item's minimum width is its content, so one long nowrap title in the top bar widened the
   whole page past the viewport. min-width:0 lets the bar and the main area take the column width.
   The bar also clips horizontally, because content that no longer widens the column would
   otherwise still overflow it and make the page scroll sideways. Only the x axis is clipped, so the
   launcher and account menus still open downward out of the bar. The sidebar needs neither: its
   column has a fixed width. ── */
.shell-topbar, .main { min-width: 0; }
.shell-topbar { overflow-x: clip; }

/* ── app shell: sidebar without a foot (KAN-322) ──
   .side-foot carries margin-top:auto, which is what keeps the collapse control at the bottom. With no
   foot the control follows the nav directly, so it takes over that margin. Not needed below 1024px,
   where the control is hidden. ── */
@media (min-width: 1024px) {
  .sidebar > nav.side + .shell-collapse { margin-top: auto; }
}

/* ── app shell: no-sidebar mode (KAN-322) ──
   For an app with only a top bar. .no-sidebar on .shell makes a single column and shows the top bar
   brand at every width, since there is no sidebar to hold it. Cluster placement, the 56px bar and the
   sticky behaviour are the shell's own. The start slot sits between brand and cluster and gives way
   first: it shrinks below its content and scrolls sideways inside itself, without a visible scrollbar,
   so a row of links cannot make the bar taller or the page wider and every link stays reachable by
   touch, trackpad and Tab (a focused link scrolls into view, and scroll-padding makes a link that is only
   partly visible count as out of view). Setting overflow-x also clips the y axis,
   so the padding and matching negative margin leave room for a focus ring without moving anything. This block comes after the collapse rules so a stray .is-collapsed cannot bring
   the 72px column back. ── */
.shell.no-sidebar { grid-template-columns: minmax(0, 1fr); }
.shell.no-sidebar .shell-topbar { grid-column: 1; }
.shell.no-sidebar .shell-topbar .brand { display: flex; flex: none; }
.shell.no-sidebar .shell-topbar-start { overflow-x: auto; scrollbar-width: none; scroll-padding-inline: 48px; white-space: nowrap; padding: 4px; margin: -4px; }
.shell.no-sidebar .shell-topbar-start::-webkit-scrollbar { display: none; }

/* ── app shell: fill mode (KAN-322) ──
   Opt-in through .is-fill on .shell, for a page that scrolls inside itself, such as a conversation
   with a composer docked at the bottom. The shell is exactly one viewport tall at every width (vh
   first as the fallback for browsers without dvh), the main area does not overflow, and .content
   is a padless flex column that hands its height to the page. The second grid row is minmax(0, 1fr)
   because a plain 1fr row grows to fit its content, which would make the document scroll again.
   The drawer is untouched: it is position:fixed, so it does not depend on the shell's height.
   Meant for the shell with a top bar (.with-topbar), which every recipe uses. ── */
.shell.is-fill { height: 100vh; height: 100dvh; grid-template-rows: auto minmax(0, 1fr); }
.shell.is-fill .main { overflow: hidden; min-height: 0; }
.shell.is-fill .content { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 0; }

/* ── search box (KAN-340) ──
   Completes the 1.2.1 .search wrapper without touching its three rules: a leading icon (.search-icon), a clear button (.search-clear) and a
   trailing shortcut hint (.search-kbd) around the .inp, and two sizes. .search-bar fills the top bar's start slot up to 448px, and
   .search-panel fills a sidebar or panel with a 14px icon and 13px text. Both are 36px high, as the two hand made boxes already were.
   The clear button and the hint share the right edge, and CSS decides which one shows from :placeholder-shown, so no script keeps them in
   step and the input must have a placeholder: the hint shows while the box is empty, the clear button once there is text. The input reserves the
   room only when one of them is there, so a bare .search renders as it did in 1.2.1. The hint is hidden below 768px, where there is no
   keyboard to press it on. Markup and behaviour: README "Search box". ── */
.search-bar { width: 100%; max-width: 448px; }
.search-panel { width: 100%; }
.search .search-icon { pointer-events: none; }
.search-panel .search-icon { left: 10px; width: 14px; height: 14px; }
.search-panel .inp { padding-left: 30px; font-size: 13px; }
.search:has(> .search-clear, > .search-kbd) .inp { padding-right: 48px; }
/* The native clear control would sit under .search-clear and duplicate it. */
.search .inp::-webkit-search-cancel-button, .search .inp::-webkit-search-decoration { -webkit-appearance: none; appearance: none; display: none; }
.search-clear { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: 0; border-radius: var(--r-sm); background: none; color: hsl(var(--muted-foreground)); cursor: pointer;
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease); }
.search-clear:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.search-clear:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 0; }
/* .search svg positions every svg in the wrapper as the leading icon, so the clear button's own icon takes that back. Later, same specificity. */
.search-clear svg { position: static; transform: none; width: 14px; height: 14px; color: inherit; }
.search .inp:placeholder-shown ~ .search-clear { display: none; }
.search-kbd { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); display: inline-flex; align-items: center; gap: 2px; padding: 1px 6px;
  border: 1px solid hsl(var(--border)); border-radius: 4px; background: hsl(var(--muted) / .5); color: hsl(var(--muted-foreground));
  font: 10px/1.4 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace; pointer-events: none; }
.search .inp:not(:placeholder-shown) ~ .search-kbd { display: none; }
@media (max-width: 767px) {
  .search-kbd { display: none; }
}

/* ── search results (KAN-340) ──
   A listbox of typeahead results under a .search box: <div class="launcher-panel search-results" role="listbox">. The popover is the launcher panel, so its
   surface, border, shadow and stacking are shared, and .search is already the positioned parent. The panel spans the box instead of hanging from
   its right edge, and shows unless it carries `hidden`. Focus stays in the input; the row the page makes active is named by aria-activedescendant on the
   input and marked aria-selected="true", and that one attribute is what draws it. A row is a title, an optional entity badge and count on the same line, and an
   optional description under them. Markup and behaviour: README "Search results". ── */
.launcher-panel.search-results { left: 0; right: 0; min-width: 0; max-height: 360px; overflow-y: auto; }
.search-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 2px 8px; padding: 8px 10px; border-radius: var(--r-sm);
  color: hsl(var(--foreground)); font-size: 14px; cursor: pointer; }
.search-row:hover, .search-row[aria-selected="true"] { background: hsl(var(--muted) / .6); }
.search-row[aria-selected="true"] { box-shadow: inset 2px 0 0 hsl(var(--primary)); }
.search-row-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.search-row-meta { font-size: 12px; color: hsl(var(--muted-foreground)); font-variant-numeric: tabular-nums; }
.search-row-desc { grid-column: 1 / -1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: hsl(var(--muted-foreground)); }
.search-empty { padding: 14px 10px; text-align: center; font-size: 13px; color: hsl(var(--muted-foreground)); }

/* ── segmented control (KAN-340) ──
   A short set of exclusive choices side by side, such as Find or Ask: <div class="segmented" role="radiogroup" aria-label="..."> holding
   <button class="segmented-item" type="button" role="radio" aria-checked="...">. aria-checked is the state and the only style hook, and the page keeps exactly one
   "true". The chosen item is raised on the card colour over the muted track. Markup: README "Segmented control". ── */
.segmented { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid hsl(var(--border)); border-radius: var(--r-sm); background: hsl(var(--muted)); }
.segmented-item { height: 28px; padding: 0 12px; border: 0; border-radius: 4px; background: none; color: hsl(var(--foreground) / .72); font-family: inherit; font-size: 13px; font-weight: 500;
  white-space: nowrap; cursor: pointer; transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease); }
.segmented-item:hover { color: hsl(var(--foreground)); }
.segmented-item[aria-checked="true"] { background: hsl(var(--card)); color: hsl(var(--foreground)); box-shadow: var(--shadow-sm); }
.segmented-item:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }

/* ── detail panel (KAN-342) ──
   The surface that describes one thing next to a list or a canvas: a type line (a dot and a few words), a serif title, a description, then sections of
   rows and chips, and a close control. <aside class="detail-panel"> holds .detail-close, .detail-type (with .detail-dot), .detail-title, .detail-desc and
   any number of <section class="detail-section"> with a .detail-heading and .detail-row or .detail-chips inside. The panel is the surface only. Where it sits
   (over a canvas, beside a list, over the whole screen) is the page's, and it fills its container below 768px. The title is Charter, which loads only on a page
   that imports fonts.css. A row is a plain block, or an a or a button when the whole row is the target. Markup and behaviour: README "Detail panel". ── */
.detail-panel { position: relative; box-sizing: border-box; width: 340px; max-width: 100%; padding: 18px; overflow: auto; overflow-wrap: anywhere;
  background: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border)); border-radius: var(--r-lg); }
.detail-close { position: absolute; top: 10px; right: 12px; width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; padding: 0;
  border: 0; border-radius: var(--r-sm); background: none; color: hsl(var(--muted-foreground)); cursor: pointer;
  transition: background-color var(--fast) var(--ease), color var(--fast) var(--ease); }
.detail-close:hover { background: hsl(var(--muted)); color: hsl(var(--foreground)); }
.detail-close:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 0; }
.detail-close svg { width: 16px; height: 16px; }
.detail-type { display: flex; align-items: center; gap: 6px; margin: 0; padding-right: 32px; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.detail-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: hsl(var(--muted-foreground)); }
.detail-dot-person { background: hsl(var(--entity-person)); }
.detail-dot-organization { background: hsl(var(--entity-organization)); }
.detail-dot-event { background: hsl(var(--entity-event)); }
.detail-dot-geo { background: hsl(var(--entity-geo)); }
.detail-title { margin: 4px 0 10px; font-family: Charter, ui-serif, Georgia, Cambria, 'Times New Roman', serif; font-size: 22px; font-weight: 600; line-height: 1.25; }
.detail-desc { margin: 0 0 14px; font-size: 13px; line-height: 1.5; }
.detail-section { margin-top: 16px; }
.detail-heading { margin: 0 0 8px; font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.detail-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; box-sizing: border-box; width: 100%; padding: 8px 0; border: 0;
  border-top: 1px solid hsl(var(--border)); background: none; color: hsl(var(--foreground)); font-family: inherit; font-size: 13px; text-align: left; text-decoration: none; }
a.detail-row, button.detail-row { cursor: pointer; transition: color var(--fast) var(--ease); }
a.detail-row:hover, button.detail-row:hover { color: hsl(var(--accent-foreground)); }
a.detail-row:focus-visible, button.detail-row:focus-visible, .detail-row a:focus-visible, .detail-chip:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.detail-row a { flex: none; font-size: 12px; color: hsl(var(--accent-foreground)); text-decoration: none; }
.detail-row a:hover { text-decoration: underline; }
.detail-row-meta { font-size: 12px; color: hsl(var(--muted-foreground)); }
.detail-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.detail-chip { padding: 3px 9px; border: 1px solid hsl(var(--border)); border-radius: 999px; background: hsl(var(--background)); color: hsl(var(--foreground));
  font-family: inherit; font-size: 12px; text-decoration: none; }
a.detail-chip, button.detail-chip { cursor: pointer; transition: background-color var(--fast) var(--ease); }
a.detail-chip:hover, button.detail-chip:hover { background: hsl(var(--muted)); }
@media (max-width: 767px) {
  .detail-panel { width: 100%; }
}

/* ── list row and day separator (KAN-342) ──
   A row in a list of things (conversations, calls): a 13px title and a 12px muted line under it, and a .day-sep heading that groups rows by day.
   <a class="list-row"> or <button class="list-row" type="button"> holds .list-row-title and .list-row-meta. The row the page is on carries aria-current
   ("page" for a link, "true" for a button), and that one attribute is what draws it, as aria-selected does for .search-row. Both lines are cut with an
   ellipsis. The separator is a label and nothing more, and the page decides the groups. Markup and behaviour: README "List row and day separator". ── */
.list-row { display: block; box-sizing: border-box; width: 100%; padding: 8px; border: 0; border-radius: var(--r-md); background: none; color: hsl(var(--foreground));
  font-family: inherit; text-align: left; text-decoration: none; cursor: pointer; transition: background-color var(--fast) var(--ease); }
.list-row:hover { background: hsl(var(--muted) / .6); }
.list-row[aria-current="true"], .list-row[aria-current="page"] { background: hsl(var(--accent) / .7); }
.list-row:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: -2px; }
.list-row-title, .list-row-meta { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row-title { font-size: 13px; font-weight: 500; }
.list-row-meta { margin-top: 1px; font-size: 12px; color: hsl(var(--muted-foreground)); }
.day-sep { margin: 0; padding: 8px 8px 2px; font-size: 11px; font-weight: 400; color: hsl(var(--muted-foreground)); }

/* ── relative time (KAN-342) ──
   <time class="fk-time" datetime="2026-09-30T08:16:00Z" title="30 Sep, 08:16">4 h ago</time>: the moment as the shared short wording, the exact time in the title
   for a hover, and the machine value in datetime. It takes its size and colour from where it sits, so the only rules are that it never breaks across
   lines and that a column of them lines up. Wording and formatter: README "Relative time". ── */
.fk-time { white-space: nowrap; font-variant-numeric: tabular-nums; }
