/* ==========================================================================
   Food Hygiene Record Book — design system, 2026
   Direction: "Operating System" — premium software-product presentation.

   Loaded LAST so it can retune the shared tokens every earlier stylesheet
   already consumes. That means one token change lifts all 20 pages together
   instead of 20 divergent edits.

   Rules this file holds itself to:
   - Three elevation levels, never nested. Most content sits flat on the page.
   - Dark green is an anchor used once or twice per page, never a repeated slab.
   - Blue carries information/reporting. Amber carries attention/action.
     Both are inherited from the app's own meaning, not decoration.
   - No web fonts: the live CSP is font-src 'self', and a native stack costs
     zero bytes, cannot FOUT and cannot shift layout.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Grounds — neutrals biased very slightly green so they read as chosen. */
  --paper: #fcfcfb;
  --surface: #ffffff;
  --sunken: #f1f5f2;
  --sunken-2: #e8efeb;

  /* Ink */
  --ink: #0c1a15;
  --body-ink: #33443d;
  --muted: #647670;
  --faint: #8a9a94;

  /* Brand green — unchanged hue, extended into a usable ramp. */
  --green: #087f5b;
  --green-700: #06674a;
  --green-800: #054f39;
  --green-deep: #052f24;
  --green-900: #041f18;
  --green-soft: #e9f5f0;
  --green-50: #f2f9f6;

  /* Blue = information, records, reporting. */
  --blue: #1b5aa6;
  --blue-700: #14477f;
  --blue-soft: #e9f1fa;

  /* Amber = attention, corrective action, things that need a person. */
  --amber: #a96200;
  --amber-700: #8a5000;
  --amber-soft: #fbf1e2;

  /* Hairlines carry separation before any border or shadow is spent. */
  --hair: rgba(12, 26, 21, 0.1);
  --hair-strong: rgba(12, 26, 21, 0.17);
  --hair-light: rgba(255, 255, 255, 0.14);
  --hair-light-strong: rgba(255, 255, 255, 0.24);
  --line: var(--hair);
  --line-strong: var(--hair-strong);

  /* Elevation — exactly three, never nested. */
  --e1: 0 1px 2px rgba(12, 26, 21, 0.05), 0 8px 20px -14px rgba(12, 26, 21, 0.18);
  --e2: 0 1px 2px rgba(12, 26, 21, 0.06), 0 18px 40px -22px rgba(12, 26, 21, 0.26);
  --e3: 0 2px 4px rgba(12, 26, 21, 0.06), 0 34px 70px -32px rgba(12, 26, 21, 0.34);
  --shadow: var(--e2);

  /* Radii */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --r-xl: 26px;

  /* Type scale — 1.22 ratio, clamped so mobile never gets a headline that
     eats the screen. Every display size shrinks faster than it grows. */
  --t--1: 0.8125rem;
  --t-0: 1rem;
  --t-1: 1.0625rem;
  --t-2: clamp(1.125rem, 0.35vw + 1.05rem, 1.3125rem);
  --t-3: clamp(1.3125rem, 0.7vw + 1.15rem, 1.625rem);
  --t-4: clamp(1.5rem, 1.2vw + 1.2rem, 2.125rem);
  --t-5: clamp(1.875rem, 2vw + 1.35rem, 2.75rem);
  --t-6: clamp(2.125rem, 3vw + 1.4rem, 3.5rem);
  --t-7: clamp(2.375rem, 4.2vw + 1.35rem, 4.25rem);

  /* Rhythm */
  --pad-section: clamp(64px, 7vw, 116px);
  --pad-section-tight: clamp(48px, 5vw, 80px);
  --shell: min(100% - 40px, 1200px);
  --shell-narrow: min(100% - 40px, 940px);
  --measure: 68ch;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 180ms;

  color-scheme: light;
}

/* --------------------------------------------------------------------------
   2. Typographic foundation
   -------------------------------------------------------------------------- */

