/* Kelbra — "Orbit Hub" dark theme.
   Shared design tokens + building blocks (starfield background, topbar,
   buttons, badges, the glowing orb, and the tool "bubbles") so every page
   in Kelbra — this dashboard and, as they're re-skinned, each tool page —
   draws from the same palette, fonts, and component styles instead of
   re-inventing them per page. */

@import url('https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;600;700&family=Sora:wght@600;700;800&display=swap');

:root {
  --k-bg: #06061A;
  --k-primary: #4158D0;
  --k-core: #00F2FE;
  --k-accent2: #C850C0;
  --k-ink: #ffffff;
  --k-ink-soft: #C9D3F0;
  --k-ink-faint: #8B99C2;
  --k-line: color-mix(in srgb, var(--k-primary) 35%, transparent);
}

.k-dark * { box-sizing: border-box; }
.k-dark {
  font-family: 'Public Sans', system-ui, sans-serif;
  background: var(--k-bg);
  color: var(--k-ink);
  min-height: 100vh;
  position: relative;
  /* Redeclared here (not just at :root, above) so it resolves using THIS
     element's own --k-primary. A custom property that embeds var() to
     another custom property freezes its resolved color at the element
     where it's declared — declaring --k-line only at :root would freeze
     it to the default accent before the Appearance script's runtime
     override (applied as an inline style on this .k-dark element) ever
     ran, since :root (html) is an ancestor of it, not the same element. */
  --k-line: color-mix(in srgb, var(--k-primary) 35%, transparent);
}
.k-dark a { color: inherit; text-decoration: none; }

/* Ambient starfield + vignette — sits behind everything. Add
   <div class="k-field"></div><div class="k-vignette"></div> right after
   the .k-dark root so they can be position:fixed and cover the viewport. */
.k-field {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    radial-gradient(rgba(255,255,255,0.9) 1.3px, transparent 1.3px),
    radial-gradient(rgba(255,255,255,0.55) 1px, transparent 1px),
    radial-gradient(rgba(255,255,255,0.35) 1px, transparent 1px);
  background-size: 220px 220px, 130px 130px, 70px 70px;
  background-position: 10px 20px, 90px 60px, 40px 100px;
}
.k-vignette {
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(circle at 50% 40%, transparent 25%, var(--k-bg) 82%);
}

.k-topbar {
  position: relative; z-index: 5;
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 40px; border-bottom: 1px solid var(--k-line);
}
.k-brand { display: flex; align-items: center; gap: 10px; }
.k-logo-mark {
  width: 28px; height: 28px; border-radius: 8px;
  background: radial-gradient(circle at 35% 30%, var(--k-core), var(--k-primary));
  color: var(--k-bg); display: flex; align-items: center; justify-content: center;
  font-family: 'Sora', sans-serif; font-weight: 800; font-size: 14px;
  box-shadow: 0 0 16px rgba(0,242,254,0.45);
}
.k-wordmark { font-family: 'Sora', sans-serif; font-weight: 700; font-size: 16px; letter-spacing: 0.02em; }

