/* ══════════════════════════════════════════
   Manuall - Fluid Typography System
   ══════════════════════════════════════════

   METHOD: Viewport-linear interpolation
   ──────────────────────────────────────────
   Every --step-N token is solved from two anchor viewports:
     vp_min = 320 px  (mobile minimum)
     vp_max = 1440 px (desktop baseline)
   plus a cap for ultrawide (1440-2560 px).

   Derivation for each step:
     slope_vw    = (max_px − min_px) / (1440 − 320) × 100
     intercept   = (min_px − slope_px × 320) / 16   [rem]
     preferred   = intercept_rem + slope_vw

   Verification:
     at 320 px:  intercept×16  + slope×3.2  = min_px  ✓
     at 1440 px: intercept×16  + slope×14.4 = max_px  ✓
     above 1440: clamped to max             = cap_px  ✓

   FONT-WEIGHT & VARIABLE FONTS
   ──────────────────────────────────────────
   Both fonts are now variable:
     - Plus Jakarta Sans: wght axis 200-800 (display / headings)
     - Public Sans:       wght axis 100-900 (body)
   font-variation-settings: 'wght' N is set on all heading elements.
   Intermediate values (e.g. 620, 450) render precisely - static fonts
   would snap to the nearest instance (600 or 700).

   FLUID WEIGHT LIMITATION
   ──────────────────────────────────────────
   font-weight: clamp(...vw...) is invalid CSS. 'font-weight' expects a
   <number>; vw resolves to a <length>. The types are incompatible in
   calc(). Viewport-responsive weight is implemented via @media breakpoints
   instead. This gives stepped (not continuous) fluid weight - still a
   major improvement over static fonts because:
     1. Intermediate stops (e.g. 620) are available, not just 600/700.
     2. Dark-mode calibration uses fine-grained values (e.g. drop 80 units)
        rather than coarse font-weight steps.
     3. Body weight can be nudged to 420 on mobile (better small-size contrast)
        and back to 400 on desktop - impossible with static fonts.

   LETTER-SPACING RATIONALE
   ──────────────────────────────────────────
   Values use em units (not vw). Em auto-scales with the fluid font-size,
   so −0.025em on a 28 px h1 already provides proportionally tighter
   tracking than −0.025em on an 18 px h3. Adding a vw term would fight
   the fluid font-size and require recalculation at every breakpoint.
   Standard rule: larger type → tighter tracking; em achieves this cleanly.

   LINE-HEIGHT RATIONALE
   ──────────────────────────────────────────
   Unitless ratios that scale with font-size. Viewport-fluid line-height
   via calc(N − Xvw) resolves to px (length units), breaking the
   font-size-relative scaling. Discrete semantic tokens are correct here.

   HTMX / SERVER-SIDE INTEGRATION
   ──────────────────────────────────────────
   All tokens live on :root and cascade through every HTMX partial swap.
   No JS required. Server-rendered Razor views inherit the full scale
   automatically - no per-partial or per-view setup needed.
   See Part D comments at the bottom of this file.
   ══════════════════════════════════════════ */


/* ══════════════════════════════════════════
   PART A - Token definitions
   ══════════════════════════════════════════ */

