/* ==========================================================================
   FFS Foods — brand layer
   --------------------------------------------------------------------------
   Loaded AFTER agriox.css. Everything brand-specific lives here so the
   template stylesheet stays untouched and upgradable.

   agriox.css has been pre-processed so all 286 previously-hardcoded brand
   hexes now resolve through the four variables below — redefining them here
   re-skins the entire site.

   Two sources, and they cover different ground:
     · Colour comes from the Brand Colour Integration brief (blue / gold /
       orange / gray). It SUPERSEDES the palette in FFS_Brand.pdf.
     · Type still comes from FFS_Brand.pdf — Sora display, Inter body. The
       brief does not touch typography and nothing here has changed it.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Palette

   Source of truth: the Brand Colour Integration brief. This SUPERSEDES the
   five-colour scheme in FFS_Brand.pdf — the navy/mid-blue/pale-tint system
   is gone, and so is the red accent (#C62828) that predated it. Do not
   reintroduce either.

     Primary Blue    #2E3192  headers, footers, key typography
     Secondary Gold  #F2A900  secondary actions, iconography, decorative accents
     Tertiary Orange #E86100  high-priority CTAs — "Enquire Now", Shopee
     Neutral Gray    #CBCFD0  subtle borders, background variations
     White           #FFFFFF  dominant background

   Two of the four cannot go anywhere they like, and the whole mapping below
   is shaped by it:

     Gold on white is 2.01:1. That is under the 3:1 that WCAG 1.4.11 asks of
     even a meaningful *graphic*, so gold is not merely bad for text on white
     — it cannot carry an icon there either. Gold is at its best on the blue
     (5.30:1), which is where the iconography and decorative accents live:
     the top bar, the hero, the blue panels, the mobile drawer, the footer.
     Where an icon must sit on white, use a blue chip with a gold glyph.

     Orange with a white label is 3.42:1, under the 4.5:1 AA floor for a
     16px button label. The fix is the one this stylesheet already uses for
     the WhatsApp and Shopee fills: keep the brand hex and flip the LABEL to
     near-black (--ffs-on-brand), which measures 5.44:1. For the same reason
     the CTA hover LIGHTENS — darkening a fill under dark text goes the
     wrong way.

   How the four brand colours map onto the template's tokens:

     --agriox-primary      Primary Blue. Dark surfaces, headings, nav.
     --agriox-base         Primary Blue again, deliberately. This is the
                           template's accent token and it feeds 86 `color:`
                           declarations — it has to hold something that is
                           safe as text on white, and of the four only the
                           blue is (10.66:1). Gold and orange are therefore
                           NOT wired in globally; they are applied by hand
                           below, on grounds chosen to clear AA.
     --agriox-base-dark    Blue darkened, for hover on blue fills. Since
                           base and primary are now the same value, any
                           template hover that went base→primary is a no-op;
                           the FFS components use this instead.
     --agriox-secondary    Gold. In agriox.css this token only reaches parts
                           the FFS pages do not render (blog, testimonials,
                           team, checkout), so it is safe to hand it a
                           white-hostile colour. Treat it as decorative.
     --agriox-base-on-dark Gold, for accent text and icons on the blue
                           surfaces (5.30:1). NEVER use on white.
     --ffs-cta             Tertiary Orange. High-priority CTAs only — it is
                           the loudest thing on the page and stops being
                           loud the moment it is used for anything else.

   Contrast, measured (WCAG 2.1, relative luminance per the sRGB formula):
     #FFFFFF on #2E3192 .... 10.66:1  AAA  — text on the blue header/footer
     #2E3192 on #FFFFFF .... 10.66:1  AAA  — headings, nav, links on white
     #0B141A on #F2A900 ....  9.26:1  AAA  — label on a gold fill
     #2E3192 on #EDEEEF ....  9.18:1  AAA  — headings on the neutral band
     #4A4A55 on #FFFFFF ....  8.74:1  AAA  — body copy
     #4A4A55 on #EDEEEF ....  7.53:1  AAA  — body copy on cards
     #2E3192 on #CBCFD0 ....  6.79:1  AA   — text on a full-strength gray
     #0B141A on #EB741F ....  6.24:1  AA   — CTA label, hover state
     #0B141A on #E86100 ....  5.44:1  AA   — CTA label, rest state
     #F2A900 on #2E3192 ....  5.30:1  AA   — gold text and icons on the blue
     #A57300 on #FFFFFF ....  4.16:1  ——   — gold, darkened, on light grounds
     #A57300 on #EDEEEF ....  3.58:1  ——   — the same, on the neutral band
     #E86100 on #FFFFFF ....  3.42:1  ——   — large text (≥24px / ≥18.66px
                                             bold) and graphics; never a
                                             16px label in either direction
     #E86100 on #2E3192 ....  3.12:1  ——   — non-text graphic: the CTA fill
                                             sitting on the blue hero
     #F2A900 on #FFFFFF ....  2.01:1  FAIL — never text, never a meaningful
                                             icon; blue grounds only
     #CBCFD0 on #FFFFFF ....  1.57:1  FAIL — borders and background
                                             variations only
   -------------------------------------------------------------------------- */
:root {
  /* Primary Blue. Headers, footers, key typography, dark surfaces. Feeds
     171 pre-processed hexes in agriox.css; changing it re-skins the whole
     template, so measure before touching it. */
  --agriox-primary: #2e3192;
  --agriox-primary-rgb: 46, 49, 146;

  /* Blue, deepened. NOT the brand gold, despite this being the template's
     "secondary" slot — the token is a background in template chrome the FFS
     pages do render, and the template hardcodes #ffffff on top of it. The
     footer bottom bar is the proof: gold here rendered a white copyright
     line and white social icons at 2.01:1. Gold reaches the page through
     --agriox-base-on-dark and --ffs-gold instead, both of which are only
     ever placed on grounds this stylesheet controls. */
  --agriox-secondary: #242672;
  --agriox-secondary-rgb: 36, 38, 114;

  /* Page base. Declared rather than left to the UA default so nothing
     downstream can quietly reintroduce the template's boxed cream. White
     stays the dominant background. */
  --ffs-page: #ffffff;

  /* The template's accent token. Blue, not gold — it lands on 86 text
     declarations and gold would take every one of them to 2.01:1. */
  --agriox-base: #2e3192;
  --agriox-base-rgb: 46, 49, 146;

  /* Blue darkened ~22%. Hover/active for blue-filled surfaces; 13.16:1 on
     white, so it is also safe as text. */
  --agriox-base-dark: #242672;

  /* Gold — accent text and icons on the blue surfaces (5.30:1). The
     on-white counterpart is --ffs-gold-dark. */
  --agriox-base-on-dark: #f2a900;

  /* Body text — warm neutral, not pure grey. The brief does not specify a
     body colour; this measures 8.74:1 on white and 7.53:1 on the band. */
  --agriox-color-1: #4a4a55;
  --agriox-color-1-rgb: 74, 74, 85;

  /* The four brand colours under their own names, for the component work
     further down. */
  --ffs-blue: #2e3192;
  --ffs-gold: #f2a900;
  --ffs-orange: #e86100;
  --ffs-gray: #cbcfd0;

  /* High-priority CTA. The label is WHITE, which only works because §4 fixes
     every button at 19px/700 — white on this orange is 3.42:1, legal as large
     text, illegal as normal text. Hover therefore DEEPENS the fill (3.42 →
     4.79), the opposite direction from the gold and WhatsApp fills below,
     which carry dark labels. */
  --ffs-cta: #e86100;
  --ffs-cta-hover: #c24e00;

  /* Gold darkened until it clears 3:1 on white AND on the neutral band
     (4.16:1 / 3.58:1). For gold-toned borders and any gold graphic that has
     to sit on a light ground. Still not body text.

     --ffs-gold-hover is the shallower darkening used for gold FILLS. It is
     visibly deeper than the rest state while keeping the near-black label at
     7.29:1; --ffs-gold-dark itself would take that label to 4.48:1, which is
     just under AA for a 16px bold button label. */
  --ffs-gold-dark: #a57300;
  --ffs-gold-hover: #d69600;

  /* Typography, per FFS_Brand.pdf: Sora for display, Inter for everything
     functional. Both are Google Fonts. The template's third font
     (Shadows Into Light) stays dropped; --agriox-font-3 is aliased to the
     display face so any stray reference still resolves. */
  --agriox-font: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --agriox-font-2: 'Sora', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --agriox-font-3: 'Sora', system-ui, sans-serif;

  /* Supporting tokens used by the FFS components below.

     --ffs-surface is the brief's "background variation": the neutral gray
     at 35% over white. Full-strength #CBCFD0 is too heavy for a section
     band behind body copy, but it is exactly right as a border, which is
     what --ffs-border takes it for. */
  --ffs-ink: #2e3192;
  --ffs-surface: #edeeef;
  --ffs-border: #cbcfd0;
  --ffs-radius: 10px;
  --ffs-btn-gap: 16px;
  --ffs-shadow: 0 2px 4px rgba(46, 49, 146, .05), 0 8px 24px rgba(46, 49, 146, .08);

  /* Third-party brand fills. Both are light, and both were carrying white
     labels that measured far under AA — WhatsApp green 2.0:1, Shopee orange
     3.4:1. Rather than dulling two recognisable brand colours, the label
     flipped to near-black (9.4:1 and 5.4:1). Same pattern Spotify uses for
     black-on-green. Hovers LIGHTEN these two, because dark text on a
     darkened fill goes the wrong way.

     Shopee now takes the brand's Tertiary Orange rather than Shopee's own
     #EE4D2D, which the brief asks for and which the two colours are close
     enough to survive: the badge still reads as Shopee without importing a
     fifth orange into the palette.

     The Shopee badge carries a WHITE label to match the CTA buttons, but it is
     a 14px pill, not a 19px button, so it cannot lean on the large-text
     exemption the way .ffs-btn--primary does. It therefore sits on a deepened
     shade of the same orange: white measures 4.79:1 on #C24E00 and 6.21:1 on
     the hover. Same colour family, legal at badge size.

     This pair is now the HEADER BADGE ONLY. The Shopee buttons in the body of
     the page take --ffs-cta (#E86100) flat, per instruction — see
     .ffs-btn--shopee in section 4. Do not "reunify" the two by pointing the
     badge at --ffs-cta as well: at 14px that is a 3.42:1 label and an outright
     AA failure, which is the one thing the deepened shade exists to avoid. */
  --ffs-whatsapp: #25d366;
  --ffs-whatsapp-hover: #2fe072;
  --ffs-shopee: #c24e00;
  --ffs-shopee-hover: #a54200;

  /* Label colour for every light brand fill — gold, orange, WhatsApp,
     Shopee. Near-black rather than pure #000 so it reads as ink, not as a
     hole in the fill. */
  --ffs-on-brand: #0b141a;

  /* Review stars. A star row rendered in the brand blue reads as decoration
     rather than a rating, and it is the only rating a sighted user sees (the
     text equivalent is for screen readers) — so it needs its own colour, and
     WCAG 1.4.11 asks 3:1 of it as a meaningful non-text graphic. This is the
     brand gold darkened until it clears that on both grounds it appears on:
     4.16:1 on white, 3.58:1 on the neutral band. Brand gold itself is 2.01:1
     and would fail both. Same value as --ffs-gold-dark; kept under its own
     name so the rating can be retuned without moving every gold border. */
  --ffs-star: var(--ffs-gold-dark);
}

