/* ==========================================================================
   GPR RHYTHM v0.5
   A numeric layer for type and spacing. No color, shape, layout or style.

   WHAT THIS FILE IS
   1. Tokens: a fluid type scale (--step-*), a fluid space scale (--s-*), a gutter,
      and size-aware line-heights. Everything derives from six preset numbers.
   2. Three opt-in classes for RUNNING TEXT areas only (article body, FAQ, legal):
      .rh-flow, .rh-prose, .rh-trim.
   3. One optional helper for display headings: .rh-display.

   WHAT THIS FILE IS NOT
   No grid, no section padding, no container. Grid, axes, section cadence, overlaps
   and bleeds belong to each project's composition spec, not here.

   RULE FOR AGENTS
   Running text and ordinary spacing use these tokens.
   A composition move that needs an off-scale value (overlap, offset, image bleed,
   negative margin) is allowed: give it a NAMED token in the project, e.g.
   --hero-overlap: -6rem. Never an anonymous magic number.

   ORDER OF WORK
   1. Load the default preset as PROVISIONAL tokens.
   2. Build the hero by eye. Departing from the provisional tokens is allowed.
   3. Tune the six preset numbers to fit the hero, then build the rest on them.
   The scale ends up derived from the concept. Do not retrofit onto sections already signed off.

   MOBILE
   Fluid values keep proportions between breakpoints. They do NOT replace a separate
   mobile composition. Structural changes are still designed, not interpolated.

   CASCADE
   Tokens are global custom properties: unused tokens affect nothing.
   Classes are unlayered and opt-in. Flow selectors carry specificity (0,2,0), so inside a
   .rh-flow area they beat theme rules up to one class plus one element, e.g.
   `.entry-content p` or `.elementor-widget-text-editor p`. A theme rule with two classes or
   an ID still wins and doubles the gap: in that case the wrapper/reset of the landing-pages
   skill is required. Verified against simulated theme rules only, not yet on a live WordPress.
   ========================================================================== */

