/* =============================================================
   KESTRL · VIVID   —  the "bolder & vibrant" visual layer
   -------------------------------------------------------------
   An ADDITIVE layer loaded AFTER kestrl.css. It keeps the kestrel
   identity (cream · rufous · slate) but turns it up: an animated
   gradient-mesh backdrop, frosted-glass surfaces, glowing key
   numbers, gradient-filled charts, sparklines, vivid Dr/Cr, and a
   maximal-but-tasteful motion system (scroll reveal, magnetic
   press, tilt, chart draw-in). New furniture is prefixed `kv-`;
   existing `k-` components are enhanced in place. Everything is
   guarded by prefers-reduced-motion.
   ============================================================= */

/* ---------------------------------------------- new tokens -- */
:root {
  /* warm second accent → gradients read as rufous→coral→gold */
  --k-accent-2:  #d97b45;
  --k-slate-2:   #6f97bc;

  /* gradients */
  --k-grad-accent: linear-gradient(135deg, var(--k-accent), var(--k-accent-2));
  --k-grad-warm:   linear-gradient(120deg, var(--k-accent), var(--k-gold) 92%);
  --k-grad-slate:  linear-gradient(135deg, var(--k-slate), var(--k-slate-2));
  --k-grad-cool:   linear-gradient(120deg, var(--k-slate), var(--k-accent));
  --k-grad-ok:     linear-gradient(135deg, var(--k-ok), #4bbf8a);

  /* frosted glass */
  --k-glass:    color-mix(in srgb, var(--k-surface) 80%, transparent);
  --k-glass-2:  color-mix(in srgb, var(--k-surface) 66%, transparent);
  --k-glass-brd: color-mix(in srgb, #ffffff 55%, var(--k-border));
  --k-blur:     saturate(1.35) blur(14px);
  --k-hair:     inset 0 1px 0 color-mix(in srgb, #ffffff 60%, transparent);

  /* coloured glows (used on hover / key figures) */
  --k-glow-accent: 0 14px 42px -14px color-mix(in srgb, var(--k-accent) 60%, transparent);
  --k-glow-slate:  0 14px 42px -14px color-mix(in srgb, var(--k-slate)  55%, transparent);
  --k-glow-ok:     0 14px 42px -14px color-mix(in srgb, var(--k-ok)     55%, transparent);
  --k-glow-err:    0 14px 42px -14px color-mix(in srgb, var(--k-err)    55%, transparent);
  --k-glow-gold:   0 14px 42px -14px color-mix(in srgb, var(--k-gold)   55%, transparent);
  --k-text-glow: 0 0 1px transparent;

  /* mesh blob tints (subtle on the cream) */
  --k-mesh-1: color-mix(in srgb, var(--k-accent) 15%, transparent);
  --k-mesh-2: color-mix(in srgb, var(--k-slate)  14%, transparent);
  --k-mesh-3: color-mix(in srgb, var(--k-gold)   13%, transparent);
  --k-mesh-4: color-mix(in srgb, var(--k-ok)     10%, transparent);

  --k-spring-big: cubic-bezier(.34, 1.56, .44, 1);
}

html[data-theme="dark"] {
  --k-accent-2: #f0a074;
  --k-slate-2:  #a7c6e2;

  --k-glass:    color-mix(in srgb, var(--k-surface) 72%, transparent);
  --k-glass-2:  color-mix(in srgb, var(--k-surface) 58%, transparent);
  --k-glass-brd: color-mix(in srgb, #ffffff 12%, var(--k-border));
  --k-hair:     inset 0 1px 0 color-mix(in srgb, #ffffff 10%, transparent);

  /* glows read brighter on the night theme — this is where vivid pops */
  --k-glow-accent: 0 16px 48px -12px color-mix(in srgb, var(--k-accent) 70%, transparent);
  --k-glow-slate:  0 16px 48px -12px color-mix(in srgb, var(--k-slate)  60%, transparent);
  --k-glow-ok:     0 16px 48px -12px color-mix(in srgb, var(--k-ok)     60%, transparent);
  --k-glow-err:    0 16px 48px -12px color-mix(in srgb, var(--k-err)    60%, transparent);

  --k-mesh-1: color-mix(in srgb, var(--k-accent) 22%, transparent);
  --k-mesh-2: color-mix(in srgb, var(--k-slate)  20%, transparent);
  --k-mesh-3: color-mix(in srgb, var(--k-gold)   14%, transparent);
  --k-mesh-4: color-mix(in srgb, var(--k-ok)     12%, transparent);
}

/* ============================================================
   1 · Animated gradient-mesh backdrop
   Two fixed, GPU-transformed layers drifting behind everything.
   The solid --k-bg still paints first, so text stays readable.
   ============================================================ */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: -25%;
  z-index: -1;
  pointer-events: none;
  will-change: transform;
}
body::before {
  background:
    radial-gradient(38% 46% at 16% 12%, var(--k-mesh-1), transparent 70%),
    radial-gradient(42% 44% at 84% 10%, var(--k-mesh-2), transparent 72%);
  animation: kv-mesh-a 34s var(--k-ease) infinite alternate;
}
/* Only the first layer drifts. Two full-viewport layers animating
   forever underneath 60-odd backdrop-filtered cards meant the whole
   composite re-rendered continuously; with one moving against one
   still, the mesh still breathes and the cost halves. The static
   layer keeps a one-off transform so its composition is unchanged. */
body::after {
  background:
    radial-gradient(46% 52% at 78% 88%, var(--k-mesh-3), transparent 72%),
    radial-gradient(44% 50% at 10% 90%, var(--k-mesh-2), transparent 72%),
    radial-gradient(30% 34% at 50% 50%, var(--k-mesh-4), transparent 70%);
  transform: translate3d(0, 0, 0) rotate(4deg) scale(1.03);
  will-change: auto;
}
@keyframes kv-mesh-a {
  from { transform: translate3d(-3%, -2%, 0) scale(1);   }
  to   { transform: translate3d( 4%,  3%, 0) scale(1.12); }
}
@keyframes kv-mesh-b {
  from { transform: translate3d(2%, 3%, 0) rotate(0deg) scale(1.05); }
  to   { transform: translate3d(-3%, -2%, 0) rotate(8deg) scale(1);  }
}
/* Two full-viewport layers animating forever under a dozen blurred
   glass cards is a real battery cost on phones. Keep the mesh — it is
   the backdrop — but hold it still on touch/small screens. */
@media (max-width: 900px), (hover: none) and (pointer: coarse) {
  body::before, body::after { animation: none; will-change: auto; }
}

/* ============================================================
   2 · Frosted-glass surfaces
   ============================================================ */
.k-card,
.k-stat,
.k-modal {
  background: var(--k-glass);
  -webkit-backdrop-filter: var(--k-blur);
  backdrop-filter: var(--k-blur);
  border-color: var(--k-glass-brd);
  box-shadow: var(--k-shadow), var(--k-hair);
}
.k-side {
  background: var(--k-glass);
  -webkit-backdrop-filter: var(--k-blur);
  backdrop-filter: var(--k-blur);
}
.k-top {
  background: color-mix(in srgb, var(--k-bg) 70%, transparent);
  -webkit-backdrop-filter: saturate(1.4) blur(16px);
  backdrop-filter: saturate(1.4) blur(16px);
}
/* cards gain a gentle lift + coloured glow on hover */
.k-card { transition: transform .3s var(--k-ease), box-shadow .3s var(--k-ease), border-color .3s var(--k-ease); }
.k-card:hover { box-shadow: var(--k-shadow-lg), var(--k-hair); }

/* Phones: a dozen live blur regions stacked over the mesh is the single
   biggest paint cost here, and at card-width the blur reads as a flat
   tint anyway — so keep the frosted look on desktop and serve phones a
   solid surface that looks the same but composites for free. */
@media (max-width: 700px), (hover: none) and (pointer: coarse) {
  .k-card, .k-stat, .k-modal, .k-side, .kv-hero {
    -webkit-backdrop-filter: none; backdrop-filter: none;
    background-color: var(--k-surface);
  }
  .k-top {
    -webkit-backdrop-filter: saturate(1.2) blur(10px);
    backdrop-filter: saturate(1.2) blur(10px);
  }
}

/* ============================================================
   3 · Gradient text + glowing figures
   ============================================================ */
.kv-grad,
.kv-grad-num {
  background: var(--k-grad-warm);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.kv-grad.slate { background: var(--k-grad-slate); -webkit-background-clip: text; background-clip: text; }
.kv-grad.cool  { background: var(--k-grad-cool);  -webkit-background-clip: text; background-clip: text; }
.kv-grad.ok    { background: var(--k-grad-ok);    -webkit-background-clip: text; background-clip: text; }

/* a soft luminous halo behind the big hero figure (dark shines) */
.kv-glow-num { position: relative; }
html[data-theme="dark"] .kv-grad-num { filter: drop-shadow(0 2px 18px color-mix(in srgb, var(--k-accent) 45%, transparent)); }

/* ============================================================
   4 · Stat tiles — vivid v2
   Adds a top gradient rail, coloured hover glow, and a slot for
   a trend delta + sparkline underneath the value.
   ============================================================ */
.k-stat { transition: transform .28s var(--k-spring-soft), box-shadow .28s var(--k-ease); overflow: hidden; }
.k-stat::after { height: 4px; background: var(--k-grad-accent); border-radius: 4px 4px 0 0; }
/* Base makes the rail always-visible (it was hover-only, so touch never
   saw it). Keep the hover nudge in step with the thicker vivid rail. */
@media (hover: hover) { .k-stat:hover::after { height: 6px; } }
.k-stat.slate::after { background: var(--k-grad-slate); }
.k-stat.ok::after    { background: var(--k-grad-ok); }
.k-stat.err::after   { background: linear-gradient(135deg, var(--k-err), #d9776b); }
.k-stat.gold::after  { background: linear-gradient(135deg, var(--k-gold), #e6c169); }
.k-stat:hover { transform: translateY(-4px); box-shadow: var(--k-glow-accent), var(--k-hair); }
.k-stat.slate:hover { box-shadow: var(--k-glow-slate), var(--k-hair); }
.k-stat.ok:hover    { box-shadow: var(--k-glow-ok), var(--k-hair); }
.k-stat.err:hover   { box-shadow: var(--k-glow-err), var(--k-hair); }
.k-stat.gold:hover  { box-shadow: var(--k-glow-gold), var(--k-hair); }
.k-stat-value { text-shadow: 0 1px 0 color-mix(in srgb, var(--k-surface) 60%, transparent); }

/* a faint watermark glyph can sit in a stat's corner */
.k-stat .kv-stat-ic {
  position: absolute; top: 14px; right: 14px;
  width: 26px; height: 26px; border-radius: 8px;
  display: grid; place-items: center;
  color: var(--k-accent); background: color-mix(in srgb, var(--k-accent) 12%, transparent);
  transition: transform .3s var(--k-spring-big);
}
.k-stat.slate .kv-stat-ic { color: var(--k-slate); background: color-mix(in srgb, var(--k-slate) 14%, transparent); }
.k-stat.ok    .kv-stat-ic { color: var(--k-ok);    background: color-mix(in srgb, var(--k-ok) 14%, transparent); }
.k-stat.err   .kv-stat-ic { color: var(--k-err);   background: color-mix(in srgb, var(--k-err) 14%, transparent); }
.k-stat:hover .kv-stat-ic { transform: scale(1.12) rotate(-6deg); }
.k-stat .kv-stat-ic svg { width: 15px; height: 15px; }
.kv-stat-foot { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }

/* ============================================================
   5 · Trend delta pills   (▲ 4.2% / ▼ 1.1%)
   ============================================================ */
.kv-delta {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 12px; font-weight: 750; letter-spacing: .01em;
  padding: 1px 8px 1px 6px; border-radius: 999px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.kv-delta svg { width: 13px; height: 13px; }
.kv-delta.up   { color: var(--k-ok);  background: var(--k-ok-soft); }
.kv-delta.down { color: var(--k-err); background: var(--k-err-soft); }
.kv-delta.flat { color: var(--k-ink-mute); background: var(--k-surface-2); }

/* ============================================================
   6 · Sparklines (SSR SVG, draw-in via pathLength=1)
   ============================================================ */
.kv-spark { display: inline-block; width: 72px; height: 22px; vertical-align: middle; line-height: 0; color: var(--k-slate); }
.kv-spark svg { width: 100%; height: 100%; overflow: visible; display: block; }
.kv-spark .kv-spark-line {
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 1; stroke-dashoffset: 1;
  animation: kv-draw 1.1s var(--k-ease) forwards;
  animation-delay: calc(var(--i, 0) * 60ms + 150ms);
}
.kv-spark .kv-spark-fill { fill: currentColor; opacity: 0; animation: kv-fade .8s var(--k-ease) forwards; animation-delay: calc(var(--i, 0) * 60ms + 400ms); }
.kv-spark .kv-spark-dot { fill: currentColor; opacity: 0; animation: kv-fade .4s var(--k-ease) forwards; animation-delay: calc(var(--i, 0) * 60ms + 900ms); }
.kv-spark.accent { color: var(--k-accent); }
.kv-spark.ok { color: var(--k-ok); }
.kv-spark.err { color: var(--k-err); }
@keyframes kv-draw { to { stroke-dashoffset: 0; } }
@keyframes kv-fade { to { opacity: var(--kv-fill-op, 1); } }

/* ============================================================
   7 · Charts — gradient bars, richer donut, line/area
   ============================================================ */
/* gradient bars (income = slate, expense = accent) */
.k-bars .bar {
  background: linear-gradient(180deg, var(--k-slate), color-mix(in srgb, var(--k-slate) 45%, transparent));
  box-shadow: 0 0 0 0 transparent;
  transition: transform .7s var(--k-ease), filter .2s var(--k-ease), box-shadow .2s var(--k-ease);
}
.k-bars .bar.alt {
  background: linear-gradient(180deg, var(--k-accent), color-mix(in srgb, var(--k-accent-2) 65%, transparent));
}
.k-bars .bar:hover {
  filter: brightness(1.08) saturate(1.1);
  box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--k-slate) 55%, transparent);
}
.k-bars .bar.alt:hover { box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--k-accent) 60%, transparent); }

/* donut: soft glow + lift the hovered slice */
.k-donut-seg { transition: stroke-width .2s var(--k-ease), opacity .2s, stroke-dasharray .9s var(--k-ease); }
.k-donut-seg:hover { stroke-width: 4.4; cursor: pointer; }

/* --- line / area chart (kv-chart) -------------------------- */
.kv-chart { position: relative; width: 100%; }
.kv-chart svg { width: 100%; height: auto; display: block; overflow: visible; }
.kv-chart .kv-grid line { stroke: var(--k-line); stroke-width: 1; }
.kv-chart .kv-area { opacity: 0; animation: kv-fade .9s var(--k-ease) .3s forwards; }
.kv-chart .kv-line {
  fill: none; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 1; stroke-dashoffset: 1;
  filter: drop-shadow(0 6px 12px color-mix(in srgb, var(--k-accent) 30%, transparent));
  animation: kv-draw 1.6s var(--k-ease) forwards;
}
.kv-chart .kv-dot { opacity: 0; animation: kv-fade .4s var(--k-ease) forwards; }
.kv-chart:hover .kv-dot { opacity: 1; }
.kv-chart .kv-hot { fill: transparent; cursor: crosshair; }
.kv-chart .kv-cursor { stroke: var(--k-ink-mute); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0; pointer-events: none; }
.kv-chart .kv-focus { opacity: 0; pointer-events: none; }

/* floating chart tooltip (positioned by JS) */
.kv-tip {
  position: absolute; z-index: 5; pointer-events: none;
  transform: translate(-50%, -120%);
  background: var(--k-ink); color: var(--k-bg);
  padding: 7px 11px; border-radius: 10px;
  font-size: 12.5px; font-weight: 600; line-height: 1.35;
  box-shadow: var(--k-shadow-lg); white-space: nowrap;
  opacity: 0; transition: opacity .15s var(--k-ease);
}
.kv-tip.is-on { opacity: 1; }
.kv-tip b { font-weight: 750; font-variant-numeric: tabular-nums; }
.kv-tip .kv-tip-k { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; vertical-align: 0; }

/* ============================================================
   8 · Insight lists (top spends / biggest movers) — inline bars
   ============================================================ */
.kv-rank { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.kv-rank li { position: relative; }
.kv-rank .kv-rank-top { display: flex; align-items: baseline; gap: 10px; font-size: 13.5px; }
.kv-rank .kv-rank-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.kv-rank .kv-rank-amt { font-variant-numeric: tabular-nums; font-weight: 700; }
.kv-rank .kv-rank-bar { height: 7px; border-radius: 999px; background: var(--k-surface-2); margin-top: 5px; overflow: hidden; }
.kv-rank .kv-rank-bar i {
  display: block; height: 100%; border-radius: 999px; width: 0;
  background: var(--k-grad-accent);
  transition: width 1s var(--k-ease); transition-delay: calc(var(--i, 0) * 90ms);
}
.kv-rank.slate .kv-rank-bar i { background: var(--k-grad-slate); }
.kv-rank.is-in .kv-rank-bar i { width: calc(var(--pct, 0) * 1%); }

/* ============================================================
   9 · Buttons — gradient primary, glow, magnetic
   ============================================================ */
.k-btn:not(.ghost):not(.soft):not(.danger):not(.danger-soft) {
  background: var(--k-grad-accent);
  box-shadow: 0 6px 18px -8px color-mix(in srgb, var(--k-accent) 55%, transparent);
}
.k-btn:not(.ghost):not(.soft):not(.danger):not(.danger-soft):hover {
  background: var(--k-grad-accent);
  filter: brightness(1.04);
  box-shadow: var(--k-glow-accent);
}
.k-btn.danger { background: linear-gradient(135deg, var(--k-err), #c85446); }

/* magnetic: JS sets --kx/--ky; only elements opting in move */
.kv-magnet { transition: transform .18s var(--k-spring-soft), box-shadow .25s var(--k-ease); }
.kv-magnet:hover { transform: translate(var(--kx, 0), var(--ky, 0)); }

/* fancy FAB glow already exists; deepen it */
.k-tab-new .fab { box-shadow: 0 12px 28px -8px color-mix(in srgb, var(--k-accent) 80%, transparent); }

/* ============================================================
   10 · Nav — gradient active pill + glow rail
   ============================================================ */
.k-nav a.is-active {
  background: linear-gradient(100deg, color-mix(in srgb, var(--k-accent) 18%, transparent), transparent 85%);
  color: var(--k-accent);
}
.k-nav a.is-active::before {
  width: 3px; background: var(--k-grad-accent);
  box-shadow: 0 0 12px color-mix(in srgb, var(--k-accent) 70%, transparent);
}
.k-nav a svg { transition: transform .2s var(--k-spring-big), opacity .2s; }
.k-nav a:hover svg { transform: scale(1.12); opacity: 1; }
.k-nav a.is-active svg { opacity: 1; }

/* ============================================================
   11 · Vivid chips + Dr/Cr
   ============================================================ */
.k-chip.dr { background: color-mix(in srgb, var(--k-slate) 16%, transparent); color: var(--k-slate); }
.k-chip.cr { background: color-mix(in srgb, var(--k-accent) 16%, transparent); color: var(--k-accent); }
.k-chip.ok   { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--k-ok) 25%, transparent); }
.k-chip.err  { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--k-err) 25%, transparent); }
.k-chip.gold { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--k-gold) 25%, transparent); }
.k-chip.slate{ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--k-slate) 22%, transparent); }

/* ============================================================
   12 · Inputs / segmented — focus glow
   ============================================================ */
.k-input:focus, .k-select:focus, .k-textarea:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--k-slate) 25%, transparent),
              0 6px 20px -12px color-mix(in srgb, var(--k-slate) 60%, transparent);
}
.k-seg .is-on { box-shadow: var(--k-shadow), var(--k-hair); }
.k-seg .is-on.dr { color: var(--k-dr); box-shadow: var(--k-shadow), 0 0 0 1px color-mix(in srgb, var(--k-slate) 30%, transparent); }
.k-seg .is-on.cr { color: var(--k-cr); box-shadow: var(--k-shadow), 0 0 0 1px color-mix(in srgb, var(--k-accent) 30%, transparent); }