/* --------------------------------------------------------------------------
   1b. Font Awesome — give bare `.fa` the solid face
   --------------------------------------------------------------------------
   Font Awesome 5 splits what v4 called `.fa` into three families, and it sets
   the weight on the NEW classes only:

     .fas  font-family: "Font Awesome 5 Free"    font-weight: 900   (1001 icons)
     .far  font-family: "Font Awesome 5 Free"    font-weight: 400   ( 151 icons)
     .fab  font-family: "Font Awesome 5 Brands"                     ( 457 icons)
     .fa   font-family: "Font Awesome 5 Free"    -- no weight at all

   Bare `.fa` is a v4 leftover: it picks the family but leaves the weight to be
   INHERITED from whatever the icon happens to sit inside. The two faces are
   400 and 900, so per CSS font matching an inherited 400 or 500 selects the
   400 face — which holds only 151 of the 1001 Free glyphs. Every solid-only
   icon in that position has no glyph to draw and renders as nothing at all.

   That is what was emptying the boxes on the homepage. `.ffs-cert__mark` sets
   no weight, so its icons inherited body's 400: fa-flask, fa-cogs, fa-box-open,
   fa-store in "What we do", and fa-shield-alt, fa-check, fa-balance-scale in
   "Why it matters", were all blank. 14 distinct icons sitewide. The ones that
   did survive (fa-check-circle, fa-clock, fa-envelope, fa-file-alt) only did so
   by happening to exist in the 151 — and they were drawing as outlines while
   their neighbours were solid.

   Setting the weight here fixes all 26 bare-`fa` icons at once, which is why
   this is a stylesheet rule and not 35 edits across the PHP. All 26 were
   checked against the solid face's codepoint table first; every one is present,
   so none of them can regress into the same blank state from this direction.

   `.fab` and `.far` are untouched: brands carry their own family, and the
   review stars deliberately use `.far` for the empty state. Neither element
   carries the `fa` class, so neither matches this selector.
   -------------------------------------------------------------------------- */
.fa {
  font-weight: 900;
}

/* --------------------------------------------------------------------------
   2. Typography scale

   The template ships 18px/34px body copy, which is oversized for reading on
   a phone and forces awkward line lengths. Reset to a fluid scale.
   -------------------------------------------------------------------------- */
body {
  font-size: 16px;
  line-height: 1.65;
  color: var(--agriox-color-1);
  background-color: var(--ffs-page);
  -webkit-font-smoothing: antialiased;
}

/* The template ships a boxed layout on a cream ground (#f6f4ec). No FFS page
   sets .boxed-wrapper, but the rule outranks the element selector above if one
   ever does, so it is neutralised here rather than left as a trapdoor. */
body.boxed-wrapper { background-color: var(--ffs-page); }

@media (min-width: 768px) {
  body {
    font-size: 17px;
    line-height: 1.7;
  }
}

h1, h2, h3, h4, h5, h6,
.section-title__title,
.main-slider__title {
  font-family: var(--agriox-font-2);
  color: var(--ffs-ink);
  font-weight: 700;
  line-height: 1.18;
  letter-spacing: -0.01em;
}

h1 { font-size: clamp(2rem, 1.4rem + 3vw, 3.5rem); }
h2 { font-size: clamp(1.6rem, 1.2rem + 2vw, 2.5rem); }
h3 { font-size: clamp(1.25rem, 1.05rem + 1vw, 1.6rem); }

/* Sora is geometric and sits wider than the serif it replaces, so optical
   tracking has to tighten as the size grows — a flat -0.01em reads loose at
   3.5rem and cramped at 1.25rem. Small headings go back to neutral. */
h1 { letter-spacing: -0.025em; }
h2 { letter-spacing: -0.02em; }
h4, h5, h6 {
  font-weight: 600;
  letter-spacing: 0;
}

/* Long-form copy should not run edge to edge on a wide screen. */
.ffs-prose p { max-width: 68ch; }

.ffs-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1000;
  padding: 12px 20px;
  background: var(--agriox-primary);
  color: #fff;
  font-weight: 700;
}
.ffs-skip-link:focus {
  left: 8px;
  top: 8px;
}

/* Visible keyboard focus — the template removes outlines in several places. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--agriox-base);
  outline-offset: 2px;
}

/* The blue focus ring is invisible on the blue grounds — 1:1, since it is
   the same colour. Anything sitting on blue takes the gold instead, which
   measures 5.30:1, well past the 3:1 a focus indicator needs. */
.ffs-hero a:focus-visible,
.ffs-hero button:focus-visible,
.ffs-section--panel a:focus-visible,
.ffs-section--panel button:focus-visible,
.main-header--one__top a:focus-visible {
  outline-color: var(--agriox-base-on-dark);
}

/* --------------------------------------------------------------------------
   3. Hero — single static banner replacing the 3-slide Swiper carousel
   -------------------------------------------------------------------------- */
.ffs-hero {
  position: relative;
  display: flex;
  align-items: flex-start;
  min-height: 460px;
  padding: 80px 0;
  background: var(--agriox-primary);
  overflow: hidden;
}

.ffs-hero > .auto-container {
  width: 100%;
}

@media (min-width: 992px) {
  .ffs-hero { min-height: 620px; }
}

.ffs-hero__media {
  position: absolute;
  inset: 0;
}
.ffs-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ffs-hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Directional scrim: keeps text legible without flattening the photo. */
  background: linear-gradient(100deg,
      rgba(var(--agriox-primary-rgb), .94) 0%,
      rgba(var(--agriox-primary-rgb), .82) 45%,
      rgba(var(--agriox-primary-rgb), .45) 100%);
}

.ffs-hero__inner {
  position: relative;
  z-index: 2;
  max-width: 720px;
  color: #fff;
  text-align: left;
}

.ffs-hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  /* Sits on the blue hero scrim — gold, not the raw accent blue. */
  color: var(--agriox-base-on-dark);
}

.ffs-hero__title {
  color: #fff;
  margin-bottom: 20px;
}

.ffs-hero__text {
  font-size: 1.0625rem;
  color: rgba(255, 255, 255, .88);
  margin-bottom: 32px;
  max-width: 52ch;
}

/* Layout only — display/gap come from the shared button-group rule in §4 so
   the hero pair is spaced identically to every other pair. */
.ffs-hero__actions {
  align-items: center;
  justify-content: flex-start;
}

/* Trust strip under the hero CTAs */
.ffs-hero__trust {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 28px;
  margin-top: 36px;
  padding-top: 26px;
  border-top: 1px solid rgba(255, 255, 255, .18);
  list-style: none;
  padding-left: 0;
  justify-content: flex-start;
}
.ffs-hero__trust li {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: .9375rem;
  color: rgba(255, 255, 255, .9);
}
.ffs-hero__trust i { color: var(--agriox-base-on-dark); }

/* --------------------------------------------------------------------------
   4. Buttons
   -------------------------------------------------------------------------- */
/* ONE button size, everywhere. Every variant below inherits this box and
   changes colour only — no variant may re-declare font-size, padding, gap or
   min-height, and no page may override them locally. If a button needs to look
   different, it needs a new colour variant, not a new size.

   One sanctioned exception: .ffs-btn--flush, below, zeroes the LEFT padding so
   its label lines up with the body copy above it. It is the only rule in this
   file permitted to touch the box, it changes one side only, and it is opt-in
   per button. Anything beyond that belongs in a colour variant.

   19px/700 is not a styling whim: it is what makes a white label legal on the
   Tertiary Orange. White on #E86100 is 3.42:1, which fails the 4.5:1 AA floor
   for normal text but passes the 3:1 floor for large text — and WCAG counts
   >=18.66px bold as large. Drop this below 1.1667rem and .ffs-btn--primary
   stops being accessible. */
.ffs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 56px;
  padding: 15px 32px;
  border: 2px solid transparent;
  border-radius: var(--ffs-radius);
  font-family: var(--agriox-font);
  font-size: 1.1875rem;   /* 19px — see note above before changing */
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  transition: background-color .18s ease, color .18s ease, border-color .18s ease;
}

/* Buttons sitting side by side get one gap value too, so a pair of CTAs is
   spaced identically on the hero, in a panel and in the footer. Every group
   wrapper below reads --ffs-btn-gap; nothing sets its own.

   This is a flex gap rather than a `.ffs-btn + .ffs-btn` margin on purpose —
   a sibling margin would stack on top of the gap inside .ffs-hero__actions and
   make the hero pair wider-spaced than every other pair on the site, which is
   exactly the inconsistency this is meant to remove. */
.ffs-btn-group,
.ffs-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ffs-btn-gap);
}

/* The group also owns the space between itself and the copy above it, for the
   same reason it owns the gap: contact.php, product-detail.php and the footer
   each used to set their own (24px, 28px, and a <p> margin from the template),
   which is how a Shopee button ended up sitting a different distance from its
   heading on all three pages. 24px matches the `mt-4` every other section CTA
   on the site is spaced by. Override it locally only with a real reason. */
.ffs-btn-group { margin: 24px 0 0; }

/* Centres the buttons within the group. The group is a flex row, so this is
   justify-content rather than text-align — and it is a modifier rather than a
   change to the base rule because most groups on the site sit under
   left-aligned copy and are meant to line up with it. */
.ffs-btn-group--center { justify-content: center; }

/* Phones: every button in a group goes full-width and shares one edge, so a
   pair reads as a stack of equal bars rather than two boxes of whatever width
   their labels happened to shrink-wrap to. At 390px that mismatch was at its
   worst next to the Shopee CTAs, whose labels are the longest on the site —
   "Buy retail packs on Shopee" set a box roughly twice the width of the "Get
   directions" button above it.

   The 32px side padding goes to 20px here for the same labels: at 390px, minus
   the grid gutter, 32px a side pushed that label onto a third line. Padding is
   the only part of the box this touches — the 19px/700 and the 56px floor are
   what keep a white label legal on the orange fills (see the base rule), and
   they are deliberately left alone. This is also why the rule is scoped to
   grouped buttons: a lone .ffs-btn in a sentence keeps its natural width. */