:root,
[data-rhythm] {
  /* ---- Preset: six numbers ("calm", default) ---- */
  --body-min: 16;        /* body px at 360 viewport  */
  --body-max: 16;        /* body px at 1440 viewport */
  --ratio-min: 1.15;      /* type scale ratio, mobile  */
  --ratio-max: 1.28;     /* type scale ratio, desktop */
  --density: 0.8;          /* multiplier on content and macro spacing only */
  --measure: 75ch;       /* line length for running text */

  /* ---- Hebrew font calibration (measured from the font files; Hebrew letters
          weighted by frequency, spaces included) ----
     glyph-w  = mean advance width in em. Used by .rh-display.
     trim-fix = Hebrew letter height minus cap-height, in em. text-box-trim cuts to the
                Latin cap-height; Hebrew letters are shorter, so this residue stays above them.

     font                   glyph-w   he-height   cap     trim-fix
     Karantina 700          0.313     0.600       0.700   -0.100
     Frank Ruhl Libre 500   0.418     0.597       0.660   -0.063
     Frank Ruhl Libre 900   0.432     0.597       0.660   -0.063
     Assistant 400          0.418     0.555       0.750   -0.195
     Rubik 400              0.461     0.571       0.700   -0.129
     Rubik 900              0.501     0.571       0.700   -0.129
     Fredoka 700            0.466     0.586       0.700   -0.114
     Heebo 400              0.468     0.575       0.711   -0.136
     Heebo 900              0.509     0.591       0.711   -0.120 */
  /* Both values describe the DISPLAY font only (they are used by headings).
     SET THEM PER PROJECT from the table. The defaults below fit Heebo 400;
     with Heebo 900 the default glyph-w is about 8% off in size. */
  --glyph-w: 0.509;  /* RHYTHM TEST: display is Heebo 800; the table has 400/900 only, 900 row used */
  --trim-fix: -0.120em;

  /* ---- System bounds ---- */
  --vw-min: 360;
  --vw-max: 1440;

  /* ---- Type scale: computed, do not edit ---- */
  --_span: calc(var(--vw-max) - var(--vw-min));

  --_a-1: calc(var(--body-min) * pow(var(--ratio-min), -1));  --_b-1: calc(var(--body-max) * pow(var(--ratio-max), -1));
  --_a0:  var(--body-min);                                     --_b0:  var(--body-max);
  --_a1:  calc(var(--body-min) * var(--ratio-min));            --_b1:  calc(var(--body-max) * var(--ratio-max));
  --_a2:  calc(var(--body-min) * pow(var(--ratio-min), 2));    --_b2:  calc(var(--body-max) * pow(var(--ratio-max), 2));
  --_a3:  calc(var(--body-min) * pow(var(--ratio-min), 3));    --_b3:  calc(var(--body-max) * pow(var(--ratio-max), 3));
  --_a4:  calc(var(--body-min) * pow(var(--ratio-min), 4));    --_b4:  calc(var(--body-max) * pow(var(--ratio-max), 4));
  --_a5:  calc(var(--body-min) * pow(var(--ratio-min), 5));    --_b5:  calc(var(--body-max) * pow(var(--ratio-max), 5));
  --_a6:  calc(var(--body-min) * pow(var(--ratio-min), 6));    --_b6:  calc(var(--body-max) * pow(var(--ratio-max), 6));
  --_a7:  calc(var(--body-min) * pow(var(--ratio-min), 7));    --_b7:  calc(var(--body-max) * pow(var(--ratio-max), 7));

  --_s-1: calc((var(--_b-1) - var(--_a-1)) / var(--_span));
  --_s0:  calc((var(--_b0) - var(--_a0)) / var(--_span));
  --_s1:  calc((var(--_b1) - var(--_a1)) / var(--_span));
  --_s2:  calc((var(--_b2) - var(--_a2)) / var(--_span));
  --_s3:  calc((var(--_b3) - var(--_a3)) / var(--_span));
  --_s4:  calc((var(--_b4) - var(--_a4)) / var(--_span));
  --_s5:  calc((var(--_b5) - var(--_a5)) / var(--_span));
  --_s6:  calc((var(--_b6) - var(--_a6)) / var(--_span));
  --_s7:  calc((var(--_b7) - var(--_a7)) / var(--_span));

  /* Each step: clamp(min, rem intercept + vw slope, max). The rem part keeps user zoom working.
     Calm preset, desktop px: -1:15  0:19  1:24  2:30  3:37  4:46  5:58  6:72  7:91
     HIERARCHY NOTE: in Hebrew, contrast comes from size and weight. Skip steps on purpose;
     moving one step at a time produces a flat hierarchy. Which steps to skip is a
     per-project decision. */
  --step--1: clamp(calc(var(--_a-1) / 16 * 1rem), calc((var(--_a-1) - var(--_s-1) * var(--vw-min)) / 16 * 1rem + var(--_s-1) * 100vw), calc(var(--_b-1) / 16 * 1rem));
  --step-0:  clamp(calc(var(--_a0) / 16 * 1rem),  calc((var(--_a0) - var(--_s0) * var(--vw-min)) / 16 * 1rem + var(--_s0) * 100vw),  calc(var(--_b0) / 16 * 1rem));
  --step-1:  clamp(calc(var(--_a1) / 16 * 1rem),  calc((var(--_a1) - var(--_s1) * var(--vw-min)) / 16 * 1rem + var(--_s1) * 100vw),  calc(var(--_b1) / 16 * 1rem));
  --step-2:  clamp(calc(var(--_a2) / 16 * 1rem),  calc((var(--_a2) - var(--_s2) * var(--vw-min)) / 16 * 1rem + var(--_s2) * 100vw),  calc(var(--_b2) / 16 * 1rem));
  --step-3:  clamp(calc(var(--_a3) / 16 * 1rem),  calc((var(--_a3) - var(--_s3) * var(--vw-min)) / 16 * 1rem + var(--_s3) * 100vw),  calc(var(--_b3) / 16 * 1rem));
  --step-4:  clamp(calc(var(--_a4) / 16 * 1rem),  calc((var(--_a4) - var(--_s4) * var(--vw-min)) / 16 * 1rem + var(--_s4) * 100vw),  calc(var(--_b4) / 16 * 1rem));
  --step-5:  clamp(calc(var(--_a5) / 16 * 1rem),  calc((var(--_a5) - var(--_s5) * var(--vw-min)) / 16 * 1rem + var(--_s5) * 100vw),  calc(var(--_b5) / 16 * 1rem));
  --step-6:  clamp(calc(var(--_a6) / 16 * 1rem),  calc((var(--_a6) - var(--_s6) * var(--vw-min)) / 16 * 1rem + var(--_s6) * 100vw),  calc(var(--_b6) / 16 * 1rem));
  --step-7:  clamp(calc(var(--_a7) / 16 * 1rem),  calc((var(--_a7) - var(--_s7) * var(--vw-min)) / 16 * 1rem + var(--_s7) * 100vw),  calc(var(--_b7) / 16 * 1rem));

  /* ---- Space scale ----
     Micro: fixed on every screen, not affected by density.
     Content and macro: the higher in the hierarchy, the harder it compresses on mobile. */
  --s-1: 0.25rem;   /*  4 */
  --s-2: 0.5rem;    /*  8 */
  --s-3: 0.75rem;   /* 12 */
  --s-4: 1rem;      /* 16 */
  --s-5:  calc(clamp(1.25rem, 1.167rem + 0.3704vw, 1.5rem) * var(--density));  /* 20 ->  24  (17%) */
  --s-6:  calc(clamp(1.5rem,  1.333rem + 0.7407vw, 2rem)   * var(--density));  /* 24 ->  32  (25%) */
  --s-7:  calc(clamp(2rem,    1.667rem + 1.481vw,  3rem)   * var(--density));  /* 32 ->  48  (33%) */
  --s-8:  calc(clamp(2.5rem,  1.833rem + 2.963vw,  4.5rem) * var(--density));  /* 40 ->  72  (44%) */
  --s-9:  calc(clamp(3.5rem,  2.333rem + 5.185vw,  7rem)   * var(--density));  /* 56 -> 112  (50%) */
  --s-10: calc(clamp(4.5rem,  2.667rem + 8.148vw,  10rem)  * var(--density));  /* 72 -> 160  (55%) */

  --gutter: clamp(1.25rem, 0.3333rem + 4.074vw, 4rem);                         /* 20 ->  64 */

  /* Line-height as a function of size: small text gets air, large headings close up.
     18px -> 1.67 | 24px -> 1.5 | 40px -> 1.3 | 60px -> 1.2 */
  --lh: calc(1em + 0.75rem);
  --lh-display: calc(1em + 0.5rem);
}