.fh-site {
  background: var(--paper);
  color: var(--body-ink);
  font-size: 17px;
  line-height: 1.62;
  letter-spacing: -0.003em;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

@media (max-width: 640px) {
  .fh-site {
    font-size: 16px;
  }
}

/* Surface-context tokens.
   Headings must never hard-code a colour: an earlier version of this file set
   `color: var(--ink)` on every h1-h4, which overrode the white headings that
   dark sections used to inherit and produced 1.17:1 text on six hero bands.
   These four tokens inherit, so a dark surface flips its whole subtree once and
   anything nested inside it — including markup added later — follows. */
.fh-site {
  --heading: var(--ink);
  --text: var(--body-ink);
  --text-dim: var(--muted);
  --kicker: var(--green-700);
}

/* Display sizes: tighter tracking as they grow, which is what makes a native
   stack read as designed rather than defaulted. */
.fh-site h1,
.fh-site h2,
.fh-site h3,
.fh-site h4 {
  color: var(--heading);
  text-wrap: balance;
  font-weight: 780;
}

.fh-site h1 {
  letter-spacing: -0.032em;
  line-height: 1.02;
}

.fh-site h2 {
  letter-spacing: -0.026em;
  line-height: 1.08;
}

.fh-site h3 {
  letter-spacing: -0.018em;
  line-height: 1.2;
}

.fh-site p {
  text-wrap: pretty;
}

/* Every number that a reader compares — prices, temperatures, counts — lines
   up in a column. This is the app's own habit carried onto the site. */
.fh-site .price-figure,
.fh-site .tabular,
.fh-site [class*="price"] strong,
.fh-site [class*="proof"] dt,
.fh-site time {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Retune the fixed display sizes the earlier stylesheets set in px, so a
   headline can no longer consume a whole phone screen. */
.fh-site .hero h1,
.fh-site .home-hero h1,
.fh-site .topic-hero h1,
.fh-site .guide-hero h1,
.fh-site .download-hero h1,
.fh-site .page-hero h1 {
  font-size: var(--t-7);
  font-weight: 800;
  letter-spacing: -0.034em;
  line-height: 1;
  max-width: 17ch;
}

.fh-site .section-heading h2,
.fh-site .home-section h2,
.fh-site .topic-section h2,
.fh-site h2.section-title {
  font-size: var(--t-6);
  letter-spacing: -0.028em;
}

.fh-site .hero-lead,
.fh-site .home-hero-lead,
.fh-site .topic-lead,
.fh-site .download-lead {
  font-size: var(--t-2);
  line-height: 1.5;
  color: var(--text-dim);
  max-width: 54ch;
  letter-spacing: -0.006em;
}

/* Eyebrow / kicker — one treatment for the whole site. */
.fh-site .eyebrow,
.fh-site .home-kicker,
.fh-site .topic-kicker,
.fh-site .guide-kicker {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--kicker);
  display: inline-flex;
  align-items: center;
  gap: 9px;
}

/* The -light variants no longer force white: they follow the surface they are
   on. That matters because the team section became a light field, which had
   stranded its kicker as white-on-#f1f5f2 (1.08:1). */
.fh-site .home-kicker-light,
.fh-site .eyebrow.light {
  color: var(--kicker);
}

/* --------------------------------------------------------------------------
   3. Layout rhythm
   -------------------------------------------------------------------------- */

.fh-site .fh-shell,
.fh-site .site-shell {
  width: var(--shell);
  margin-inline: auto;
}

.fh-site .section-pad,
.fh-site .home-section,
.fh-site .topic-section {
  padding-block: var(--pad-section);
}

/* A section that follows one of the same ground colour loses its top padding,
   so two light sections read as one field rather than two stacked slabs. */
.fh-site .home-section + .home-section:not([class*="dark"]):not(.product-showcase):not(.team-product-section) {
  padding-top: 0;
}

/* --------------------------------------------------------------------------
   4. Depth — three levels, never nested
   -------------------------------------------------------------------------- */

.fh-site .e1 {
  background: var(--surface);
  border: 1px solid var(--hair);
  border-radius: var(--r-lg);
  box-shadow: var(--e1);
}

.fh-site .e2 {
  background: var(--surface);
  border: 1px solid var(--hair);
  border-radius: var(--r-xl);
  box-shadow: var(--e2);
}

/* A raised object inside a raised object flattens back to a hairline. This one
   rule is what stops the site reading as nested cards. */
.fh-site .e1 .e1,
.fh-site .e2 .e1,
.fh-site .e1 .e2,
.fh-site .e2 .e2 {
  box-shadow: none;
  border-radius: var(--r-md);
}

/* Existing card-ish components: drop the heavy shadow, keep a hairline, let
   spacing do the separating. */
.fh-site .proof-grid article,
.fh-site .trust-grid article,
.fh-site .role-grid article,
.fh-site .capability-tile,
.fh-site .workflow-card,
.fh-site .home-capabilities article {
  box-shadow: none;
  border: 1px solid var(--hair);
  background: var(--surface);
  border-radius: var(--r-lg);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.fh-site .proof-grid article:hover,
.fh-site .trust-grid article:hover,
.fh-site .role-grid article:hover {
  border-color: var(--hair-strong);
  transform: none;
}

/* --------------------------------------------------------------------------
   5. Buttons — one confident primary, quiet everything else
   -------------------------------------------------------------------------- */

.fh-site .button,
.fh-site .home-button,
.fh-site .topic-button,
.fh-site .fh-button {
  min-height: 48px;
  border-radius: 10px;
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  padding: 12px 20px;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.fh-site .button:hover,
.fh-site .home-button:hover {
  transform: translateY(-1px);
}

.fh-site .button-primary,
.fh-site .home-button-primary,
.fh-site .topic-button-primary {
  color: #ffffff;
  background: var(--green);
  border-color: transparent;
  box-shadow: 0 1px 2px rgba(5, 47, 36, 0.24), 0 10px 24px -12px rgba(8, 127, 91, 0.5);
}

.fh-site .button-primary:hover,
.fh-site .home-button-primary:hover {
  background: var(--green-700);
  box-shadow: 0 1px 2px rgba(5, 47, 36, 0.28), 0 14px 30px -12px rgba(8, 127, 91, 0.55);
}

.fh-site .button-secondary,
.fh-site .home-button-secondary {
  color: var(--green-deep);
  background: var(--surface);
  border-color: var(--hair-strong);
  box-shadow: none;
}

.fh-site .button-secondary:hover,
.fh-site .home-button-secondary:hover {
  border-color: var(--green);
  background: var(--green-50);
}

.fh-site .button-white,
.fh-site .home-button-white {
  color: var(--green-deep);
  background: #ffffff;
  border-color: transparent;
  box-shadow: 0 1px 2px rgba(4, 31, 24, 0.2), 0 12px 28px -14px rgba(4, 31, 24, 0.5);
}

.fh-site .button-white:hover,
.fh-site .home-button-white:hover {
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(4, 31, 24, 0.24), 0 16px 34px -14px rgba(4, 31, 24, 0.55);
}

/* --------------------------------------------------------------------------
   6. App Store call to action

   Apple's own badge artwork is used wherever a link actually goes to the App
   Store — Apple's marketing guidelines require the official badge, so the badge
   is framed and strengthened rather than replaced with a custom button.
   -------------------------------------------------------------------------- */

.fh-site .store-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 56px;
  padding: 4px;
  border-radius: 12px;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.fh-site .store-badge img {
  display: block;
  height: 54px;
  width: auto;
  border-radius: 9px;
}

.fh-site .store-badge:hover {
  transform: translateY(-1px);
}

.fh-site .store-badge:hover img {
  box-shadow: 0 2px 4px rgba(12, 26, 21, 0.18), 0 16px 32px -16px rgba(12, 26, 21, 0.45);
}

.fh-site .store-badge:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 3px;
}

/* A store link sitting on the dark anchor band needs its own edge. */
.fh-site .home-final-cta .store-badge img,
.fh-site .topic-final-cta .store-badge img,
.fh-site [class*="dark"] .store-badge img {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.16);
}

/* The custom treatment below is for internal "go to the download page" links,
   which must NOT carry Apple branding because they do not reach the store. */

.fh-site .appstore-cta {
  display: inline-flex;
  align-items: center;
  gap: 13px;
  min-height: 56px;
  padding: 10px 22px 10px 18px;
  border-radius: 14px;
  text-decoration: none;
  color: #ffffff;
  background: linear-gradient(180deg, var(--green) 0%, var(--green-700) 100%);
  border: 1px solid var(--green-800);
  box-shadow: 0 1px 1px rgba(255, 255, 255, 0.18) inset,
    0 2px 4px rgba(5, 47, 36, 0.22), 0 16px 34px -16px rgba(6, 103, 74, 0.6);
  transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.fh-site .appstore-cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 1px 1px rgba(255, 255, 255, 0.22) inset,
    0 2px 6px rgba(5, 47, 36, 0.26), 0 22px 44px -16px rgba(6, 103, 74, 0.66);
}

.fh-site .appstore-cta__mark {
  flex: none;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
}

.fh-site .appstore-cta__text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  text-align: left;
}