@media (max-width: 575.98px) {
  .ffs-btn-group .ffs-btn,
  .ffs-hero__actions .ffs-btn {
    flex: 1 1 100%;
    padding-left: 20px;
    padding-right: 20px;
  }
  /* --flush is opt-in flush-left against body copy; full-width must not give
     it a left inset back. */
  .ffs-btn-group .ffs-btn--flush,
  .ffs-hero__actions .ffs-btn--flush {
    padding-left: 0;
  }
}

/* Flush-left label. Drops the left padding to 0 so the button's TEXT — not its
   fill — starts on the same vertical line as the paragraph above it. Without
   this the label sits 32px in, and against a left-aligned block of copy that
   reads as an indent.

   The base rule centres the label with justify-content, which would fight this
   the moment the button is ever given a width, so the alignment is restated
   here rather than left to depend on the box shrink-wrapping its text.

   Two things this deliberately does not do. It keeps the right padding, so the
   fill still has somewhere to breathe. And it keeps the 2px transparent
   border, which offsets the label by 2px: removing it would buy exact optical
   alignment at the cost of making this variant a different height from every
   other button, which is the one thing items 7/11 asked me not to do.

   Use sparingly, and only on a button that directly follows body copy. */
.ffs-btn--flush {
  padding-left: 0;
  justify-content: flex-start;
  text-align: left;
}

/* The high-priority CTA — "Enquire now", "Explore products", "Contact us".
   Tertiary Orange, and the loudest thing on any page it appears on, which is
   why nothing else in this stylesheet is allowed to use it.

   White label on the orange, at the 19px/700 the base rule sets: 3.42:1, which
   clears the 3:1 large-text floor. Hover DEEPENS the fill to #C85200 (white
   4.65:1) rather than lightening it — under a white label, lighter is the
   wrong direction.

   The 3.42:1 the fill itself measures against white is above the 3:1 WCAG
   1.4.11 asks of a control boundary, so the button reads as a button without
   needing a border. */
.ffs-btn--primary {
  background: var(--ffs-cta);
  color: #fff;
}
.ffs-btn--primary:hover,
.ffs-btn--primary:focus {
  background: var(--ffs-cta-hover);
  color: #fff;
}

/* Secondary action — Secondary Gold. Near-black label at 9.26:1.

   Gold on white is 2.01:1, so unlike the orange CTA this fill does NOT clear
   the 3:1 boundary contrast on its own; the darkened-gold border supplies it
   (4.16:1). Drop the border and the button dissolves into the page for anyone
   with low contrast sensitivity. */
.ffs-btn--gold {
  background: var(--ffs-gold);
  border-color: var(--ffs-gold-dark);
  color: var(--ffs-on-brand);
}
.ffs-btn--gold:hover,
.ffs-btn--gold:focus {
  background: var(--ffs-gold-hover);
  border-color: var(--ffs-gold-dark);
  color: var(--ffs-on-brand);
}

.ffs-btn--ghost {
  background: transparent;
  border-color: rgba(255, 255, 255, .55);
  color: #fff;
}
.ffs-btn--ghost:hover,
.ffs-btn--ghost:focus {
  background: #fff;
  border-color: #fff;
  color: var(--agriox-primary);
}

/* Blue-filled, white label at 10.66:1. Hover deepens to --agriox-base-dark
   (13.16:1) — NOT to --agriox-base, which now holds the same value as
   --agriox-primary and would make the hover a no-op. */
.ffs-btn--solid {
  background: var(--agriox-primary);
  color: #fff;
}
.ffs-btn--solid:hover,
.ffs-btn--solid:focus {
  background: var(--agriox-base-dark);
  color: #fff;
}

/* Tertiary action on a LIGHT background. --ghost is the dark-background
   equivalent; using it here gives a white border on white.

   The border is blue rather than --ffs-border: the neutral gray is 1.57:1 on
   white, fine as a decorative rule between cards but not as the boundary of
   an interactive control, which WCAG 1.4.11 wants at 3:1. */
.ffs-btn--outline {
  background: transparent;
  border-color: var(--agriox-primary);
  color: var(--agriox-primary);
}
.ffs-btn--outline:hover,
.ffs-btn--outline:focus {
  border-color: var(--agriox-primary);
  background: var(--agriox-primary);
  color: #fff;
}

/* Shopee action. This is the storefront CTA everywhere it appears in the body
   of a page — product detail, contact, the reviews section, the footer — and
   it is a colour variant precisely so it stops being a different size from
   the button it stands next to. It was a 14px pill; on product-detail.php
   that put it shoulder to shoulder with a 19px/56px .ffs-btn--primary in the
   same row, which is the mismatch items 7/11 were about.

   The ONE Shopee link that is not this variant is the badge in the header's
   utility bar (see .ffs-shopee, section 8). That bar is 40-odd pixels tall
   and a 56px button would not fit in it, let alone belong there — it is
   chrome sitting beside the phone number and opening hours, not a call to
   action. Standardising it into a button would break the header rather than
   tidy it.

   Colour is the brand Tertiary Orange, #E86100 — the same fill --primary
   carries, by instruction: every Shopee CTA on the site is one orange with a
   white label, full stop. This variant is therefore a semantic alias, not a
   different colour; it stays a separate class so the storefront CTAs remain
   greppable and can be repainted in one place later.

   Two consequences to know about before touching this.

   White on #E86100 is 3.42:1. That passes AA as LARGE text and fails it as
   normal text, so this variant now depends on the base rule's 19px/700 to stay
   legal — exactly as --primary does. It previously sat on a deepened orange
   measuring 4.79:1 and had margin to spare; it no longer does. Do not use this
   class on anything below 18.66px bold.

   On product-detail.php "Enquire about <product>" and "Buy on Shopee" now sit
   side by side in identical fills. The deeper orange used to be what kept them
   from reading as one control split in half; the 390px rule below stacks them
   into separate full-width bars, and the icon distinguishes them above that.

   --ffs-shopee / --ffs-shopee-hover are NOT unused after this: they still
   carry the header utility-bar badge (section 8), which is a 14px pill and
   cannot make the large-text argument this button can. */
.ffs-btn--shopee {
  background: var(--ffs-cta);
  color: #fff;
}
.ffs-btn--shopee:hover,
.ffs-btn--shopee:focus {
  background: var(--ffs-cta-hover);
  color: #fff;
}

/* Sized in em, not px, so the glyph tracks the label rather than pinning a
   second size into a variant. A 1em bag next to 19px bold caps reads heavy —
   the glyph fills its em box where the letters do not. */
.ffs-btn--shopee i { font-size: .9em; }

/* --------------------------------------------------------------------------
   5. Product grid + category filter
   -------------------------------------------------------------------------- */
.ffs-filter {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-bottom: 44px;
  padding: 0;
  list-style: none;
}

.ffs-filter button {
  min-height: 44px;
  padding: 10px 22px;
  border: 1px solid var(--ffs-border);
  border-radius: 999px;
  background: #fff;
  color: var(--agriox-color-1);
  font-size: .9375rem;
  font-weight: 500;
  cursor: pointer;
  transition: all .18s ease;
}
.ffs-filter button:hover {
  border-color: var(--agriox-primary);
  color: var(--agriox-primary);
}
.ffs-filter button[aria-pressed="true"] {
  background: var(--agriox-primary);
  border-color: var(--agriox-primary);
  color: #fff;
}

.ffs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 26px;
}

.ffs-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #fff;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  overflow: hidden;
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
.ffs-card:hover {
  transform: translateY(-4px);
  border-color: transparent;
  box-shadow: var(--ffs-shadow);
}

/* 3:2, not square. The product photographs are landscape crops out of the
   original flyers (roughly 1.6:1 and 2.7:1); a square box zoomed the wider
   family so far in that the product left the frame. */
.ffs-card__media {
  position: relative;
  aspect-ratio: 3 / 2;
  background: var(--ffs-surface);
  overflow: hidden;
}
.ffs-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ffs-card__tag {
  position: absolute;
  top: 12px;
  left: 12px;
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(var(--agriox-primary-rgb), .92);
  color: #fff;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.ffs-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 20px 22px 24px;
}

.ffs-card__title {
  margin: 0 0 8px;
  font-size: 1.125rem;
}
.ffs-card__title a { color: var(--ffs-ink); }
/* --ffs-ink and --agriox-primary now hold the same blue, so the old colour
   hover was a no-op. Deepen it and underline: the underline is what actually
   carries the state for anyone who cannot separate the two blues. */
.ffs-card__title a:hover,
.ffs-card__title a:focus-visible {
  color: var(--agriox-base-dark);
  text-decoration: underline;
}

.ffs-card__text {
  margin: 0 0 18px;
  font-size: .9375rem;
  line-height: 1.55;
}

.ffs-card__link {
  margin-top: auto;
  font-weight: 700;
  font-size: .9375rem;
  color: var(--agriox-primary);
}
.ffs-card__link i {
  margin-left: 6px;
  transition: transform .18s ease;
}
.ffs-card__link:hover i { transform: translateX(4px); }

.ffs-grid__empty {
  grid-column: 1 / -1;
  padding: 60px 20px;
  text-align: center;
  color: var(--agriox-color-1);
}

/* --------------------------------------------------------------------------
   6. WhatsApp floating button
   -------------------------------------------------------------------------- */
.ffs-wa {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 99;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 56px;
  padding: 0 18px;
  border-radius: 999px;
  background: var(--ffs-whatsapp);
  color: var(--ffs-on-brand);
  font-weight: 700;
  font-size: .9375rem;
  box-shadow: 0 6px 20px rgba(37, 211, 102, .38);
  transition: background-color .18s ease, transform .18s ease, box-shadow .18s ease;
}
.ffs-wa:hover,
.ffs-wa:focus {
  background: var(--ffs-whatsapp-hover);
  color: var(--ffs-on-brand);
  transform: translateY(-2px);
  box-shadow: 0 10px 26px rgba(37, 211, 102, .46);
}
.ffs-wa i { font-size: 1.5rem; }

/* On phones collapse to a circle so it never covers form fields.
   Sits left of the template's scroll-to-top button, which is bottom-right. */
@media (max-width: 575.98px) {
  .ffs-wa {
    width: 56px;
    padding: 0;
    justify-content: center;
    right: 16px;
    bottom: 78px;
  }
  .ffs-wa__label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .ffs-wa,
  .ffs-card,
  .ffs-card__link i { transition: none; }
  .ffs-wa:hover,
  .ffs-card:hover { transform: none; }
}

/* Speech bubble above the FAB.
   Ships with the HTML hidden attribute (not just opacity), so it does not
   appear when JS is blocked or still loading. ffs.js flips the attribute
   after the ten-second delay. role="status" is inert — it keeps the bubble
   out of the tab order and lets a screen reader hear it in passing without
   yanking the user out of whatever they were reading. */