/* ============================================================
   13 · Hero band (kv-hero) — mesh + parallax bird + big figure
   Opt-in on a card: <div class="k-card kv-hero"> … </div>
   ============================================================ */
.kv-hero {
  position: relative; overflow: hidden;
  background:
    radial-gradient(60% 120% at 12% 0%, color-mix(in srgb, var(--k-accent) 16%, transparent), transparent 60%),
    radial-gradient(60% 120% at 100% 100%, color-mix(in srgb, var(--k-slate) 16%, transparent), transparent 60%),
    var(--k-glass);
}
.kv-hero-bird {
  position: absolute; right: -30px; bottom: -44px;
  width: 190px; height: 190px; color: var(--k-accent);
  opacity: .08; pointer-events: none; transform: rotate(-8deg);
  will-change: transform;
}
html[data-theme="dark"] .kv-hero-bird { opacity: .12; }
.kv-hero-figure { font-size: clamp(30px, 6vw, 46px); font-weight: 800; letter-spacing: -.02em; line-height: 1.05; font-variant-numeric: tabular-nums; }

/* ============================================================
   14 · Section divider + eyebrow
   ============================================================ */
.kv-eyebrow { font-size: 11px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase; color: var(--k-accent); }

/* ============================================================
   15 · Motion — scroll reveal, tilt
   JS toggles .is-in when the element scrolls into view.
   ============================================================ */
