/* ============================================================================
 * HOUSE THEME AXES
 * ----------------------------------------------------------------------------
 * Five independent axes compose into an app's visual identity. Each axis has a
 * curated, closed set of values. Apps pick one value per axis; they do not
 * invent values inline. Adding a value to an axis is a deliberate act that
 * requires a palette-lab session and a contrast run (see VERIFICATION below).
 *
 *   data-theme    light | dark                      -- MODE (not an axis)
 *   data-accent   oxblood | ember | signal | teal   -- the committed color
 *   data-surface  warm-paper | clinical | ink       -- ground + panel + text
 *   data-form     sharp | soft | round              -- radius + border + depth
 *   data-density  compact | editorial | open        -- spacing + rhythm
 *   data-type     serif-display | sans-tight | condensed
 *
 * `data-theme` stays the mode switch so the existing Alpine theme store (which
 * stamps data-theme="dark" on <html>) needs no change. The five axes are new
 * attributes stamped server-side from one app-level config value.
 *
 *   <html data-theme="dark" data-accent="oxblood" data-surface="warm-paper"
 *         data-form="sharp" data-density="editorial" data-type="serif-display">
 *
 * WHY AXES RATHER THAN PRESETS
 *   Monolithic presets bundle accent + surface + radius into one block, so any
 *   app that wants three of the four traits must fork the block -- and forks
 *   drift. Axes let an app take warm-paper + sharp while choosing its own
 *   accent, with no duplicated values anywhere.
 *
 * WHAT IS NOT IN THIS FILE
 *   Accent *budget* (how much surface area the color is allowed) and layout
 *   posture are the two strongest identity levers and neither is expressible
 *   as a custom property. They live in brief.md as usage rules. A perfectly
 *   tokenized system still produces accent-filled hero blocks if nobody says
 *   otherwise.
 *
 * VERIFICATION (run 2026-07-28, scratchpad/axis_matrix.py)
 *   All 4 accents x 3 surfaces x 2 modes = 24 combinations checked for WCAG AA
 *   and sRGB gamut. Body text lands 13.4:1-18.8:1 everywhere. Accent-as-text
 *   passes at >= 4.5:1 in every combination except ember-light and teal-light,
 *   which use --color-primary-strong.
 *
 *   Useful result: primary-strong is a property of the ACCENT ALONE, not of the
 *   accent/surface pair. One value per accent clears AA on all three surfaces
 *   (4.52-4.80:1), so the axes stay genuinely independent.
 *
 * GAMUT
 *   Base declarations are sRGB-safe. Higher-chroma P3 targets layer on at the
 *   bottom via `@media (color-gamut: p3)`. The gate is display gamut, NOT
 *   syntax support -- every current browser parses oklch(), so
 *   `@supports (color: oklch(...))` would be a no-op.
 * ========================================================================= */


/* ==========================================================================
 * 0. STRUCTURAL BASE
 * daisyUI needs a theme to exist before anything can override it. These two
 * blocks supply structure only -- the axis layers below own every value that
 * carries identity. `color-scheme` stays omitted pending #141.
 * ====================================================================== */

@plugin "daisyui/theme" {
  name: "light";
  default: true;
  prefersdark: false;
}

@plugin "daisyui/theme" {
  name: "dark";
  default: false;
  prefersdark: false;
}


/* ==========================================================================
 * 1. AXIS — ACCENT
 * The one committed color. Hue choice is semantic before it is aesthetic:
 * this family of apps uses red for rejection/error and amber for pending, so
 * an accent sharing those hues spends vocabulary it may need.
 * ====================================================================== */

/* -- oxblood: rust, hue 36. HOUSE DEFAULT.
 * Collides with no status convention, and meets AA as small text in both
 * modes on all three surfaces with no helper token. */
[data-accent="oxblood"] {
  --color-primary: oklch(0.520 0.178 36);
  --color-primary-content: oklch(0.990 0.004 36);
  --color-primary-strong: oklch(0.520 0.178 36);   /* 5.46:1 — same as primary */
}
[data-theme="dark"][data-accent="oxblood"] {
  --color-primary: oklch(0.640 0.195 36);
  --color-primary-content: oklch(0.190 0.010 36);
  --color-primary-strong: oklch(0.640 0.195 36);   /* 5.09:1 */
}

/* -- ember: amber, hue 66. Source: 016 Daylight Computer.
 * Light accent is far too light for small text (1.89:1) — kicker labels and
 * inline links MUST use primary-strong. Reads as "pending" next to a status
 * vocabulary; avoid where amber already means something. */