.ffs-wa-bubble {
  position: fixed;
  right: 18px;
  bottom: 86px; /* 18px floor + 56px FAB + 12px gap */
  z-index: 99;
  max-width: 280px;
  padding: 14px 32px 14px 16px;
  border-radius: var(--ffs-radius);
  background: #fff;
  color: var(--agriox-color-1);
  font-size: .9375rem;
  line-height: 1.5;
  box-shadow: var(--ffs-shadow), 0 6px 20px rgba(0, 0, 0, .12);
  transform: translateY(8px);
  opacity: 0;
  transition: transform .35s ease, opacity .35s ease;
}
/* Arrow tail — a 10px right-pointing triangle rotated 45° and positioned
   just above the right-hand third of the FAB so it points straight at it. */
.ffs-wa-bubble::after {
  content: '';
  position: absolute;
  bottom: -6px;
  right: 26px;
  width: 12px;
  height: 12px;
  background: inherit;
  border-radius: 0 0 3px 0;
  transform: rotate(45deg);
  box-shadow: 3px 3px 4px rgba(0, 0, 0, .06);
}
.ffs-wa-bubble.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.ffs-wa-bubble__close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 24px;
  height: 24px;
  border: 0;
  padding: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--agriox-color-1);
  font-size: 13px;
  line-height: 24px;
  text-align: center;
  cursor: pointer;
  transition: background .15s ease;
}
.ffs-wa-bubble__close:hover,
.ffs-wa-bubble__close:focus-visible {
  background: var(--ffs-surface);
}

.ffs-wa-bubble__link {
  display: block;
  color: inherit;
  text-decoration: none;
}
.ffs-wa-bubble__link:hover { text-decoration: underline; }

@media (max-width: 575.98px) {
  .ffs-wa-bubble {
    right: 16px;
    bottom: 146px; /* 78px mobile FAB + 56px circle + 12px gap */
    max-width: calc(100vw - 48px);
    padding: 12px 28px 12px 14px;
    font-size: .875rem;
  }
  .ffs-wa-bubble::after { right: 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .ffs-wa-bubble { transition: none; }
}

@media print {
  .ffs-wa-bubble { display: none !important; }
}

/* --------------------------------------------------------------------------
   7. Map facade — static image swapped for the real iframe on click
   -------------------------------------------------------------------------- */
.ffs-map {
  position: relative;
  aspect-ratio: 16 / 9;
  min-height: 320px;
  border-radius: var(--ffs-radius);
  overflow: hidden;
  background: var(--ffs-surface);
}
.ffs-map iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.ffs-map__facade {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  width: 100%;
  padding: 24px;
  border: 0;
  background: var(--ffs-surface);
  background-size: cover;
  background-position: center;
  cursor: pointer;
  text-align: center;
}
.ffs-map__facade::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(var(--agriox-primary-rgb), .12);
}
.ffs-map__facade-inner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 22px 26px;
  border-radius: var(--ffs-radius);
  background: rgba(255, 255, 255, .95);
  box-shadow: var(--ffs-shadow);
  max-width: 420px;
}
.ffs-map__facade-icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--agriox-primary);
  color: #fff;
  font-size: 1.25rem;
}
.ffs-map__facade-note {
  font-size: .8125rem;
  color: var(--agriox-color-1);
  margin: 0;
}

/* --------------------------------------------------------------------------
   8. Shopee badge — header utility bar only

   Every other Shopee link on the site is .ffs-btn--shopee, a normal-sized
   button (section 4). This class is what is left over: the small pill in the
   blue utility bar at the top of every page, sitting in a 40px-tall strip
   beside the phone number and the opening hours. It is chrome, not a call to
   action, and it is the one place a 56px button cannot go.

   Because it is a 14px pill it cannot use WCAG's large-text exemption the way
   the 19px buttons can, which is the whole reason --ffs-shopee is a deepened
   orange rather than the brand's Tertiary Orange: white measures 4.79:1 on it
   and clears AA for normal text.
   -------------------------------------------------------------------------- */
.ffs-shopee {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 15px;
  border-radius: 999px;
  background: var(--ffs-shopee);
  color: #fff;
  font-size: .875rem;
  font-weight: 700;
  line-height: 1.2;
  transition: background-color .18s ease;
}
.ffs-shopee:hover,
.ffs-shopee:focus {
  background: var(--ffs-shopee-hover);
  color: #fff;
}
.ffs-shopee i { font-size: 1rem; }

/* `.main-header--one__top a` (0,1,1) outranks `.ffs-shopee` (0,1,0) and was
   repainting the label to white at 90% opacity. The label is white now, but
   90% white is not — it measured 3.4:1 on the old fill. These two carry the
   same declarations at 0,2,0 purely to win that. Do not fold them back into
   the base rule.

   The matching `.footer-one .ffs-shopee` pair that used to sit here is gone
   with the badge it guarded: the footer's Shopee link is a .ffs-btn--shopee
   now, and .ffs-btn variants set their own colour at the same weight the
   template's footer rules do not reach. */
.main-header--one__top .ffs-shopee {
  color: #fff;
}
.main-header--one__top .ffs-shopee:hover,
.main-header--one__top .ffs-shopee:focus {
  color: #fff;
}

/* The footer's focus ring is the blue one from section 2, which is the same
   blue as the footer ground — 1:1, i.e. no ring at all for a keyboard user.
   Same fix already applied to the hero and the panel sections: gold on the
   footer blue measures 5.30:1, well past the 3:1 an indicator needs.

   Scoped to the whole footer rather than to the button, because the contact
   links, the explore list and the social icons all share the problem and all
   share the ground. */
.footer-one a:focus-visible,
.footer-one button:focus-visible {
  outline-color: var(--agriox-base-on-dark);
}

/* --------------------------------------------------------------------------
   8b. Shopee customer reviews — scroll-snap carousel

   Rendered by partials/reviews.php from data/reviews.php.

   This is a CSS carousel, not a slider library. The track is one overflowing
   flex row with scroll snapping, so it works on touch and trackpad with no
   JavaScript at all; assets/js/ffs.js only adds the arrow buttons on top and
   reveals them once it has measured an actually-overflowing track. Nothing
   here depends on that script running — the failure mode is a plain
   swipeable strip, not a broken one.

   Cards are sized in thirds of the VIEWPORT rather than of the track. The
   track is a block box inside the scroll container, so its width is the
   visible width and the items overflow it; a percentage flex-basis therefore
   resolves against what the reader can see, which is what "3 across" means.

   Note the deliberate ugliness of .ffs-review--sample: placeholder rows are
   meant to be impossible to miss in a client review, so nobody signs off a
   page carrying invented testimonials. Delete nothing there when the samples
   go — the rules simply stop matching. As of the real Shopee reviews landing
   in data/reviews.php, they no longer do.
   -------------------------------------------------------------------------- */
.ffs-reviews__head {
  max-width: 720px;
  margin-bottom: 36px;
}

.ffs-reviews__aggregate {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin: 14px 0 16px;
  font-size: 1.0625rem;
}
.ffs-reviews__aggregate strong { color: var(--ffs-ink); }
.ffs-reviews__count {
  font-size: .9375rem;
  color: var(--agriox-color-1);
}

/* These represent a third-party (Shopee) rating. --ffs-star is the brand
   gold darkened to 4.16:1 on white and 3.58:1 on the neutral band, so the
   rating clears the 3:1 that meaningful non-text graphics need on both
   grounds it appears on. Brand gold as shipped is 2.01:1 and would not. */
.ffs-stars {
  display: inline-flex;
  gap: 2px;
  color: var(--ffs-star);
  font-size: .9375rem;
  line-height: 1;
}

.ffs-reviews__carousel {
  --ffs-review-gap: 24px;
  /* Card width is explicit rather than a share of the container: the track is
     `width: max-content` for the marquee, so a percentage flex-basis would be
     resolving against a width the cards themselves define. 368px puts three
     across the 1170px container with both gaps, matching the old layout. */
  --ffs-review-w: 368px;
  position: relative;
}

/* Hides everything outside the strip and clips the animation. Overflow is
   `hidden` rather than `auto` because the animation and a scroll position
   fight each other — under prefers-reduced-motion, where the animation is
   off, it goes back to `auto` and the strip is a plain scroller again. */
.ffs-reviews__viewport {
  overflow: hidden;
  /* Headroom above and below the cards so their drop shadow is not sheared
     off: clipping horizontally clips vertically too, shadow included. */
  padding: 8px 0 20px;
}

.ffs-reviews__viewport:focus-visible {
  outline: 3px solid var(--agriox-base);
  outline-offset: 2px;
}

.ffs-reviews {
  display: flex;
  width: max-content;
  gap: var(--ffs-review-gap);
  margin: 0;
  padding: 0;
  list-style: none;
  animation: ffs-review-marquee var(--ffs-marquee-duration, 60s) linear infinite;
}

/* Pause so a reader can finish a card. :focus-within covers the keyboard —
   without it, tabbing to the strip would leave it sliding out from under the
   focus ring. */
.ffs-reviews__carousel:hover .ffs-reviews,
.ffs-reviews__carousel:focus-within .ffs-reviews {
  animation-play-state: paused;
}

/* The list is rendered twice, so translating by exactly one set width lands
   the duplicate's first card where the original's was and the reset is
   invisible. That distance is half the track plus half a gap: the track's
   total width carries one more gap than the two sets do between them, since
   the seam between set one and set two is itself a gap. */
@keyframes ffs-review-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-50% - var(--ffs-review-gap) / 2)); }
}

/* No animation, and the strip becomes a normal horizontal scroller — the
   cards are still all reachable, they just wait to be pushed. The duplicate
   set is left in place: it reads as more cards rather than as a glitch, and
   it is aria-hidden so it never doubles up for a screen reader. */
@media (prefers-reduced-motion: reduce) {
  .ffs-reviews {
    animation: none;
  }
  .ffs-reviews__viewport {
    overflow-x: auto;
    /* Stops a horizontal flick from chaining into the browser's back-swipe
       once the strip hits its end. */
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--ffs-border) transparent;
  }
}

.ffs-review {
  flex: 0 0 var(--ffs-review-w);
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 28px 26px;
  border-radius: 14px;
  background: #fff;
  box-shadow: var(--ffs-shadow);
  /* A hairline at 6% rather than the usual --ffs-border. These are white
     cards that sit on a white section, and the shadow alone leaves the top
     edge indistinct; this defines it without reading as a drawn border. It
     is decoration, not a control boundary, so the 3:1 of 1.4.11 does not
     apply to it. */
  border: 1px solid rgba(46, 49, 146, .06);
}

/* Viewport units, not percentages, for the same reason the base width is a
   fixed value: the track is `width: max-content`, so a percentage basis would
   be resolving against a width the cards are themselves defining. */
@media (max-width: 991.98px) {
  .ffs-reviews__carousel { --ffs-review-w: calc(50vw - 32px); }
}
@media (max-width: 767.98px) {
  .ffs-reviews__carousel {
    --ffs-review-gap: 16px;
    /* Not the full width: on a phone the sliver of the next card is what
       tells a reader the strip keeps going. */
    --ffs-review-w: 78vw;
  }
}