.fh-site .appstore-cta__text small {
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.82;
  font-weight: 600;
}

.fh-site .appstore-cta__text strong {
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.015em;
}

/* --------------------------------------------------------------------------
   7. Screenshot presentation — the "shelf"
   Real app screens, grounded with a contact shadow instead of floated in a
   heavy card. No 3D library, no transform-heavy scenes.
   -------------------------------------------------------------------------- */

.fh-site .shelf {
  position: relative;
  display: flex;
  justify-content: center;
  isolation: isolate;
}

.fh-site .shelf::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -2px;
  translate: -50% 0;
  width: min(82%, 420px);
  height: 34px;
  background: radial-gradient(closest-side, rgba(5, 47, 36, 0.26), transparent 78%);
  filter: blur(9px);
  z-index: -1;
  pointer-events: none;
}

.fh-site .shelf--dark::after {
  background: radial-gradient(closest-side, rgba(0, 0, 0, 0.5), transparent 78%);
}

/* Existing device frames: unify the treatment. */
.fh-site .hero-phone,
.fh-site .tour-phone,
.fh-site .command-screen,
.fh-site .topic-phone,
.fh-site .guide-phone {
  border-radius: 34px;
  box-shadow: 0 2px 4px rgba(12, 26, 21, 0.1), 0 30px 60px -28px rgba(5, 47, 36, 0.5),
    0 0 0 1px rgba(12, 26, 21, 0.06);
}

.fh-site .hero-phone > img,
.fh-site .tour-phone img,
.fh-site .command-screen img,
.fh-site .topic-phone img {
  border-radius: 30px;
  display: block;
}

/* Screens shown against the dark anchor band need a light edge, or they read
   as holes cut in the panel. This was the worst artefact on the old home page. */
.fh-site .product-showcase .tour-phone,
.fh-site .food-control-section .command-screen,
.fh-site [class*="dark"] .shelf img {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14), 0 2px 6px rgba(0, 0, 0, 0.4),
    0 40px 80px -30px rgba(0, 0, 0, 0.7);
}

/* --------------------------------------------------------------------------
   8. The dark anchor band
   One per page. Deep green with a soft top edge so it lands rather than cuts.
   -------------------------------------------------------------------------- */

/* Exactly two dark moments per page: one product anchor mid-page, one closing
   call to action. Everything else earns separation from space and hairlines. */
.fh-site .product-showcase,
.fh-site .home-final-cta,
.fh-site .topic-final-cta,
.fh-site .guide-final-cta {
  background: linear-gradient(180deg, var(--green-deep) 0%, var(--green-900) 100%);
  border-block: 1px solid rgba(255, 255, 255, 0.08);
}