[data-accent="ember"] {
  --color-primary: oklch(0.780 0.170 66);
  --color-primary-content: oklch(0.190 0.010 66);
  --color-primary-strong: oklch(0.552 0.123 66);   /* 4.52–4.80:1 all surfaces */
}
[data-theme="dark"][data-accent="ember"] {
  --color-primary: oklch(0.760 0.169 66);
  --color-primary-content: oklch(0.190 0.010 66);
  --color-primary-strong: oklch(0.760 0.169 66);   /* 8.13–8.52:1 */
}

/* -- signal: editorial red, hue 27. Source: 012 The Economist.
 * Fully sRGB in both modes; no P3 override, no helper token. NOT the house
 * default only because hue 27 collides with Rejected/error. An app adopting
 * it needs a non-red rejection state. Thinnest margin in the set:
 * 4.54:1 on clinical dark. */
[data-accent="signal"] {
  --color-primary: oklch(0.548 0.210 27);
  --color-primary-content: oklch(0.990 0.004 27);
  --color-primary-strong: oklch(0.548 0.210 27);   /* 4.96–5.27:1 */
}
[data-theme="dark"][data-accent="signal"] {
  --color-primary: oklch(0.620 0.190 27);
  --color-primary-content: oklch(0.190 0.010 27);
  --color-primary-strong: oklch(0.620 0.190 27);   /* 4.54–4.76:1 */
}

/* -- teal: hue 176. Bulma's original primary, recovered.
 * Largest gamut gap in the set — the dark accent's tuned chroma (0.240) sits
 * well above the sRGB ceiling at that L/H (0.142), so non-P3 displays render
 * it noticeably duller. Light accent needs primary-strong (2.89:1). */
[data-accent="teal"] {
  --color-primary: oklch(0.640 0.120 176);
  --color-primary-content: oklch(0.190 0.010 176);
  --color-primary-strong: oklch(0.532 0.100 176);  /* 4.52–4.79:1 all surfaces */
}
[data-theme="dark"][data-accent="teal"] {
  --color-primary: oklch(0.760 0.142 176);
  --color-primary-content: oklch(0.190 0.010 176);
  --color-primary-strong: oklch(0.760 0.142 176);  /* 8.98–9.41:1 */
}


/* ==========================================================================
 * 2. AXIS — SURFACE
 * Ground, lifted panel, and body text. Independent of accent: warmth is a
 * property of the field, not of the color sitting on it.
 *
 * House rule from brief.md §0: light mode is warm and low-blue for reading
 * comfort; dark mode is a near-black field with panels lifted one step.
 * Even `clinical` stays neutral-to-barely-warm rather than going cool —
 * "precise" is a chroma decision, not a blue one.
 * ====================================================================== */

/* -- warm-paper: the house light surface. Warm tinted ground, near-white
 * lifted panels, warm near-black text. */
[data-surface="warm-paper"] {
  --color-base-100: oklch(0.968 0.010 70);
  --color-base-200: oklch(0.988 0.006 70);   /* was 0.995 0.005 — out of gamut */
  --color-base-300: oklch(0.930 0.012 70);
  --color-base-content: oklch(0.205 0.020 35);
}
[data-theme="dark"][data-surface="warm-paper"] {
  --color-base-100: oklch(0.185 0.008 35);
  --color-base-200: oklch(0.235 0.010 35);
  --color-base-300: oklch(0.285 0.012 35);
  --color-base-content: oklch(0.940 0.005 60);
}

/* -- clinical: minimum chroma. For dense forms and compliance work where the
 * field must not editorialize. Warm-neutral, never cool. */
[data-surface="clinical"] {
  --color-base-100: oklch(0.988 0.003 90);
  --color-base-200: oklch(1.000 0.000 0);
  --color-base-300: oklch(0.945 0.004 90);
  --color-base-content: oklch(0.225 0.006 90);
}
[data-theme="dark"][data-surface="clinical"] {
  --color-base-100: oklch(0.200 0.004 90);
  --color-base-200: oklch(0.255 0.005 90);
  --color-base-300: oklch(0.305 0.005 90);
  --color-base-content: oklch(0.945 0.003 90);
}

/* -- ink: editorial. Highest contrast in the set (17.8:1 light / 16.3:1 dark),
 * true-white lifted panels. Pairs with hairline rules rather than fills. */
[data-surface="ink"] {
  --color-base-100: oklch(0.980 0.005 90);
  --color-base-200: oklch(1.000 0.000 0);
  --color-base-300: oklch(0.938 0.006 90);
  --color-base-content: oklch(0.180 0.008 30);
}
[data-theme="dark"][data-surface="ink"] {
  --color-base-100: oklch(0.175 0.004 30);
  --color-base-200: oklch(0.222 0.006 30);
  --color-base-300: oklch(0.272 0.007 30);
  --color-base-content: oklch(0.948 0.003 60);
}