/* ---- Presets: set data-rhythm on <html> or on any region ---- */
[data-rhythm="compact"] { --body-min: 16; --body-max: 17; --ratio-min: 1.15;  --ratio-max: 1.2;   --density: 0.85; --measure: 65ch; }
[data-rhythm="open"]    { --body-min: 17; --body-max: 20; --ratio-min: 1.2;   --ratio-max: 1.333; --density: 1.25; --measure: 56ch; }

/* ==========================================================================
   RUNNING TEXT (opt-in)
   ========================================================================== */

/* Vertical rhythm belongs to the parent, not to the children.
   Margins are written directly (no inherited custom property), so nested flows cannot leak.
   The doubled class is deliberate: it raises specificity to (0,2,0). */
.rh-flow { font-size: var(--step-0); line-height: var(--lh); }
.rh-flow.rh-flow > * { margin-block: 0; }
.rh-flow.rh-flow > * + * { margin-block-start: var(--s-5); }
.rh-flow.rh-flow > * + :is(h2, h3, h4) { margin-block-start: var(--s-8); }   /* heading sits far from what is above it */
.rh-flow.rh-flow > :is(h2, h3, h4) + * { margin-block-start: var(--s-4); }   /* and close to what is below it */
.rh-flow :is(h2, h3, h4) { text-wrap: balance; }
.rh-flow p { text-wrap: pretty; }

.rh-prose { max-inline-size: var(--measure); }

/* Optical trim of the font's built-in air above and below headings, plus the Hebrew residue fix.
   Do NOT use on headings that carry a background, border or padding: trimming shrinks their box. */
@supports (text-box: trim-both cap alphabetic) {
  .rh-trim :is(h1, h2, h3, h4) { text-box: trim-both cap alphabetic; }
  .rh-trim.rh-flow.rh-flow > * + :is(h2, h3, h4),
  .rh-trim .rh-flow.rh-flow > * + :is(h2, h3, h4) { margin-block-start: calc(var(--s-8) + var(--trim-fix, 0em)); }
}

/* ==========================================================================
   DISPLAY HEADING HELPER (optional, never a default)
   Sizes a heading as a share of its region's width:
     font-size = fill * region width / (chars * glyph-w)
   fill  = share of the region the longest line should span (0-1)
   chars = characters in the longest line, spaces included.
   This is a MEASURING TOOL, not a safeguard: if the copy changes and --chars does not,
   nothing breaks loudly; the heading is just slightly off or wraps. For client-editable
   headings (WordPress editor) find the size with it, then freeze the result as a step or a
   named token. Judge by eye. The eye wins.
   Keep max <= 2.5 * min so browser zoom still enlarges the text.
   ========================================================================== */

.rh-region { container-type: inline-size; }

.rh-display {
  --fill: 0.8;
  --chars: 18;
  --display-min: 2.25rem;
  --display-max: 5.5rem;
  font-size: clamp(var(--display-min), calc(var(--fill) * 100cqi / (var(--chars) * var(--glyph-w))), var(--display-max));
  line-height: var(--lh-display);
  text-wrap: balance;
}