:root {

    /* ── Fluid type scale ──
       step-5 = h1 display | min 28 px  → max 36 px  | cap 44 px
       step-4 = h2         | min 22 px  → max 28 px  | cap 34 px
       step-3 = h3         | min 18 px  → max 22 px  | cap 26 px
       step-2 = h4         | min 16 px  → max 19 px  | cap 22 px
       step-1 = h5 / label | min 14 px  → max 16 px  | cap 18 px
       step-0 = body prose | min 14 px  → max 16 px  | cap 17 px
       step--1 = small     | min 12 px  → max 13 px  | cap 14 px
       step--2 = micro     | min 10 px  → max 11 px  | cap 12 px  */

    --step-5:  clamp(1.75rem,   1.607rem + 0.714vw, 2.75rem);
    --step-4:  clamp(1.375rem,  1.268rem + 0.536vw, 2.125rem);
    --step-3:  clamp(1.125rem,  1.054rem + 0.357vw, 1.625rem);
    --step-2:  clamp(1rem,      0.946rem + 0.268vw, 1.375rem);
    --step-1:  clamp(0.875rem,  0.839rem + 0.179vw, 1.125rem);
    --step-0:  clamp(0.875rem,  0.839rem + 0.179vw, 1.0625rem);
    --step--1: clamp(0.75rem,   0.732rem + 0.089vw, 0.875rem);
    --step--2: clamp(0.625rem,  0.607rem + 0.089vw, 0.75rem);

    /* ── Letter-spacing scale (em - proportional to fluid font-size) ──
       Negative values tighten large display type (Poppins natural setting).
       Positive at small sizes prevents crowding at 10-13 px. */
    --ls-display:  -0.025em;   /* h1 - large display, tight */
    --ls-heading:  -0.015em;   /* h2-h3 - section headings */
    --ls-subhead:  -0.01em;    /* h4-h5 - sub-headings */
    --ls-body:      0em;       /* body - neutral */
    --ls-small:     0.01em;    /* small / captions */
    --ls-caps:      0.08em;    /* uppercase label utility */

    /* ── Line-height scale (unitless - scales with font-size) ──
       Tighter for large display type, generous for reading prose. */
    --lh-display:  1.15;   /* h1 - very large, short bursts */
    --lh-heading:  1.25;   /* h2-h3 - section headings */
    --lh-subhead:  1.35;   /* h4-h5 - sub-headings, dense UI */
    --lh-body:     1.6;    /* body prose, descriptions */
    --lh-ui:       1.4;    /* compact UI: labels, nav, table cells */
    --lh-tight:    1.2;    /* max-density: badges, kpi values */

    /* ── Discrete font-weight map (static fonts - no wght axis) ──
       Named semantically so dark-mode overrides stay readable. */
    --fw-normal:   400;
    --fw-medium:   500;
    --fw-semibold: 600;
    --fw-bold:     700;

    /* Heading weights: bold for h1, semibold for h2-h6. In dark mode these
       drop one step to compensate for higher apparent weight on dark bg. */
    --fw-h1:  var(--fw-bold);
    --fw-h2:  var(--fw-semibold);
    --fw-h3:  var(--fw-semibold);
    --fw-h4:  var(--fw-semibold);
    --fw-h5:  var(--fw-semibold);
    --fw-h6:  var(--fw-medium);
}


/* ══════════════════════════════════════════
   PART B - Heading element overrides
   Supersedes the h1-h5 declarations in app.css (this file loads after).
   ══════════════════════════════════════════ */

h1, h2, h3, h4, h5, h6 {
    font-family:  var(--font-display);
    color:        var(--heading);
    text-wrap:    balance;
}

h1 {
    font-size:      var(--step-5);
    line-height:    var(--lh-display);
    letter-spacing: var(--ls-display);
    font-weight:    var(--fw-h1);
}

h2 {
    font-size:      var(--step-4);
    line-height:    var(--lh-heading);
    letter-spacing: var(--ls-heading);
    font-weight:    var(--fw-h2);
}

h3 {
    font-size:      var(--step-3);
    line-height:    var(--lh-heading);
    letter-spacing: var(--ls-heading);
    font-weight:    var(--fw-h3);
}

h4 {
    font-size:      var(--step-2);
    line-height:    var(--lh-subhead);
    letter-spacing: var(--ls-subhead);
    font-weight:    var(--fw-h4);
}

h5 {
    font-size:      var(--step-1);
    line-height:    var(--lh-subhead);
    letter-spacing: var(--ls-subhead);
    font-weight:    var(--fw-h5);
}

h6 {
    font-size:      var(--step--1);
    line-height:    var(--lh-ui);
    letter-spacing: var(--ls-small);
    font-weight:    var(--fw-h6);
    text-transform: uppercase;
    color:          var(--text-muted);
}

/* Body prose adopts step-0 line-height and tracking.
   font-size intentionally NOT overridden here - body uses --text-md
   from app.css for compact UI density. Use .prose for reading contexts. */
p {
    line-height:    var(--lh-body);
    letter-spacing: var(--ls-body);
}

small {
    font-size:      var(--step--1);
    line-height:    var(--lh-ui);
    letter-spacing: var(--ls-small);
}


/* ══════════════════════════════════════════
   PART B (cont.) - Utility classes
   Apply the fluid scale to any element.
   ══════════════════════════════════════════ */


