/* ===========================================================================
   CAN-Agri prototype base styles

   Each block is tagged for the WordPress rebuild:
     [theme provides]  Hello Elementor or the block theme already does this.
     [carry over]      Must be pasted into Additional CSS or the child theme.

   Fallback metrics are MEASURED, not estimated: Valley Sans sits at 109.0 percent
   of Helvetica's advance width, Geist at 99.9 (Geist Mono retired, D-194),
   measured in Chromium at 100px. The swap moves nothing.
   =========================================================================== */

/* ---- Fonts [carry over] -------------------------------------------------- */
/* Valley Sans replaced Krub as the display face 2026-09-08 (D-85). One variable
   file covers 100 to 900. The RANGE MUST BE DECLARED: Valley's default instance
   is Thin, so a bare font-family renders every heading hairline. The Krub files
   stay in assets/fonts unreferenced rather than being deleted. */
@font-face { font-family: "Valley Sans"; src: url("../fonts/ValleySans-var.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap; }
/* Measured, not estimated: Valley's advance is 109.2 percent of Helvetica Neue's
   at weight 700, 106.9 at 600. 109 is used because h1 and h2, the type most
   visible during the swap, are 700. Ascent and descent come from the font's own
   hhea table: 960 and 240 on a 1000 em. */
@font-face { font-family: "Valley Sans fallback"; src: local("Helvetica Neue"), local("Helvetica"), local("Arial");
  size-adjust: 109.0%; ascent-override: 96.0%; descent-override: 24.0%; line-gap-override: 0%; }

@font-face { font-family: "Geist"; src: url("../fonts/Geist-var.woff2") format("woff2");
  font-weight: 400 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Geist fallback"; src: local("Helvetica Neue"), local("Helvetica"), local("Arial");
  size-adjust: 99.9%; ascent-override: 101.1%; descent-override: 29.0%; line-gap-override: 0%; }


/* ---- Reset [theme provides, mostly] ------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* [carry over] Any component that sets its own display would otherwise beat
   the UA rule for [hidden] and stay visible. The pause control did exactly
   that. One guard prevents the whole class of bug. */
[hidden] { display: none !important; }

/* [carry over] */
html {
  -webkit-text-size-adjust: 100%;
  /* header height plus the float offset plus breathing room */
  scroll-padding-top: calc(var(--ca-header-clear) + var(--ca-space-4));
}

/* [carry over] The body background must be painted, not inherited. */
body {
  margin: 0;
  background: var(--ca-bg);
  color: var(--ca-text);
  font-family: var(--ca-font-text);
  font-size: var(--ca-size-body);
  font-weight: var(--ca-weight-regular);
  line-height: var(--ca-lh-body);
  letter-spacing: var(--ca-track);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* [theme provides] */
img, svg, video { display: block; max-width: 100%; }
img { height: auto; }

/* ---- Type roles [carry over] --------------------------------------------
   Krub carries h1 to h3 and display figures. Geist takes h4, where the title
   face has done its work and the text face reads better small. Every display
   level carries negative tracking by band.                                  */
h1, h2, h3 {
  font-family: var(--ca-font-display);
  margin: 0;
  text-wrap: balance;
}
h1 {
  font-size: var(--ca-size-h1);
  font-weight: var(--ca-weight-h1);
  line-height: var(--ca-lh-display);
  letter-spacing: var(--ca-track-h1);
  color: var(--ca-teal);
}
h2 {
  font-size: var(--ca-size-h2);
  font-weight: var(--ca-weight-h2);
  line-height: var(--ca-lh-tight);
  letter-spacing: var(--ca-track-h2);
  color: var(--ca-teal);
}
h3 {
  font-size: var(--ca-size-h3);
  font-weight: var(--ca-weight-h3);
  line-height: var(--ca-lh-tight);
  letter-spacing: var(--ca-track-h3);
  color: var(--ca-teal);
}
h4 {
  font-family: var(--ca-font-text);
  font-size: var(--ca-size-h4);
  font-weight: var(--ca-weight-semibold);
  line-height: var(--ca-lh-tight);
  letter-spacing: var(--ca-track-h4);
  color: var(--ca-teal);
  margin: 0;
  text-wrap: balance;
}

p { margin: 0; max-width: var(--ca-measure); text-wrap: pretty; }
/* ---- Split-text entrance (D-161) ---------------------------------------
   Values measured off lightshiprv.com/specs: 800ms a character, 10ms stagger,
   cubic-bezier(.38, 0, .215, 1), and the word wrapper clipped flush to the line
   box so the characters rise into a mask rather than fading in place. The
   classes only exist after the script has split the text, so nothing here can
   hide content with JavaScript off. */
.ca-split__w { display: inline-block; clip-path: inset(0 -0.08em); }
/* The hero line carries a hard shadow 2 to 3px under the letters (D-122), which
   the word clip cut off. Its clip reaches down by the shadow's depth instead of
   the hero taking still looser leading (D-212). */
.ca-hero__title .ca-split__w { clip-path: inset(0 -0.08em -0.08em); }
.ca-split__c {
  display: inline-block;
  transform: translateY(0.8em);
  opacity: 0;
}
.is-revealed .ca-split__c,
.is-revealed.ca-split__w .ca-split__c { transform: none; opacity: 1; }
.is-split.is-revealed .ca-split__c {
  transition: transform 800ms cubic-bezier(.38, 0, .215, 1),
              opacity 800ms cubic-bezier(.38, 0, .215, 1);
  transition-delay: calc(var(--char-index, 0) * 10ms);
}
/* The text under a title slides in as a BLOCK, not per character. Lightship
   splits only the heading; their body copy uses a separate slide with its own
   longer duration, and per-character on a paragraph would read as a gimmick. */
.ca-subsplit { transform: translateY(var(--ca-space-5)); opacity: 0; }
.is-revealed.ca-subsplit,
.ca-subsplit.is-revealed {
  transform: none; opacity: 1;
  transition: transform var(--ca-dur-entrance) var(--ca-ease-out) 260ms,
              opacity var(--ca-dur-entrance) var(--ca-ease-out) 260ms;
}


p + p { margin-top: var(--ca-space-5); }

/* On the dark ground the roles invert. */
.ca-on-dark h1, .ca-on-dark h2, .ca-on-dark h3, .ca-on-dark h4 { color: var(--ca-text-on-dark); }
.ca-on-dark p { color: var(--ca-text-muted-on-dark); }

/* ---- The eyebrow: CAN-Agri's own signature ------------------------------
   Geist, uppercase, 0.1em (D-194; it was Geist Mono at 0.14em). Documented
   as a signature per D-48. It replaces Fedgroup's 22px
   sentence-case descriptor, which was their signature and is not ours.      */
.ca-eyebrow {
  font-family: var(--ca-font-label);
  font-size: var(--ca-size-eyebrow);
  /* 500 to 600, and every gap below it halved: Jacqui 2026-10-12, "go up a
     weight and halve the space below it" (D-202). */
  font-weight: var(--ca-weight-semibold);
  line-height: var(--ca-lh-flat);
  letter-spacing: var(--ca-track-eyebrow);
  text-transform: uppercase;   /* visual-tells-approved:A10 CAN-Agri signature, D-48 */
  color: var(--ca-muted);
  margin: 0 0 var(--ca-space-1) 0;   /* 8 to 4 (D-202); was halved once before from space-4 */
}
/* In a stack the gap is the NEXT element's margin-top, set by the stack
   rhythm at 24. Halved to 12 for whatever follows an eyebrow (D-202). */
:is(.ca-head, .ca-stack, .ca-partnerband__text) > .ca-eyebrow + * { margin-top: var(--ca-space-2); }   /* 12 to 8, D-243 */
.ca-on-dark .ca-eyebrow { color: var(--ca-accent); }

.ca-lead {
  font-size: var(--ca-size-lead);
  font-weight: var(--ca-weight-lead);   /* medium since D-247 */
  line-height: var(--ca-lh-lead);
  color: var(--ca-muted);
  /* 48ch, not 60. Geist's average character is about 0.51em while `ch` is
     0.66em, so 48ch renders at roughly 62 characters a line, inside the 45 to
     75 band. Measured, not assumed. */
  max-width: 48ch;
}
.ca-on-dark .ca-lead { color: var(--ca-text-muted-on-dark); }
.ca-small { font-size: var(--ca-size-small); max-width: 54ch; }

/* Measure caps, set from RENDERED line measurement, not from a guess at the
   character width (D-66). Geist's average character is about 0.51em where the
   `ch` unit measures 0.66em, so a cap written in `ch` runs about 30 percent
   wider than it reads. Both caps below were solved backwards from the target
   band: a bare paragraph in a stack lands at 55 to 66 characters a line, and a
   14px note at 54ch lands at 51 to 66. */
.ca-stack > p:not([class]) { max-width: 51ch; }
.ca-caption { font-size: var(--ca-size-caption); color: var(--ca-muted); }

/* ---- Links [carry over] -------------------------------------------------
   :where() contributes ZERO specificity, so component classes still win. A
   bare a:not(.c-btn) outranked .ca-nav__link and underlined every nav item.

   ANY anchor that is really a button must be named in this exclusion list, or
   the link colour overrides its own. It has bitten twice now: first .c-btn,
   where a tertiary CTA came out white on a near-white face, then .ca-cta in the
   floating masthead, where .ca-on-dark painted the label white on the accent
   and axe correctly reported 1.28:1. If you add a button-shaped anchor, add it
   here. */
a:where(:not(.c-btn, .ca-cta)) {
  color: var(--ca-teal);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
a:where(:not(.c-btn, .ca-cta)):hover { color: var(--ca-teal-900); }
.ca-on-dark a:where(:not(.c-btn, .ca-cta)) { color: var(--ca-white); }
.ca-on-dark a:where(:not(.c-btn, .ca-cta)):hover { color: var(--ca-accent); }

/* ---- Focus [carry over], never removed --------------------------------- */
:focus-visible { outline: 3px solid var(--ca-teal); outline-offset: 2px; }
.ca-on-dark :focus-visible { outline-color: var(--ca-accent); }

/* ---- Skip link [carry over; Hello Elementor supplies its own to #content,
   so the rebuild must NOT add a second one] -------------------------------- */
.ca-skip {
  position: absolute; left: var(--ca-space-4); top: var(--ca-space-4); z-index: 200;
  padding: var(--ca-space-3) var(--ca-space-4);
  background: var(--ca-teal); color: var(--ca-white);
  border-radius: var(--ca-r-control);
  font-family: var(--ca-font-label); font-size: var(--ca-size-caption);
  letter-spacing: var(--ca-track-eyebrow); text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform var(--ca-dur-fast) var(--ca-ease-out);
}
.ca-skip:focus-visible { transform: translateY(0); }

/* ---- Visually hidden [carry over] --------------------------------------- */
.ca-vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---- Section scaffold [carry over] --------------------------------------
   One section, one wrapper. The outer band is FULL WIDTH and SQUARE: radius
   belongs to inset surfaces, and both references keep their structural bands
   square. Sections are separated by a 1px hairline, not by a radial gradient.
   Nesting never goes deeper than band > content > group > component.        */
.ca-band { border-radius: 0; }
.ca-band + .ca-band { border-top: 1px solid var(--ca-hairline); }
.ca-band--dark { background: var(--ca-bg-dark); }
.ca-band--tint { background: var(--ca-bg-tint); }
.ca-band--white { background: var(--ca-bg); }
.ca-band--dark + .ca-band, .ca-band + .ca-band--dark { border-top: 0; }

.ca-content {
  max-width: var(--ca-content-max);
  margin-inline: auto;
  padding: var(--ca-space-11) var(--ca-gutter);   /* 128 to 84 (D-251; 64 was tried and read too tight) */
}
.ca-content--tight { padding-block: var(--ca-space-10); }   /* 84 to 64 (D-251) */

/* The internal stack: eyebrow 16, heading 24, lead 24, body 24, CTA 40 */
/* The head does not carry a measure itself: each child owns its own, or the
   parent clamps the lead into three short lines. */
.ca-head { margin-bottom: var(--ca-space-8); }
.ca-head > * + * { margin-top: var(--ca-space-5); }
/* 20ch, the same measure as the page titles (D-154), and for the same reason:
   Valley Sans sets a wide zero, so 24ch at 42px was about 994px and let a 675px
   title run as one line. Measured across all fifteen multi-word section
   headings at 1440 and 1280 before choosing: the measure has to sit under 675
   to stack "From seed to stack, under one roof" and over 490 so the longest,
   980px on partners, stays on two lines. 20ch is 584 at 1440 and 501 at 1280,
   which stacks every heading of 34 characters or more and leaves "What has
   actually happened" and the short legal titles on one line. D-168. */
.ca-head h2 { max-width: 20ch; }
/* A heading that sets its own first line with a <br> takes the full measure,
   so the second line holds together as one phrase. Tim, 2026-09-22, on
   "Different commercial possibilities.": "Poor line break". */
.ca-head .ca-h-free, .ca-pagehead .ca-h-free { max-width: none; }
/* 48ch, unchanged, and BALANCED. Jacqui asked for the technology lead to wrap
   under its two-line heading (D-169). Narrowing the measure was tried first:
   40ch wrapped it, but put the 131-character contact lead and the 123-character
   PAIA lead on three lines at 600 to 1024, which is 41 to 44 characters a line,
   and A11 failed on 25 width and page combinations. A11's own arithmetic also
   rules out a two-line 64-character lead at ANY measure: two lines of it are 32
   characters each. So the wrap came from the copy, not the CSS: the lead now
   names the stages the tabs below it carry, runs 100 characters, and sets on
   two lines of about 50 from 480 up. Replayed A11's measure on every section
   lead at all eleven check widths before keeping 48: zero flags, no widows.
   Balanced so a two-line lead splits evenly instead of leaving a short second
   line; balance never changes the line count, so A11 is unaffected by it. */
.ca-head .ca-lead { max-width: 48ch; text-wrap: balance; }
.ca-stack > * + * { margin-top: var(--ca-space-5); }
/* A heading and the lead that follows it sit 4px closer than the 24px rhythm, in section heads and in
   text-and-photo rows alike (D-245). Placed after both rhythm rules so it wins on order. */
:is(.ca-head, .ca-stack) > :is(h2, h3) + .ca-lead { margin-top: var(--ca-space-heading-lead); }
/* An H3 sits closer to the body paragraph under it than the 24 stack rhythm: 12, so the heading reads as that
   paragraph's title (Jacqui, 2026-10-26, "reduce the spacing between this level heading and the paragraph and
   update it in the design system", D-284). Under an H3 the paragraph is body, never a lead (D-252). */
.ca-stack > h3 + p:not([class]) { margin-top: var(--ca-space-3); }
.ca-cta-row { margin-top: var(--ca-space-7); }

/* Rows: a plain two-column split. No alternation rule, because alternating
   every row was Fedgroup's rhythm and reads as a template. */
.ca-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--ca-gutter); align-items: center; }
/* Proportions taken off Jacqui's reference frame rather than chosen. In it the
   text column is 321px and the media 560px in a 972px measure, a ratio of
   1 : 1.74, which is 4fr to 7fr. It was 5fr to 7fr, so the text was too wide
   and the media too small. The reference is also TOP aligned, not centred: its
   eyebrow sits at 455 against a media top of 448. D-139. */
.ca-row--wide { grid-template-columns: 4fr 7fr; align-items: start; }
/* The media is the tall element in the reference, not the short one. With the
   text column carrying an eyebrow, a three-line heading, a lead, a four-item
   list and a CTA, it runs about 700px against a 3:2 image's 490, which left 200
   px of empty band beside the picture. The media column stretches to the row
   and the image covers, so the picture is as tall as the text beside it. Only
   above 1024, where the Z exists at all. */
@media (min-width: 1025px) {
  .ca-row--wide > * { align-self: stretch; }
  .ca-row--wide .c-media { height: 100%; aspect-ratio: auto; }
  /* A film keeps its 16:9 and sits at the top: stretched to a tall text column,
     the ratio widened it past its track and the page scrolled sideways (D-348). */
  .ca-row--wide > .ca-film { align-self: start; }
}
/* The Z. Jacqui, 2026-09-08, for "who it's for": each section alternates the
   side its media sits on, so the reading path zig-zags down the page instead of
   three identical rows.

   `.ca-row--reverse` is NOT a new class. It was already on two pages, on the
   investors section here and on one section of farms-and-produce, and it had NO
   CSS ANYWHERE: the alternation was intended when those pages were built and
   never implemented, which is exactly why the page reads as three identical
   rows. Defining it fixes both pages at once. Caught 2026-09-08, and A25 now
   exists so a class in the markup with no rule behind it fails the build.

   The DOM order stays text then media on every section, so the reading and tab
   order do not alternate with the layout; only the grid placement flips, and
   the wide track follows the media. D-136. */
.ca-row--reverse { grid-template-columns: 7fr 4fr; }
.ca-row--reverse > *:first-child { grid-column: 2; grid-row: 1; }
.ca-row--reverse > *:last-child  { grid-column: 1; grid-row: 1; }

@media (max-width: 1024px) {
  .ca-content { padding-block: var(--ca-space-10); }   /* 84 to 64 (D-251) */
  .ca-row, .ca-row--wide, .ca-row--reverse { grid-template-columns: 1fr; gap: var(--ca-space-8); }
  /* Below 1025 the Z collapses and the media goes back under its text. */
  .ca-row--reverse > *:first-child, .ca-row--reverse > *:last-child { grid-column: 1; grid-row: auto; }
}
@media (max-width: 767px) {
  .ca-content, .ca-content--tight { padding-block: var(--ca-space-8); }   /* 64 to 48 (D-251) */
  .ca-head { max-width: none; }
}

/* ---- Motion [carry over] -------------------------------------------------
   Entrances animate TRANSFORM ONLY, never opacity. Text parked at opacity 0
   waiting on an observer is invisible to a contrast check and to anyone whose
   observer never fires, and it breaks the rule that everything meant to be
   read is visible once the page has loaded. So content is always fully
   opaque and simply settles into place. Deviation from the brief's fade-up,
   recorded as D-64. */
.ca-reveal { transform: none; }
/* 32px, not 16. Jacqui, 2026-09-08: "The stats do not animate in, unless they
   happen during the scroll, I do not see it." Two causes, both fixed: 16px is
   below the threshold at which a rise reads as movement at all, and the
   observer fired at 5 percent visibility with a -8% root margin, so the
   entrance was over before the element was properly on screen. */
.is-ready .ca-reveal { transform: translateY(var(--ca-space-6)); }
.is-ready .ca-reveal.is-in {
  transform: none;
  transition: transform var(--ca-dur-entrance) var(--ca-ease-out);
}
/* The stat columns come in one after another rather than as a block. Transform
   only, so nothing reflows and nothing is ever invisible. */
.is-ready .c-stats.ca-reveal > .c-stat { transform: translateY(var(--ca-space-5)); }
.is-ready .c-stats.ca-reveal.is-in > .c-stat {
  transform: none;
  transition: transform var(--ca-dur-entrance) var(--ca-ease-out);
}
.is-ready .c-stats.ca-reveal.is-in > .c-stat:nth-child(2) { transition-delay: 70ms; }
.is-ready .c-stats.ca-reveal.is-in > .c-stat:nth-child(3) { transition-delay: 140ms; }
.is-ready .c-stats.ca-reveal.is-in > .c-stat:nth-child(4) { transition-delay: 210ms; }

/* SCROLL-LINKED, not time-linked. This is the third attempt at the entrance and
   the first two missed the actual cause. Diagnosed by instrumenting it: the
   observer fires when the element's top is around 690, and the transition then
   runs on a 700ms CLOCK. Measured decay of the stats grid: 32px of offset at
   scroll 2600, 19.9px at 2800, 10.0px at 3000, 4.6px at 3200, done by 3800. So
   while the entrance played, the page itself travelled 1200px. A 32px rise is
   invisible against that. Jacqui: "The stats do not animate in, unless they
   happen during the scroll, I do not see it", which is exactly right, and no
   amount of making it bigger or later fixes a clock racing a scroll.

   Tying it to the scroll position instead means the element rises AS it enters,
   at whatever speed you scroll, and it cannot happen while you are not looking.
   Same mechanism as the hero fold. Chromium only for now, so the observer path
   above stays as the fallback. D-135. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes ca-reveal-in {
      from { transform: translateY(var(--ca-space-8)); }
      to   { transform: none; }
    }
    .is-ready .ca-reveal {
      transform: none; transition: none;
      animation: ca-reveal-in linear both;
      animation-timeline: view();
      /* entry alone lasts only the element's own height, which for a short
         band is a couple of hundred pixels. Running it from the moment the
         element appears to a quarter of the way across the screen gives the
         rise real scroll distance to happen over. */
      animation-range: entry 0% cover 35%;
    }
    /* The stats stagger by giving each column a later slice of the same entry,
       so the four arrive in sequence as the band comes up. The grid itself sits
       still, or the child and parent transforms would compound. */
    .is-ready .c-stats.ca-reveal { animation: none; transform: none; }
    .is-ready .c-stats.ca-reveal > .c-stat {
      transform: none; transition: none;
      animation: ca-reveal-in linear both;
      animation-timeline: view();
    }
    .is-ready .c-stats.ca-reveal > .c-stat:nth-child(1) { animation-range: entry 0%  cover 42%; }
    .is-ready .c-stats.ca-reveal > .c-stat:nth-child(2) { animation-range: entry 0%  cover 36%; }
    .is-ready .c-stats.ca-reveal > .c-stat:nth-child(3) { animation-range: entry 0%  cover 42%; }
    .is-ready .c-stats.ca-reveal > .c-stat:nth-child(4) { animation-range: entry 0%  cover 48%; }
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .is-ready .ca-reveal, .is-ready .ca-reveal.is-in,
  .is-ready .c-stats.ca-reveal > .c-stat { opacity: 1; transform: none; }
}