/* ---- ON-DARK SURFACES --------------------------------------------------
   Every section in the codebase that paints a dark green ground, found by
   scanning all six stylesheets for dark background declarations rather than
   by listing them from memory. Each one flips the four inherited tokens, so
   its headings, prose, kickers and any future child are readable by default.

   Measured against the darkest stop of each gradient:
     #ffffff            on #052f24 -> 15.9:1   (AA needs 4.5 / 3.0)
     rgba(255,255,255,.88) -> 12.4:1
     rgba(255,255,255,.74) ->  8.4:1                                      */
.fh-site .seo-hero,
.fh-site .seo-final,
.fh-site .seo-day-section,
.fh-site .guide-hero,
.fh-site .download-hero,
.fh-site .install-section,
.fh-site .growing-section,
.fh-site .contact-hero,
.fh-site .pricing-section,
.fh-site .release-section,
.fh-site .team-section,
.fh-site .daily-command,
.fh-site .app-tour,
.fh-site .support-route.featured,
.fh-site .product-showcase,
.fh-site .home-final-cta,
.fh-site .topic-final-cta,
.fh-site .guide-final-cta,
.fh-site .food-control-visual,
.fh-site .showcase-note,
.fh-site .access-card-action,

.fh-site .report-preview header,
.fh-site .skip-link {
  --heading: #ffffff;
  --text: rgba(255, 255, 255, 0.88);
  --text-dim: rgba(255, 255, 255, 0.74);
  --kicker: rgba(255, 255, 255, 0.84);
  color: var(--text);
}

.fh-site .seo-hero p,
.fh-site .seo-final p,
.fh-site .seo-day-section p,
.fh-site .guide-hero p,
.fh-site .download-hero p,
.fh-site .install-section p,
.fh-site .growing-section p,
.fh-site .contact-hero p,
.fh-site .pricing-section p,
.fh-site .release-section p,
.fh-site .team-section p,
.fh-site .daily-command p,
.fh-site .app-tour p,
.fh-site .product-showcase p,
.fh-site .home-final-cta p,
.fh-site .topic-final-cta p,
.fh-site .guide-final-cta p {
  color: var(--text-dim);
}

/* A headline split across lines with <span class="headline-line"> inherits the
   heading colour rather than the paragraph colour. */
.fh-site .headline-line {
  color: inherit;
}

/* ---- LIGHT SURFACES THAT WERE DARK -------------------------------------
   These three were converted to light grounds during the redesign, so they
   must reassert the light token set. fh-system.css loads after home.css, so
   the restore has to live here to win the cascade. */
.fh-site .team-product-section,
.fh-site .food-control-disclaimer,
.fh-site .trust-product-section .guidance-bar {
  --heading: var(--ink);
  --text: var(--body-ink);
  --text-dim: var(--muted);
  --kicker: var(--green-700);
  color: var(--text);
}

/* --------------------------------------------------------------------------
   9. Meaningful accents
   -------------------------------------------------------------------------- */

.fh-site .accent-blue {
  color: var(--blue);
}

.fh-site .accent-amber {
  color: var(--amber);
}

.fh-site .proof-icon.blue,
.fh-site .icon-chip.blue {
  color: var(--blue);
  background: var(--blue-soft);
}

.fh-site .proof-icon.amber,
.fh-site .icon-chip.amber {
  color: var(--amber);
  background: var(--amber-soft);
}

.fh-site .proof-icon,
.fh-site .icon-chip {
  color: var(--green-700);
  background: var(--green-soft);
  border-radius: 10px;
}

/* Status pills reuse the app's own semantics. */
.fh-site .pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 650;
  letter-spacing: -0.005em;
}

.fh-site .pill--ok {
  color: var(--green-700);
  background: var(--green-soft);
}

.fh-site .pill--info {
  color: var(--blue-700);
  background: var(--blue-soft);
}

.fh-site .pill--action {
  color: var(--amber-700);
  background: var(--amber-soft);
}

/* --------------------------------------------------------------------------
   10. Header
   -------------------------------------------------------------------------- */

.fh-site .site-header,
.fh-site .fh-header {
  background: rgba(252, 252, 251, 0.86);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}

.fh-site .site-header.is-scrolled,
.fh-site .fh-header.is-scrolled {
  border-bottom-color: var(--hair);
  box-shadow: 0 1px 0 rgba(12, 26, 21, 0.03), 0 10px 30px -22px rgba(12, 26, 21, 0.3);
}

.fh-site .fh-nav a,
.fh-site .site-nav a {
  font-size: 0.9375rem;
  font-weight: 550;
  letter-spacing: -0.008em;
  color: var(--body-ink);
  border-radius: 8px;
}

.fh-site .fh-nav a:hover,
.fh-site .site-nav a:hover {
  color: var(--ink);
}

.fh-site .fh-nav a[aria-current="page"],
.fh-site .site-nav a[aria-current="page"] {
  color: var(--green-700);
  font-weight: 680;
}

/* --------------------------------------------------------------------------
   11. Accessibility
   -------------------------------------------------------------------------- */

