/* =============================================================================
   floor.css — the frontend floor
   =============================================================================
   Codified 2026-07-17. J: "I'm not saying create a style, create me a better
   floor for you. So our front end doesn't suck."

   WHY THIS EXISTS
   A skin (skills/luna/skins/*) defines colors, radii, and one shadow. That is
   all. Type scale, spacing, font family, focus ring, motion, states, and light
   mode were never written down anywhere -- so they got re-invented from scratch,
   uninformed, on every build. navy-chancery shipped --text-muted at 4.23:1 on
   its own primary surface while _TAXONOMY.md promised "WCAG contrast is a floor
   that holds." Nothing checked. This is the thing that was missing.

   WHAT IT IS
   The skin-INDEPENDENT layer, as CSS rather than prose. Prose guidance can be
   half-followed; a stylesheet is either included or it isn't, and check_floor.py
   scores the rest. This file is also the reason the floor is portable: a cheap
   model can @import a file exactly as well as a frontier model can.

   HOW TO USE
   Include FIRST, then the skin's tokens.css overrides the palette on top:
       <style>  (inline it -- no network on Electron/Tauri/local-HTML)
   Skins override ONLY the --bg- , --text- , --accent and --border slots in
   section 4. (Deliberately written without a star-slash pair: that sequence
   closes this comment early.)
   Everything else here is invariant and should not be overridden per-skin.

   TARGETS: Electron, Tauri, single-file local HTML. No build step, no network.
   Deliberately NOT tuned for deployed marketing web -- see section 9.

   HONEST SCOPE: this file fixes the DEFAULTS. It cannot make a bad layout good,
   and check_floor.py only reads tokens statically -- it does not render. Rendered
   defects (overflow, unstyled states on real data) need the render oracle, which
   is not built yet.
   ============================================================================= */


/* ============================ 1. RESET ==================================== */

*, *::before, *::after { box-sizing: border-box; }

body { margin: 0; }

/* Media never blows out its container. */
img, svg, video, canvas { max-width: 100%; height: auto; }

/* Form controls do NOT inherit font by default -- the single most common reason
   an app's inputs look foreign to the rest of the app. */
input, button, textarea, select { font: inherit; color: inherit; }


/* ====================== 2. FONTS (system stack) =========================== */
/* No @import, no <link>, no url(). Every webfont (Inter, Geist, Mona Sans,
   Manrope) SILENTLY falls back to Times on a no-network target. A fallback you
   never see is worse than a font you chose. Skins may override --font-display
   only if the face is genuinely present on the machine. */

:root {
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", serif;
  --font-mono: ui-monospace, "Cascadia Code", "Segoe UI Mono", Menlo, Consolas, monospace;

  /* The face for display nouns. Skins that want a serif register set this. */
  --font-display: var(--font-sans);
}


/* ========================== 3. TYPE SCALE ================================= */
/* A real scale, so sizes stop being ad-hoc guesses. ~1.25 ratio, rem-based.
   Desktop-density: base is 15px, not the web-default 16px. */

:root {
  --text-xs:   0.75rem;   /* 12px — captions, dense metadata */
  --text-sm:   0.8125rem; /* 13px — secondary UI text */
  --text-base: 0.9375rem; /* 15px — body */
  --text-lg:   1.125rem;  /* 18px */
  --text-xl:   1.375rem;  /* 22px */
  --text-2xl:  1.75rem;   /* 28px */
  --text-3xl:  2.25rem;   /* 36px */

  --leading-tight:   1.2;  /* headings */
  --leading-normal:  1.5;  /* UI text */
  --leading-relaxed: 1.65; /* long-form prose */

  --weight-normal: 400;
  --weight-medium: 500;
  --weight-bold:   650;

  /* Prose measure. Unbounded line length is the #1 tell of unconsidered text. */
  --measure: 68ch;
}

body {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--text-primary);
  background: var(--bg-app);
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  line-height: var(--leading-tight);
  font-weight: var(--weight-bold);
  margin: 0 0 var(--space-3) 0;
}
h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }

code, pre, kbd, samp { font-family: var(--font-mono); font-size: 0.925em; }


/* =========================== 4. SPACING =================================== */
/* One rhythm. Skins do not override these. */

:root {
  --space-0: 0;
  --space-1: 2px;
  --space-2: 4px;
  --space-3: 8px;
  --space-4: 12px;
  --space-5: 16px;
  --space-6: 24px;
  --space-7: 32px;
  --space-8: 48px;
  --space-9: 64px;
}


/* ===================== 5. COLOR — semantic slots =========================== */
/* Neutral defaults that PASS WCAG AA unaided (verified by check_floor.py).
   These are the ONLY slots a skin overrides. Both schemes are mandatory: a
   dark-only token block is how an app ends up unreadable the first time someone
   opens it in a light OS. */