.ffs-review__rating {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
}
/* Slightly larger than the aggregate row in the section head — in a card the
   star row is the first thing read, and 15px reads as a footnote. */
.ffs-review .ffs-stars {
  gap: 3px;
  font-size: 1.0625rem;
}
/* The glyph row is aria-hidden, so the rating is also stated in text —
   visually hidden, but read aloud and available to search. */
.ffs-review__rating-text {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* `flex: 1 1 auto` is what pushes the attribution to the bottom of the card.
   Cards in a flex row stretch to match the tallest, so without it a short
   review would leave its author floating in the middle of the box while its
   neighbour's sat on the floor. */
.ffs-review__body {
  flex: 1 1 auto;
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 1rem;
  line-height: 1.65;
  color: var(--agriox-color-1);
}
.ffs-review__body p { margin: 0; }
.ffs-review__body p::before { content: '\201C'; }
.ffs-review__body p::after  { content: '\201D'; }

/* Attribution stacks: name first, the product it refers to directly beneath.
   A review of the 1kg commercial mix and a review of the retail pack say
   different things to a trade buyer, so the product is not decoration. */
.ffs-review__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  margin: 0;
  font-size: .875rem;
  color: var(--agriox-color-1);
}
.ffs-review__author {
  font-weight: 700;
  font-size: .9375rem;
  color: var(--ffs-ink);
}
.ffs-review__product,
.ffs-review__date {
  font-size: .8125rem;
}

/* --- carousel arrows ------------------------------------------------------
   Centred under the track. Both are the same 52px circle; only the fill
   differs, so the pair reads as one control rather than two widgets.

   Shipped with the `hidden` attribute and revealed by ffs.js, so the arrows
   never appear when they would do nothing — script blocked, or a track that
   already fits. Giving `display` only to the :not([hidden]) case means the
   UA's `display: none` for [hidden] still wins by default, without needing
   an !important to fight a base display value. */
/* The prev/next arrows and their .ffs-reviews__nav container are gone: the
   strip now loops on its own, so there is nothing left for a reader to page
   through. Their rules were removed with them rather than left orphaned. */

/* Spacing and width now come from .ffs-btn-group, which the element carries
   alongside this class — the 24px it used to set here was the same 24px the
   group sets, and keeping both meant two places to change it. The class stays
   as the section's hook for this CTA; it just no longer owns the box. */

/* ---- placeholder state — must never survive to production ---- */
.ffs-reviews__warning {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0 0 26px;
  padding: 16px 20px;
  border: 2px dashed #b7791f;
  border-radius: var(--ffs-radius);
  background: #fffbeb;
  color: #7a4f0a;
  font-size: .9375rem;
}
.ffs-reviews__warning i {
  margin-top: 3px;
  font-size: 1.125rem;
}

.ffs-review--sample {
  border: 2px dashed #b7791f;
  background: repeating-linear-gradient(
      135deg,
      #fffbeb 0 12px,
      #fef6dc 12px 24px);
}
.ffs-review__flag {
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  background: #b7791f;
  color: #fff;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   9. Certification badges
   -------------------------------------------------------------------------- */
.ffs-certs {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  padding: 0;
  margin: 0;
  list-style: none;
}
.ffs-cert {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 20px;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  background: #fff;
}

/* Stacked variant — one box per row instead of a wrapped horizontal strip.

   Grid rather than `flex-direction: column`, and specifically for
   `grid-auto-rows: 1fr`: it sizes every row to the tallest one, so the three
   boxes come out genuinely equal even though "Regulatory compliance" carries
   three times the caption text of "Halal". A flex column would let each box
   shrink to its own content, which is the uneven result this replaces.

   The rule overrides display/gap from .ffs-certs above, so it must stay after
   it in the file. */
.ffs-certs--stacked {
  display: grid;
  grid-template-columns: 1fr;
  grid-auto-rows: 1fr;
  gap: 16px;
}
.ffs-certs--stacked .ffs-cert {
  width: 100%;
  padding: 20px 24px;
}
.ffs-cert__mark {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  flex: 0 0 42px;
  border-radius: 50%;
  background: rgba(var(--agriox-primary-rgb), .08);
  color: var(--agriox-primary);
  font-size: 1.125rem;
}
.ffs-cert__name {
  display: block;
  font-weight: 700;
  color: var(--ffs-ink);
  line-height: 1.3;
}
.ffs-cert__meta {
  display: block;
  font-size: .8125rem;
  color: var(--agriox-color-1);
}

/* --------------------------------------------------------------------------
   9b. Certificate flip cards — accreditation.php
   --------------------------------------------------------------------------
   The HACCP and Halal cards, with the copy on the front and the certificate
   PDF on the back.

   Two things drive the structure:

   - The front face is in normal flow and the back is absolutely positioned
     over it, so the card's height comes from the prose rather than from a
     magic number that would clip the moment the client edits a sentence. The
     inner then carries a min-height purely to guarantee the iframe on the back
     has room to be worth looking at.

   - Visibility, not just backface-visibility, hides the face that is turned
     away. backface-visibility only stops it painting: the "Open full PDF" link
     on the back would still be in the tab order behind the front of the card,
     which is a focus trap you cannot see. `visibility: hidden` takes it out of
     the tab order too, and is delayed to the midpoint of the turn so the face
     does not vanish before it has rotated away.

   Hover is behind `(hover: hover)`. On a touchscreen a :hover rule latches on
   first tap and stays stuck until you tap elsewhere, which is exactly the
   behaviour the click toggle exists to avoid. */
.ffs-flip {
  height: 100%;
  perspective: 1000px;
}

.ffs-flip__inner {
  position: relative;
  height: 100%;
  min-height: 520px;
  transform-style: preserve-3d;
  transition: transform .6s cubic-bezier(.2, .7, .3, 1);
}

@media (hover: hover) {
  .ffs-flip:hover .ffs-flip__inner { transform: rotateY(180deg); }
}
.ffs-flip.is-flipped .ffs-flip__inner { transform: rotateY(180deg); }

.ffs-flip__face {
  display: flex;
  flex-direction: column;
  padding: 32px;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  background: #fff;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  /* Delayed to the midpoint of the 600ms turn: the face is edge-on by then,
     so the swap is invisible. Going the other way it must be instant, or the
     face being turned towards the reader spends 300ms unfocusable. */
  transition: visibility 0s linear .3s;
}

.ffs-flip__front { height: 100%; }

.ffs-flip__back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
  visibility: hidden;
}

@media (hover: hover) {
  .ffs-flip:hover .ffs-flip__front { visibility: hidden; }
  .ffs-flip:hover .ffs-flip__back  { visibility: visible; }
}
.ffs-flip.is-flipped .ffs-flip__front { visibility: hidden; }
.ffs-flip.is-flipped .ffs-flip__back  { visibility: visible; }

.ffs-flip__title {
  font-size: 1.375rem;
  margin: 0 0 6px;
}
.ffs-flip__meta {
  font-size: .875rem;
  color: var(--agriox-color-1);
  margin: 0 0 18px;
}

/* Pushed to the bottom of the front face so it sits in the same place on both
   cards regardless of how much copy each carries. */
.ffs-flip__toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  align-self: flex-start;
  margin-top: auto;
  padding: 10px 0 0;
  border: 0;
  background: none;
  font-size: .875rem;
  font-weight: 700;
  color: var(--agriox-primary);
  cursor: pointer;
}
.ffs-flip__toggle:hover span,
.ffs-flip__toggle:focus-visible span { text-decoration: underline; }
.ffs-flip__toggle i {
  transition: transform .3s ease;
}
.ffs-flip__toggle:hover i { transform: rotate(180deg); }

/* The preview takes the slack in the back face and the actions sit under it,
   so a tall card gives the certificate more room rather than leaving a gap. */
.ffs-flip__preview {
  flex: 1 1 auto;
  min-height: 0;
  margin-bottom: 18px;
  border: 1px solid var(--ffs-border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--ffs-surface);
}
.ffs-flip__preview iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.ffs-flip__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}
/* The two links in here are .ffs-btn, which goes full-width below 768px via
   .ffs-btn-group — this is not a btn group, so they keep their own width and
   only the wrap handles a narrow card. */
.ffs-flip__actions .ffs-btn {
  flex: 0 1 auto;
  min-height: 48px;
  padding: 12px 22px;
  font-size: .9375rem;
}

.ffs-flip__back-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  padding: 8px 4px;
  border: 0;
  background: none;
  font-size: .875rem;
  font-weight: 700;
  color: var(--agriox-primary);
  cursor: pointer;
}
.ffs-flip__back-btn:hover span,
.ffs-flip__back-btn:focus-visible span { text-decoration: underline; }

@media (max-width: 767.98px) {
  .ffs-flip__inner { min-height: 460px; }
  .ffs-flip__face { padding: 24px; }
}

/* A 600ms rotation is exactly the kind of movement the setting is asking about,
   so the card swaps faces instantly instead. Everything still works — the
   toggle, the preview and the links are unaffected. */
@media (prefers-reduced-motion: reduce) {
  .ffs-flip__inner { transition: none; }
  .ffs-flip__face { transition: none; }
  .ffs-flip__toggle i { transition: none; }
}

/* --------------------------------------------------------------------------
   10. Proposition pillars
   -------------------------------------------------------------------------- */
/* The four "why fun food works" cards on business-solutions.php. These used to
   borrow .ffs-cert with a style attribute overriding its flex direction; they
   are their own component now, because a cert badge is a flat trust marker
   while these are cards that lift on hover and carry a top accent rule.

   .ffs-cert__mark is deliberately still reused for the icon badge — it is the
   site's shared icon-badge token, already used standalone on the package cards
   here and on accreditation.php, not a strict BEM child of .ffs-cert. */
.ffs-pillar {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  height: 100%;
  padding: 28px 24px 26px;
  background: #fff;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  overflow: hidden;
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}

/* Accent drawn as a pseudo-element rather than a real border-top: a border
   would add to the box on one side only, so the 1px outline would read uneven
   where the radius curves at the two top corners. */
.ffs-pillar::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--agriox-primary);
}

.ffs-pillar:hover {
  transform: translateY(-4px);
  border-color: transparent;
  box-shadow: var(--ffs-shadow);
}

/* Sora at 700 on --ffs-ink (10.4:1 on white) against the body copy's
   --agriox-color-1 (8.6:1) — the weight and family carry the hierarchy, and
   both sides clear AA comfortably. */
.ffs-pillar__name {
  display: block;
  margin-bottom: 6px;
  font-family: var(--agriox-font-2);
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ffs-ink);
}

.ffs-pillar__text {
  display: block;
  font-size: .9375rem;
  line-height: 1.6;
  color: var(--agriox-color-1);
}