/* ==========================================================================
 * 3. AXIS — FORM
 * Radius AND how edges and depth are expressed. These travel together: a 2px
 * radius with a drop shadow reads as an accident, and an 18px radius with a
 * hairline reads as unfinished.
 * ====================================================================== */

/* -- sharp: built-instrument. Hairline edges, no shadow. Raises the bar —
 * sharp corners read precise when spacing and alignment are exact, and severe
 * when they are not. */
[data-form="sharp"] {
  --radius-box: 2px;
  --radius-field: 2px;
  --radius-selector: 2px;
  --border-panel: 1px;
  --shadow-panel: none;
}

/* -- soft: bordered panels with a hint of lift. The safe middle. */
[data-form="soft"] {
  --radius-box: 12px;
  --radius-field: 12px;
  --radius-selector: 12px;
  --border-panel: 1px;
  --shadow-panel: 0 1px 2px oklch(0% 0 0 / 0.06), 0 2px 8px oklch(0% 0 0 / 0.04);
}

/* -- round: shadow-led, borderless. Floating panels over a field
 * (016 Daylight). Needs a surface with enough base-100/base-200 separation to
 * read without an edge. */
[data-form="round"] {
  --radius-box: 18px;
  --radius-field: 18px;
  --radius-selector: 18px;
  --border-panel: 0px;
  --shadow-panel: 0 2px 6px oklch(0% 0 0 / 0.08), 0 8px 24px oklch(0% 0 0 / 0.06);
}


/* ==========================================================================
 * 4. AXIS — DENSITY
 * The strongest identity lever that is not color, and the one most often left
 * on default. Drives Tailwind v4's `--spacing` base unit, so it propagates
 * through every p-*, gap-*, and m-* utility without per-component work.
 * ====================================================================== */

/* -- compact: list density (009b Metal Injection). For boards, feeds, tables,
 * anything where more rows on screen is the point. */
[data-density="compact"] {
  --spacing: 0.2rem;
  --leading-body: 1.45;
  --leading-display: 1.05;
  --control-height: 2rem;
}

/* -- editorial: house default. Dense enough to be scannable, calm enough to
 * live in all day. */
[data-density="editorial"] {
  --spacing: 0.25rem;
  --leading-body: 1.6;
  --leading-display: 1.1;
  --control-height: 2.25rem;
}

/* -- open: marketing and landing surfaces (016 Daylight). Explicitly wrong
 * for logged-in app chrome — see brief.md §2, "airy marketing spacing". */
[data-density="open"] {
  --spacing: 0.3rem;
  --leading-body: 1.7;
  --leading-display: 1.15;
  --control-height: 2.75rem;
}


/* ==========================================================================
 * 5. AXIS — TYPE
 * NOTE ON ASSETS: only Inter, Inter Tight, and JetBrains Mono are currently
 * self-hosted. `serif-display` and `condensed` name their intended faces first
 * and degrade to system stacks, so they are safe to select before the font
 * files land. Shipping either as a real identity means adding self-hosted
 * woff2 — a per-repo asset task, not a token change.
 * ====================================================================== */

/* -- serif-display: serif headings over sans body (012 Economist, 016
 * Daylight). The strongest departure from the AI-default look. */
[data-type="serif-display"] {
  --font-display: "Source Serif 4", "Iowan Old Style", Georgia, serif;
  --font-body: "Inter", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;
  --scale-ratio: 1.25;
  --display-tracking: -0.01em;
  --display-weight: 600;
}

/* -- sans-tight: tight-tracked sans display over sans body. Closest to what
 * currently ships. Legitimate, but it is one Inter-swap away from the default
 * documented in brief.md §4 — choose it deliberately, not by omission. */
[data-type="sans-tight"] {
  --font-display: "Inter Tight", "Inter", system-ui, sans-serif;
  --font-body: "Inter", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;
  --scale-ratio: 1.2;
  --display-tracking: -0.03em;
  --display-weight: 700;
}

/* -- condensed: italic bold condensed headings (009b Metal Injection). Energy
 * without a second display family. Pairs naturally with compact density. */
[data-type="condensed"] {
  --font-display: "Roboto Condensed", "Archivo Narrow", "Inter Tight", sans-serif;
  --font-body: "Inter", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;
  --scale-ratio: 1.333;
  --display-tracking: -0.02em;
  --display-weight: 800;
}


/* ==========================================================================
 * 6. P3 LAYER
 * Wide-gamut displays get the chroma actually tuned on the reference monitor.
 * Contrast re-verified at both chroma levels — oxblood light is 5.38:1 at the
 * P3 target and 5.46:1 at the sRGB value, so the gate is safe in either state.
 * signal is already fully in sRGB in both modes and takes no override.
 * ====================================================================== */