/* --i is authored per PAGE, not per viewport-group: vendor_view runs it
   to 16, vehicle_view to 10. Unclamped that made the last card wait a
   full second after it had already scrolled into view, which reads as
   the page being slow rather than as choreography. Four steps is enough
   to feel staggered; past that everyone arrives together. */
.kv-reveal {
  opacity: 0; transform: translateY(22px);
  transition: opacity .7s var(--k-ease), transform .75s var(--k-spring-soft);
  transition-delay: calc(min(var(--i, 0), 4) * 65ms);
  will-change: opacity, transform;
}
.kv-reveal.left  { transform: translateX(-26px); }
.kv-reveal.right { transform: translateX(26px); }
.kv-reveal.scale { transform: scale(.94); }
.kv-reveal.is-in { opacity: 1; transform: none; }
/* Release the compositor layer once the card has arrived — otherwise
   every revealed card holds its own layer for the life of the page. */
.kv-reveal.is-in { will-change: auto; }

/* .kv-tilt removed — it had no call sites in any page, and its JS is
   gone. .kv-parallax never had CSS at all; its JS is gone too, so the
   class survives in ~10 templates as inert markup. */

/* subtle continuous float for hero marks */
.kv-float { animation: kv-float 7s ease-in-out infinite; }
@keyframes kv-float { 50% { transform: translateY(-8px) rotate(-8deg); } }

/* number "count settle" pop applied by JS when a countup finishes.
   These targets are plain <span>s, and transform does not apply to a
   non-replaced inline box — so this animation had never once rendered.
   inline-block is set permanently rather than alongside the class so
   the box doesn't reflow at the moment the pop starts. */
[data-countup] { display: inline-block; }
.kv-settle { animation: kv-settle .5s var(--k-spring-big); }
@keyframes kv-settle { 0% { transform: scale(1.06); } 100% { transform: scale(1); } }

/* ============================================================
   16 · Quick-action tiles — gradient wash + icon pop
   ============================================================ */
.kd-tile { position: relative; overflow: hidden; }
.kd-tile::before {
  content: ""; position: absolute; inset: 0; opacity: 0;
  background: radial-gradient(120% 120% at 100% 0%, color-mix(in srgb, currentColor 10%, transparent), transparent 60%);
  transition: opacity .3s var(--k-ease);
}
.kd-tile:hover { box-shadow: var(--k-shadow-lg); transform: translateY(-3px) scale(1.008); }
.kd-tile:hover::before { opacity: 1; }
.kd-tile .ic { transition: transform .3s var(--k-spring-big); }
.kd-tile:hover .ic { transform: translateY(-2px) scale(1.08) rotate(-4deg); }

