/* Design tokens — the single source of truth for colour, type, spacing and shape.
 *
 * v2.0.0. The palette and scale come from the 2026-08-18 redesign mock-up
 * (`Cultiva Business/Website/2026-08-18_Cultiva-Website_Mockup.html`), whose CSS header states
 * the four composition rules this stylesheet implements:
 *
 *   1. two hairline weights — a structural rule and a card edge, nothing between them
 *   2. section-head rhythm — hairline, h2 in the left field, intro in the right
 *   3. weight carries hierarchy, and green is rationed
 *   4. only actionable things move, 220ms, off under prefers-reduced-motion
 *
 * No other stylesheet, template or content file may hardcode a colour or a spacing value.
 * Every contrast ratio in the comments is recomputed from these hexes by tests/test_contrast.py,
 * so the stylesheet and the stated ratio cannot drift apart.
 *
 * Note on the greys: Gray 50 is 3.00:1 on white and 2.82:1 on the tinted band, so it is
 * DECORATIVE ONLY in v2 — captions, breadcrumbs and stat labels all take Gray 70. Hierarchy in
 * small type is carried by size and weight, which is composition rule 3 anyway.
 */

:root {
  /* ---- Brand ------------------------------------------------------------- */
  --green-primary: #00643c;    /* 7.27:1 on white, 6.82:1 on the tinted band */
  --green-secondary: #008453;  /* 4.75:1 on white — inline links ONLY (and the primary-button hover) */
  --green-hover: #004d2e;      /* primary-button hover */
  --green-deep: #004c2e;       /* footer and the dark band — white on it is 10.13:1 */

  /* The brand values as raw channels, so every translucent surface in the stylesheet is
   * provably a tint of a palette colour and not a new one. A wash, a scrim or a shadow may
   * only be built with rgb(var(--…-rgb) / a) — never a fresh hex. */
  --green-primary-rgb: 0 100 60;
  --green-secondary-rgb: 0 132 83;
  --green-deep-rgb: 0 76 46;
  --black-rgb: 0 0 0;
  --white-rgb: 255 255 255;

  /* ---- Greys ------------------------------------------------------------- */
  --ink: #2b2c2e;              /* 13.98:1 on white — card and prose h3, form labels */
  --gray-70: #6d6e71;          /* 5.10:1 on white, 4.78:1 on the tinted band — ALL body copy */
  --gray-50: #939598;          /* 3.00:1 on white — DECORATIVE ONLY. Never text. */
  --gray-30: #bcbec0;          /* the structural hairline */

  /* ---- Surfaces ---------------------------------------------------------- */
  --paper: #ffffff;
  --paper-soft: #f6f8f7;       /* the one tinted band; gray-70 on it is 4.78:1 */

  /* ---- Semantic ---------------------------------------------------------- */
  --bg: var(--paper);
  --text: var(--gray-70);
  --heading: var(--green-primary);
  --link-color: var(--green-secondary);
  --link-hover: var(--green-primary);

  /* Composition rule 1: exactly two hairline weights, and they mean different things.
   * --rule is structural (over a section head, over a step). --rule-soft is a card edge. */
  --rule: var(--gray-30);
  --rule-soft: #e4e7e5;
  --field-border: var(--gray-30);

  --btn-primary-bg: var(--green-primary);
  --btn-primary-fg: var(--paper);
  --btn-primary-bg-hover: var(--green-secondary);
  --btn-ghost-fg: var(--green-primary);
  --btn-ghost-border: var(--green-primary);
  --btn-ghost-bg-hover: rgb(var(--green-primary-rgb) / 0.07);

  /* The focus ring (2026-09-27 motion pass). One ring on every focusable thing: 2px, 3px clear
   * of the element, never transitioned. Green primary is 7.27:1 on white and 6.82:1 on the
   * tinted band; WCAG 1.4.11 needs 3:1. Dark surfaces re-bind --focus-color to white: the green
   * and deep bands below, and the photo hero in main.css §2. --focus-ring was the colour until
   * this pass; it is the width now, which is the name the motion handoff uses. */
  --focus-color: var(--green-primary);
  --focus-ring: 2px;
  --focus-offset: 3px;

  /* ---- Spacing: 8px base ------------------------------------------------- */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 16px;
  --s-4: 24px;
  --s-5: 32px;
  --s-6: 48px;
  --s-7: 64px;
  --s-8: 96px;
  --s-9: 128px;

  /* Section rhythm. Fluid, so a dense page and an open one breathe differently without a
   * per-section padding field in the content files. */
  --pad-section: clamp(3.2rem, 7.5vh, 5.5rem);
  --pad-tight: clamp(2rem, 4.5vh, 3.2rem);
  /* The photo hero is one height on every page from 701px up (Brian, 2026-10-02: the home,
   * /lab and /solutions pictures were three different heights). Its copy is centred in that
   * height, so the two paddings below are a floor, not the rhythm. 47.5rem is the tallest
   * copy any photo hero has — the Indonesian /textile-auxiliaries, a four-line headline,
   * 545px — plus that floor, plus room to spare. Phones keep the copy's
   * own height: main.css §7 says why. */
  --hero-photo-h: 47.5rem;
  --pad-hero-top: 5rem;
  --pad-hero-bottom: 4.2rem;
  --pad-page-hero-top: clamp(2.6rem, 6vh, 4rem);
  --pad-page-hero-bottom: clamp(1.6rem, 4vh, 2.6rem);
  --gap-grid: clamp(1.1rem, 2.4vw, 1.8rem);
  --gap-shead: clamp(1.8rem, 4vh, 2.8rem);

  /* ---- Type -------------------------------------------------------------- */
  --font-sans: "Mont", "Montserrat", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --fs-h1: clamp(2rem, 4.6vw, 3.3rem);
  --fs-h2: clamp(1.45rem, 2.6vw, 2.1rem);
  --fs-h3: clamp(1.02rem, 1.4vw, 1.16rem);
  --fs-body: 1rem;             /* 16px — also the mobile input floor, so iOS never zooms */
  --fs-lede: clamp(1.05rem, 1.6vw, 1.24rem);
  --fs-small: 0.875rem;
  --fs-eyebrow: 0.8rem;
  --fs-stat: clamp(1.7rem, 3.2vw, 2.4rem);
  --fs-result: clamp(2.8rem, 5.2vw, 4.5rem);   /* a plant result's figure: 72px at desktop width */
  --fs-nav: 0.93rem;
  --fs-button: 1rem;

  --lh-heading: 1.16;
  --lh-body: 1.62;
  --lh-lede: 1.58;

  --fw-light: 300;
  --fw-book: 350;              /* the lede weight, and nothing else */
  --fw-regular: 400;
  --fw-bold: 700;

  --tracking-heading: -0.012em;
  --tracking-eyebrow: 0.14em;

  /* ---- Shape ------------------------------------------------------------- */
  --radius: 14px;              /* cards and panels */
  --radius-input: 10px;
  --radius-small: 8px;
  --radius-pill: 999px;

  /* Depth is black at low alpha only. A card rests almost flat and moves on hover — rule 4. */
  --shadow-card: 0 16px 34px rgb(20 40 30 / 0.10);
  --shadow-drop: 0 14px 40px rgb(20 40 30 / 0.13);

  /* ---- Motion ------------------------------------------------------------ */
  --dur: 220ms;
  --ease: cubic-bezier(0.2, 0, 0, 1);

  /* Entrance motion. Separate from --dur on purpose: --dur is for something the reader just
   * clicked, and an entrance nobody asked for has to be slower to read as calm rather than
   * twitchy. Keeping them apart also stops reveal timings creeping into hover states later.
   * main.css §16 is the only consumer. */
  --reveal-move: 520ms;
  --reveal-fade: 340ms;
  /* The entrance curve (2026-09-27 motion pass). NOT --ease: that one starts from rest, so the
   * first ~100ms of a 520ms rise shows almost no travel, and a rise that has not visibly
   * started reads as a delay. This one leaves fast and settles long. Every entrance uses it;
   * the panel row keeps its own curve, and hovers keep --ease. */
  --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* The desync — fade starts this long after the move. This single value is what separates
   * "arriving" from "appearing"; at 0 the effect dies. Read off otsuka-air.jp, scaled down
   * from its 300ms (that site is a 38,000px consumer scroll narrative; this is not). */
  --reveal-fade-delay: 160ms;
  --reveal-stagger: 55ms;    /* per sibling, capped at the 6th in §16 */
  --reveal-shift: 14px;      /* travel. Small: a long slide is what makes a site look templated */
  /* A second or third group entering the viewport in the same batch as a first one in its
   * section waits this long per place: the heading lands, then its cards, then (in the stat
   * band) the figures. Two stagger steps, 110ms. main.css §16 and motion.js carry it out. */
  --reveal-after: calc(var(--reveal-stagger) * 2);

  /* Panel row (main.css §17). The curve and the duration are produx.design's own, read out of
   * their bundle on 2026-08-27 where GSAP registers it as
   *   CustomEase.create("customBezier","M0,0 C0.83,0 0.17,1 1,1")
   * and runs it at duration 1.25. It is a strong symmetric ease-in-out: slow to leave, quick
   * through the middle, slow to settle. Much longer than --dur on purpose — this is a panel
   * opening, not a button acknowledging a click. */
  --panel-ease: cubic-bezier(0.83, 0, 0.17, 1);
  --panel-dur: 1.25s;
  --panel-grow: 4.4;         /* open panel's flex-grow against 1 for each closed one */
  /* Tuned for the field state the row ships in. A photograph fills the space above the
   * copy; a flat green field does not, and 540px left roughly 380px of nothing. Raise
   * this back toward 540px once industry photographs exist — it is the only change
   * needed, the layout is the same. */
  --panel-h: clamp(320px, 34vw, 440px);

  /* ---- Glow (2026-10-01) ------------------------------------------------
   * The sub-page opener and the closing band (main.css §7 and §11). A port of the
   * AnimatedGradientBackground component Brian sent: a radial gradient with a dark core and rings
   * toward its edge, entering with a 2s zoom-out and fade. Every ring here is a palette green or
   * one mixed with white, and the outermost ring is the page's own white, so the glow ends in
   * the page instead of on a line.
   *
   * Text sits on the core. Up to --glow-safe the rings run deep green to primary green, where
   * every white the glow sets (0.82 and up) clears 4.5:1; the brighter rings live in
   * --glow-horizon, a band at the open edge that holds no text. The painted box is measured in
   * px from that edge, so a longer title moves the text up, never into the glow.
   * tests/test_glow.py recomputes both halves of that from these values. */
  --glow-mint: color-mix(in srgb, var(--green-secondary) 50%, var(--paper));
  --glow-haze: color-mix(in srgb, var(--green-secondary) 18%, var(--paper));
  --glow-core-end: 55%;      /* deep green out to here */
  --glow-safe: 80%;          /* primary green: the last ring text may sit on */
  --glow-glow: 88%;          /* secondary green */
  --glow-mint-at: 94%;
  --glow-haze-at: 97.5%;     /* then the page's white at 100% */
  --glow-h: 560px;           /* the painted box, from the open edge */
  --glow-rx: 160%;           /* horizontal radius, against the box width */
  --glow-x: 50%;             /* the opener's core; off-centre on wide screens, below */
  --glow-horizon: 9rem;      /* the text-free band the glow lives in */
  /* The glow at the foot of a page with no closing band (main.css §13). Its text is the
   * footer's, whose quiet lines are white at 0.66: 5.34:1 on deep green but 4.1:1 on primary.
   * So here text may sit on the core alone, and the box is sized from the gap above the
   * footer's text (horizon + the grid's 2.6rem minimum padding) so that it does: wider than
   * the others (flatter, and a smaller horizontal term) and 1.9 times that gap tall. */
  --glow-foot-rx: 220%;
  --glow-foot-h: calc((var(--glow-horizon) + 2.6rem) * 1.9);
  /* The entrance is the component's own: 2s, scale 1.5 to 1 and opacity 0 to 1, on its
   * cubic-bezier. It plays once, at load, and only on the opener. */
  --glow-in: 2s;
  --glow-from-scale: 1.5;
  --glow-ease: cubic-bezier(0.25, 0.1, 0.25, 1);

  /* ---- Layout ------------------------------------------------------------ */
  --maxw: 1180px;
  --pad-x: clamp(1.1rem, 4vw, 2.4rem);
  --measure: 68ch;
  --tap-min: 44px;

  /* Hero photo scrim. Flat, so the floor is provable; the directional gradient in main.css
   * paints ON TOP of it and can only ever darken. app/styles.py clamps this to 0.55, which is
   * where white text clears 4.5:1 over the lightest possible patch of a photograph. */
  --overlay: 0.6;
}