@media (prefers-reduced-motion: reduce) {
  .ffs-pillar { transition: none; }
  .ffs-pillar:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   11. Outlets
   -------------------------------------------------------------------------- */
.ffs-outlets {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
  padding: 0;
  list-style: none;
}
.ffs-outlet {
  padding: 22px 24px;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  background: #fff;
}
.ffs-outlet h3 {
  font-size: 1.0625rem;
  margin: 0 0 6px;
}
.ffs-outlet p {
  margin: 0 0 14px;
  font-size: .9375rem;
}
.ffs-outlet a {
  font-weight: 700;
  font-size: .9375rem;
  color: var(--agriox-primary);
}

/* --------------------------------------------------------------------------
   12. Forms
   -------------------------------------------------------------------------- */
.ffs-field { margin-bottom: 20px; }

.ffs-field label {
  display: block;
  margin-bottom: 7px;
  font-size: .9375rem;
  font-weight: 500;
  color: var(--ffs-ink);
}
.ffs-field .req { color: var(--agriox-base); }

.ffs-field input,
.ffs-field select,
.ffs-field textarea {
  width: 100%;
  min-height: 50px;
  padding: 13px 16px;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  background: #fff;
  font-family: var(--agriox-font);
  font-size: 1rem;
  color: var(--ffs-ink);
}
.ffs-field textarea { min-height: 150px; resize: vertical; }

.ffs-field input:focus,
.ffs-field select:focus,
.ffs-field textarea:focus {
  border-color: var(--agriox-primary);
}

.ffs-field label.error {
  margin-top: 6px;
  font-size: .875rem;
  font-weight: 500;
  color: #c0392b;
}
.ffs-field input.error,
.ffs-field select.error,
.ffs-field textarea.error {
  border-color: #c0392b;
}

/* Honeypot — off-screen rather than display:none, which some bots detect. */
.ffs-hp {
  position: absolute !important;
  left: -9999px !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.ffs-form__status {
  margin-top: 18px;
  padding: 14px 18px;
  border-radius: var(--ffs-radius);
  font-size: .9375rem;
}
.ffs-form__status[data-state="ok"] {
  background: #e8f6ee;
  border: 1px solid #b6e0c7;
  color: #1d6b3e;
}
.ffs-form__status[data-state="error"] {
  background: #fdeceb;
  border: 1px solid #f5c2be;
  color: #a02c22;
}
.ffs-form__status:empty { display: none; }

/* --------------------------------------------------------------------------
   13. Header / footer adjustments
   -------------------------------------------------------------------------- */
.main-header--one__top { background: var(--agriox-primary); }
.main-header--one__top,
.main-header--one__top a,
.main-header--one__top p { color: rgba(255, 255, 255, .9); }
.main-header--one__top a:hover { color: var(--agriox-base-on-dark); }

.ffs-topbar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 24px;
  padding: 9px 0;
}
.ffs-topbar__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 22px;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: .875rem;
}
.ffs-topbar__list li {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ffs-topbar__list i { color: var(--agriox-base-on-dark); }

@media (max-width: 767.98px) {
  .ffs-topbar__inner { justify-content: center; }
  .ffs-topbar__hours { display: none; }
}

.main-menu__list > li > a { font-weight: 500; }
.main-menu__list > li.current > a,
.main-menu__list > li > a:hover { color: var(--agriox-primary); }

/* ---- Brand lockup -------------------------------------------------------
   ffs-logo-original.gif is the whole lockup: the FFS roundel and the
   "FFS FOODS (M) SDN. BHD." wordmark are both baked into the one 398x49
   image, so there is no separate text node here to align against. The flex
   rules sit on the anchor regardless — drop a <span> wordmark in beside the
   mark later and it centres with a 12px gap, no further CSS needed.
   ------------------------------------------------------------------------ */
.ffs-logo,
.ffs-logo a {
  display: flex;
  align-items: center;
  gap: 12px;
}

.ffs-logo img {
  height: 46px;
  width: auto;
  max-width: 100%;
}

/* The drawer is 300px wide inside 15px of padding. The lockup at its natural
   46px height is 373px, which used to run straight out of it. */
.mobile-nav__content .ffs-logo img {
  height: auto;
  width: 100%;
}

/* ---- Header bar layout --------------------------------------------------
   The template centres the brand absolutely (position: absolute; left: 50%)
   over a left-aligned menu, which holds only while the menu is short enough
   to stay clear of the middle. FFS runs seven top-level items: the list
   reaches ~950px and the lockup covers 186px either side of centre, so the
   logo, the wordmark inside it and the links all claimed the same pixels.

   Fix: the brand is back in normal flow at the left — it is also first in the
   DOM now, see partials/header.php — and the nav plus the sales-enquiry box
   are pushed right with margin-left: auto. Nothing in the bar is absolutely
   positioned any more, so nothing can sit on top of anything else.
   ------------------------------------------------------------------------ */
.main-header--one__bottom-inner {
  gap: 12px 40px;
}

.main-header--one__bottom-inner .main-header--one__bottom-middel {
  position: static;
  top: auto;
  left: auto;
  transform: none;
  flex: 0 0 auto;
}

/* Nav and contact box hug the right edge as one group. */
.main-header--one__bottom .main-menu--1 { margin-left: auto; }

/* 60px between seven items is 360px of gutter the bar cannot spare. */
.main-menu .main-menu__list > li + li,
.stricky-header .main-menu__list > li + li {
  margin-left: clamp(20px, 1.9vw, 34px);
}

/* The sticky bar clones .main-menu into an otherwise unstyled container, so
   it inherits no page gutter — without this the logo touches the viewport
   edge. Mirrors .main-header--one__bottom .auto-container. */
.sticky-header__content > .main-menu__inner {
  max-width: 1755px;
  margin: 0 auto;
  padding: 0 50px;
  gap: 12px 40px;
}

/* The sales-enquiry box needs ~240px. Below 1600px the bar cannot carry it
   and the full menu at a comfortable spacing — and the same number already
   sits in the utility bar directly above, so drop the duplicate rather than
   crowd the links. */
@media (max-width: 1599.98px) {
  .main-header--one__bottom-right { display: none; }
}

/* Narrow desktops: shrink the lockup so the links keep their spacing. */
@media (min-width: 1200px) and (max-width: 1499.98px) {
  .ffs-logo img { height: 34px; }
}

@media (max-width: 1199.98px) {
  /* Below the desktop menu breakpoint the template pins the brand and the
     hamburger absolutely into opposite corners of the bar. At phone widths
     the 373px lockup simply ran underneath the toggler. Put both back in
     flow and let flexbox keep them apart. */
  .main-header--one__bottom-inner {
    flex-wrap: nowrap;
    gap: 20px;
  }
  .main-header--one__bottom-inner .main-header--one__bottom-middel {
    min-width: 0;
  }
  .main-header--one__bottom .main-menu--1 {
    position: static;
    top: auto;
    right: auto;
    bottom: auto;
    width: auto;
    height: auto;
    margin-left: auto;
    flex: 0 0 auto;
  }
  /* Sized by width rather than height: the lockup is 8:1, so a height that
     reads well on desktop is wider than the phone it is on. */
  .ffs-logo img {
    height: auto;
    width: clamp(180px, 46vw, 300px);
  }
}

/* ---- Mobile drawer ------------------------------------------------------
   Everything below 1200px navigates through this drawer, so it is the whole
   menu on a phone. agriox.js clones .main-menu__list into it verbatim, which
   means the desktop menu's colours come along for the ride — and the desktop
   bar sits on white while the drawer sits on the brand blue. Three of the
   inherited colours were therefore illegible or invisible on the drawer:

     current page link   blue on blue ........  1.00:1  invisible
     .expanded parent    blue on blue ........  1.00:1  invisible
     toggle + contact    blue on blue ........  1.00:1  invisible

   The first is the one that reads as a bug: on index.php the "Home" row was
   a blank 46px gap between the logo and "About", and every page lost its own
   link the same way. All three are re-pointed at the gold, which is the one
   colour §1 sanctions on a blue ground — 5.30:1, past AA for the text rows
   and well past the 3:1 WCAG 1.4.11 asks of the icons and the toggle.

   Measured at 390x844 (iPhone 12/13/14, the narrowest common viewport).
   ------------------------------------------------------------------------ */

/* 100vh is the *largest* viewport on mobile Safari — the tallest the page gets
   once the toolbars retract. Sized by it, the drawer's last rows sit behind the
   toolbar on first paint, which at 390x844 is the whole contact block. dvh is
   the live value; the vh line stays as the fallback for browsers without it. */
.mobile-nav__wrapper {
  height: 100vh;
  height: 100dvh;
}

/* 300px of a 390px viewport leaves 90px of overlay to tap out through, which
   is comfortable — but the same 300px on a 320px phone leaves 20px, and the
   only way out is then the close button. The min() keeps 390px exactly as it
   was and only narrows the drawer where it has to. */
.mobile-nav__content {
  width: min(300px, 82vw);
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding-top: 20px;
}

/* The lockup is one image with a white ground baked in, so on the blue it
   lands as a white slab either way. Made deliberate — a white card, inset
   from the close button rather than running under it. Was full-width, which
   put the wordmark directly beneath the ×. */
.mobile-nav__content .logo-box {
  max-width: calc(100% - 56px);
  margin-bottom: 30px;
  padding: 9px 11px;
  background: #fff;
  border-radius: 8px;
}

/* 18px of glyph is a ~20px target; the AAA floor is 44px. Grown to 44 and
   given a hover/focus ground so it is findable against the blue. */
.mobile-nav__close {
  top: 12px;
  right: 12px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 8px;
  font-size: 20px;
  color: var(--agriox-base-on-dark);
  transition: background-color 200ms ease, color 200ms ease;
}
.mobile-nav__close:hover,
.mobile-nav__close:focus-visible {
  background: rgba(242, 169, 0, .22);
  color: #fff;
}

/* 46px fixed height clipped the two-line labels ("Business Solutions" wraps
   at this width once the toggle button takes its 36px). Auto height with a
   48px floor keeps the target size and lets the row grow. */
.mobile-nav__content .main-menu__list li a {
  height: auto;
  min-height: 48px;
  padding: 7px 0;
  gap: 12px;
  color: rgba(255, 255, 255, .92);
  font-family: var(--agriox-font);
}

/* The fix for the invisible row. Gold at 600 (5.30:1 on the blue), plus a
   gold rule down the leading edge so the current page is marked by more than
   colour alone (WCAG 1.4.1). Only the current item is indented, so nothing
   else shifts. */
.mobile-nav__content .main-menu__list > li.current > a {
  padding-left: 12px;
  box-shadow: inset 3px 0 0 var(--agriox-base-on-dark);
  color: var(--agriox-base-on-dark);
  font-weight: 600;
}

.mobile-nav__content .main-menu__list li a:hover,
.mobile-nav__content .main-menu__list li a.expanded {
  color: var(--agriox-base-on-dark);
}

.mobile-nav__content .main-menu__list li a:focus-visible {
  outline: 2px solid var(--agriox-base-on-dark);
  outline-offset: -2px;
}

/* Submenu rows step back half a level so the Products children read as
   children at a glance rather than as seven more top-level items. */
.mobile-nav__content .main-menu__list li ul li a {
  min-height: 44px;
  font-size: .9375rem;
  color: rgba(255, 255, 255, .78);
}

/* Collapsed: outlined in gold with a gold chevron — the border gives the
   control a visible boundary at 5.30:1 on the blue, which a bare glyph would
   not have. Expanded: filled gold with a near-black chevron (9.26:1), so open
   and closed differ in fill as well as in the template's rotation. */
.mobile-nav__content .main-menu__list li a button {
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background-color: transparent;
  border: 1px solid var(--agriox-base-on-dark);
  color: var(--agriox-base-on-dark);
}
.mobile-nav__content .main-menu__list li a button.expanded {
  background-color: var(--agriox-base-on-dark);
  color: var(--ffs-on-brand);
}

/* --agriox-text-dark is not a token this stylesheet defines — the template's
   rule resolved to nothing and the li fell back to inheriting body colour. */
.mobile-nav__contact li {
  color: rgba(255, 255, 255, .9);
  font-size: .9375rem;
  letter-spacing: normal;
}
.mobile-nav__contact li > i {
  width: 38px;
  height: 38px;
  font-size: 15px;
  background-color: var(--agriox-base-on-dark);
  color: var(--agriox-primary);
}
.mobile-nav__contact li a:hover {
  color: var(--agriox-base-on-dark);
}

/* Both floats sit under the drawer's overlay while it is open — visible,
   dimmed and unclickable, which reads as a dead control. Taken out for the
   duration; body.locked is already toggled with the drawer by agriox.js. */
body.locked .ffs-wa,
body.locked .scroll-to-top {
  display: none;
}

.ffs-footer-note {
  font-size: .875rem;
  color: rgba(255, 255, 255, .65);
}

/* --------------------------------------------------------------------------
   12b. Legal pages — privacy-policy.php
   -------------------------------------------------------------------------- */
.ffs-legal h2 {
  font-size: 1.25rem;
  margin-top: 2.2em;
  /* Anchored from the table of contents; keep the heading clear of the
     sticky header when a #fragment is followed. */
  scroll-margin-top: 100px;
}
.ffs-legal h2:first-of-type { margin-top: 1.4em; }

.ffs-legal ul {
  padding-left: 1.2em;
  max-width: 68ch;
}
.ffs-legal li + li { margin-top: .5em; }

.ffs-legal__updated {
  display: inline-block;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--ffs-surface);
  border: 1px solid var(--ffs-border);
  font-size: .875rem;
}