/* ============================================================
   17 · Reduced motion — collapse the whole show
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  body::before, body::after { animation: none !important; }
  .kv-reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .kv-spark-line, .kv-spark-fill, .kv-spark-dot,
  .kv-chart .kv-line, .kv-chart .kv-area, .kv-chart .kv-dot,
  .kv-float, .kv-settle { animation: none !important; }
  .kv-spark-line { stroke-dashoffset: 0 !important; }
  .kv-chart .kv-line { stroke-dashoffset: 0 !important; }
  .kv-chart .kv-area, .kv-chart .kv-dot, .kv-spark-fill, .kv-spark-dot { opacity: 1 !important; }
  .kv-rank .kv-rank-bar i { transition: none !important; }
  .kv-magnet:hover { transform: none !important; }
  .k-stat:hover, .k-card:hover, .kd-tile:hover { transform: none !important; }
}

/* keep the mesh out of print */
@media print {
  body::before, body::after { display: none !important; }
  .k-card, .k-stat { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; background: #fff !important; }
}

/* =============================================================
   VIVID · WAVE 2  (2026-08 upgrade pass)
   Command palette · budget rings · heatmap calendar · gauge ·
   dual-series charts · sortable headers · kestrel empty states ·
   toast v2 · sun↔moon morph · shortcut sheet.
   Additive only — nothing above is redefined.
   ============================================================= */

/* Pre-reveal transforms (.kv-reveal.right etc.) must never widen
   the page. `clip` (not `hidden`) so no scroll container is created
   and position:sticky keeps working; clipping .k-content kills the
   overflow at its source (fixed-position overlays are unaffected —
   their containing block is the viewport). */
html, body { overflow-x: clip; }
.k-content { overflow-x: clip; }

/* ---- shared kbd pill -------------------------------------- */
.k-kbd {
  display: inline-block; min-width: 20px; text-align: center;
  padding: 2px 7px; border-radius: 6px;
  border: 1px solid var(--k-border); border-bottom-width: 2px;
  background: var(--k-surface-2); color: var(--k-ink-soft);
  font-size: 11px; font-weight: 700; line-height: 1.45;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* ============================================================
   18 · Command palette  (Ctrl+K)
   ============================================================ */
html.kp-lock body { overflow: hidden; }
.kp-back {
  position: fixed; inset: 0; z-index: 220;
  display: grid; justify-items: center; align-items: start;
  padding: min(14vh, 130px) 16px 16px;
  background: color-mix(in srgb, #14171c 34%, transparent);
  -webkit-backdrop-filter: saturate(1.1) blur(7px);
  backdrop-filter: saturate(1.1) blur(7px);
  opacity: 0; pointer-events: none;
  transition: opacity .24s var(--k-ease);
}
.kp-back.is-open { opacity: 1; pointer-events: auto; }
.kp-panel {
  width: min(640px, 100%);
  background: var(--k-glass);
  -webkit-backdrop-filter: saturate(1.4) blur(22px);
  backdrop-filter: saturate(1.4) blur(22px);
  border: 1px solid var(--k-glass-brd);
  border-radius: 18px;
  box-shadow: var(--k-shadow-lg), var(--k-hair), var(--k-glow-accent);
  overflow: hidden;
  transform: translateY(16px) scale(.97);
  transition: transform .38s var(--k-spring-big);
}
.kp-back.is-open .kp-panel { transform: none; }
.kp-head {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--k-line);
}
.kp-head-ic { display: grid; place-items: center; color: var(--k-accent); }
.kp-head-ic svg { width: 18px; height: 18px; }
.kp-input {
  flex: 1; min-width: 0;
  border: 0; outline: none; background: transparent;
  font: inherit; font-size: 16px; color: var(--k-ink);
  padding: 2px 0;
}
.kp-input::placeholder { color: var(--k-ink-mute); }
.kp-esc {
  font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--k-ink-mute);
  border: 1px solid var(--k-border); border-radius: 6px;
  padding: 2px 7px; background: var(--k-surface-2);
}
.kp-list { max-height: min(56vh, 470px); overflow-y: auto; padding: 10px; overscroll-behavior: contain; }
.kp-sec {
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--k-ink-mute); margin: 12px 10px 6px;
}
.kp-sec:first-child { margin-top: 4px; }
.kp-item {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 12px; border-radius: 12px; cursor: pointer;
  animation: kp-in .32s var(--k-ease) both;
  animation-delay: calc(var(--i, 0) * 22ms);
}
@keyframes kp-in { from { opacity: 0; transform: translateY(7px); } }
.kp-item.is-active {
  background: linear-gradient(100deg, color-mix(in srgb, var(--k-accent) 15%, transparent), transparent 88%);
  transform: translateX(2px);
}
.kp-ic {
  width: 30px; height: 30px; flex: 0 0 auto;
  display: grid; place-items: center; border-radius: 10px;
  background: var(--k-surface-2); color: var(--k-ink-soft);
  transition: transform .25s var(--k-spring-big), background .2s, color .2s;
}
.kp-ic svg { width: 15px; height: 15px; }
.kp-item.is-active .kp-ic {
  background: color-mix(in srgb, var(--k-accent) 15%, transparent);
  color: var(--k-accent);
  transform: scale(1.08) rotate(-4deg);
}
.kp-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.kp-label { font-size: 14px; font-weight: 620; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kp-label b { color: var(--k-accent); font-weight: 800; }
.kp-sub { font-size: 12px; color: var(--k-ink-mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kp-amt { font-size: 13px; font-weight: 750; font-variant-numeric: tabular-nums; color: var(--k-ink-soft); white-space: nowrap; }
.kp-kbd {
  font-size: 10.5px; font-weight: 700; color: var(--k-ink-mute);
  border: 1px solid var(--k-border); border-radius: 6px;
  padding: 1px 6px; background: var(--k-surface-2); white-space: nowrap;
}
.kp-none { padding: 30px 16px 26px; text-align: center; color: var(--k-ink-mute); font-size: 13.5px; }
.kp-none-bird { display: block; width: 40px; margin: 0 auto 10px; color: var(--k-accent); opacity: .35; animation: kv-empty-bob 5s ease-in-out infinite; }
.kp-none-bird svg { width: 40px; height: 40px; }
.kp-skel-row { display: flex; align-items: center; gap: 12px; padding: 9px 12px; }
.kp-skel-ic { width: 30px; height: 30px; border-radius: 10px; flex: 0 0 auto; }
.kp-skel-t { height: 13px; border-radius: 6px; }
.kp-hints {
  display: flex; gap: 16px; align-items: center;
  padding: 10px 16px; border-top: 1px solid var(--k-line);
  font-size: 12px; color: var(--k-ink-mute);
}
.kp-hints kbd {
  font: inherit; font-size: 10.5px; font-weight: 700;
  border: 1px solid var(--k-border); border-radius: 5px;
  padding: 1px 5px; background: var(--k-surface-2);
}
@media (max-width: 640px) {
  .kp-back { padding: 10px 10px 16px; }
  .kp-hints { display: none; }
  .kp-list { max-height: calc(100dvh - 150px); }
}

/* the topbar trigger pill */
.kp-trigger {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 12px;
  border: 1px solid var(--k-border); border-radius: 10px;
  background: var(--k-surface); color: var(--k-ink-mute);
  font: inherit; font-size: 13px; cursor: pointer;
  transition: color .18s, border-color .18s, transform .14s var(--k-ease), box-shadow .2s;
}
.kp-trigger svg { width: 14px; height: 14px; }
.kp-trigger:hover { color: var(--k-ink); border-color: var(--k-ink-mute); transform: translateY(-1px); box-shadow: var(--k-shadow); }
.kp-trigger .k-kbd { font-size: 10px; padding: 1px 5px; margin-left: 2px; }
@media (max-width: 700px) {
  .kp-trigger { width: 36px; padding: 0; justify-content: center; }
  .kp-trigger .kp-trigger-t, .kp-trigger .k-kbd { display: none; }
}

/* ============================================================
   19 · Budget progress rings
   ============================================================ */
.kb-ring {
  position: relative;
  width: var(--kb-size, 124px); height: var(--kb-size, 124px);
  display: grid; place-items: center; flex: 0 0 auto;
}
.kb-ring svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); overflow: visible; }
.kb-ring-track { fill: none; stroke: color-mix(in srgb, var(--k-border) 70%, transparent); stroke-width: 10; }
.kb-ring-val {
  fill: none; stroke-width: 10; stroke-linecap: round;
  stroke-dasharray: 100 100; stroke-dashoffset: 100;
  transition: stroke-dashoffset 1.3s var(--k-spring-soft);
  transition-delay: calc(var(--i, 0) * 90ms + .15s);
  filter: drop-shadow(0 4px 10px color-mix(in srgb, var(--kb-col, var(--k-accent)) 40%, transparent));
}
.kb-ring.is-in .kb-ring-val { stroke-dashoffset: var(--kb-to, 100); }
.kb-ring-c { position: relative; text-align: center; padding: 0 12px; }
.kb-ring-c b { display: block; font-size: 15.5px; font-weight: 800; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.kb-ring-c span { display: block; font-size: 10.5px; color: var(--k-ink-mute); margin-top: 1px; }
.kb-ring.over .kb-ring-c b { color: var(--k-err); }
.kb-ring.over .kb-ring-val { animation: kb-pulse 2.4s ease-in-out infinite .8s; }
@keyframes kb-pulse {
  50% { filter: drop-shadow(0 0 12px color-mix(in srgb, var(--k-err) 75%, transparent)); }
}

/* ============================================================
   20 · Spending heatmap calendar
   ============================================================ */
.khm { --khm-c: 13px; --khm-gap: 3px; }
.khm-scroll { overflow-x: auto; padding: 34px 2px 4px; margin-top: -30px; }
.khm-months { display: flex; gap: var(--khm-gap); margin-left: 34px; margin-bottom: 6px; }
.khm-months span {
  width: var(--khm-c); flex: 0 0 auto;
  font-size: 10px; font-weight: 700; color: var(--k-ink-mute);
  white-space: nowrap; overflow: visible;
}
.khm-body { display: flex; gap: 6px; }
.khm-days {
  display: grid; grid-template-rows: repeat(7, var(--khm-c));
  row-gap: var(--khm-gap); width: 28px; flex: 0 0 auto;
  font-size: 9.5px; color: var(--k-ink-mute); text-align: right;
}
.khm-days span { line-height: var(--khm-c); }
.khm-grid { display: flex; gap: var(--khm-gap); }
.khm-col {
  display: grid; grid-template-rows: repeat(7, var(--khm-c));
  row-gap: var(--khm-gap);
  animation: khm-pop .4s var(--k-ease) both;
  animation-delay: calc(var(--i, 0) * 12ms);
}
@keyframes khm-pop { from { opacity: 0; transform: translateY(4px); } }
.khm-c {
  width: var(--khm-c); height: var(--khm-c); display: block;
  border-radius: 3.5px; position: relative;
  background: var(--k-surface-2);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--k-border) 55%, transparent);
  transition: transform .18s var(--k-spring), box-shadow .18s var(--k-ease);
}
.khm-c.l1 { background: color-mix(in srgb, var(--k-accent) 24%, var(--k-surface-2)); }
.khm-c.l2 { background: color-mix(in srgb, var(--k-accent) 46%, var(--k-surface-2)); }
.khm-c.l3 { background: color-mix(in srgb, var(--k-accent) 70%, var(--k-surface-2)); }
.khm-c.l4 { background: var(--k-accent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--k-accent-hi) 80%, transparent); }
.khm-c.void { visibility: hidden; }
a.khm-c:hover, .khm-c[data-tip]:hover,
.khm-c[data-tip]:focus-visible, .khm-c[data-tip].is-tapped {
  transform: scale(1.4); z-index: 6;
  box-shadow: 0 4px 14px -4px color-mix(in srgb, var(--k-accent) 60%, transparent);
}
/* Touch has no hover, so a tapped/focused cell shows the same bubble —
   otherwise a phone user can see the colour but never read the value. */
.khm-c[data-tip]:hover::after,
.khm-c[data-tip]:focus-visible::after,
.khm-c[data-tip].is-tapped::after {
  content: attr(data-tip);
  position: absolute; bottom: calc(100% + 7px); left: 50%;
  transform: translateX(-50%) scale(.72);   /* undo parent scale visually */
  background: var(--k-ink); color: var(--k-bg);
  font-size: 11.5px; font-weight: 650; line-height: 1.3;
  padding: 5px 9px; border-radius: 8px; white-space: nowrap;
  box-shadow: var(--k-shadow-lg);
  pointer-events: none; z-index: 7;
}
.khm-key {
  display: flex; align-items: center; gap: 4px; justify-content: flex-end;
  margin-top: 10px; font-size: 11px; color: var(--k-ink-mute);
}
.khm-key .khm-c { width: 11px; height: 11px; }
.khm-key span { margin: 0 3px; }

/* ============================================================
   21 · Gauge (semi-circle)
   ============================================================ */
.kv-gauge { position: relative; width: min(230px, 100%); margin: 0 auto; }
.kv-gauge svg { width: 100%; height: auto; overflow: visible; }
.kv-gauge-track { fill: none; stroke: color-mix(in srgb, var(--k-border) 70%, transparent); stroke-width: 13; stroke-linecap: round; }
.kv-gauge-val {
  fill: none; stroke-width: 13; stroke-linecap: round;
  stroke-dasharray: 100 100; stroke-dashoffset: 100;
  transition: stroke-dashoffset 1.5s var(--k-spring-soft) .25s;
  filter: drop-shadow(0 6px 14px color-mix(in srgb, var(--k-ok) 35%, transparent));
}
.kv-gauge.is-in .kv-gauge-val { stroke-dashoffset: calc(100 - var(--kvg, 0)); }
.kv-gauge-c { position: absolute; left: 0; right: 0; bottom: 4px; text-align: center; }
.kv-gauge-c b { display: block; font-size: clamp(24px, 4vw, 32px); font-weight: 800; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.kv-gauge-c span {
  display: block; font-size: 10.5px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--k-ink-mute);
  margin-top: 2px;
}

/* ============================================================
   22 · Dual-series chart extras (legend, staggered draw, tip rows)
   ============================================================ */
.kv-legend {
  display: flex; gap: 16px; margin-bottom: 8px;
  font-size: 12.5px; font-weight: 650; color: var(--k-ink-soft);
}
.kv-legend span { display: inline-flex; align-items: center; gap: 6px; }
.kv-legend i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.kv-chart .kv-line.b { animation-delay: .3s; }
.kv-chart .kv-area.b { animation-delay: .55s; }
.kv-tip-hd { display: block; font-size: 11px; font-weight: 800; letter-spacing: .04em; opacity: .8; margin-bottom: 3px; }
.kv-tip-row { display: flex; align-items: center; gap: 6px; }
.kv-tip-row b { margin-left: auto; padding-left: 12px; }

/* ============================================================
   23 · Sortable table headers
   ============================================================ */
.k-table th[data-sort] {
  cursor: pointer; user-select: none; position: relative;
  padding-right: 22px;
  transition: color .18s var(--k-ease);
}
.k-table th[data-sort]:hover { color: var(--k-ink); }
.k-table th[data-sort]::after {
  content: ""; position: absolute; right: 8px; top: 50%;
  width: 6px; height: 6px;
  border-right: 1.8px solid currentColor; border-bottom: 1.8px solid currentColor;
  opacity: 0; transform: translateY(-70%) rotate(45deg);
  transition: opacity .2s var(--k-ease), transform .3s var(--k-spring);
}
.k-table th[data-sort]:hover::after { opacity: .4; }
.k-table th.is-sort-desc, .k-table th.is-sort-asc { color: var(--k-accent); }
.k-table th.is-sort-desc::after { opacity: 1; transform: translateY(-70%) rotate(45deg); }
.k-table th.is-sort-asc::after  { opacity: 1; transform: translateY(-25%) rotate(-135deg); }