:root {
  color-scheme: light dark;

  --bg-app:        #ffffff;
  --bg-panel:      #f4f6f8;
  --bg-panel-soft: #eceff3;
  --bg-inset:      #e4e8ed;

  --text-primary:   #14171c;
  --text-secondary: #464e58;
  --text-muted:     #5b636e;   /* MUST hold 4.5:1 on every --bg-* above */
  --text-on-accent: #ffffff;

  --border:        #d5dae0;
  --border-strong: #b4bcc6;

  --accent:      #1a56b8;
  --accent-soft: rgba(26, 86, 184, 0.10);
  --success:     #14653a;
  --warning:     #7a4f00;
  --danger:      #a51c14;

  --focus-ring: #1a56b8;

  /* Status badges. A pill is a tinted background carrying small text -- the
     exact place an uninformed build invents a 2.5:1 color (the render gate
     caught DeepSeek doing it). These pairs are AA-verified so the model has a
     floored pill to reach for. check_floor.py auto-pairs each -fg with its -bg. */
  --badge-neutral-bg: #eef1f5;  --badge-neutral-fg: #5d6c82;
  --badge-success-bg: #e7f6ee;  --badge-success-fg: #117b38;
  --badge-warning-bg: #fdf1e3;  --badge-warning-fg: #a15904;
  --badge-danger-bg:  #fdecea;  --badge-danger-fg:  #ce2121;
  --badge-info-bg:    #e8f0ff;  --badge-info-fg:    #1558ff;

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;

  --shadow-panel: 0 1px 2px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.06);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg-app:        #0e1116;
    --bg-panel:      #161b22;
    --bg-panel-soft: #1c222b;
    --bg-inset:      #232a34;

    --text-primary:   #e9edf2;
    --text-secondary: #b6bfca;
    --text-muted:     #949eaa;   /* MUST hold 4.5:1 on every --bg-* above */
    --text-on-accent: #0e1116;

    --border:        rgba(180, 195, 214, 0.16);
    --border-strong: rgba(190, 204, 222, 0.28);

    --accent:      #7cb0ff;
    --accent-soft: rgba(124, 176, 255, 0.14);
    --success:     #5bc47b;
    --warning:     #e0a33a;
    --danger:      #ff8078;

    --focus-ring: #7cb0ff;

    --badge-neutral-bg: #232a34;  --badge-neutral-fg: #959fab;
    --badge-success-bg: #12301f;  --badge-success-fg: #5dc57c;
    --badge-warning-bg: #332411;  --badge-warning-fg: #e0a43c;
    --badge-danger-bg:  #3a1a17;  --badge-danger-fg:  #ff827a;
    --badge-info-bg:    #16233a;  --badge-info-fg:    #7eb1ff;

    --shadow-panel: 0 1px 0 rgba(255,255,255,0.025) inset, 0 18px 50px rgba(0,0,0,0.28);
  }
}

/* Explicit override wins over the OS in both directions (theme toggles). */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }


/* ============================ 6. FOCUS ==================================== */
/* Non-negotiable. Removing an outline without replacing it is the single most
   common accessibility regression, and it is invisible to anyone using a mouse
   -- which is why it survives review. */

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}


/* ============================ 7. MOTION =================================== */

:root {
  --duration-fast: 120ms;
  --duration-base: 200ms;
  --ease: cubic-bezier(0.2, 0, 0.2, 1);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ===================== 8. THE STATES THAT GET SKIPPED ====================== */
/* Every list has four states. Uninformed builds style exactly one -- the happy
   path with three tidy rows -- and the other three only appear on real data,
   in front of the user. */

.floor-empty,
.floor-error,
.floor-loading {
  padding: var(--space-7) var(--space-5);
  text-align: center;
  font-size: var(--text-sm);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
}
.floor-empty   { color: var(--text-muted); }
.floor-loading { color: var(--text-muted); }
.floor-error   { color: var(--danger); border-style: solid; border-color: var(--danger); text-align: left; }

/* An empty state is an invitation to act, not a shrug. Put the action here. */
.floor-empty > :last-child { margin-top: var(--space-4); }

/* Status badges/pills -- reach for these instead of inventing a color. Every
   variant is AA-verified in both schemes. Usage: <span class="floor-badge info">. */
.floor-badge {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 1.5;
  white-space: nowrap;
  background: var(--badge-neutral-bg);
  color: var(--badge-neutral-fg);
}
.floor-badge.success { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.floor-badge.warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg); }
.floor-badge.danger  { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.floor-badge.info    { background: var(--badge-info-bg);    color: var(--badge-info-fg); }


/* ========================= 9. CONTAINMENT ================================= */
/* Real data breaks layouts: a 400-char string with no spaces, a 40-column table,
   a pasted URL. The page body must never scroll sideways -- the wide thing
   scrolls inside its own box. */

.floor-scroll-x { overflow-x: auto; max-width: 100%; }

table { border-collapse: collapse; width: 100%; }
th, td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
th { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); font-weight: var(--weight-medium); }

/* Long unbroken tokens wrap instead of pushing the layout. */
body, p, td, li, dd { overflow-wrap: anywhere; }

pre { overflow-x: auto; padding: var(--space-4); background: var(--bg-inset); border-radius: var(--radius-md); }

p, li { max-width: var(--measure); }


/* ======================== 10. HIT TARGETS ================================= */
/* 32px, NOT the 44px you will find in every web accessibility guide. 44 is a
   TOUCH minimum. These are dense desktop apps driven by a mouse; 44px controls
   in a data-dense Electron panel look like a toy. Tuned to the target, not
   copied from the web. */

:root { --hit-min: 32px; }

button, [role="button"], input[type="submit"], select {
  min-height: var(--hit-min);
  padding: 0 var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg-panel);
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease), border-color var(--duration-fast) var(--ease);
}
button:hover, [role="button"]:hover { background: var(--bg-panel-soft); border-color: var(--border-strong); }
button:disabled { opacity: 0.5; cursor: not-allowed; }

input[type="text"], input[type="search"], input[type="email"], input[type="number"], select, textarea {
  min-height: var(--hit-min);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-app);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}

a { color: var(--accent); text-underline-offset: 2px; }