/* The glow's two other shapes. From 1180px, where the 1180px wrap leaves empty ground right of
 * the text, the opener's core moves left over the copy and the glow climbs the right side, the
 * way the photo heroes lighten toward their subject. At 700px and under, where the text runs the
 * full width, the glow is wider, shorter and its horizon smaller. In every shape the painted
 * box's top row stays inside the deep core ((100% - x) / rx <= --glow-core-end), so an opener
 * taller than the box shows no step where the box ends. tests/test_glow.py holds each shape to
 * its contrast bound at root font sizes of 16, 20 and 24px. */
@media (min-width: 1180px) {
  :root {
    --glow-x: 35%;
    --glow-rx: 125%;
    --glow-h: 520px;
  }
}

@media (max-width: 700px) {
  :root {
    --glow-h: 480px;
    --glow-rx: 220%;
    --glow-horizon: 6rem;
  }
}

/* Lighter entrances on a phone (2026-09-27 motion pass): half the travel, a shorter stagger,
 * and a group never waits on the one before it. main.css §16 also caps the stagger at the third
 * child and holds section headings still below this width. */
@media (max-width: 600px) {
  :root {
    --reveal-shift: 8px;
    --reveal-stagger: 30ms;
    --reveal-after: 0ms;
  }
}

/* Composition rule 3, enforced: #008453 is 4.45:1 on the tinted band, which fails AA, so an
 * inline link there drops to the darker green (6.82:1). The eyebrow, the card numeral and the
 * step numeral use --green-primary on every surface for the same reason. */