/* ============================================================
   24 · Kestrel empty states
   ============================================================ */
.kv-empty .kv-empty-art { position: relative; width: 74px; margin: 0 auto 14px; }
.kv-empty .kv-empty-art > svg {
  width: 74px; height: 74px; margin: 0; opacity: .16;
  color: var(--k-accent);
  animation: kv-empty-bob 5.5s ease-in-out infinite;
}
html[data-theme="dark"] .kv-empty .kv-empty-art > svg { opacity: .22; }
.kv-empty .kv-empty-badge {
  position: absolute; right: -8px; bottom: -2px;
  width: 30px; height: 30px; border-radius: 10px;
  display: grid; place-items: center;
  background: var(--k-glass);
  -webkit-backdrop-filter: var(--k-blur);
  backdrop-filter: var(--k-blur);
  border: 1px solid var(--k-glass-brd);
  box-shadow: var(--k-shadow);
  color: var(--k-accent);
}
.kv-empty .kv-empty-badge svg { width: 15px; height: 15px; margin: 0; opacity: 1; }
@keyframes kv-empty-bob { 50% { transform: translateY(-6px) rotate(-4deg); } }

/* ============================================================
   25 · Toast v2 — icon + progress rail
   ============================================================ */
.k-toast { position: relative; overflow: hidden; padding: 11px 18px 13px 14px; }
/* The toast body is --k-ink, so it flips to a LIGHT panel in dark mode:
   the icon has to take its colour from the ok tone, not a fixed pale
   green, or it disappears against that background. */