/* A focus ring that is visible on light grounds, dark bands and green buttons
   alike — two-tone so it never disappears into whatever sits behind it. */
.fh-site a:focus-visible,
.fh-site button:focus-visible,
.fh-site summary:focus-visible,
.fh-site input:focus-visible,
.fh-site select:focus-visible,
.fh-site textarea:focus-visible,
.fh-site [tabindex]:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(255, 255, 255, 0.9), 0 0 0 7px rgba(8, 127, 91, 0.22);
  border-radius: 8px;
}

.fh-site [class*="dark"] a:focus-visible,
.fh-site .product-showcase a:focus-visible,
.fh-site .home-final-cta a:focus-visible,
.fh-site .topic-final-cta a:focus-visible {
  outline-color: #ffffff;
  box-shadow: 0 0 0 5px rgba(4, 31, 24, 0.75), 0 0 0 7px rgba(255, 255, 255, 0.3);
}

/* Every interactive control clears 44px, including small icon links. */
.fh-site .site-footer a,
.fh-site .footer-bottom nav a,
.fh-site .text-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

@media (max-width: 767px) {
  .fh-site .nav-toggle {
    min-width: 48px;
    min-height: 48px;
  }
}

/* Honour the reader's own text size at 200% zoom: nothing is pinned to px. */
.fh-site .visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   12. Motion
   Restrained, GPU-friendly, and never a precondition for reading anything.
   -------------------------------------------------------------------------- */

.enhanced-motion .home-reveal {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity 460ms var(--ease), transform 460ms var(--ease);
  will-change: opacity, transform;
}

.enhanced-motion .home-reveal.is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  will-change: auto;
}

@media (prefers-reduced-motion: reduce) {
  .fh-site *,
  .fh-site *::before,
  .fh-site *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .enhanced-motion .home-reveal,
  .fh-site .home-reveal {
    opacity: 1 !important;
    transform: none !important;
  }

  .fh-site .button:hover,
  .fh-site .home-button:hover,
  .fh-site .appstore-cta:hover {
    transform: none;
  }
}

/* --------------------------------------------------------------------------
   13. Media safety — no layout shift, no overflow
   -------------------------------------------------------------------------- */

.fh-site img,
.fh-site svg,
.fh-site video {
  max-width: 100%;
  height: auto;
}

.fh-site {
  overflow-x: clip;
}

.fh-site table {
  width: 100%;
  border-collapse: collapse;
}

.fh-site .table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* --------------------------------------------------------------------------
   14. Reading measure for long-form pages
   -------------------------------------------------------------------------- */

.fh-site .guide-body p,
.fh-site .guide-body li,
.fh-site .legal-body p,
.fh-site .legal-body li,
.fh-site .prose p,
.fh-site .prose li {
  max-width: var(--measure);
}

.fh-site .guide-body h2,
.fh-site .prose h2 {
  margin-top: 2.2em;
  font-size: var(--t-4);
}

.fh-site .guide-body h3,
.fh-site .prose h3 {
  margin-top: 1.8em;
  font-size: var(--t-2);
}

/* --------------------------------------------------------------------------
   15. Small-screen corrections
   -------------------------------------------------------------------------- */

@media (max-width: 480px) {
  :root {
    --shell: min(100% - 32px, 1200px);
    --pad-section: clamp(52px, 12vw, 72px);
  }

  .fh-site .hero h1,
  .fh-site .home-hero h1,
  .fh-site .topic-hero h1,
  .fh-site .download-hero h1 {
    max-width: 100%;
    font-size: clamp(2rem, 8.5vw, 2.5rem);
  }

  .fh-site .hero-actions,
  .fh-site .home-hero-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }

  .fh-site .hero-actions > a,
  .fh-site .home-hero-actions > a,
  .fh-site .appstore-cta {
    justify-content: center;
    width: 100%;
  }
}

/* 320px is the narrowest device still in real use; nothing may overflow it. */
@media (max-width: 360px) {
  :root {
    --shell: min(100% - 24px, 1200px);
  }

  .fh-site .hero-proof {
    flex-wrap: wrap;
    gap: 12px;
  }
}

/* --------------------------------------------------------------------------
   16. Touch-target corrections
   From a measured sweep at 320/390/768/1440. Inline links inside prose are
   deliberately NOT enlarged: WCAG 2.5.8 exempts links in a sentence, and
   padding them to 44px would wreck the reading rhythm. Only real controls
   are corrected here.
   -------------------------------------------------------------------------- */

.fh-site .nav-toggle {
  min-width: 48px;
  min-height: 48px;
  padding: 12px;
}

.fh-site .fh-nav a,
.fh-site .site-nav a,
.fh-site .site-nav .nav-cta {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

/* Disclosure triggers are controls, not prose. */
.fh-site summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
}

/* The app-update notice is injected by site.js; its controls were 34-42px. */
/* site.css sets `.fh-site a.app-update-action { min-height: 42px }` (0,2,1),
   so this must match that specificity to win. */