@media (color-gamut: p3) {
  [data-accent="oxblood"] {
    --color-primary: oklch(0.520 0.230 36);
    --color-primary-strong: oklch(0.520 0.230 36);
  }
  [data-accent="ember"] {
    --color-primary: oklch(0.780 0.185 66);
    /* primary-strong stays at sRGB chroma — it is a text color, and the
     * verified 4.52:1 margin is too thin to re-derive at higher chroma. */
  }
  [data-theme="dark"][data-accent="ember"] {
    --color-primary: oklch(0.760 0.225 66);
    --color-primary-strong: oklch(0.760 0.225 66);
  }
  [data-accent="teal"] {
    --color-primary: oklch(0.640 0.135 176);
  }
  [data-theme="dark"][data-accent="teal"] {
    --color-primary: oklch(0.760 0.240 176);
    --color-primary-strong: oklch(0.760 0.240 176);
  }
}


/* ==========================================================================
 * 7. COMPOSITIONS
 * Named starting points. An app stamps the five attributes; it does not get a
 * CSS block of its own. Anything an app needs beyond these five values is a
 * signal that it wants a curated language, not a composition — see SKILL.md.
 *
 *   resume_maker  oxblood  warm-paper  sharp  editorial  serif-display
 *   RankedJobs    oxblood  warm-paper  soft   editorial  sans-tight
 *   leads_ai_fe   teal     clinical    soft   compact    sans-tight
 *   strategiq     signal   ink         sharp  editorial  serif-display
 *   hollis        oxblood  warm-paper  sharp  compact    condensed
 *
 * Formana, 525_OP_Site, and atelier are curated-language repos and define
 * their own compositions (and possibly new axis values) through the pilot
 * process. atelier is a special case: as a white-label CMS it exposes these
 * axes to tenants, so its value sets are a product surface.
 * ====================================================================== */


/* ==========================================================================
 * 8. 525_OP_SITE — daisyUI SEMANTIC + STRUCTURAL BRIDGE (repo-specific,
 * NOT part of the house copy — issue #9 Part B, per-template migration)
 * ----------------------------------------------------------------------------
 * The five axes above supply --color-primary, the --color-base-N family,
 * and the --radius-N family — exactly what daisyUI needs for the
 * accent/surface/form axes. daisyUI
 * components also read a second tier of slots the house axis system has no
 * opinion on (secondary/neutral/info/success/warning/error, plus a handful
 * of structural sizing vars). Left undefined, those slots would either
 * render unstyled or silently fall back to daisyUI's own default
 * indigo/violet palette — neither is this app's actual status-color
 * vocabulary.
 *
 * Values below are this app's REAL, currently-rendered Bulma colors
 * (src/frontend/scss/_variables.scss: $secondary/$warning/$danger/$info —
 * note $secondary is this app's success color, not a distinct brand hue),
 * carried over byte-for-byte so a migrated component's color does not
 * change, per the "token values port before they change" migration
 * principle. Content (text-on-fill) colors are computed, not guessed: none
 * of these four clears WCAG AA (4.5:1) against white text (2.10-3.82:1),
 * so every one uses a near-black content color instead (5.05-9.17:1) —
 * verified by calling backend/core/design_axes.py's own
 * relative_luminance()/contrast_ratio() helpers directly against each hex
 * value's linear-sRGB form (not reimplemented inline here since these are
 * fixed brand colors, not a derived axis, so there's no call site in
 * design_axes.py itself to route this through).
 *
 * `--color-secondary` intentionally mirrors `--color-success` here — this
 * app has no independent secondary-brand-color concept, only the
 * success-repurposed-as-secondary naming already present in its own SCSS.
 *
 * Scoped to :root, not gated behind [data-theme]/[data-accent]/etc: these
 * are brand-fixed status colors, not part of any axis, and — unlike the
 * daisyUI `rootcolor` module /review flagged in the coexistence-build PR —
 * declaring custom properties on :root is inert on its own. The actual
 * `rootcolor` collision came from :root ALSO setting `background-color`/
 * `color` directly, which is not done here or anywhere in this block.
 * ====================================================================== */

:root {
  --color-success: #2ecc71;
  --color-success-content: #190a0a;
  --color-secondary: #2ecc71;
  --color-secondary-content: #190a0a;
  --color-warning: #f39c12;
  --color-warning-content: #190a0a;
  --color-error: #e74c3c;
  --color-error-content: #190a0a;
  --color-info: #3498db;
  --color-info-content: #190a0a;
  --color-neutral: #2c3e50;   /* $grey-dark, _variables.scss */
  --color-neutral-content: #f8f9fa;   /* $light, _variables.scss */

  /* daisyUI structural defaults this app has no axis opinion on yet —
     daisyUI's own built-in values, not derived from anything Bulma-side. */
  --size-selector: 0.25rem;
  --size-field: 0.25rem;
  --border: 1px;
  --depth: 1;
  --noise: 0;
}