.ffs-legal__toc {
  position: sticky;
  top: 100px;
  padding: 26px;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  background: var(--ffs-surface);
}
.ffs-legal__toc h2 {
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--agriox-primary);
  margin: 0 0 14px;
}
.ffs-legal__toc ol {
  margin: 0;
  padding-left: 1.3em;
  font-size: .9375rem;
}
.ffs-legal__toc li + li { margin-top: .45em; }
.ffs-legal__toc a { color: var(--agriox-color-1); }
.ffs-legal__toc a:hover { color: var(--agriox-base); }

@media (max-width: 991.98px) {
  .ffs-legal__toc { position: static; }
}

/* --------------------------------------------------------------------------
   12c. Thank-you page
   -------------------------------------------------------------------------- */
.ffs-thanks {
  max-width: 680px;
  margin: 0 auto;
  text-align: center;
}
.ffs-thanks .ffs-lede { margin-inline: auto; }

.ffs-thanks__mark {
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  margin: 0 auto 26px;
  border-radius: 50%;
  background: rgba(var(--agriox-base-rgb), .1);
  color: var(--agriox-base);
  font-size: 1.75rem;
}

.ffs-thanks__panel {
  margin-top: 40px;
  padding: 30px;
  border: 1px solid var(--ffs-border);
  border-radius: var(--ffs-radius);
  background: var(--ffs-surface);
  text-align: left;
}
.ffs-thanks__panel h2 { font-size: 1.125rem; margin-top: 0; }

.ffs-thanks__contact {
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  font-size: .9375rem;
}
.ffs-thanks__contact li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
.ffs-thanks__contact li + li { margin-top: 12px; }
.ffs-thanks__contact i {
  width: 20px;
  color: var(--agriox-base);
  text-align: center;
}

.ffs-thanks__next {
  font-size: 1.125rem;
  margin-top: 48px;
}
.ffs-thanks__links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 20px;
}

/* --------------------------------------------------------------------------
   12d. Footer columns and contact icons
   --------------------------------------------------------------------------
   The footer markup is the template's, so everything below is the FFS layer
   reaching into it rather than editing agriox.css.
   -------------------------------------------------------------------------- */

/* The four contact icons — address, phone, email, WhatsApp — set to white.
   White on the footer blue is 10.66:1.

   Note the `::before` in every selector, which is the whole point of this rule.
   Font Awesome does not draw the glyph on the `<i>`; it draws it in the `<i>`'s
   ::before, and agriox.css hard-colours THAT in three places:

     .footer-widget__about-contact-box .text  i::before
     .footer-widget__about-contact-box .phone a i::before
     .footer-widget__about-contact-box p     a i::before

   all of them `color: var(--agriox-base)`. A pseudo-element's own colour
   declaration always wins over inheriting from the element it hangs off, so
   colouring the `<i>` alone — which is what this rule used to do — has no
   effect on the glyph at all. And because the FFS layer re-skins --agriox-base
   to #2e3192, the same blue as the footer ground, the glyphs were painting at
   1.00:1: still rendering, perfectly invisible.

   Specificity, since the three template rules do not all sit at the same
   weight — the selectors below are built to win outright rather than to rely
   on this file loading second:

     .text  i::before   (0,2,2)  <  `… p i::before`    (0,2,3)   address row
     .phone a i::before (0,2,3)  <  `… p a i::before`  (0,2,4)   phone, WhatsApp
     p a    i::before   (0,1,4)  <  `… p a i::before`  (0,2,4)   email

   Every contact row is a <p>, so the `p` costs nothing in coverage while
   buying the extra weight. */
.footer-one .footer-widget__about-contact-box p i,
.footer-one .footer-widget__about-contact-box p i::before,
.footer-one .footer-widget__about-contact-box p a i::before,
.footer-one .footer-widget__about-contact-box p a:hover i::before,
.footer-one .footer-widget__about-contact-box p a:focus i::before {
  color: #fff;
}

/* Fixed-width icon column so the four lines align on their text rather than
   stepping in and out with each glyph's natural width.

   The template puts `padding-right: 10px` on the ::before to space the glyph
   off its label. That has to go, or it is added INSIDE this 22px box — pushing
   the glyph off centre and overflowing the column it is supposed to define.
   The gap is this element's margin-right instead. */
.footer-one .footer-widget__about-contact-box p i {
  display: inline-block;
  width: 22px;
  margin-right: 10px;
  text-align: center;
}
.footer-one .footer-widget__about-contact-box p i::before,
.footer-one .footer-widget__about-contact-box p a i::before {
  padding-right: 0;
}

/* Even vertical rhythm inside the contact box — the template gives these
   paragraphs mixed margins depending on which class they carry. */
.footer-one .footer-widget__about-contact-box p {
  margin: 0 0 14px;
}
.footer-one .footer-widget__about-contact-box p:last-child {
  margin-bottom: 0;
}

/* Item 9 — one shared start position for every footer column, so Products,
   Company and Get in touch line up on their headings and on their first row of
   content regardless of how much copy sits above them. The column widths are
   equalised in partials/footer.php; this equalises the internal spacing. */
.footer-one .footer-widget__column {
  height: 100%;
}

/* The three template offsets that stopped the four columns from sharing a top
   edge or an even rhythm across the row. All of them are the Agriox demo's
   layout, where the footer was 1 wide column + 2 narrow lists + a newsletter
   block; none of them survive contact with four equal quarters.

     .footer-widget__about   margin-top: -15px   lifted "Fun Food Specialist"
                                                 15px above the other three
     .footer-widget__explore margin-left: 50px   indented Products and Company
                                                 50px right of their gutter, so
                                                 the four gaps read 30/80/30/30
     .footer-widget__explore margin-top: 60px    below 1200px, dropped those two
                                                 columns 60px down the page —
                                                 declared three times over, in
                                                 the <=1199, <=991 and <=767
                                                 blocks of agriox.css

   Zeroed rather than re-tuned: the row is four `col-xl-3`s and Bootstrap's own
   gutter already distributes them evenly, so the correct offset is none. Each
   selector is scoped to .footer-one, which puts it at 0,2,0 against the
   template's 0,1,0 — that wins on specificity regardless of media query, so
   the responsive margin-top is covered by this one rule and does not need
   restating per breakpoint (and does not need !important).

   Top alignment itself comes from the row's default `align-items: stretch`
   plus the height:100% above: with the margins gone, every column's first line
   box starts on the same y. Do not add `align-items: flex-start` to force it —
   that collapses the stretched height these columns are measured by. */
.footer-one .footer-widget__about,
.footer-one .footer-widget__explore {
  margin-top: 0;
  margin-left: 0;
}

/* Even columns are not even SPACING, which is the thing actually being asked
   for here. Four col-xl-3s put the column boxes on a perfect 300px pitch, but
   the boxes are not what a reader sees — the text inside them is, and the four
   blocks do not fill their boxes equally:

     Fun Food Specialist   262px of ink in a 270px box   (a wrapped paragraph)
     Products              135px                          (short link labels)
     Company               137px                          (short link labels)
     Get in touch          270px                          (paragraph + buttons)

   which rendered as gaps of 38px / 166px / 162px. The first two columns nearly
   touch while the others sit in a lake of blue, and no amount of equalising the
   GRID fixes it, because the grid was already equal.

   So above 1200px, where all four sit on one line, the columns size to their
   content and space-between shares out what is left. Every column carries the
   same 15px of gutter padding on both sides, so equal box gaps are equal ink
   gaps — the leftover divides into three identical channels of ~119px.

   max-width caps the two prose columns at the 300px they occupy today, so
   their paragraphs wrap exactly as before rather than running out to their
   max-content width (one long unwrapped line, which is what `width: auto`
   would otherwise give a <p>). flex-shrink stays at 1 so a future longer menu
   label compresses the channels instead of overflowing the container.

   Scoped to >=1200px deliberately. Below that the four become 2x2 and then a
   single stack, where content-width columns would leave one item hugging the
   left edge and its neighbour floating mid-row; the even 50/50 grid is the
   right answer there and is left alone. */