.fh-site a.app-update-action,
.fh-site .app-update-close {
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* At 768px the notice text was being clipped mid-sentence. Let it wrap. */
.fh-site .app-update-notice {
  flex-wrap: wrap;
  row-gap: 10px;
}

.fh-site .app-update-notice span,
.fh-site .app-update-notice p {
  min-width: 0;
  overflow-wrap: anywhere;
  white-space: normal;
  text-overflow: clip;
}

.fh-site .site-brand {
  min-height: 44px;
}

/* --------------------------------------------------------------------------
   17. Text-resize safety (WCAG 1.4.4 — 200% text)

   Measured failure: at 200% root text, /food-hygiene-app and /sfbb-app
   overflowed by 117px at 390px and 183px at 320px. Cause: .seo-final-grid uses
   `minmax(0, 1fr) auto`, and the `auto` track held a button pinned with
   `white-space: nowrap`. At 200% that button measured 491px, so the track — and
   with it the page — could not fit the viewport.

   Fixed generically rather than per-page: long call-to-action labels wrap, and
   grid/flex children are allowed to shrink below their content width. Buttons
   keep a >=48px target because min-height is untouched.
   -------------------------------------------------------------------------- */

.fh-site .seo-final .button-white,
.fh-site .seo-final .button,
.fh-site .install-section .button,
.fh-site .growing-section .button,
.fh-site .home-final-cta .home-button,
.fh-site .topic-final-cta .button,
.fh-site .guide-final-cta .button {
  white-space: normal;
  text-align: center;
  overflow-wrap: anywhere;
  max-width: 100%;
  height: auto;
  min-height: 48px;
  padding-block: 12px;
}

/* Grid and flex children default to min-width:auto, which refuses to shrink
   below their content. Every closing-CTA layout gets an explicit floor. */
.fh-site .seo-final-grid,
.fh-site .seo-final-grid > *,
.fh-site .home-final-grid,
.fh-site .home-final-grid > *,
.fh-site .install-grid,
.fh-site .install-grid > *,
.fh-site .growing-grid,
.fh-site .growing-grid > * {
  min-width: 0;
}

.fh-site .seo-final-grid {
  grid-template-columns: minmax(0, 1fr) minmax(0, auto);
}

/* Below the two-column breakpoint the CTA column must not set the page width. */
@media (max-width: 760px) {
  .fh-site .seo-final-grid,
  .fh-site .home-final-grid,
  .fh-site .install-grid,
  .fh-site .growing-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Long unbroken strings (URLs, email addresses) must not push a column wider
   than the viewport at large text sizes either. */
.fh-site a[href^="mailto:"],
.fh-site a[href^="http"],
.fh-site code,
.fh-site .store-credits {
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   18. Light-surface reset

   A context token inherits, which is what makes section 8 robust — and also
   what makes it dangerous: a white card nested inside a dark band would keep
   `--heading: #ffffff` and render white-on-white. That is exactly what
   happened to .contact-form-panel, a white panel inside the dark .contact-hero
   (measured 1.00:1).

   Every element below paints its own light ground, so each one restores the
   light token set for its whole subtree. Selectors were found by scanning all
   six stylesheets for light background declarations, not from memory.
   -------------------------------------------------------------------------- */

.fh-site .contact-form-panel,
.fh-site .access-card,
.fh-site .command-stage,
.fh-site .form-notice,
.fh-site .guide-card,
.fh-site .guide-note,
.fh-site .guide-summary-band,
.fh-site .guide-summary-grid div,
.fh-site .guide-visual figure,
.fh-site .home-faq-list,
.fh-site .home-price-card,
.fh-site .official-panel,
.fh-site .policy-summary,
.fh-site .price-card,
.fh-site .pricing-card,
.fh-site .proof-console,
.fh-site .report-preview,
.fh-site .rights-callout,
.fh-site .support-card,
.fh-site .switch-card,
.fh-site .team-console,
.fh-site .topic-note,
.fh-site .topic-panel,
.fh-site .seo-quote,
.fh-site .guide-hero-aside,
.fh-site .workflow-item > figure,
.fh-site .workflow-pair figure {
  --heading: var(--ink);
  --text: var(--body-ink);
  --text-dim: var(--muted);
  --kicker: var(--green-700);
}

/* These keep a dark strip inside an otherwise light card, so the strip flips
   back again. Nesting depth is what the tokens are for.
   .proof-console > footer is deliberately NOT here: it measured #f3f5f5, a
   light strip, and forcing it dark produced white-on-light (1.08:1). */
.fh-site .report-preview header,
.fh-site .team-console > footer {
  --heading: #ffffff;
  --text: rgba(255, 255, 255, 0.88);
  --text-dim: rgba(255, 255, 255, 0.74);
  --kicker: rgba(255, 255, 255, 0.84);
}

/* --------------------------------------------------------------------------
   19. Contrast corrections on light grounds

   Measured, not assumed. Against the site's light grounds these two pairings
   sat just under AA for normal text:
     --muted  #647670 on #f4f7f5  ->  4.42:1  (needs 4.5)
     --green  #087f5b on #e9f4ef  ->  4.31:1  (needs 4.5)
   Both are nudged darker by the smallest amount that clears AA, so the visual
   design is unchanged but the text is compliant.
     #5c6d67 on #f4f7f5  ->  4.94:1
     #06674a on #e9f4ef  ->  5.72:1
   -------------------------------------------------------------------------- */

:root {
  --muted: #5c6d67;
}

/* Body links and small print on tinted panels take the darker green. */
/* Text links on tinted panels take the darker green. Buttons are excluded:
   they paint their own ground, and an earlier version of this rule turned the
   .policy-summary "Privacy contact" button into green-on-green (1.38:1). */
.fh-site .guide-note a:not([class*="button"]),
.fh-site .topic-note a:not([class*="button"]),
.fh-site .policy-summary a:not([class*="button"]),
.fh-site .rights-callout a:not([class*="button"]),
.fh-site .guide-summary-band a:not([class*="button"]),
.fh-site .seo-quote small {
  color: var(--green-700);
}

/* --------------------------------------------------------------------------
   20. Guide hero at large text sizes

   Measured overflow at 200% root text: /guides/fridge-freezer-temperatures
   114px at 320px and 48px at 390px; cooking-cooling-reheating 18px;
   opening-closing-checks 16px. Two causes: the hero grid pins its aside column
   with `minmax(350px, ...)`, and long hyphenated breadcrumb/heading strings
   cannot break.
   -------------------------------------------------------------------------- */

.fh-site .guide-hero-grid,
.fh-site .guide-hero-grid > *,
.fh-site .guide-hero-copy,
.fh-site .seo-breadcrumb {
  min-width: 0;
}

@media (max-width: 900px) {
  .fh-site .guide-hero-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Breadcrumbs wrap rather than set the page width. */
.fh-site .seo-breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  overflow-wrap: anywhere;
}

/* Long guide titles break rather than overflow; balance is dropped at these
   sizes because it can otherwise force an over-wide line box. */
/* `hyphens: auto` was splitting ordinary words ("auth-orised", "busi-ness") at
   normal mobile sizes, so it is removed. `break-word` still prevents a genuine
   overflow but, unlike `anywhere`, only breaks a word that cannot fit on a line
   of its own — the 200% fix is carried by the min-width:0 grid rules instead. */
.fh-site .guide-hero h1,
.fh-site .seo-hero h1,
.fh-site .topic-hero h1 {
  overflow-wrap: break-word;
  hyphens: manual;
}

@media (max-width: 480px) {
  .fh-site .guide-hero h1,
  .fh-site .seo-hero h1 {
    text-wrap: initial;
  }
}

/* --------------------------------------------------------------------------
   21. Remaining measured contrast corrections

   Each figure below was measured against the actual composited background,
   not estimated.
   -------------------------------------------------------------------------- */

/* .guide-visual-note floats directly on the dark hero with only a translucent
   border, so it is an on-dark surface. Its <small> was #087f5b on #063d2e
   (2.45:1) and its <strong> inherited ink (1.38:1). */
.fh-site .guide-visual-note {
  --heading: #ffffff;
  --text: rgba(255, 255, 255, 0.9);
  --text-dim: rgba(255, 255, 255, 0.78);
  --kicker: #8fe3c0;
  background: rgba(4, 25, 19, 0.9);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.fh-site .guide-visual-note small {
  color: #8fe3c0;          /* on rgba(4,31,24,.55) over #063d2e -> 8.9:1 */
}

.fh-site .guide-visual-note strong {
  color: #ffffff;          /* -> 14.6:1 */
}

/* The header call-to-action sets its own white-on-dark-green. Section 10 gave
   every nav link `color: var(--body-ink)`, which outranked it and produced
   #33443d on #052f24 (1.41:1). Restored with matching specificity. */
.fh-site .site-nav .nav-cta,
.fh-site .fh-nav .nav-cta,
.fh-site .site-nav .nav-cta .icon {
  color: #ffffff;          /* on #052f24 -> 15.9:1 */
}

/* The Android tile is informational, never an available option. It keeps its
   own ink on the light ground rather than inheriting a hero's light-on-dark. */
.fh-site .android-coming-soon,
.fh-site .android-coming-soon strong,
.fh-site .android-coming-soon small,
.fh-site .android-coming-soon span {
  color: var(--text);
}

/* On a dark ground the tile keeps its own readable surface so "Coming soon"
   is never ambiguous about availability. */
.fh-site .download-hero .android-coming-soon,
.fh-site .install-section .android-coming-soon,
.fh-site .growing-section .android-coming-soon {
  --text: rgba(255, 255, 255, 0.9);
  border-color: rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.08);
}

/* Amber on its own tint was 4.24:1; darkened to clear AA for normal text. */
:root {
  --amber: #9a5900;        /* on #fbf1e2 -> 5.06:1 */
}

/* Brand green on the soft green tint measured 4.48:1 — a hair under AA. Small
   uppercase labels and avatar initials take the darker green instead. */
.fh-site .seo-kicker,
.fh-site .member-avatar,
.fh-site .chain-board > header b,
.fh-site .proof-console > header b,
.fh-site .access-card > header b,
.fh-site .switch-card.is-app > header span {
  color: var(--green-700);  /* #06674a on #e9f5f0 -> 5.72:1 */
}

/* Decorative step numerals were #a2aea9 on white (2.29:1). They carry real
   sequence information, so they are text and must meet AA. */
.fh-site .download-proof-grid span,
.fh-site .proof-step,
.fh-site .step-number,
.fh-site .seo-day-card > b,
.fh-site .switch-card > header span {
  color: var(--muted);      /* #5c6d67 on #ffffff -> 6.0:1 */
}

/* --------------------------------------------------------------------------
   22. Final measured corrections
   -------------------------------------------------------------------------- */

/* --faint was #8a9a94: 2.95:1 on white and 2.73:1 on the tinted ground, so it
   failed AA everywhere it was used for real text. Darkened to the lightest
   value that clears AA on BOTH grounds.
     #5f706a on #ffffff  -> 5.25:1
     #5f706a on #f4f7f5  -> 4.78:1  */
:root {
  --faint: #5f706a;
}

.fh-site .outcome-number,
.fh-site .updated-date,
.fh-site .switch-footnote,
.fh-site .store-credits {
  color: var(--muted);
}

/* Labels sitting on top of a screenshot cannot rely on the image behind them,
   so they carry their own scrim. Measured on the scrim: 13.9:1. */
.fh-site .proof-screen-secondary > span,
.fh-site .proof-screen > span,
.fh-site .command-screen > span,
.fh-site [class*="proof-screen"] > span {
  color: #ffffff;
  background: rgba(4, 25, 19, 0.82);
  padding: 4px 10px;
  border-radius: 6px;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}

/* --------------------------------------------------------------------------
   23. Last measured corrections
   -------------------------------------------------------------------------- */

/* .chain-board is a light card sitting inside the dark .growing-section, so it
   restores the light token set (its <strong> was white-on-white). */
.fh-site .chain-board,
.fh-site .proof-console > footer {
  --heading: var(--ink);
  --text: var(--body-ink);
  --text-dim: var(--muted);
  --kicker: var(--green-700);
  color: var(--text);
}

/* Avatar initials were #087f5b on #e9f5f0 = 4.48:1, two hundredths short. */
.fh-site .chain-avatar,
.fh-site .member-avatar {
  color: var(--green-700);          /* -> 5.72:1 */
}

/* On the Contact page the header call-to-action IS the current page, so the
   aria-current rule painted it green-700 on dark green (2.12:1). The CTA keeps
   its own white; only plain nav links take the current-page colour. */
.fh-site .fh-nav a[aria-current="page"]:not(.nav-cta),
.fh-site .site-nav a[aria-current="page"]:not(.nav-cta) {
  color: var(--green-700);
}

.fh-site .fh-nav .nav-cta[aria-current="page"],
.fh-site .site-nav .nav-cta[aria-current="page"] {
  color: #ffffff;
}

/* Status pills reading "Today complete" used --green on --green-soft: 4.48:1,
   two hundredths under AA. The brand green is left untouched (it is correct on
   white and as a button ground); only this pairing takes the darker step. */
.fh-site .chain-row em,
.fh-site .chain-row > em,
.fh-site .proof-console em,
.fh-site .access-card em,
.fh-site .team-console em {
  color: var(--green-700);   /* #06674a on #e9f5f0 -> 5.72:1 */
}

/* --------------------------------------------------------------------------
   24. Cookie-consent banner at large text sizes

   Measured: at 200% root text the banner overflowed /pricing by 100px at 320px
   and 35px at 390px. Its grid is `auto minmax(0,1fr) auto`, so the icon and the
   button group hold their intrinsic width and push the page wider.

   Behaviour is untouched — this is layout only. The banner still appears,
   still carries both choices, and still never covers a page control because it
   stacks rather than widening.
   -------------------------------------------------------------------------- */

.fh-site .analytics-consent {
  max-width: calc(100vw - 24px);
  overflow-wrap: break-word;
}

.fh-site .analytics-consent > * {
  min-width: 0;
}

@media (max-width: 760px), (max-height: 560px) {
  .fh-site .analytics-consent {
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
    right: 12px;
    left: 12px;
    bottom: 12px;
    width: auto;
    max-height: 82vh;
    overflow-y: auto;
  }

  .fh-site .analytics-consent-mark {
    display: none;      /* decorative only; the text carries the message */
  }

  .fh-site .analytics-consent-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
  }

  .fh-site .analytics-consent-actions > * {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 44px;
    justify-content: center;
  }
}

/* --------------------------------------------------------------------------
   25. App-update notice at large text sizes

   Measured at 320px with 200% root text on /pricing: the notice rendered 392px
   wide inside a 320px viewport (its action alone was 364px), giving the page a
   100px horizontal scroll. It is position:fixed, so nothing else constrained it.

   Behaviour untouched: site.js still shows, dismisses and remembers it. This
   only stops it setting the page width, and keeps it from covering controls by
   letting it stack and scroll within its own box.
   -------------------------------------------------------------------------- */

.fh-site .app-update-notice {
  max-width: calc(100vw - 20px);
  box-sizing: border-box;
  overflow-wrap: break-word;
}

.fh-site .app-update-notice > * {
  min-width: 0;
  max-width: 100%;
}

@media (max-width: 760px), (max-height: 600px) {
  .fh-site .app-update-notice {
    left: 10px;
    right: 10px;
    width: auto;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px;
    max-height: 76vh;
    overflow-y: auto;
  }

  .fh-site a.app-update-action {
    grid-column: 1 / -1;
    width: 100%;
    justify-content: center;
  }
}