/* Prose context - opt in for reading-density text blocks.
   Applies step-0 font-size + generous line-height + paragraph spacing.
   Use on article bodies, KB articles, descriptions, invoice notes. */
.prose {
    font-size:   var(--step-0);
    line-height: var(--lh-body);
    max-width:   68ch;  /* ~65-75ch is the optimal reading line length */
}
.prose p    { margin-bottom: 1em; letter-spacing: var(--ls-body); }
.prose h2   { margin-top: 1.75em; margin-bottom: 0.5em; }
.prose h3   { margin-top: 1.5em;  margin-bottom: 0.4em; }
.prose h4   { margin-top: 1.25em; margin-bottom: 0.35em; }
.prose ul,
.prose ol   { padding-left: 1.5em; margin-bottom: 1em; }
.prose li   { margin-bottom: 0.35em; line-height: var(--lh-body); }
.prose blockquote {
    border-left: 3px solid var(--primary);
    padding-left: 1em;
    color: var(--text-muted);
    font-style: italic;
    margin: 1.25em 0;
}


/* Numeric / KPI value - Poppins bold with tabular figures for alignment.
   Already used via .kpi-value in app.css; this extends to any context. */
.numeric {
    font-family: var(--font-display);
    font-weight: var(--fw-bold);
    font-variant-numeric: tabular-nums;
    letter-spacing: var(--ls-subhead);
}

/* Label caps - uppercase micro-label above a value or section.
   Compose with a step--1 or step--2 size class. */
.label-caps {
    font-size:      var(--step--1);
    font-weight:    var(--fw-semibold);
    letter-spacing: var(--ls-caps);
    text-transform: uppercase;
    color:          var(--text-muted);
    line-height:    var(--lh-tight);
}


/* ══════════════════════════════════════════
   PART C - Dark mode adjustments
   [data-theme="dark"] toggled by Alpine.js / app.js theme button.
   @media prefers-color-scheme dark is NOT used - the app has an
   explicit user toggle that writes data-theme="dark" to <html>.

   Variable-font advantage: we can drop weight by exactly 80 units
   rather than coarsely snapping from 700→600. This keeps headings
   feeling bold-ish in dark mode without looking painted-on.
   ══════════════════════════════════════════ */

/* Headings: drop ~80 units in dark mode to compensate for halation.
   Halation = light scattering around bright strokes on dark bg makes
   strokes appear ~10-15% heavier than on white at the same weight.
   620 → 540 and 700 → 620 restores optical equilibrium. */
[data-theme="dark"] h1 {
    font-variation-settings: 'wght' 620;
}
[data-theme="dark"] h2,
[data-theme="dark"] h3 {
    font-variation-settings: 'wght' 540;
}
[data-theme="dark"] h4 {
    font-variation-settings: 'wght' 500;
}
[data-theme="dark"] h5,
[data-theme="dark"] h6 {
    font-variation-settings: 'wght' 460;
}

/* Body text: nudge heavier in dark mode for the same reason.
   400 → 430 is barely perceptible but measurably improves readability
   of body copy at 14-16 px on dark backgrounds. */
[data-theme="dark"] body {
    font-variation-settings: 'wght' 430;
}

/* Small text: more aggressive nudge - thin strokes at step--1/--2 sizes
   on dark bg lose definition rapidly. 500 (medium) restores their weight. */
[data-theme="dark"] small,
[data-theme="dark"] .label-caps {
    font-variation-settings: 'wght' 500;
}

/* Prose in dark mode: widen letter-spacing a hair to improve readability.
   At step-0 sizes (14-17 px) in dark mode, character shapes blend more
   on dark backgrounds; tiny extra air compensates. */
[data-theme="dark"] .prose {
    letter-spacing: 0.005em;
}

/* ── Viewport-responsive weight breakpoints ──
   font-weight: clamp(N, vw-expr, M) is invalid CSS (number vs length
   type mismatch). These @media rules give stepped viewport-responsive
   weight - the practical equivalent using variable font stops. */

/* Mobile (< 640px): slightly heavier body for small-screen legibility */
@media (width <= 640px) {
    body { font-variation-settings: 'wght' 420; }
}