@media (min-width: 1200px) {
  .footer-one__top .row {
    justify-content: space-between;
  }
  .footer-one__top .row > [class*="col-"] {
    flex: 0 1 auto;
    width: auto;
    max-width: 300px;
  }
}

.footer-one .footer-widget__title {
  margin: 0 0 24px;
}
.footer-one .footer-widget__explore-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.footer-one .footer-widget__explore-list-item {
  margin: 0 0 12px;
}
.footer-one .footer-widget__explore-list-item:last-child {
  margin-bottom: 0;
}

/* Footer CTA pair — "Contact us" and the Shopee store link — as one stack of
   identical bars, on every screen size rather than only below 576px.

   The base .ffs-btn-group wraps a row, which is fine in a wide panel: the
   buttons shrink-wrap their labels and sit side by side. In a footer column
   roughly 255px wide they can never fit on one line, so they wrapped to two
   rows anyway — but as two DIFFERENT widths, because "Shopee Store" is a much
   shorter label than "Contact us" is not. Column direction with stretch makes
   the pair share the column's width, so both edges line up and both labels
   centre on the same axis.

   Width, padding and alignment all come from this one rule plus the base
   .ffs-btn box; the 16px between them is --ffs-btn-gap, the same value every
   other button pair on the site uses. Nothing here re-declares padding — the
   buttons already carry the site's single 15px/32px, and full-width is what
   makes that identical rather than merely equal.

   `align-items: stretch` is the default for a flex column, but it is written
   out because the whole point of the rule is that it must not be overridden. */
.footer-one .ffs-btn-group {
  flex-direction: column;
  align-items: stretch;
}

/* The same defect, one row down. The social icons on the bottom bar rest at
   #fff but the template hovers them to var(--agriox-base) — which is now the
   brand blue, sitting on the bottom bar's #242672 at 1.24:1. Hovering made them
   vanish. Gold is the accent that belongs on the dark surfaces (6.55:1). */
.footer-one .footer-one__bottom-social-links ul li a:hover,
.footer-one .footer-one__bottom-social-links ul li a:focus {
  color: var(--agriox-base-on-dark);
}

/* --------------------------------------------------------------------------
   Token-collapse fallout
   --------------------------------------------------------------------------
   All four rules below share one cause, so they are kept together.

   agriox.css was built on a palette where --agriox-base was gold (#f1cf69) and
   --agriox-primary was green. It leans on that difference constantly: paint a
   surface in one token, paint the thing sitting ON that surface in the other.
   The FFS layer sets BOTH to #2e3192, because the brand has one blue and 86
   text declarations hang off --agriox-base. Every such pairing therefore
   resolves to blue-on-blue — 1.00:1. The glyphs still render; you just cannot
   see them.

   These are the instances that survive on this site's markup. Anything else in
   the list is on a page the FFS build does not ship. If a future page pulls in
   a new template component, check it for this before trusting it.
   -------------------------------------------------------------------------- */

/* Footer contact links. White at rest, and the template hovers them to
   --agriox-base — straight into the footer ground. Gold: 5.30:1. */
.footer-one .footer-widget__about-contact-box p a:hover,
.footer-one .footer-widget__about-contact-box p a:focus {
  color: var(--agriox-base-on-dark);
}

/* Back-to-top button, bottom-right of every page. Its ground is --agriox-base
   and the template draws the arrow in --agriox-primary, so the control has
   been sitting there blank — it only appeared on hover, where the template
   happens to hard-code #fff. White at rest too, which also makes the hover a
   background change rather than the arrow blinking into existence. 10.66:1. */
.scroll-to-top i {
  color: #fff;
}

/* Header phone chip. Rests white-on-#242672 (13.16:1) and is correct; the
   hover swaps the chip to --agriox-base and the glyph to --agriox-primary,
   i.e. both to the same blue. Hold the glyph at white — 10.66:1 on the hover
   ground, and the chip colour change still reads as the hover feedback. */
.main-header--one__bottom-right .contact-box .icon:hover span::before,
.main-header--one__bottom-right .contact-box .icon:focus span::before {
  color: #fff;
}

/* --------------------------------------------------------------------------
   12e. Scroll fade
   --------------------------------------------------------------------------
   Both classes are added by initScrollFade() in assets/js/ffs.js — .ffs-fade
   on load, .ffs-fade--in when the element scrolls into view. Nothing in the
   markup carries .ffs-fade, and it must stay that way: if the hidden state
   were in the HTML, a visitor whose JS failed would be left with an empty
   page instead of an un-animated one.
   -------------------------------------------------------------------------- */
.ffs-fade {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity .6s ease-out, transform .6s ease-out;
  /* Hint only — the element is never fixed or layered, so this stays cheap. */
  will-change: opacity, transform;
}
.ffs-fade--in {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* Belt and braces. The script already declines to animate under reduced
   motion, but if .ffs-fade is ever applied by some other route, this makes
   sure it still resolves to visible rather than to a blank section. */
@media (prefers-reduced-motion: reduce) {
  .ffs-fade,
  .ffs-fade--in {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   14. Utilities
   -------------------------------------------------------------------------- */
.ffs-section { padding: 80px 0; }
@media (min-width: 992px) { .ffs-section { padding: 110px 0; } }

.ffs-section--tint { background: var(--ffs-surface); }

/* Interior-page masthead. The brief keeps white the dominant background and
   asks the neutral gray for "background variations", so the banner takes the
   gray band and the blue is reserved for the header, the footer and the hero.
   No colour overrides are needed inside: the default heading (blue, 9.18:1)
   and body (7.53:1) colours both clear AA on the band. */
.ffs-page-banner {
  padding: 56px 0;
  background: var(--ffs-surface);
  border-bottom: 1px solid var(--ffs-border);
}
@media (min-width: 992px) {
  .ffs-page-banner { padding: 72px 0; }
}

/* The blue as a contained panel, not a band across the viewport. The section
   itself stays white and the container carries the fill, so the blue reads as
   a deliberate block on the page rather than a change of ground for the whole
   width. Was .ffs-section--dark. */
.ffs-section--panel {
  padding-top: 20px;
  padding-bottom: 20px;
}
@media (min-width: 992px) {
  .ffs-section--panel {
    padding-top: 40px;
    padding-bottom: 40px;
  }
}
.ffs-section--panel > .auto-container {
  padding: clamp(32px, 5vw, 56px) clamp(20px, 4vw, 40px);
  border-radius: 16px;
  background: var(--agriox-primary);
  color: rgba(255, 255, 255, .88);
}
.ffs-section--panel h1,
.ffs-section--panel h2,
.ffs-section--panel h3 { color: #fff; }

/* The CTA needs no inversion on the blue grounds any more. Tertiary Orange
   measures 3.12:1 against Primary Blue, which clears the 3:1 WCAG 1.4.11
   asks of a control boundary, and its near-black label is unaffected by the
   ground behind the fill — 5.44:1 either way. The old rule inverted the
   button here because the accent blue was 1.65:1 on the old navy; that
   problem is gone with the palette that caused it. */
.ffs-hero .ffs-btn--outline,
.ffs-section--panel .ffs-btn--outline {
  border-color: rgba(255, 255, 255, .55);
  color: #fff;
}
.ffs-hero .ffs-btn--outline:hover,
.ffs-hero .ffs-btn--outline:focus,
.ffs-section--panel .ffs-btn--outline:hover,
.ffs-section--panel .ffs-btn--outline:focus {
  background: #fff;
  border-color: #fff;
  color: var(--agriox-primary);
}

.ffs-lede {
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--agriox-color-1);
  max-width: 62ch;
}

/* Lifts the 62ch measure so a lede spans the same width as the h1 above it,
   which carries no max-width of its own and fills .auto-container.

   CURRENTLY UNUSED. Both callers were page banners that have since been
   rebuilt as .ffs-hero sections, where the copy is .ffs-hero__text and takes
   its own 52ch measure against the scrim instead:
     - the About banner, whose 86-character sentence broke onto a second line
       with one orphaned word ("since 1998.") under it;
     - the Business Solutions banner, whose ~330-character lede wrapped at
       roughly half the headline width and read as a narrow column beside it.

   Kept because the problem it solves recurs on any wide banner lede, and it is
   an opt-in modifier that costs nothing while it has no callers. 62ch is a
   readability cap and is right for every other .ffs-lede on the site, which is
   why this is a modifier rather than a change to the base rule.

   Deliberately NOT `white-space: nowrap`. Below roughly 800px the sentence
   cannot fit on one line at any width, and nowrap would push it out of the
   container — where .page-wrapper's overflow:hidden would silently clip the
   end of it rather than wrap it. This lets it be one line wherever one line
   fits and wrap naturally where it does not. */
.ffs-lede--wide {
  max-width: none;
}

.ffs-eyebrow {
  display: block;
  margin-bottom: 12px;
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--agriox-primary);
}

/* Global heading/paragraph spacing. Eyebrows get 0.75rem below; headings get
   1rem below; body text gets 1.6 line-height and 1rem between paragraphs.
   Paragraphs use text-wrap: pretty globally to reduce single-word widows;
   component-level overrides remain in their own rules. */
p {
  text-wrap: pretty;
}

.ffs-section h1,
.ffs-section h2,
.ffs-section h3 {
  margin-bottom: 1rem;
}

.ffs-section p {
  line-height: 1.6;
  margin-bottom: 1rem;
  text-wrap: pretty;
}

.ffs-section p:last-child {
  margin-bottom: 0;
}

.ffs-section--panel .ffs-eyebrow { color: var(--agriox-base-on-dark); }

/* Body copy on the blue panel. --agriox-color-1 is a mid grey tuned for white
   grounds and drops to roughly 2:1 on #2e3192, so a lede inside the panel has
   to be lifted off the base rule. White at 85% measures 8.4:1 — comfortably
   AAA, and still visibly secondary to the pure-white heading above it. */
.ffs-section--panel .ffs-lede { color: rgba(255, 255, 255, .85); }

/* White cards sitting inside the blue panel.
   The panel paints h1-h3 white so its own headings read on the blue; a card
   with a white background inverts that ground, so its heading has to go back
   to the ink blue or it disappears entirely. Same reasoning for the body copy,
   which returns to the standard grey it uses everywhere else on white. */
.ffs-panel-card {
  height: 100%;
  padding: 28px;
  border-radius: var(--ffs-radius);
  background: #fff;
}
.ffs-section--panel .ffs-panel-card h3 {
  margin: 0 0 8px;
  font-size: 1.0625rem;
  color: var(--ffs-ink);
}
.ffs-section--panel .ffs-panel-card p {
  margin: 0;
  font-size: .9375rem;
  color: var(--agriox-color-1);
}

.ffs-stack > * + * { margin-top: 1.1em; }