.band-soft {
  --link-color: var(--green-primary);
}

/* Green and deep surfaces invert the whole button and link contract at once, so no template
 * has to decide per element how a button on a green band should look. */
.band-green,
.band-deep {
  --bg: var(--green-primary);
  --text: var(--paper);
  --heading: var(--paper);
  --link-color: var(--paper);
  --link-hover: var(--paper);
  --rule: rgb(var(--white-rgb) / 0.35);
  --rule-soft: rgb(var(--white-rgb) / 0.22);
  --btn-primary-bg: var(--paper);
  --btn-primary-fg: var(--green-primary);
  --btn-primary-bg-hover: #eaf3ee;   /* green-primary on it is 6.42:1 */
  --btn-ghost-fg: var(--paper);
  --btn-ghost-border: var(--paper);
  --btn-ghost-bg-hover: rgb(var(--white-rgb) / 0.12);
  --focus-color: var(--paper);
}

.band-deep {
  --bg: var(--green-deep);
}

/* The sub-page opener on the glow (main.css §7) is dark ground as well, but it takes only the
 * band's button contract. Its text colours are set in main.css, where the print rules can put
 * them back; re-binding --text and --heading here would leave those rules nothing to restore.
 * No opener has a button today; this is so the first one is not green on green (1.39:1). */
.hero--glow {
  --btn-primary-bg: var(--paper);
  --btn-primary-fg: var(--green-primary);
  --btn-primary-bg-hover: #eaf3ee;   /* the band's own hover */
  --btn-ghost-fg: var(--paper);
  --btn-ghost-border: var(--paper);
  --btn-ghost-bg-hover: rgb(var(--white-rgb) / 0.12);
}