.k-toast-ic {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 22px; height: 22px; border-radius: 50%;
  background: color-mix(in srgb, var(--k-ok) 22%, transparent);
  color: color-mix(in srgb, var(--k-ok) 92%, var(--k-bg));
}
.k-toast.err .k-toast-ic { background: rgba(255, 255, 255, .18); color: #fff; }
.k-toast-ic svg { width: 12px; height: 12px; }
.k-toast-tx { min-width: 0; }
.k-toast-bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: color-mix(in srgb, currentColor 30%, transparent);
  transform-origin: left;
  animation: k-toast-run linear forwards;
}
@keyframes k-toast-run { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* ============================================================
   26 · Sun ↔ moon morph (theme toggle)
   ============================================================ */
[data-theme-icon] .k-thememorph { width: 16px; height: 16px; overflow: visible; }
[data-theme-icon] .tm-core,
[data-theme-icon] .tm-rays,
[data-theme-icon] .tm-moon {
  transform-origin: center;
  transition: transform .5s var(--k-spring-big), opacity .3s var(--k-ease);
}
/* light mode → offering the moon */
[data-theme-icon] .tm-core { opacity: 0; transform: scale(.3); }
[data-theme-icon] .tm-rays { opacity: 0; transform: scale(.4) rotate(-90deg); }
[data-theme-icon] .tm-moon { opacity: 1; transform: none; }
/* dark mode → offering the sun */
[data-theme-icon].is-dark .tm-core { opacity: 1; transform: none; }
[data-theme-icon].is-dark .tm-rays { opacity: 1; transform: none; }
[data-theme-icon].is-dark .tm-moon { opacity: 0; transform: scale(.4) rotate(90deg); }

/* ============================================================
   27 · Shortcut sheet
   ============================================================ */
.ksh-list { margin: 4px 0 0; }
.ksh-row {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 2px; border-bottom: 1px solid var(--k-line);
}
.ksh-row:last-child { border-bottom: 0; }
.ksh-what { flex: 1; font-size: 14px; }
.ksh-keys { display: flex; gap: 4px; align-items: center; }
.ksh-keys .plus { color: var(--k-ink-mute); font-size: 11px; margin: 0 1px; }

/* ============================================================
   28 · Reduced motion + print for wave 2
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .kp-item, .khm-col { animation: none !important; opacity: 1 !important; transform: none !important; }
  .kp-panel { transition: none !important; }
  .kb-ring-val, .kv-gauge-val { transition: none !important; }
  .kb-ring.is-in .kb-ring-val { stroke-dashoffset: var(--kb-to, 100) !important; }
  .kv-gauge.is-in .kv-gauge-val { stroke-dashoffset: calc(100 - var(--kvg, 0)) !important; }
  .kb-ring.over .kb-ring-val { animation: none !important; }
  .k-toast-bar { display: none; }
  .kv-empty .kv-empty-art > svg, .kp-none-bird { animation: none !important; }
  [data-theme-icon] .tm-core, [data-theme-icon] .tm-rays, [data-theme-icon] .tm-moon { transition: none !important; }
  a.khm-c:hover, .khm-c[data-tip]:hover { transform: none; }
}
@media print {
  .kp-back, .kp-trigger, .k-toasts { display: none !important; }
}

/* =============================================================
   VIVID · WAVE 3  —  refinement pass
   Nothing new to learn here: the same identity, tightened. Focus
   rings that match the brand, motion that starts and settles the
   same way everywhere, and surfaces that respond to touch the way
   they respond to a cursor. Additive only.
   ============================================================= */

/* ---- legibility on tinted surfaces ------------------------- */
/* Chips, soft buttons and muted captions sit on washed backgrounds
   (accent-soft, slate-soft, tile gradients), which eats 1–1.5 points
   of contrast versus plain white. In the light theme the ink is
   therefore a step darker than the fill it is named after; the dark
   theme already clears AA because its tints are darker than the ink. */
:root {
  --k-slate-ink: #3a5268;
  --k-accent-ink: #8f421f;
  --k-ok-ink: #1f6b4b;
  --k-err-ink: #93291f;
  --k-gold-ink: #7d5312;
}
html[data-theme="dark"] {
  --k-slate-ink: var(--k-slate);
  --k-accent-ink: var(--k-accent);
  --k-ok-ink: var(--k-ok);
  --k-err-ink: var(--k-err);
  --k-gold-ink: var(--k-warn);
}
/* Captions inside washed tiles/modals lose ~1 point against plain
   surface, so they step up to ink-soft rather than dragging the
   global mute token darker (which would flatten the whole scale). */
.kd-tile .k-muted, .kd-tile .k-small,
a.sd-card .k-muted, .k-modal-sub { color: var(--k-ink-soft); }

.k-chip.dr, .k-chip.slate { color: var(--k-slate-ink); }
.k-chip.cr                { color: var(--k-accent-ink); }
.k-chip.ok                { color: var(--k-ok-ink); }
.k-chip.err               { color: var(--k-err-ink); }
.k-chip.gold              { color: var(--k-gold-ink); }
.k-btn.soft               { color: var(--k-accent-ink); }
.k-btn.soft:hover         { color: var(--k-accent-ink); }
.k-btn.danger-soft        { color: var(--k-err-ink); }
.kv-eyebrow               { color: var(--k-accent-ink); }
.k-pos, .k-stat-sub b.up   { color: var(--k-ok-ink); }
.k-neg, .k-stat-sub b.down { color: var(--k-err-ink); }
.kv-delta.up   { color: var(--k-ok-ink); }
.kv-delta.down { color: var(--k-err-ink); }

/* ---- focus: a brand ring, not a browser outline ------------ */
/* Keyboard users see the same rufous/slate language as everything
   else, and the ring never gets clipped by a card's overflow. */
:focus-visible {
  outline: 2px solid var(--k-accent);
  outline-offset: 2px;
  border-radius: 6px;
}
.k-input:focus-visible, .k-select:focus-visible, .k-textarea:focus-visible {
  outline: none;   /* these already draw their own focus glow */
}
.k-btn:focus-visible, .k-iconbtn:focus-visible, .k-tab:focus-visible {
  outline-offset: 3px;
}
/* row-links are focusable now — show it, and keep the card look */
.k-table tr.k-row-link:focus-visible {
  outline: 2px solid var(--k-accent);
  outline-offset: -2px;
}

/* ---- in-page anchors clear the sticky header --------------- */
html { scroll-behavior: smooth; }
:target { scroll-margin-top: 88px; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* ---- charts: bars rise with the same spring as the cards --- */
.k-bars .bar {
  transition: transform .72s var(--k-spring-soft), filter .2s var(--k-ease), box-shadow .2s var(--k-ease);
}
.k-bars .bar:first-child { transition-delay: 0ms; }

/* ---- tables: a warm accent rail on the hovered row --------- */
@media (hover: hover) {
  .k-table tbody tr { position: relative; }
  .k-table tbody tr.k-row-link:hover {
    background: color-mix(in srgb, var(--k-accent) 5%, transparent);
  }
  .k-table tbody tr.k-row-link:hover td:first-child {
    box-shadow: inset 2px 0 0 var(--k-accent);
  }
}
/* touch: the row acknowledges the press instead of hovering */
@media (hover: none) {
  .k-table tbody tr.k-row-link:active {
    background: color-mix(in srgb, var(--k-accent) 7%, transparent);
  }
}

/* ---- disabled controls read as "not yet", not broken ------- */
.k-btn:disabled, .k-iconbtn:disabled {
  opacity: .5; filter: saturate(.5); box-shadow: none;
}

/* ---- empty states breathe with the page -------------------- */
.k-empty b { letter-spacing: -.01em; }

/* ---- skeletons match the surface they stand in ------------- */
.k-skel { background: color-mix(in srgb, var(--k-ink) 7%, transparent); }

/* ---- one shared press language across every surface -------- */
.kd-tile:active, a.sd-card:active, .ks-file:active, .kp-item:active {
  transform: scale(.985);
}
.kd-tile, a.sd-card, .ks-file, .kp-item {
  transition: transform .18s var(--k-spring-soft), box-shadow .22s var(--k-ease),
              background .2s var(--k-ease), border-color .2s var(--k-ease);
}

@media (prefers-reduced-motion: reduce) {
  .k-bars .bar { transition: none !important; }
  .kd-tile:active, a.sd-card:active, .ks-file:active, .kp-item:active { transform: none; }
}

/* ============================================================
   29 · Tasks — sidebar due-badge
   ============================================================ */
.k-nav a .k-navbadge {
  flex: 0 0 auto; margin-left: auto; min-width: 19px; height: 19px; padding: 0 6px;
  border-radius: 10px; display: grid; place-items: center; overflow: visible;
  background: var(--k-grad-accent, var(--k-accent)); color: #fff;
  font-size: 10.5px; font-weight: 800; letter-spacing: .01em;
  box-shadow: 0 3px 10px color-mix(in srgb, var(--k-accent) 45%, transparent);
  animation: k-navbadge-in .5s var(--k-spring-big, var(--k-ease)) both;
}
.k-nav a.is-active .k-navbadge { background: var(--k-accent); }
@keyframes k-navbadge-in {
  from { transform: scale(.4); opacity: 0; }
  to   { transform: scale(1);  opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .k-nav a .k-navbadge { animation: none !important; }
}

/* =============================================================
 * §30  GROUPED SIDEBAR  (k_nav.php -> k_nav_render)
 * 21 flat links became named, collapsible groups. A group header
 * is itself a link when the module has a hub page; the chevron
 * beside it is a separate button, so "open the hub" and "peek at
 * what is inside" never fight over the same click.
 * ============================================================= */
.k-navgrp { margin: 2px 0 4px; }
.k-navgrp + .k-navgrp { margin-top: 3px; }

.k-navgrp-head {
  display: flex; align-items: center; gap: 2px;
  border-radius: var(--k-radius-sm);
  position: relative;
  transition: background .18s var(--k-ease);
}
.k-navgrp-head:hover { background: var(--k-surface-2); }
.k-nav a.k-navgrp-link,
.k-nav .k-navgrp-link {
  flex: 1 1 auto; min-width: 0;
  background: none; font-weight: 640; letter-spacing: .005em;
  color: var(--k-ink-soft);
}
.k-nav a.k-navgrp-link:hover { background: none; transform: none; color: var(--k-ink); }
.k-navgrp-head:hover .k-navgrp-link { color: var(--k-ink); }
.k-navgrp-head.is-active { background: var(--k-accent-soft); }
.k-navgrp-head.is-active .k-navgrp-link { color: var(--k-accent); }
.k-navgrp-head.is-active::before {
  content: ""; position: absolute; left: -12px; top: 20%; bottom: 20%;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--k-accent);
}
.k-navgrp-link svg { opacity: .9; }
.k-navgrp-head.is-active .k-navgrp-link svg { opacity: 1; }

.k-navgrp-tog {
  flex: 0 0 auto; width: 26px; height: 26px; margin-right: 4px;
  display: grid; place-items: center;
  border: 0; background: none; padding: 0; cursor: pointer;
  color: var(--k-ink-mute); border-radius: 7px;
  transition: color .18s var(--k-ease), background .18s var(--k-ease);
}
.k-navgrp-tog svg { width: 14px; height: 14px; transition: transform .3s var(--k-spring-soft, var(--k-ease)); }
.k-navgrp-tog:hover { color: var(--k-ink); background: var(--k-surface-2); }
.k-navgrp.is-open .k-navgrp-tog svg { transform: rotate(90deg); }
.k-navgrp.is-fixed .k-navgrp-tog { display: none; }

/* The reveal itself: 0fr -> 1fr is the one height transition that
   works without measuring anything in JS. */
.k-navgrp-body {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .32s var(--k-ease);
}
.k-navgrp.is-open > .k-navgrp-body { grid-template-rows: 1fr; }
.k-navgrp-inner {
  overflow: hidden; min-height: 0;
  padding: 0 0 0 14px; position: relative;
}
/* the hairline the child links hang off */
.k-navgrp-inner::before {
  content: ""; position: absolute; left: 7px; top: 3px; bottom: 3px; width: 1.5px;
  border-radius: 2px; background: var(--k-border);
  opacity: 0; transition: opacity .3s var(--k-ease) .06s;
}
.k-navgrp.is-open .k-navgrp-inner::before { opacity: 1; }
.k-navgrp.is-fixed .k-navgrp-inner { padding-left: 0; }
.k-navgrp.is-fixed .k-navgrp-inner::before { display: none; }

/* active child: a segment on that hairline, not the -12px rail
   (which the overflow the grid trick needs would clip) */
.k-nav a.k-nav-lnk.is-active::before {
  left: -7.5px; top: 22%; bottom: 22%; width: 2.5px; border-radius: 2px;
}

/* Staggered slide-in, but only for a group the user just opened -
   replaying it on every page load would make the sidebar twitch. */
.k-navgrp.is-anim.is-open .k-nav-lnk {
  animation: k-nav-in .34s var(--k-spring-soft, var(--k-ease)) backwards;
  animation-delay: calc(var(--i, 0) * 34ms);
}
@keyframes k-nav-in {
  from { opacity: 0; transform: translateX(-9px); }
  to   { opacity: 1; transform: none; }
}

/* a collapsed group still shows what is waiting inside it */
.k-nav .k-navbadge.grp {
  margin: 0 2px 0 auto;
  transition: opacity .22s var(--k-ease), transform .22s var(--k-ease);
}
.k-navgrp.is-open > .k-navgrp-head .k-navbadge.grp {
  opacity: 0; transform: scale(.6); pointer-events: none;
}

/* "you are here" for the profile row in the sidebar foot */
.k-side-foot .k-flex.is-active { color: var(--k-accent); }
.k-side-foot .k-flex.is-active .k-avatar {
  background: var(--k-accent-soft); color: var(--k-accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--k-accent) 32%, transparent);
}

/* the inert copy admin_users renders inside the Access Explorer */
.k-nav.is-preview { pointer-events: none; padding: 4px 8px; }
.k-nav.is-preview .k-navgrp-tog { display: none; }
.k-nav.is-preview .k-nav-lnk,
.k-nav.is-preview .k-navgrp-link { cursor: default; }

@media (prefers-reduced-motion: reduce) {
  .k-navgrp-body { transition: none; }
  .k-navgrp.is-anim.is-open .k-nav-lnk { animation: none; }
  .k-navgrp-tog svg { transition: none; }
}

/* =============================================================
 * §31  HUB TAB STRIPS  (k_subnav)
 * The shared sub-navigation for Money, Reports and Purchasing.
 * Deliberately the same shape as the pets module's kph-tabs,
 * which predates this and is left alone.
 * ============================================================= */
.k-subnav { display: flex; gap: 7px; flex-wrap: wrap; margin: 0 0 18px; }
.k-subnav-t {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 15px; border-radius: 999px;
  border: 1px solid var(--k-border); background: var(--k-surface);
  color: var(--k-ink-soft); font-size: 13px; font-weight: 600;
  white-space: nowrap;
  transition: color .18s var(--k-ease), border-color .18s var(--k-ease),
              transform .22s var(--k-spring-soft, var(--k-ease)), box-shadow .22s var(--k-ease);
  animation: k-subnav-in .45s var(--k-spring-soft, var(--k-ease)) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
@keyframes k-subnav-in { from { opacity: 0; transform: translateY(7px) scale(.95); } }
.k-subnav-t svg { width: 15px; height: 15px; }
.k-subnav-t:hover:not(.is-active) {
  border-color: var(--k-accent); color: var(--k-accent); transform: translateY(-1px);
}
.k-subnav-t:active { transform: scale(.96); }
.k-subnav-t.is-active {
  border-color: transparent; color: #fff;
  background: var(--k-grad-accent, var(--k-accent));
  box-shadow: 0 6px 18px color-mix(in srgb, var(--k-accent) 34%, transparent);
}
/* Neutral by default — a tab count is usually just a count. Pass
   'badgeTone' => 'alert' on the item when it really is a warning. */
.k-subnav-b {
  min-width: 17px; height: 17px; padding: 0 5px; border-radius: 999px;
  display: inline-grid; place-items: center;
  font-size: 10px; font-weight: 800;
  background: var(--k-surface-2); color: var(--k-ink-mute);
}
.k-subnav-b.alert { background: var(--k-err-soft, var(--k-accent-soft)); color: var(--k-err, var(--k-accent)); }
.k-subnav-t.is-active .k-subnav-b { background: rgba(255, 255, 255, .28); color: #fff; }
@media (max-width: 640px) {
  .k-subnav { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; scrollbar-width: none; }
  .k-subnav::-webkit-scrollbar { display: none; }
  .k-subnav-t { flex: 0 0 auto; }
}
/* 44px minimum wherever there is no real pointer */
@media (hover: none), (max-width: 700px) {
  .k-subnav-t { min-height: 44px; }
}
@media (prefers-reduced-motion: reduce) { .k-subnav-t { animation: none; transition: none; } }

/* =============================================================
 * §32  THE "MORE" SHEET  (phones)
 * A 250px side drawer over a 390px screen was cramped; this is a
 * bottom sheet holding every permitted page as a tile. Closed
 * state is visibility, not [hidden], so opening is one class flip
 * and the slide plays without waiting on a second frame.
 * ============================================================= */
.k-sheet-scrim {
  position: fixed; inset: 0; z-index: 78;
  background: color-mix(in srgb, #000 42%, transparent);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .26s var(--k-ease), visibility .26s;
}
.k-sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
  max-height: min(82vh, 82dvh);
  display: flex; flex-direction: column;
  background: var(--k-surface);
  border-top: 1px solid var(--k-border);
  border-radius: 22px 22px 0 0;
  box-shadow: 0 -18px 50px rgba(0, 0, 0, .28);
  transform: translateY(101%);
  visibility: hidden;
  transition: transform .34s var(--k-spring-soft, var(--k-ease)), visibility .34s;
  padding-bottom: calc(10px + env(safe-area-inset-bottom));
}
html.k-sheet-open .k-sheet-scrim { opacity: 1; visibility: visible; pointer-events: auto; }
html.k-sheet-open .k-sheet { transform: translateY(0); visibility: visible; }
html.k-sheet-open, html.k-sheet-open body { overflow: hidden; }

.k-sheet-grip { flex: 0 0 auto; height: 22px; display: grid; place-items: center; cursor: pointer; }
.k-sheet-grip::before {
  content: ""; width: 42px; height: 4px; border-radius: 3px;
  background: var(--k-border); transition: background .18s var(--k-ease);
}
.k-sheet-grip:active::before { background: var(--k-accent); }
.k-sheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 2px 14px 8px; }

.k-sheet-sec { margin-bottom: 16px; }
html.k-sheet-open .k-sheet-sec {
  animation: k-sheet-sec-in .42s var(--k-spring-soft, var(--k-ease)) backwards;
  animation-delay: calc(60ms + var(--g, 0) * 52ms);
}
@keyframes k-sheet-sec-in { from { opacity: 0; transform: translateY(16px); } }
.k-sheet-sech {
  display: flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--k-ink-mute); margin: 0 2px 8px;
}
a.k-sheet-sech { color: var(--k-accent); }
a.k-sheet-sech svg { width: 13px; height: 13px; }

.k-sheet-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
@media (max-width: 359px) { .k-sheet-grid { grid-template-columns: repeat(3, 1fr); } }
.k-sheet-tile {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 12px 5px 10px; border-radius: 15px;
  background: var(--k-surface-2); color: var(--k-ink-soft);
  min-height: 76px; text-align: center; min-width: 0;
  transition: transform .2s var(--k-spring-soft, var(--k-ease)),
              background .18s var(--k-ease), color .18s var(--k-ease);
}
.k-sheet-tile:active { transform: scale(.94); }
.k-sheet-tile.is-active { background: var(--k-accent-soft); color: var(--k-accent); }
.k-sheet-ic { position: relative; display: grid; place-items: center; }
.k-sheet-ic svg { width: 21px; height: 21px; }
.k-sheet-ic .k-navbadge {
  position: absolute; top: -6px; right: -11px;
  min-width: 17px; height: 17px; padding: 0 5px; border-radius: 9px;
  display: grid; place-items: center; font-size: 10px; font-weight: 800;
  background: var(--k-grad-accent, var(--k-accent)); color: #fff;
}
.k-sheet-lbl {
  font-size: 11px; font-weight: 600; line-height: 1.25;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}

.k-sheet-foot {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 12px 16px 4px; border-top: 1px solid var(--k-border); margin-top: 2px;
}
.k-sheet-me { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; color: inherit; }
.k-sheet-me .k-who { min-width: 0; }
.k-sheet-me .k-who b { display: block; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.k-sheet-me .k-who span { font-size: 11px; color: var(--k-ink-mute); text-transform: capitalize; }

.k-tabbar .k-tab { position: relative; }
.k-tabbar .k-navbadge.tab {
  position: absolute; top: 4px; left: 50%; margin-left: 4px;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 9px;
  display: grid; place-items: center; font-size: 9.5px; font-weight: 800;
  background: var(--k-grad-accent, var(--k-accent)); color: #fff;
  /* a count worth reading deserves to announce itself once */
  animation: kv-badge-pop .42s var(--k-spring-big) both .18s;
}
@keyframes kv-badge-pop {
  from { transform: scale(0); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}
@media (min-width: 681px) { .k-sheet, .k-sheet-scrim { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .k-sheet, .k-sheet-scrim { transition: none; }
  html.k-sheet-open .k-sheet-sec { animation: none; }
}

/* =============================================================
 * §33  TEXT-ONLY NAVIGATION
 * The owner asked for a menu of words, not glyphs. Every nav
 * surface — sidebar, phone tabs, More sheet, hub strips, command
 * palette — now renders labels alone, so the job here is to give
 * the hierarchy back what the icons used to carry: indentation,
 * tracking and weight instead of shape and colour. The `… svg`
 * rules in the sections above simply stop matching.
 *
 * Two glyphs deliberately survive, because they are affordances
 * rather than labels: the group chevron (a collapsed section with
 * no chevron looks like a dead end) and the top-bar burger.
 * ============================================================= */

/* ---- sidebar ---------------------------------------------- */
.k-nav a { gap: 0; }

/* A section header steps down to small tracked caps — matching
   the fixed "Everyday" header — so the group reads as a label and
   its children read as the destinations. */
.k-nav-sec { margin: 15px 10px 4px; }
.k-navgrp { margin: 0 0 2px; }
.k-navgrp + .k-navgrp { margin-top: 9px; }
.k-navgrp-head { gap: 2px; }
.k-nav a.k-navgrp-link,
.k-nav .k-navgrp-link {
  padding: 7px 10px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--k-ink-mute);
}
/* the header is a link when the module has a hub; say so on hover */
.k-nav a.k-navgrp-link { text-decoration-color: transparent; }
.k-nav a.k-navgrp-link:hover {
  color: var(--k-ink-soft);
  text-decoration: underline; text-decoration-thickness: 1px;
  text-underline-offset: 3px; text-decoration-color: currentColor;
}
.k-navgrp-head:hover .k-navgrp-link { color: var(--k-ink-soft); }
/* a filled bar behind 10px caps reads as a banner, not a link —
   the accent text plus the rail is enough */
.k-navgrp-head.is-active { background: none; }
.k-navgrp-head.is-active .k-navgrp-link { color: var(--k-accent); }

/* the children now carry the emphasis */
.k-nav a.k-nav-lnk { padding: 8px 11px; font-size: 14px; font-weight: 560; }
.k-nav a.k-nav-lnk.is-active { font-weight: 660; }
.k-navgrp-inner { padding-left: 15px; }

/* ---- phone tab bar ---------------------------------------- */
@media (max-width: 680px) {
  .k-tab {
    gap: 0; padding: 8px 3px; min-height: 46px;
    border-radius: 11px; font-size: 11.5px; font-weight: 640;
    position: relative; isolation: isolate;
    transition: color .2s var(--k-ease), transform .25s var(--k-spring);
  }
  /* The active state used to be a flat background swap on the most-
     touched control in the app. It is now a pill that springs in behind
     the label — and because it carries a view-transition-name, and the
     tab bar itself is pinned across navigations, the browser tweens the
     pill from the old tab to the new one as you move between pages.
     Only ever one .is-active tab, so the name is never duplicated. */
  .k-tab::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    border-radius: inherit;
    background: var(--k-accent-soft);
    opacity: 0; transform: scale(.72);
    transition: opacity .2s var(--k-ease), transform .34s var(--k-spring);
  }
  .k-tab.is-active::before {
    opacity: 1; transform: none;
    view-transition-name: k-tab-pill;
  }
  .k-tab.is-active { font-weight: 720; }
  .k-tab:active { transform: scale(.92); }
  .k-tab:active::before { opacity: 1; transform: scale(.96); }
  /* the centre action is a word now, so the disc becomes a pill */
  .k-tab-new .fab {
    width: auto; height: 46px; min-width: 62px; padding: 0 18px;
    margin-top: -16px; border-radius: 999px;
    font-size: 13.5px; font-weight: 750; letter-spacing: .02em;
  }
  .k-tab-new:active .fab { transform: scale(.92); }
  .k-tabbar .k-navbadge.tab { top: 2px; }
}

/* ---- "More" sheet ----------------------------------------- */
/* Four icon squares per row became two wide labels: a word needs
   room a glyph does not. */
.k-sheet-grid { grid-template-columns: repeat(2, 1fr); gap: 7px; }
@media (max-width: 359px) { .k-sheet-grid { grid-template-columns: 1fr; } }
.k-sheet-tile {
  flex-direction: row; align-items: center; justify-content: space-between;
  gap: 8px; padding: 13px 14px; border-radius: 13px;
  min-height: 0; text-align: left;
}
.k-sheet-lbl { font-size: 13.5px; font-weight: 620; white-space: nowrap; }
.k-sheet-tile .k-navbadge {
  flex: 0 0 auto; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px; display: grid; place-items: center;
  font-size: 10px; font-weight: 800;
  background: var(--k-grad-accent, var(--k-accent)); color: #fff;
}
.k-sheet-sech-go {
  margin-left: auto; font-size: 9.5px; font-weight: 700;
  letter-spacing: .12em; color: var(--k-accent);
}

/* ---- hub strips ------------------------------------------- */
.k-subnav-t { gap: 6px; }
.kph-tab { gap: 6px; }

/* ---- command palette -------------------------------------- */
.kp-item { padding: 9px 14px; }
.kp-skel-ic { display: none; }
/* the trigger lost its magnifier, so it must keep its word —
   the old rule shrank it to a 36px square and hid the label,
   which would now leave an empty button */
@media (max-width: 700px) {
  .kp-trigger { width: auto; padding: 0 12px; justify-content: flex-start; }
  .kp-trigger .kp-trigger-t { display: inline; }
  .kp-trigger .k-kbd { display: none; }
}

/* the sidebar foot's sign-out was an icon-only button; it needs a
   word now, but a quiet one — it must not outweigh the profile row */
.k-signout {
  flex: 0 0 auto; border: 1px solid var(--k-border); background: none;
  color: var(--k-ink-mute); font: inherit; font-size: 11px; font-weight: 650;
  padding: 5px 9px; border-radius: 8px; cursor: pointer; white-space: nowrap;
  transition: color .18s var(--k-ease), border-color .18s var(--k-ease),
              background .18s var(--k-ease);
}
.k-signout:hover {
  color: var(--k-err, var(--k-accent));
  border-color: color-mix(in srgb, var(--k-err, var(--k-accent)) 45%, transparent);
  background: var(--k-err-soft, var(--k-accent-soft));
}

/* =============================================================
 * §34  KSEL — the listbox in front of every native <select>
 * The trigger inherits .k-select, so it picks up every width and
 * sizing rule already written for the field it replaces. The
 * popup is fixed-position on <body> (a modal's overflow-y:auto
 * would clip an absolute one) and becomes a bottom sheet on a
 * phone, where a 340px floating list is a poor target.
 * ============================================================= */

select[data-ksel-native] {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; pointer-events: none;
  padding: 0; border: 0; margin: -1px; z-index: -1;
  /* deliberately NOT display:none — a `required` select hidden
     that way makes the browser refuse to submit, silently */
}

.ksel-btn {
  display: inline-flex; align-items: center; gap: 10px;
  text-align: left; cursor: pointer;
  justify-content: space-between;
  -webkit-tap-highlight-color: transparent;
}
.ksel-btn:disabled { opacity: .55; cursor: not-allowed; }
.ksel-val {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ksel-val.is-empty::after { content: "Select…"; color: var(--k-ink-mute); }
.ksel-btn.is-bad { border-color: var(--k-err); box-shadow: 0 0 0 3px var(--k-err-soft); }

/* a drawn caret, not a glyph — it rotates when the list is open */
.ksel-caret {
  flex: 0 0 auto; width: 9px; height: 9px; margin-left: 2px;
  border-right: 1.8px solid currentColor; border-bottom: 1.8px solid currentColor;
  border-radius: 0 0 2px 0; opacity: .5;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .28s var(--k-spring-soft, var(--k-ease)), opacity .2s var(--k-ease);
}
.ksel-btn:hover .ksel-caret { opacity: .85; }
.ksel-btn.is-open .ksel-caret { transform: translateY(1px) rotate(-135deg); opacity: 1; }
.ksel-btn.is-open { border-color: var(--k-slate); box-shadow: 0 0 0 3px var(--k-slate-soft); }

/* ---- the popup ------------------------------------------- */
.ksel-pop {
  /* Above the modal backdrop (150) — a picker opened from inside a
     modal was rendering behind it — but below toasts (200) and the
     command palette (220), neither of which can coexist with it. */
  position: fixed; z-index: 160;
  display: flex; flex-direction: column;
  background: var(--k-surface); color: var(--k-ink);
  border: 1px solid var(--k-border); border-radius: 14px;
  box-shadow: 0 22px 48px -18px rgba(0, 0, 0, .38), 0 3px 10px -4px rgba(0, 0, 0, .18);
  overflow: hidden;
  opacity: 0; transform: translateY(-6px) scale(.975);
  transform-origin: top center;
  transition: opacity .17s var(--k-ease), transform .24s var(--k-spring-soft, var(--k-ease));
}
.ksel-pop.is-up { transform-origin: bottom center; transform: translateY(6px) scale(.975); }
.ksel-pop.is-open { opacity: 1; transform: none; }

.ksel-search { padding: 9px 9px 5px; flex: 0 0 auto; }
.ksel-search-i {
  width: 100%; padding: 8px 11px; font: inherit; font-size: 13.5px;
  color: var(--k-ink); background: var(--k-surface-2);
  border: 1px solid transparent; border-radius: 9px; outline: none;
  transition: border-color .18s var(--k-ease), background .18s var(--k-ease);
}
.ksel-search-i:focus { border-color: var(--k-slate); background: var(--k-surface); }

.ksel-list { overflow-y: auto; overscroll-behavior: contain; padding: 6px; min-height: 0; }
.ksel-grp {
  padding: 9px 10px 4px;
  font-size: 10px; font-weight: 750; letter-spacing: .13em;
  text-transform: uppercase; color: var(--k-ink-mute);
}
.ksel-opt {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 11px; border-radius: 9px; cursor: pointer;
  font-size: 14px; color: var(--k-ink-soft);
  transition: background .14s var(--k-ease), color .14s var(--k-ease);
}
.ksel-opt-t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ksel-opt.is-cur { background: var(--k-surface-2); color: var(--k-ink); }
.ksel-opt.is-on  { color: var(--k-accent); font-weight: 640; }
.ksel-opt.is-on.is-cur { background: var(--k-accent-soft); }
.ksel-opt.is-off { opacity: .42; cursor: not-allowed; }

/* a drawn tick, shown only on the chosen row */
.ksel-tick {
  flex: 0 0 auto; width: 6px; height: 11px; margin-right: 2px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(42deg) scale(.4); opacity: 0;
  transition: opacity .18s var(--k-ease), transform .3s var(--k-spring-big, var(--k-ease));
}
.ksel-opt.is-on .ksel-tick { opacity: 1; transform: rotate(42deg) scale(1); }

.ksel-none { padding: 18px 14px 20px; text-align: center; font-size: 13px; color: var(--k-ink-mute); }

/* options drift in behind the popup, capped so a 200-account
   ledger list does not spend two seconds arriving */
.ksel-pop.is-open .ksel-opt {
  animation: ksel-opt-in .26s var(--k-ease) backwards;
  animation-delay: calc(var(--n, 0) * 14ms);
}
@keyframes ksel-opt-in { from { opacity: 0; transform: translateY(5px); } }

/* ---- phone: a bottom sheet ------------------------------- */
.ksel-scrim {
  position: fixed; inset: 0; z-index: 159;
  background: rgba(12, 10, 9, .42); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity .22s var(--k-ease);
}
.ksel-scrim.is-open { opacity: 1; }

.ksel-pop.is-sheet {
  left: 0 !important; right: 0; bottom: 0 !important; top: auto !important;
  max-width: none !important; min-width: 0 !important;
  max-height: min(72vh, 560px) !important;
  border-radius: 20px 20px 0 0; border-bottom: 0;
  transform: translateY(100%); transform-origin: bottom center;
  padding-bottom: env(safe-area-inset-bottom);
  transition: opacity .2s var(--k-ease), transform .34s var(--k-spring-soft, var(--k-ease));
}
.ksel-pop.is-sheet.is-open { transform: none; }
.ksel-sheet-head {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 14px 16px 8px;
}
.ksel-sheet-title { font-size: 13px; font-weight: 700; color: var(--k-ink); }
.ksel-sheet-x {
  border: 0; background: none; font: inherit; font-size: 13px; font-weight: 650;
  color: var(--k-accent); cursor: pointer; padding: 4px 2px;
}
.ksel-pop.is-sheet .ksel-opt { padding: 13px 14px; font-size: 15px; }
.ksel-pop.is-sheet .ksel-list { padding: 4px 10px calc(10px + env(safe-area-inset-bottom)); }

@media (max-width: 700px) {
  .ksel-btn { font-size: 16px; }      /* matches .k-select — no iOS focus zoom */
  .ksel-search-i { font-size: 16px; }
}

/* the phone override that used to reach the select by class */
@media (max-width: 640px) {
  .jf-table .ksel-btn { min-width: 0; width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .ksel-pop, .ksel-scrim, .ksel-caret, .ksel-tick { transition: none; }
  .ksel-pop.is-open .ksel-opt { animation: none; }
}

/* =============================================================
 * §35  Account popover + a smoother sidebar accordion
 * ============================================================= */

.k-um { position: relative; flex: 0 0 auto; }
.k-um-btn {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 9px 0 5px;
  border: 1px solid transparent; border-radius: 10px;
  background: none; color: var(--k-ink-soft); font: inherit; font-size: 13px;
  cursor: pointer; max-width: 190px;
  transition: background .18s var(--k-ease), border-color .18s var(--k-ease),
              color .18s var(--k-ease);
}
.k-um-btn:hover, .k-um-btn.is-open {
  background: var(--k-surface); border-color: var(--k-border); color: var(--k-ink);
}
.k-um-name {
  font-weight: 620; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.k-um-btn .ksel-caret { width: 7px; height: 7px; border-width: 1.6px; }
.k-um-btn.is-open .ksel-caret { transform: translateY(1px) rotate(-135deg); }
.k-avatar.sm { width: 26px; height: 26px; font-size: 11px; }
@media (max-width: 860px) { .k-um-name, .k-um-btn .ksel-caret { display: none; }
                            .k-um-btn { padding: 0 5px; } }

.k-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  min-width: 226px; padding: 7px;
  background: var(--k-surface); border: 1px solid var(--k-border);
  border-radius: 14px;
  box-shadow: 0 22px 48px -18px rgba(0, 0, 0, .38), 0 3px 10px -4px rgba(0, 0, 0, .18);
  opacity: 0; visibility: hidden;
  transform: translateY(-7px) scale(.97); transform-origin: top right;
  transition: opacity .17s var(--k-ease),
              transform .26s var(--k-spring-soft, var(--k-ease)),
              visibility 0s linear .26s;
}
.k-menu.is-open {
  opacity: 1; visibility: visible; transform: none;
  transition-delay: 0s, 0s, 0s;
}

.k-menu-head {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 9px 10px; margin-bottom: 5px;
  border-bottom: 1px solid var(--k-line);
}
.k-menu-who { min-width: 0; display: flex; flex-direction: column; }
.k-menu-who b { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-menu-who span { font-size: 11px; color: var(--k-ink-mute); text-transform: capitalize; }

.k-menu-i {
  display: block; width: 100%; text-align: left;
  padding: 9px 10px; border: 0; border-radius: 9px;
  background: none; color: var(--k-ink-soft);
  font: inherit; font-size: 13.5px; font-weight: 560;
  cursor: pointer; text-decoration: none;
  transition: background .15s var(--k-ease), color .15s var(--k-ease),
              transform .15s var(--k-ease);
}
.k-menu-i:hover, .k-menu-i:focus-visible {
  background: var(--k-surface-2); color: var(--k-ink); transform: translateX(2px);
  outline: none;
}
.k-menu-i.is-danger { color: var(--k-err, var(--k-accent)); }
.k-menu-i.is-danger:hover { background: var(--k-err-soft, var(--k-accent-soft)); }
.k-menu-sep { height: 1px; margin: 5px 4px; background: var(--k-line); }

/* the items arrive just behind the panel */
.k-menu.is-open .k-menu-i {
  animation: k-menu-i-in .24s var(--k-ease) backwards;
  animation-delay: calc(60ms + var(--i, 0) * 18ms);
}
@keyframes k-menu-i-in { from { opacity: 0; transform: translateX(-5px); } }

/* ---- sidebar accordion: fade the contents with the height,
        so a fold reads as one gesture rather than a curtain ---- */
.k-navgrp-body { transition: grid-template-rows .34s cubic-bezier(.32, .72, 0, 1); }
.k-navgrp-inner {
  opacity: 0; transform: translateY(-3px);
  transition: opacity .2s var(--k-ease), transform .3s cubic-bezier(.32, .72, 0, 1);
}
.k-navgrp.is-open > .k-navgrp-body > .k-navgrp-inner {
  opacity: 1; transform: none;
  transition-delay: .04s;
}

@media (prefers-reduced-motion: reduce) {
  .k-menu { transition: opacity .01s, visibility 0s; transform: none; }
  .k-menu.is-open .k-menu-i { animation: none; }
  .k-navgrp-inner { transition: none; }
}