/* Large desktop (≥ 1440px): headings can carry a little more weight */
@media (width >= 1440px) {
    h1 { font-variation-settings: 'wght' 740; }
    h2, h3 { font-variation-settings: 'wght' 650; }
}

/* ══════════════════════════════════════════
   PART D - HTMX + C# server-side integration notes
   (Encoded as CSS comments so they live near the implementation.)

   1. HTMX PARTIAL SWAPS
      All --step-N / --lh-* / --ls-* tokens are defined on :root and
      inherited by every element in the document, including HTMX-swapped
      partials. No extra setup is required. Because HTMX replaces only
      the target element's innerHTML (not the <html> or <head>), :root
      persists across every navigation.

   2. RAZOR VIEW CONSISTENCY
      Server-rendered Razor views produced by RazorViewRenderer automatically
      pick up all tokens. The rendering pipeline writes HTML strings; no CSS
      scoping or isolation layer is applied. Views can use utility classes
      (e.g. class="prose") directly without any C# coupling.

   3. CSS CASCADE IN PARTIAL SWAPS
      HTMX swaps can change content in the middle of the page. Because all
      typography tokens are custom properties on :root (not scoped to a
      component), they cascade correctly into any partial, regardless of
      swap target. No layout shift occurs - the loaded font files (Poppins
      Regular/Medium/SemiBold/Bold + Public Sans 300-700) are already cached
      from the initial page load.

   4. FONT DISPLAY
      Both font stacks declare font-display: swap. The fluid clamp values
      reference rem (which respects the user's browser base font-size
      preference) rather than px - so 1.75rem at a browser base of 18px
      renders as 31.5 px, honouring accessibility zoom settings correctly.
      Pure-vw preferred terms would NOT honour browser zoom (vw is viewport-
      relative, not user-preference-relative). The hybrid rem + vw form used
      here is the correct approach.

   5. BROWSER ZOOM CORRECTNESS
      clamp(1.75rem, 1.607rem + 0.714vw, 2.75rem):
        - rem terms scale with browser base font-size (zoom-aware).
        - vw term scales with viewport (zoom-unaware), but its contribution
          is bounded by the rem min/max, so the worst-case zoom error is small
          and the step always stays within [min_rem, max_rem] which ARE zoom-
          correct. This is a deliberate tradeoff - widely accepted in the
          community (see Utopia, Every Layout).

   6. AVOIDING LAYOUT SHIFT ON HTMX SWAP
      The top-level #page-content has overflow-anchor: none (set in app.css)
      which prevents scroll-jump when content above the visible area changes.
      Typography tokens don't contribute to layout shift because they're
      already applied at initial paint - no repaint after swap.

   7. DATA-THEME="dark" IN PARTIALS
      The theme toggle writes data-theme="dark" to <html>. HTMX partials
      load into an element under <html>, so they always see the correct
      theme attribute. No per-partial theme detection needed in Razor.
   ══════════════════════════════════════════ */


/* ══════════════════════════════════════════
   PART E - Optional: fluid rhythm grid
   ══════════════════════════════════════════

   Typographic rhythm means vertical spacing is a multiple of the base
   line-height. At body font-size ~16px and line-height 1.6, the rhythm
   unit is 16 × 1.6 = 25.6 px ≈ 1.6rem.

   Tokens for rhythm-aligned spacing (complementary to the spacing scale
   in app.css; these are typography-specific stacking distances): */

:root {
    /* Rhythm unit: body font × body line-height. Fluid because font is fluid. */
    --rhythm:       calc(var(--step-0) * var(--lh-body));  /* ~1.4rem at 320px → ~1.6rem at 1440px */

    /* Common multiples for stacking typography-adjacent elements. */
    --rhythm-half:  calc(var(--rhythm) * 0.5);   /* between tightly-related items */
    --rhythm-1x:    var(--rhythm);               /* paragraph / sibling spacing */
    --rhythm-2x:    calc(var(--rhythm) * 2);     /* section separation */
    --rhythm-3x:    calc(var(--rhythm) * 3);     /* major section breaks */
}

/* Heading margin presets that lock to the rhythm grid.
   Applied only inside .prose blocks to avoid affecting dense UI layouts. */
.prose :where(h2, h3, h4, h5) { margin-top: var(--rhythm-2x); }
.prose :where(p, ul, ol)       { margin-bottom: var(--rhythm-1x); }