.k-status-pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: color-mix(in srgb, var(--k-core) 10%, transparent); color: var(--k-core);
  border: 1px solid color-mix(in srgb, var(--k-core) 35%, transparent);
  padding: 3px 11px; border-radius: 999px; font-size: 11px; font-weight: 600;
}
.k-status-dot { width: 5px; height: 5px; border-radius: 999px; background: var(--k-core); box-shadow: 0 0 6px var(--k-core); }
.k-status-pill.k-status-warn { background: rgba(200,80,192,0.12); color: #E9A6E4; border-color: rgba(200,80,192,0.4); }
.k-status-pill.k-status-warn .k-status-dot { background: var(--k-accent2); box-shadow: 0 0 6px var(--k-accent2); }

.k-btn {
  font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 13.5px;
  border-radius: 8px; padding: 9px 16px; cursor: pointer; border: 1px solid transparent;
  display: inline-flex; align-items: center; gap: 6px;
}
.k-btn-primary { background: var(--k-primary); color: #fff; }
.k-btn-primary:hover { filter: brightness(1.12); }
.k-btn-secondary { background: color-mix(in srgb, var(--k-primary) 12%, transparent); border-color: var(--k-line); color: var(--k-ink-soft); }
.k-btn-secondary:hover { border-color: rgba(0,242,254,0.5); color: #fff; }
.k-btn:disabled { opacity: 0.6; cursor: default; }

/* A real button (not just colored text) back to the dashboard, so no one
   has to reach for the browser's back button. Every owner-facing page
   loads this shared stylesheet, so this one definition covers all of them
   — see server/public/index.html, appearance.html, and every tool page's
   topbar for where it's used. */
.k-home-btn {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: 'Public Sans', sans-serif; font-weight: 600; font-size: 13px;
  color: var(--k-ink-soft); text-decoration: none; white-space: nowrap;
  background: color-mix(in srgb, var(--k-primary) 12%, transparent);
  border: 1px solid var(--k-line); border-radius: 8px;
  padding: 7px 13px 7px 10px; cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}
.k-home-btn:hover { border-color: color-mix(in srgb, var(--k-primary) 55%, transparent); color: #fff; background: color-mix(in srgb, var(--k-primary) 20%, transparent); }
.k-home-btn svg { flex-shrink: 0; }

/* The orb + orbit ring + tool bubbles */
.k-stage { position: relative; z-index: 5; width: 860px; max-width: 92vw; height: 860px; margin: 20px auto 40px; }
.k-orbit-ring { position: absolute; border-radius: 50%; border: 1px dashed color-mix(in srgb, var(--k-primary) 32%, transparent); }
.k-ring-main { width: 600px; height: 600px; left: 130px; top: 130px; animation: k-spin 42s linear infinite; }
.k-ring-outer { width: 760px; height: 760px; left: 50px; top: 50px; border-color: rgba(200,80,192,0.16); animation: k-spin 68s linear infinite reverse; }
.k-core-wrap { position: absolute; width: 170px; height: 170px; left: 345px; top: 345px; display: flex; align-items: center; justify-content: center; }
.k-core {
  position: absolute; width: 150px; height: 150px; border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, #EAFFFE 0%, var(--k-core) 22%, var(--k-primary) 62%, color-mix(in srgb, var(--k-primary) 0%, transparent) 100%);
  box-shadow: 0 0 70px color-mix(in srgb, var(--k-core) 55%, transparent), 0 0 140px color-mix(in srgb, var(--k-primary) 35%, transparent);
  animation: k-pulse 4.5s ease-in-out infinite;
}
.k-core-label {
  position: relative; z-index: 1; font-family: 'Sora', sans-serif; font-weight: 800;
  font-size: 16px; letter-spacing: 0.08em; text-transform: uppercase; color: #fff;
  text-shadow: 0 2px 10px rgba(0,0,0,0.85), 0 0 3px rgba(0,0,0,0.9); cursor: pointer;
}

.k-bubble {
  position: absolute; width: 96px; height: 96px; border-radius: 50%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 5px; text-align: center; padding: 8px;
  background: radial-gradient(circle at 32% 28%, rgba(255,255,255,0.08), rgba(10,10,28,0.92));
  border: 1px solid color-mix(in srgb, var(--k-primary) 40%, transparent); box-shadow: 0 0 18px color-mix(in srgb, var(--k-primary) 22%, transparent);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; cursor: pointer;
}
.k-bubble:hover { transform: scale(1.1); border-color: var(--k-core); box-shadow: 0 0 32px color-mix(in srgb, var(--k-core) 50%, transparent); }
.k-bubble-icon { color: var(--k-core); }
.k-bubble-label { font-size: 9.3px; font-weight: 600; letter-spacing: 0.02em; color: var(--k-ink-soft); line-height: 1.25; }

.k-bubble-ghost { background: transparent; border: 1.5px dashed color-mix(in srgb, var(--k-primary) 40%, transparent); box-shadow: none; opacity: 0.75; cursor: default; }
.k-bubble-ghost:hover { transform: none; border-color: color-mix(in srgb, var(--k-primary) 40%, transparent); box-shadow: none; opacity: 0.75; }
.k-bubble-ghost .k-bubble-icon, .k-bubble-ghost .k-bubble-label { color: #6B7AA8; }

.k-bubble-locked { cursor: default; opacity: 0.45; }
.k-bubble-locked:hover { transform: none; border-color: color-mix(in srgb, var(--k-primary) 40%, transparent); box-shadow: 0 0 18px color-mix(in srgb, var(--k-primary) 22%, transparent); }
.k-bubble-lock-icon { position: absolute; top: 8px; right: 10px; color: #6B7AA8; }

@keyframes k-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes k-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }

/* Appearance presets — same structure, different core + ring treatment.
   Default (no data-theme, or data-theme="orbit") is styled above already. */
.k-dark[data-theme="cyber"] .k-orbit-ring { display: none; }
.k-dark[data-theme="cyber"] .k-core { box-shadow: 0 0 100px rgba(0,242,254,0.6), 0 0 200px color-mix(in srgb, var(--k-primary) 40%, transparent); }

/* Matrix — the same Orbit Hub ring/bubble/core layout, but with the
   starfield swapped for an animated falling-code background (canvas,
   driven by k-matrix-rain.js logic inlined per page) and a classic
   Matrix green palette instead of the user's chosen accent — the green
   is the whole point of this preset, same reasoning as Neural Core. */
.k-dark[data-theme="matrix"] { --k-core: #00FF41; --k-primary: #0AA33A; }
.k-dark[data-theme="matrix"] .k-field { display: none; }
.k-dark[data-theme="matrix"] .k-vignette { background: radial-gradient(circle at 50% 40%, rgba(6,10,6,0.45) 0%, var(--k-bg) 85%); }
.k-matrix-canvas { display: none; position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.k-dark[data-theme="matrix"] .k-matrix-canvas { display: block; }

/* Neural Core — a real glowing-brain image (a photo David generated and we
   background-removed) at the center, with jagged lightning bolts reaching
   out to every tool bubble, including the empty "Add app" slots so it
   already looks complete before any bubble is added. This preset gets its
   own fixed emerald palette rather than the user's chosen accent color —
   the green is core to the look, the same way Cyber Core always drops the
   orbit rings regardless of accent. */
.k-dark[data-theme="neural"] { --k-core: #39FFC1; --k-primary: #0FBE87; }
.k-dark[data-theme="neural"] .k-orbit-ring,
.k-dark[data-theme="neural"] .k-core,
.k-dark[data-theme="neural"] .k-core-label { display: none; }
.k-dark[data-theme="neural"] .k-bubble-icon,
.k-dark[data-theme="matrix"] .k-bubble-icon { color: var(--k-core); }
.k-neural-stage { display: none; }
.k-dark[data-theme="neural"] .k-neural-stage { display: block; }
.k-neural-stage { position: absolute; inset: 0; pointer-events: none; }
.k-neural-svg { position: absolute; inset: 0; width: 100%; height: 100%; }

.k-brain-img {
  position: absolute; left: 50%; top: 50%;
  width: 250px; max-width: 34%; height: auto;
  filter: drop-shadow(0 0 30px color-mix(in srgb, var(--k-primary) 55%, transparent)) drop-shadow(0 0 60px color-mix(in srgb, var(--k-core) 30%, transparent));
  /* A dedicated keyframe (rather than reusing k-pulse) because the
     centering translate(-50%,-50%) and the pulse scale both animate
     `transform` — an element can only have one `transform` animation at a
     time, so k-pulse alone would silently replace the centering offset
     with just the scale, snapping the image to the stage's corner. */
  animation: k-brain-pulse 4.5s ease-in-out infinite;
}
@keyframes k-brain-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50% { transform: translate(-50%, -50%) scale(1.06); }
}
/* The "Black & white" combo is a genuine two-tone override — but the brain
   photo is a fixed set of pixels (a green glass photo), not something a
   CSS variable alone can recolor. When that combo is active (data-mono,
   set alongside data-theme by the dashboard/Appearance JS), desaturate the
   photo itself to match its now-white/black glow above. */
.k-dark[data-mono="1"] .k-brain-img {
  filter: grayscale(1) contrast(1.15) drop-shadow(0 0 30px rgba(255,255,255,0.5)) drop-shadow(0 0 55px rgba(0,0,0,0.55));
}

.k-bolt { fill: none; stroke: #EAFFF8; stroke-width: 2.1; stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 0 4px #fff) drop-shadow(0 0 10px var(--k-core)) drop-shadow(0 0 20px var(--k-core)) drop-shadow(0 0 32px color-mix(in srgb, var(--k-primary) 80%, transparent));
  animation: k-bolt-flicker ease-in-out infinite; }
@keyframes k-bolt-flicker { 0%, 100% { opacity: 0.55; } 45% { opacity: 1; } 55% { opacity: 0.8; } }

/* Business — the orb becomes the business's own uploaded logo instead of
   Kelbra's built-in art, and the accent color comes from a color sampled
   out of that logo (set the same way Orbit/Cyber's user-picked accent is
   set — an inline --k-primary override — see appearance.html) rather
   than a fixed built-in palette like Neural Core/Matrix, since the whole
   point of this preset is to match whatever the business actually looks
   like, not a look Kelbra chose for them. */
.k-dark[data-theme="business"] .k-orbit-ring { display: none; }
.k-dark[data-theme="business"] .k-core-label { display: none; }
.k-dark[data-theme="business"] .k-core {
  background: #0D1026;
  border: 1px solid color-mix(in srgb, var(--k-primary) 55%, transparent);
  box-shadow: 0 0 60px color-mix(in srgb, var(--k-primary) 45%, transparent), 0 0 120px color-mix(in srgb, var(--k-primary) 20%, transparent);
}
.k-business-logo {
  display: none;
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 112px; height: 112px; object-fit: contain; border-radius: 12px;
  pointer-events: none;
}
.k-dark[data-theme="business"] .k-business-logo { display: block; }

/* ---------------------------------------------------------------------
   Date and time fields
   ---------------------------------------------------------------------
   Every date field in Kelbra is already a real <input type="date">, so
   Chrome, Edge and Safari all give it a native calendar picker for free.
   The problem was that browsers draw that little calendar icon in near
   black, and Kelbra's fields sit on a dark background — so the control
   was there but invisible, and people assumed they had to type the date
   in by hand (David hit this in Review Manager, Sep 2 2026).

   Inverting the icon makes it light instead. It's scoped to .k-dark, so
   if a light surface is ever added the icon there keeps its normal dark
   glyph. Applies to every tool that loads this stylesheet, including any
   added later — which is the point of fixing it here rather than per
   tool. Firefox draws no icon at all and simply ignores these rules.
   --------------------------------------------------------------------- */
.k-dark input[type="date"]::-webkit-calendar-picker-indicator,
.k-dark input[type="time"]::-webkit-calendar-picker-indicator,
.k-dark input[type="datetime-local"]::-webkit-calendar-picker-indicator,
.k-dark input[type="month"]::-webkit-calendar-picker-indicator,
.k-dark input[type="week"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: 0.62;
  cursor: pointer;
  padding: 2px;
  border-radius: 4px;
}

.k-dark input[type="date"]::-webkit-calendar-picker-indicator:hover,
.k-dark input[type="time"]::-webkit-calendar-picker-indicator:hover,
.k-dark input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
.k-dark input[type="month"]::-webkit-calendar-picker-indicator:hover,
.k-dark input[type="week"]::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
  background: color-mix(in srgb, var(--k-primary) 28%, transparent);
}

.k-dark input[type="date"]::-webkit-calendar-picker-indicator:focus-visible,
.k-dark input[type="time"]::-webkit-calendar-picker-indicator:focus-visible {
  opacity: 1;
  outline: 2px solid var(--k-primary);
  outline-offset: 1px;
}

/* An empty date field shows placeholder text (mm/dd/yyyy) that browsers
   render at full strength, so it reads as a filled-in value at a glance.
   Dimming it makes an empty field look empty. */
.k-dark input[type="date"]:not(:focus):invalid::-webkit-datetime-edit,
.k-dark input[type="date"]:not(:focus):placeholder-shown::-webkit-datetime-edit {
  color: var(--k-ink-faint);
}

/* ---------------------------------------------------------------------
   Open dropdown lists
   ---------------------------------------------------------------------
   The closed <select> is styled by each tool, and on the dark theme that
   means light text. The list that drops open, though, is painted by the
   operating system — and on Windows it defaults to a white background.
   Light text on a white list is invisible: you can only find an item by
   sweeping the cursor down until one highlights (David hit this on Sep 2
   2026, and it was the same root cause as Review Manager's black stars —
   a control styled for one background rendering on another).

   Setting both colours on <option> and <optgroup> fixes it everywhere at
   once, whatever the individual tool did to its select. Explicit values
   rather than tool tokens, because the tools don't share a palette.
   macOS and Linux mostly ignore these; Windows and Chrome honour them,
   which is where the problem is.
   --------------------------------------------------------------------- */
.k-dark select option,
.k-dark select optgroup {
  background-color: #12173a;
  color: #F2F4FF;
}

/* The item under the cursor or selected — keep it clearly distinct from
   the rest of the list rather than relying on the OS default, which can
   land on another low-contrast pairing. */
.k-dark select option:checked,
.k-dark select option:hover {
  background-color: #2A3573;
  color: #ffffff;
}

/* ======================================================================
   PHONES AND SMALL TABLETS
   ----------------------------------------------------------------------
   Added Sep 3 2026, after David opened the dashboard on his phone and
   found half of it off the side of the screen. Until now this stylesheet
   had no media queries at all — every screen was built at desktop width
   and never checked narrower, which stopped being acceptable the moment
   Kelbra became something you install on a phone.

   The cause of the specific breakage: .k-stage is a fixed 860x860 canvas
   and each of the fifteen tool bubbles is absolutely positioned at a
   pixel coordinate on that circle (left:382px, top:82px, and so on, set
   inline from the TOOLS array in public/app/index.html). max-width:92vw
   shrinks the BOX on a phone but not the coordinates inside it, so the
   bubbles kept their 860px-grid positions, spilled past the right edge,
   and pushed the page into horizontal scroll — which is why the dark
   background stopped part-way across and the rest showed through pale.
   ====================================================================== */

/* The browser's default 8px body margin is why a pale strip showed down
   the sides of the dashboard on a phone: .k-dark paints the dark
   background, but it only ever covered the page minus that margin, so the
   browser's own white showed through at the edges. The dashboard, Design
   and Help pages never zeroed it; the tool pages all set it themselves in
   their own <style> blocks, and those still win, since this file is linked
   before them. */
body { margin: 0; }

/* Nothing in Kelbra is meant to scroll sideways. `clip` rather than
   `hidden` on purpose: `hidden` would turn .k-dark into a scroll
   container, which breaks position:sticky and can produce a second
   scrollbar. */
.k-dark { overflow-x: clip; }

@media (max-width: 860px) {
  /* The orbit ring is a desktop showpiece. It cannot be shrunk to a phone
     and stay usable — fifteen 96px bubbles on a 380px circle would be
     unreadable and unhittable — so below this width the decoration is
     dropped and the same bubbles become a plain grid of tap targets.
     Same links, same order, no JavaScript involved. */
  .k-stage {
    width: 100%;
    max-width: 560px;
    height: auto;
    margin: 6px auto 34px;
    padding: 0 14px;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
  }
  .k-orbit-ring,
  .k-core-wrap,
  .k-neural-stage { display: none !important; }

  /* !important is doing real work here: `left`/`top` are inline styles on
     each bubble, and position:static is what makes the browser ignore
     them. */
  .k-bubble {
    position: static !important;
    width: 100%;
    height: auto;
    min-height: 94px;
    border-radius: 16px;
    padding: 13px 8px;
    gap: 7px;
  }
  .k-bubble-label { font-size: 11px; }
  /* Hover scaling is a mouse idea; on a touch screen it just leaves a
     bubble stuck large after a tap. */
  .k-bubble:hover { transform: none; }
  .k-bubble-lock-icon { top: 7px; right: 8px; }

  .k-topbar { padding: 12px 16px; }
  .k-dark header { padding: 12px 16px !important; }
  .hero { padding: 20px 16px 4px !important; }

  /* ---- The fifteen tool pages ----------------------------------------
     Same story as the dashboard: built at desktop width, never checked
     narrower. These rules live here rather than in each page's own
     <style> so one change covers all fifteen and anything added later. */

  /* Payroll, Price Scout, Support Bot and Kelbra Design put the tool's
     one-line description in a .brand-tag sitting in the same flex row as
     the Home / Help / Get the App buttons. Those buttons never shrink
     (.k-home-btn is white-space: nowrap), so on a phone they ate the whole
     row and squeezed the description into a sliver hanging off the right
     edge. Letting the row wrap gives the description a line of its own. */
  .k-dark header.topbar { flex-wrap: wrap; row-gap: 10px; }
  .k-dark .brand { flex-wrap: wrap; row-gap: 8px; }
  .k-dark .brand-tag { flex: 1 0 100%; width: 100%; }

  /* Wide tables — Expense Tracker, Payroll, Analytics, Hub Directory and
     Price Scout all have them — scroll inside themselves instead of
     dragging the whole page sideways. Ledgerline and Lead Follow-Up
     already wrap theirs in a .tablewrap; this covers the rest, and the
     same rule on .tablewrap is harmless where it's already handled. */
  .k-dark table { display: block; width: 100%; overflow-x: auto; }
  .k-dark .tablewrap { overflow-x: auto; }

  /* The Help and notification buttons are fixed to the bottom-right
     corner, so they sit on top of whatever happens to be there — on
     Payroll that was the "Add worker" button. Smaller on a phone, closer
     together, and every page gets room at the bottom so its last control
     is never permanently trapped underneath them. */
  .khb-bubble { width: 44px !important; height: 44px !important; }
  .knt-bell { width: 40px !important; height: 40px !important; }
  .knt-root { bottom: 72px !important; }
  .knt-panel { bottom: 122px !important; }
  .k-dark { padding-bottom: 88px; }

  /* Long unbroken strings — a webhook URL, a business id, an email —
     must wrap rather than widen the page. */
  .k-dark code, .k-dark pre, .k-dark .mono { overflow-wrap: anywhere; }

  /* iOS Safari zooms the whole page in the moment you tap a field whose
     text is smaller than 16px, and does not zoom back out. Kelbra's forms
     are set between 13px and 14.5px, so on an iPhone every search box and
     every form field did that. This is not a typography preference — 16px
     is the documented threshold. !important because each tool page sets
     its own font-size on these elements.

     Checkboxes and radios are excluded: they have no text of their own and
     sizing them here would change their box. */
  .k-dark input:not([type="checkbox"]):not([type="radio"]),
  .k-dark select,
  .k-dark textarea { font-size: 16px !important; }
}

@media (max-width: 400px) {
  /* Two columns below this, or the labels start wrapping to three lines. */
  .k-stage { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
}
